Commit b601b587 authored by Mark Brown's avatar Mark Brown

Merge branch 'topic/adsp' of...

Merge branch 'topic/adsp' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound into asoc-arizona
parents c38a1ffb 10337b07
...@@ -121,6 +121,11 @@ ...@@ -121,6 +121,11 @@
#define ADSP2_WDMA_CONFIG_2 0x31 #define ADSP2_WDMA_CONFIG_2 0x31
#define ADSP2_RDMA_CONFIG_1 0x34 #define ADSP2_RDMA_CONFIG_1 0x34
#define ADSP2_SCRATCH0 0x40
#define ADSP2_SCRATCH1 0x41
#define ADSP2_SCRATCH2 0x42
#define ADSP2_SCRATCH3 0x43
/* /*
* ADSP2 Control * ADSP2 Control
*/ */
...@@ -229,16 +234,18 @@ struct wm_coeff_ctl_ops { ...@@ -229,16 +234,18 @@ struct wm_coeff_ctl_ops {
struct wm_coeff_ctl { struct wm_coeff_ctl {
const char *name; const char *name;
struct wm_adsp_alg_region region; const char *fw_name;
struct wm_adsp_alg_region alg_region;
struct wm_coeff_ctl_ops ops; struct wm_coeff_ctl_ops ops;
struct wm_adsp *adsp; struct wm_adsp *dsp;
void *private;
unsigned int enabled:1; unsigned int enabled:1;
struct list_head list; struct list_head list;
void *cache; void *cache;
unsigned int offset;
size_t len; size_t len;
unsigned int set:1; unsigned int set:1;
struct snd_kcontrol *kcontrol; struct snd_kcontrol *kcontrol;
unsigned int flags;
}; };
static int wm_adsp_fw_get(struct snd_kcontrol *kcontrol, static int wm_adsp_fw_get(struct snd_kcontrol *kcontrol,
...@@ -246,9 +253,9 @@ static int wm_adsp_fw_get(struct snd_kcontrol *kcontrol, ...@@ -246,9 +253,9 @@ static int wm_adsp_fw_get(struct snd_kcontrol *kcontrol,
{ {
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
struct wm_adsp *adsp = snd_soc_codec_get_drvdata(codec); struct wm_adsp *dsp = snd_soc_codec_get_drvdata(codec);
ucontrol->value.integer.value[0] = adsp[e->shift_l].fw; ucontrol->value.integer.value[0] = dsp[e->shift_l].fw;
return 0; return 0;
} }
...@@ -258,18 +265,18 @@ static int wm_adsp_fw_put(struct snd_kcontrol *kcontrol, ...@@ -258,18 +265,18 @@ static int wm_adsp_fw_put(struct snd_kcontrol *kcontrol,
{ {
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
struct wm_adsp *adsp = snd_soc_codec_get_drvdata(codec); struct wm_adsp *dsp = snd_soc_codec_get_drvdata(codec);
if (ucontrol->value.integer.value[0] == adsp[e->shift_l].fw) if (ucontrol->value.integer.value[0] == dsp[e->shift_l].fw)
return 0; return 0;
if (ucontrol->value.integer.value[0] >= WM_ADSP_NUM_FW) if (ucontrol->value.integer.value[0] >= WM_ADSP_NUM_FW)
return -EINVAL; return -EINVAL;
if (adsp[e->shift_l].running) if (dsp[e->shift_l].running)
return -EBUSY; return -EBUSY;
adsp[e->shift_l].fw = ucontrol->value.integer.value[0]; dsp[e->shift_l].fw = ucontrol->value.integer.value[0];
return 0; return 0;
} }
...@@ -340,28 +347,47 @@ static struct wm_adsp_region const *wm_adsp_find_region(struct wm_adsp *dsp, ...@@ -340,28 +347,47 @@ static struct wm_adsp_region const *wm_adsp_find_region(struct wm_adsp *dsp,
return NULL; return NULL;
} }
static unsigned int wm_adsp_region_to_reg(struct wm_adsp_region const *region, static unsigned int wm_adsp_region_to_reg(struct wm_adsp_region const *mem,
unsigned int offset) unsigned int offset)
{ {
if (WARN_ON(!region)) if (WARN_ON(!mem))
return offset; return offset;
switch (region->type) { switch (mem->type) {
case WMFW_ADSP1_PM: case WMFW_ADSP1_PM:
return region->base + (offset * 3); return mem->base + (offset * 3);
case WMFW_ADSP1_DM: case WMFW_ADSP1_DM:
return region->base + (offset * 2); return mem->base + (offset * 2);
case WMFW_ADSP2_XM: case WMFW_ADSP2_XM:
return region->base + (offset * 2); return mem->base + (offset * 2);
case WMFW_ADSP2_YM: case WMFW_ADSP2_YM:
return region->base + (offset * 2); return mem->base + (offset * 2);
case WMFW_ADSP1_ZM: case WMFW_ADSP1_ZM:
return region->base + (offset * 2); return mem->base + (offset * 2);
default: default:
WARN(1, "Unknown memory region type"); WARN(1, "Unknown memory region type");
return offset; return offset;
} }
} }
static void wm_adsp2_show_fw_status(struct wm_adsp *dsp)
{
u16 scratch[4];
int ret;
ret = regmap_raw_read(dsp->regmap, dsp->base + ADSP2_SCRATCH0,
scratch, sizeof(scratch));
if (ret) {
adsp_err(dsp, "Failed to read SCRATCH regs: %d\n", ret);
return;
}
adsp_dbg(dsp, "FW SCRATCH 0:0x%x 1:0x%x 2:0x%x 3:0x%x\n",
be16_to_cpu(scratch[0]),
be16_to_cpu(scratch[1]),
be16_to_cpu(scratch[2]),
be16_to_cpu(scratch[3]));
}
static int wm_coeff_info(struct snd_kcontrol *kcontrol, static int wm_coeff_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo) struct snd_ctl_elem_info *uinfo)
{ {
...@@ -372,40 +398,39 @@ static int wm_coeff_info(struct snd_kcontrol *kcontrol, ...@@ -372,40 +398,39 @@ static int wm_coeff_info(struct snd_kcontrol *kcontrol,
return 0; return 0;
} }
static int wm_coeff_write_control(struct snd_kcontrol *kcontrol, static int wm_coeff_write_control(struct wm_coeff_ctl *ctl,
const void *buf, size_t len) const void *buf, size_t len)
{ {
struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value; struct wm_adsp_alg_region *alg_region = &ctl->alg_region;
struct wm_adsp_alg_region *region = &ctl->region;
const struct wm_adsp_region *mem; const struct wm_adsp_region *mem;
struct wm_adsp *adsp = ctl->adsp; struct wm_adsp *dsp = ctl->dsp;
void *scratch; void *scratch;
int ret; int ret;
unsigned int reg; unsigned int reg;
mem = wm_adsp_find_region(adsp, region->type); mem = wm_adsp_find_region(dsp, alg_region->type);
if (!mem) { if (!mem) {
adsp_err(adsp, "No base for region %x\n", adsp_err(dsp, "No base for region %x\n",
region->type); alg_region->type);
return -EINVAL; return -EINVAL;
} }
reg = ctl->region.base; reg = ctl->alg_region.base + ctl->offset;
reg = wm_adsp_region_to_reg(mem, reg); reg = wm_adsp_region_to_reg(mem, reg);
scratch = kmemdup(buf, ctl->len, GFP_KERNEL | GFP_DMA); scratch = kmemdup(buf, ctl->len, GFP_KERNEL | GFP_DMA);
if (!scratch) if (!scratch)
return -ENOMEM; return -ENOMEM;
ret = regmap_raw_write(adsp->regmap, reg, scratch, ret = regmap_raw_write(dsp->regmap, reg, scratch,
ctl->len); ctl->len);
if (ret) { if (ret) {
adsp_err(adsp, "Failed to write %zu bytes to %x: %d\n", adsp_err(dsp, "Failed to write %zu bytes to %x: %d\n",
ctl->len, reg, ret); ctl->len, reg, ret);
kfree(scratch); kfree(scratch);
return ret; return ret;
} }
adsp_dbg(adsp, "Wrote %zu bytes to %x\n", ctl->len, reg); adsp_dbg(dsp, "Wrote %zu bytes to %x\n", ctl->len, reg);
kfree(scratch); kfree(scratch);
...@@ -424,42 +449,41 @@ static int wm_coeff_put(struct snd_kcontrol *kcontrol, ...@@ -424,42 +449,41 @@ static int wm_coeff_put(struct snd_kcontrol *kcontrol,
if (!ctl->enabled) if (!ctl->enabled)
return 0; return 0;
return wm_coeff_write_control(kcontrol, p, ctl->len); return wm_coeff_write_control(ctl, p, ctl->len);
} }
static int wm_coeff_read_control(struct snd_kcontrol *kcontrol, static int wm_coeff_read_control(struct wm_coeff_ctl *ctl,
void *buf, size_t len) void *buf, size_t len)
{ {
struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value; struct wm_adsp_alg_region *alg_region = &ctl->alg_region;
struct wm_adsp_alg_region *region = &ctl->region;
const struct wm_adsp_region *mem; const struct wm_adsp_region *mem;
struct wm_adsp *adsp = ctl->adsp; struct wm_adsp *dsp = ctl->dsp;
void *scratch; void *scratch;
int ret; int ret;
unsigned int reg; unsigned int reg;
mem = wm_adsp_find_region(adsp, region->type); mem = wm_adsp_find_region(dsp, alg_region->type);
if (!mem) { if (!mem) {
adsp_err(adsp, "No base for region %x\n", adsp_err(dsp, "No base for region %x\n",
region->type); alg_region->type);
return -EINVAL; return -EINVAL;
} }
reg = ctl->region.base; reg = ctl->alg_region.base + ctl->offset;
reg = wm_adsp_region_to_reg(mem, reg); reg = wm_adsp_region_to_reg(mem, reg);
scratch = kmalloc(ctl->len, GFP_KERNEL | GFP_DMA); scratch = kmalloc(ctl->len, GFP_KERNEL | GFP_DMA);
if (!scratch) if (!scratch)
return -ENOMEM; return -ENOMEM;
ret = regmap_raw_read(adsp->regmap, reg, scratch, ctl->len); ret = regmap_raw_read(dsp->regmap, reg, scratch, ctl->len);
if (ret) { if (ret) {
adsp_err(adsp, "Failed to read %zu bytes from %x: %d\n", adsp_err(dsp, "Failed to read %zu bytes from %x: %d\n",
ctl->len, reg, ret); ctl->len, reg, ret);
kfree(scratch); kfree(scratch);
return ret; return ret;
} }
adsp_dbg(adsp, "Read %zu bytes from %x\n", ctl->len, reg); adsp_dbg(dsp, "Read %zu bytes from %x\n", ctl->len, reg);
memcpy(buf, scratch, ctl->len); memcpy(buf, scratch, ctl->len);
kfree(scratch); kfree(scratch);
...@@ -473,17 +497,25 @@ static int wm_coeff_get(struct snd_kcontrol *kcontrol, ...@@ -473,17 +497,25 @@ static int wm_coeff_get(struct snd_kcontrol *kcontrol,
struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value; struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value;
char *p = ucontrol->value.bytes.data; char *p = ucontrol->value.bytes.data;
if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) {
if (ctl->enabled)
return wm_coeff_read_control(ctl, p, ctl->len);
else
return -EPERM;
}
memcpy(p, ctl->cache, ctl->len); memcpy(p, ctl->cache, ctl->len);
return 0; return 0;
} }
struct wmfw_ctl_work { struct wmfw_ctl_work {
struct wm_adsp *adsp; struct wm_adsp *dsp;
struct wm_coeff_ctl *ctl; struct wm_coeff_ctl *ctl;
struct work_struct work; struct work_struct work;
}; };
static int wmfw_add_ctl(struct wm_adsp *adsp, struct wm_coeff_ctl *ctl) static int wmfw_add_ctl(struct wm_adsp *dsp, struct wm_coeff_ctl *ctl)
{ {
struct snd_kcontrol_new *kcontrol; struct snd_kcontrol_new *kcontrol;
int ret; int ret;
...@@ -502,17 +534,25 @@ static int wmfw_add_ctl(struct wm_adsp *adsp, struct wm_coeff_ctl *ctl) ...@@ -502,17 +534,25 @@ static int wmfw_add_ctl(struct wm_adsp *adsp, struct wm_coeff_ctl *ctl)
kcontrol->put = wm_coeff_put; kcontrol->put = wm_coeff_put;
kcontrol->private_value = (unsigned long)ctl; kcontrol->private_value = (unsigned long)ctl;
ret = snd_soc_add_card_controls(adsp->card, if (ctl->flags) {
if (ctl->flags & WMFW_CTL_FLAG_WRITEABLE)
kcontrol->access |= SNDRV_CTL_ELEM_ACCESS_WRITE;
if (ctl->flags & WMFW_CTL_FLAG_READABLE)
kcontrol->access |= SNDRV_CTL_ELEM_ACCESS_READ;
if (ctl->flags & WMFW_CTL_FLAG_VOLATILE)
kcontrol->access |= SNDRV_CTL_ELEM_ACCESS_VOLATILE;
}
ret = snd_soc_add_card_controls(dsp->card,
kcontrol, 1); kcontrol, 1);
if (ret < 0) if (ret < 0)
goto err_kcontrol; goto err_kcontrol;
kfree(kcontrol); kfree(kcontrol);
ctl->kcontrol = snd_soc_card_get_kcontrol(adsp->card, ctl->kcontrol = snd_soc_card_get_kcontrol(dsp->card,
ctl->name); ctl->name);
list_add(&ctl->list, &adsp->ctl_list);
return 0; return 0;
err_kcontrol: err_kcontrol:
...@@ -520,6 +560,358 @@ static int wmfw_add_ctl(struct wm_adsp *adsp, struct wm_coeff_ctl *ctl) ...@@ -520,6 +560,358 @@ static int wmfw_add_ctl(struct wm_adsp *adsp, struct wm_coeff_ctl *ctl)
return ret; return ret;
} }
static int wm_coeff_init_control_caches(struct wm_adsp *dsp)
{
struct wm_coeff_ctl *ctl;
int ret;
list_for_each_entry(ctl, &dsp->ctl_list, list) {
if (!ctl->enabled || ctl->set)
continue;
if (ctl->flags & WMFW_CTL_FLAG_VOLATILE)
continue;
ret = wm_coeff_read_control(ctl,
ctl->cache,
ctl->len);
if (ret < 0)
return ret;
}
return 0;
}
static int wm_coeff_sync_controls(struct wm_adsp *dsp)
{
struct wm_coeff_ctl *ctl;
int ret;
list_for_each_entry(ctl, &dsp->ctl_list, list) {
if (!ctl->enabled)
continue;
if (ctl->set && !(ctl->flags & WMFW_CTL_FLAG_VOLATILE)) {
ret = wm_coeff_write_control(ctl,
ctl->cache,
ctl->len);
if (ret < 0)
return ret;
}
}
return 0;
}
static void wm_adsp_ctl_work(struct work_struct *work)
{
struct wmfw_ctl_work *ctl_work = container_of(work,
struct wmfw_ctl_work,
work);
wmfw_add_ctl(ctl_work->dsp, ctl_work->ctl);
kfree(ctl_work);
}
static int wm_adsp_create_control(struct wm_adsp *dsp,
const struct wm_adsp_alg_region *alg_region,
unsigned int offset, unsigned int len,
const char *subname, unsigned int subname_len,
unsigned int flags)
{
struct wm_coeff_ctl *ctl;
struct wmfw_ctl_work *ctl_work;
char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
char *region_name;
int ret;
if (flags & WMFW_CTL_FLAG_SYS)
return 0;
switch (alg_region->type) {
case WMFW_ADSP1_PM:
region_name = "PM";
break;
case WMFW_ADSP1_DM:
region_name = "DM";
break;
case WMFW_ADSP2_XM:
region_name = "XM";
break;
case WMFW_ADSP2_YM:
region_name = "YM";
break;
case WMFW_ADSP1_ZM:
region_name = "ZM";
break;
default:
adsp_err(dsp, "Unknown region type: %d\n", alg_region->type);
return -EINVAL;
}
switch (dsp->fw_ver) {
case 0:
case 1:
snprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "DSP%d %s %x",
dsp->num, region_name, alg_region->alg);
break;
default:
ret = snprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN,
"DSP%d%c %.12s %x", dsp->num, *region_name,
wm_adsp_fw_text[dsp->fw], alg_region->alg);
/* Truncate the subname from the start if it is too long */
if (subname) {
int avail = SNDRV_CTL_ELEM_ID_NAME_MAXLEN - ret - 2;
int skip = 0;
if (subname_len > avail)
skip = subname_len - avail;
snprintf(name + ret,
SNDRV_CTL_ELEM_ID_NAME_MAXLEN - ret, " %.*s",
subname_len - skip, subname + skip);
}
break;
}
list_for_each_entry(ctl, &dsp->ctl_list,
list) {
if (!strcmp(ctl->name, name)) {
if (!ctl->enabled)
ctl->enabled = 1;
return 0;
}
}
ctl = kzalloc(sizeof(*ctl), GFP_KERNEL);
if (!ctl)
return -ENOMEM;
ctl->fw_name = wm_adsp_fw_text[dsp->fw];
ctl->alg_region = *alg_region;
ctl->name = kmemdup(name, strlen(name) + 1, GFP_KERNEL);
if (!ctl->name) {
ret = -ENOMEM;
goto err_ctl;
}
ctl->enabled = 1;
ctl->set = 0;
ctl->ops.xget = wm_coeff_get;
ctl->ops.xput = wm_coeff_put;
ctl->dsp = dsp;
ctl->flags = flags;
ctl->offset = offset;
if (len > 512) {
adsp_warn(dsp, "Truncating control %s from %d\n",
ctl->name, len);
len = 512;
}
ctl->len = len;
ctl->cache = kzalloc(ctl->len, GFP_KERNEL);
if (!ctl->cache) {
ret = -ENOMEM;
goto err_ctl_name;
}
list_add(&ctl->list, &dsp->ctl_list);
ctl_work = kzalloc(sizeof(*ctl_work), GFP_KERNEL);
if (!ctl_work) {
ret = -ENOMEM;
goto err_ctl_cache;
}
ctl_work->dsp = dsp;
ctl_work->ctl = ctl;
INIT_WORK(&ctl_work->work, wm_adsp_ctl_work);
schedule_work(&ctl_work->work);
return 0;
err_ctl_cache:
kfree(ctl->cache);
err_ctl_name:
kfree(ctl->name);
err_ctl:
kfree(ctl);
return ret;
}
struct wm_coeff_parsed_alg {
int id;
const u8 *name;
int name_len;
int ncoeff;
};
struct wm_coeff_parsed_coeff {
int offset;
int mem_type;
const u8 *name;
int name_len;
int ctl_type;
int flags;
int len;
};
static int wm_coeff_parse_string(int bytes, const u8 **pos, const u8 **str)
{
int length;
switch (bytes) {
case 1:
length = **pos;
break;
case 2:
length = le16_to_cpu(*((__le16 *)*pos));
break;
default:
return 0;
}
if (str)
*str = *pos + bytes;
*pos += ((length + bytes) + 3) & ~0x03;
return length;
}
static int wm_coeff_parse_int(int bytes, const u8 **pos)
{
int val = 0;
switch (bytes) {
case 2:
val = le16_to_cpu(*((__le16 *)*pos));
break;
case 4:
val = le32_to_cpu(*((__le32 *)*pos));
break;
default:
break;
}
*pos += bytes;
return val;
}
static inline void wm_coeff_parse_alg(struct wm_adsp *dsp, const u8 **data,
struct wm_coeff_parsed_alg *blk)
{
const struct wmfw_adsp_alg_data *raw;
switch (dsp->fw_ver) {
case 0:
case 1:
raw = (const struct wmfw_adsp_alg_data *)*data;
*data = raw->data;
blk->id = le32_to_cpu(raw->id);
blk->name = raw->name;
blk->name_len = strlen(raw->name);
blk->ncoeff = le32_to_cpu(raw->ncoeff);
break;
default:
blk->id = wm_coeff_parse_int(sizeof(raw->id), data);
blk->name_len = wm_coeff_parse_string(sizeof(u8), data,
&blk->name);
wm_coeff_parse_string(sizeof(u16), data, NULL);
blk->ncoeff = wm_coeff_parse_int(sizeof(raw->ncoeff), data);
break;
}
adsp_dbg(dsp, "Algorithm ID: %#x\n", blk->id);
adsp_dbg(dsp, "Algorithm name: %.*s\n", blk->name_len, blk->name);
adsp_dbg(dsp, "# of coefficient descriptors: %#x\n", blk->ncoeff);
}
static inline void wm_coeff_parse_coeff(struct wm_adsp *dsp, const u8 **data,
struct wm_coeff_parsed_coeff *blk)
{
const struct wmfw_adsp_coeff_data *raw;
const u8 *tmp;
int length;
switch (dsp->fw_ver) {
case 0:
case 1:
raw = (const struct wmfw_adsp_coeff_data *)*data;
*data = *data + sizeof(raw->hdr) + le32_to_cpu(raw->hdr.size);
blk->offset = le16_to_cpu(raw->hdr.offset);
blk->mem_type = le16_to_cpu(raw->hdr.type);
blk->name = raw->name;
blk->name_len = strlen(raw->name);
blk->ctl_type = le16_to_cpu(raw->ctl_type);
blk->flags = le16_to_cpu(raw->flags);
blk->len = le32_to_cpu(raw->len);
break;
default:
tmp = *data;
blk->offset = wm_coeff_parse_int(sizeof(raw->hdr.offset), &tmp);
blk->mem_type = wm_coeff_parse_int(sizeof(raw->hdr.type), &tmp);
length = wm_coeff_parse_int(sizeof(raw->hdr.size), &tmp);
blk->name_len = wm_coeff_parse_string(sizeof(u8), &tmp,
&blk->name);
wm_coeff_parse_string(sizeof(u8), &tmp, NULL);
wm_coeff_parse_string(sizeof(u16), &tmp, NULL);
blk->ctl_type = wm_coeff_parse_int(sizeof(raw->ctl_type), &tmp);
blk->flags = wm_coeff_parse_int(sizeof(raw->flags), &tmp);
blk->len = wm_coeff_parse_int(sizeof(raw->len), &tmp);
*data = *data + sizeof(raw->hdr) + length;
break;
}
adsp_dbg(dsp, "\tCoefficient type: %#x\n", blk->mem_type);
adsp_dbg(dsp, "\tCoefficient offset: %#x\n", blk->offset);
adsp_dbg(dsp, "\tCoefficient name: %.*s\n", blk->name_len, blk->name);
adsp_dbg(dsp, "\tCoefficient flags: %#x\n", blk->flags);
adsp_dbg(dsp, "\tALSA control type: %#x\n", blk->ctl_type);
adsp_dbg(dsp, "\tALSA control len: %#x\n", blk->len);
}
static int wm_adsp_parse_coeff(struct wm_adsp *dsp,
const struct wmfw_region *region)
{
struct wm_adsp_alg_region alg_region = {};
struct wm_coeff_parsed_alg alg_blk;
struct wm_coeff_parsed_coeff coeff_blk;
const u8 *data = region->data;
int i, ret;
wm_coeff_parse_alg(dsp, &data, &alg_blk);
for (i = 0; i < alg_blk.ncoeff; i++) {
wm_coeff_parse_coeff(dsp, &data, &coeff_blk);
switch (coeff_blk.ctl_type) {
case SNDRV_CTL_ELEM_TYPE_BYTES:
break;
default:
adsp_err(dsp, "Unknown control type: %d\n",
coeff_blk.ctl_type);
return -EINVAL;
}
alg_region.type = coeff_blk.mem_type;
alg_region.alg = alg_blk.id;
ret = wm_adsp_create_control(dsp, &alg_region,
coeff_blk.offset,
coeff_blk.len,
coeff_blk.name,
coeff_blk.name_len,
coeff_blk.flags);
if (ret < 0)
adsp_err(dsp, "Failed to create control: %.*s, %d\n",
coeff_blk.name_len, coeff_blk.name, ret);
}
return 0;
}
static int wm_adsp_load(struct wm_adsp *dsp) static int wm_adsp_load(struct wm_adsp *dsp)
{ {
LIST_HEAD(buf_list); LIST_HEAD(buf_list);
...@@ -568,12 +960,22 @@ static int wm_adsp_load(struct wm_adsp *dsp) ...@@ -568,12 +960,22 @@ static int wm_adsp_load(struct wm_adsp *dsp)
goto out_fw; goto out_fw;
} }
if (header->ver != 0) { switch (header->ver) {
case 0:
adsp_warn(dsp, "%s: Depreciated file format %d\n",
file, header->ver);
break;
case 1:
case 2:
break;
default:
adsp_err(dsp, "%s: unknown file format %d\n", adsp_err(dsp, "%s: unknown file format %d\n",
file, header->ver); file, header->ver);
goto out_fw; goto out_fw;
} }
adsp_info(dsp, "Firmware version: %d\n", header->ver); adsp_info(dsp, "Firmware version: %d\n", header->ver);
dsp->fw_ver = header->ver;
if (header->core != dsp->type) { if (header->core != dsp->type) {
adsp_err(dsp, "%s: invalid core %d != %d\n", adsp_err(dsp, "%s: invalid core %d != %d\n",
...@@ -638,6 +1040,12 @@ static int wm_adsp_load(struct wm_adsp *dsp) ...@@ -638,6 +1040,12 @@ static int wm_adsp_load(struct wm_adsp *dsp)
text = kzalloc(le32_to_cpu(region->len) + 1, text = kzalloc(le32_to_cpu(region->len) + 1,
GFP_KERNEL); GFP_KERNEL);
break; break;
case WMFW_ALGORITHM_DATA:
region_name = "Algorithm";
ret = wm_adsp_parse_coeff(dsp, region);
if (ret != 0)
goto out_fw;
break;
case WMFW_INFO_TEXT: case WMFW_INFO_TEXT:
region_name = "Information"; region_name = "Information";
text = kzalloc(le32_to_cpu(region->len) + 1, text = kzalloc(le32_to_cpu(region->len) + 1,
...@@ -730,444 +1138,316 @@ static int wm_adsp_load(struct wm_adsp *dsp) ...@@ -730,444 +1138,316 @@ static int wm_adsp_load(struct wm_adsp *dsp)
return ret; return ret;
} }
static int wm_coeff_init_control_caches(struct wm_adsp *adsp) static void wm_adsp_ctl_fixup_base(struct wm_adsp *dsp,
const struct wm_adsp_alg_region *alg_region)
{ {
struct wm_coeff_ctl *ctl; struct wm_coeff_ctl *ctl;
int ret;
list_for_each_entry(ctl, &adsp->ctl_list, list) { list_for_each_entry(ctl, &dsp->ctl_list, list) {
if (!ctl->enabled || ctl->set) if (ctl->fw_name == wm_adsp_fw_text[dsp->fw] &&
continue; alg_region->alg == ctl->alg_region.alg &&
ret = wm_coeff_read_control(ctl->kcontrol, alg_region->type == ctl->alg_region.type) {
ctl->cache, ctl->alg_region.base = alg_region->base;
ctl->len); }
if (ret < 0)
return ret;
} }
return 0;
} }
static int wm_coeff_sync_controls(struct wm_adsp *adsp) static void *wm_adsp_read_algs(struct wm_adsp *dsp, size_t n_algs,
unsigned int pos, unsigned int len)
{ {
struct wm_coeff_ctl *ctl; void *alg;
int ret; int ret;
__be32 val;
list_for_each_entry(ctl, &adsp->ctl_list, list) { if (n_algs == 0) {
if (!ctl->enabled) adsp_err(dsp, "No algorithms\n");
continue; return ERR_PTR(-EINVAL);
if (ctl->set) {
ret = wm_coeff_write_control(ctl->kcontrol,
ctl->cache,
ctl->len);
if (ret < 0)
return ret;
}
} }
return 0; if (n_algs > 1024) {
} adsp_err(dsp, "Algorithm count %zx excessive\n", n_algs);
return ERR_PTR(-EINVAL);
static void wm_adsp_ctl_work(struct work_struct *work) }
{
struct wmfw_ctl_work *ctl_work = container_of(work,
struct wmfw_ctl_work,
work);
wmfw_add_ctl(ctl_work->adsp, ctl_work->ctl);
kfree(ctl_work);
}
static int wm_adsp_create_control(struct wm_adsp *dsp,
const struct wm_adsp_alg_region *region)
{
struct wm_coeff_ctl *ctl;
struct wmfw_ctl_work *ctl_work;
char *name;
char *region_name;
int ret;
name = kmalloc(PAGE_SIZE, GFP_KERNEL);
if (!name)
return -ENOMEM;
switch (region->type) { /* Read the terminator first to validate the length */
case WMFW_ADSP1_PM: ret = regmap_raw_read(dsp->regmap, pos + len, &val, sizeof(val));
region_name = "PM"; if (ret != 0) {
break; adsp_err(dsp, "Failed to read algorithm list end: %d\n",
case WMFW_ADSP1_DM: ret);
region_name = "DM"; return ERR_PTR(ret);
break;
case WMFW_ADSP2_XM:
region_name = "XM";
break;
case WMFW_ADSP2_YM:
region_name = "YM";
break;
case WMFW_ADSP1_ZM:
region_name = "ZM";
break;
default:
ret = -EINVAL;
goto err_name;
} }
snprintf(name, PAGE_SIZE, "DSP%d %s %x", if (be32_to_cpu(val) != 0xbedead)
dsp->num, region_name, region->alg); adsp_warn(dsp, "Algorithm list end %x 0x%x != 0xbeadead\n",
pos + len, be32_to_cpu(val));
list_for_each_entry(ctl, &dsp->ctl_list, alg = kzalloc(len * 2, GFP_KERNEL | GFP_DMA);
list) { if (!alg)
if (!strcmp(ctl->name, name)) { return ERR_PTR(-ENOMEM);
if (!ctl->enabled)
ctl->enabled = 1;
goto found;
}
}
ctl = kzalloc(sizeof(*ctl), GFP_KERNEL); ret = regmap_raw_read(dsp->regmap, pos, alg, len * 2);
if (!ctl) { if (ret != 0) {
ret = -ENOMEM; adsp_err(dsp, "Failed to read algorithm list: %d\n",
goto err_name; ret);
} kfree(alg);
ctl->region = *region; return ERR_PTR(ret);
ctl->name = kmemdup(name, strlen(name) + 1, GFP_KERNEL);
if (!ctl->name) {
ret = -ENOMEM;
goto err_ctl;
} }
ctl->enabled = 1;
ctl->set = 0;
ctl->ops.xget = wm_coeff_get;
ctl->ops.xput = wm_coeff_put;
ctl->adsp = dsp;
ctl->len = region->len; return alg;
ctl->cache = kzalloc(ctl->len, GFP_KERNEL); }
if (!ctl->cache) {
ret = -ENOMEM;
goto err_ctl_name;
}
ctl_work = kzalloc(sizeof(*ctl_work), GFP_KERNEL); static struct wm_adsp_alg_region *wm_adsp_create_region(struct wm_adsp *dsp,
if (!ctl_work) { int type, __be32 id,
ret = -ENOMEM; __be32 base)
goto err_ctl_cache; {
} struct wm_adsp_alg_region *alg_region;
ctl_work->adsp = dsp; alg_region = kzalloc(sizeof(*alg_region), GFP_KERNEL);
ctl_work->ctl = ctl; if (!alg_region)
INIT_WORK(&ctl_work->work, wm_adsp_ctl_work); return ERR_PTR(-ENOMEM);
schedule_work(&ctl_work->work);
found: alg_region->type = type;
kfree(name); alg_region->alg = be32_to_cpu(id);
alg_region->base = be32_to_cpu(base);
return 0; list_add_tail(&alg_region->list, &dsp->alg_regions);
err_ctl_cache: if (dsp->fw_ver > 0)
kfree(ctl->cache); wm_adsp_ctl_fixup_base(dsp, alg_region);
err_ctl_name:
kfree(ctl->name); return alg_region;
err_ctl:
kfree(ctl);
err_name:
kfree(name);
return ret;
} }
static int wm_adsp_setup_algs(struct wm_adsp *dsp) static int wm_adsp1_setup_algs(struct wm_adsp *dsp)
{ {
struct regmap *regmap = dsp->regmap;
struct wmfw_adsp1_id_hdr adsp1_id; struct wmfw_adsp1_id_hdr adsp1_id;
struct wmfw_adsp2_id_hdr adsp2_id;
struct wmfw_adsp1_alg_hdr *adsp1_alg; struct wmfw_adsp1_alg_hdr *adsp1_alg;
struct wmfw_adsp2_alg_hdr *adsp2_alg; struct wm_adsp_alg_region *alg_region;
void *alg, *buf;
struct wm_adsp_alg_region *region;
const struct wm_adsp_region *mem; const struct wm_adsp_region *mem;
unsigned int pos, term; unsigned int pos, len;
size_t algs, buf_size; size_t n_algs;
__be32 val;
int i, ret; int i, ret;
switch (dsp->type) { mem = wm_adsp_find_region(dsp, WMFW_ADSP1_DM);
case WMFW_ADSP1:
mem = wm_adsp_find_region(dsp, WMFW_ADSP1_DM);
break;
case WMFW_ADSP2:
mem = wm_adsp_find_region(dsp, WMFW_ADSP2_XM);
break;
default:
mem = NULL;
break;
}
if (WARN_ON(!mem)) if (WARN_ON(!mem))
return -EINVAL; return -EINVAL;
switch (dsp->type) { ret = regmap_raw_read(dsp->regmap, mem->base, &adsp1_id,
case WMFW_ADSP1: sizeof(adsp1_id));
ret = regmap_raw_read(regmap, mem->base, &adsp1_id,
sizeof(adsp1_id));
if (ret != 0) {
adsp_err(dsp, "Failed to read algorithm info: %d\n",
ret);
return ret;
}
buf = &adsp1_id;
buf_size = sizeof(adsp1_id);
algs = be32_to_cpu(adsp1_id.algs);
dsp->fw_id = be32_to_cpu(adsp1_id.fw.id);
adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n",
dsp->fw_id,
(be32_to_cpu(adsp1_id.fw.ver) & 0xff0000) >> 16,
(be32_to_cpu(adsp1_id.fw.ver) & 0xff00) >> 8,
be32_to_cpu(adsp1_id.fw.ver) & 0xff,
algs);
region = kzalloc(sizeof(*region), GFP_KERNEL);
if (!region)
return -ENOMEM;
region->type = WMFW_ADSP1_ZM;
region->alg = be32_to_cpu(adsp1_id.fw.id);
region->base = be32_to_cpu(adsp1_id.zm);
list_add_tail(&region->list, &dsp->alg_regions);
region = kzalloc(sizeof(*region), GFP_KERNEL);
if (!region)
return -ENOMEM;
region->type = WMFW_ADSP1_DM;
region->alg = be32_to_cpu(adsp1_id.fw.id);
region->base = be32_to_cpu(adsp1_id.dm);
list_add_tail(&region->list, &dsp->alg_regions);
pos = sizeof(adsp1_id) / 2;
term = pos + ((sizeof(*adsp1_alg) * algs) / 2);
break;
case WMFW_ADSP2:
ret = regmap_raw_read(regmap, mem->base, &adsp2_id,
sizeof(adsp2_id));
if (ret != 0) {
adsp_err(dsp, "Failed to read algorithm info: %d\n",
ret);
return ret;
}
buf = &adsp2_id;
buf_size = sizeof(adsp2_id);
algs = be32_to_cpu(adsp2_id.algs);
dsp->fw_id = be32_to_cpu(adsp2_id.fw.id);
adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n",
dsp->fw_id,
(be32_to_cpu(adsp2_id.fw.ver) & 0xff0000) >> 16,
(be32_to_cpu(adsp2_id.fw.ver) & 0xff00) >> 8,
be32_to_cpu(adsp2_id.fw.ver) & 0xff,
algs);
region = kzalloc(sizeof(*region), GFP_KERNEL);
if (!region)
return -ENOMEM;
region->type = WMFW_ADSP2_XM;
region->alg = be32_to_cpu(adsp2_id.fw.id);
region->base = be32_to_cpu(adsp2_id.xm);
list_add_tail(&region->list, &dsp->alg_regions);
region = kzalloc(sizeof(*region), GFP_KERNEL);
if (!region)
return -ENOMEM;
region->type = WMFW_ADSP2_YM;
region->alg = be32_to_cpu(adsp2_id.fw.id);
region->base = be32_to_cpu(adsp2_id.ym);
list_add_tail(&region->list, &dsp->alg_regions);
region = kzalloc(sizeof(*region), GFP_KERNEL);
if (!region)
return -ENOMEM;
region->type = WMFW_ADSP2_ZM;
region->alg = be32_to_cpu(adsp2_id.fw.id);
region->base = be32_to_cpu(adsp2_id.zm);
list_add_tail(&region->list, &dsp->alg_regions);
pos = sizeof(adsp2_id) / 2;
term = pos + ((sizeof(*adsp2_alg) * algs) / 2);
break;
default:
WARN(1, "Unknown DSP type");
return -EINVAL;
}
if (algs == 0) {
adsp_err(dsp, "No algorithms\n");
return -EINVAL;
}
if (algs > 1024) {
adsp_err(dsp, "Algorithm count %zx excessive\n", algs);
print_hex_dump_bytes(dev_name(dsp->dev), DUMP_PREFIX_OFFSET,
buf, buf_size);
return -EINVAL;
}
/* Read the terminator first to validate the length */
ret = regmap_raw_read(regmap, mem->base + term, &val, sizeof(val));
if (ret != 0) { if (ret != 0) {
adsp_err(dsp, "Failed to read algorithm list end: %d\n", adsp_err(dsp, "Failed to read algorithm info: %d\n",
ret); ret);
return ret; return ret;
} }
if (be32_to_cpu(val) != 0xbedead) n_algs = be32_to_cpu(adsp1_id.n_algs);
adsp_warn(dsp, "Algorithm list end %x 0x%x != 0xbeadead\n", dsp->fw_id = be32_to_cpu(adsp1_id.fw.id);
term, be32_to_cpu(val)); adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n",
dsp->fw_id,
alg = kzalloc((term - pos) * 2, GFP_KERNEL | GFP_DMA); (be32_to_cpu(adsp1_id.fw.ver) & 0xff0000) >> 16,
if (!alg) (be32_to_cpu(adsp1_id.fw.ver) & 0xff00) >> 8,
return -ENOMEM; be32_to_cpu(adsp1_id.fw.ver) & 0xff,
n_algs);
ret = regmap_raw_read(regmap, mem->base + pos, alg, (term - pos) * 2);
if (ret != 0) { alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_ZM,
adsp_err(dsp, "Failed to read algorithm list: %d\n", adsp1_id.fw.id, adsp1_id.zm);
ret); if (IS_ERR(alg_region))
goto out; return PTR_ERR(alg_region);
}
alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_DM,
adsp1_alg = alg; adsp1_id.fw.id, adsp1_id.dm);
adsp2_alg = alg; if (IS_ERR(alg_region))
return PTR_ERR(alg_region);
for (i = 0; i < algs; i++) {
switch (dsp->type) { pos = sizeof(adsp1_id) / 2;
case WMFW_ADSP1: len = (sizeof(*adsp1_alg) * n_algs) / 2;
adsp_info(dsp, "%d: ID %x v%d.%d.%d DM@%x ZM@%x\n",
i, be32_to_cpu(adsp1_alg[i].alg.id), adsp1_alg = wm_adsp_read_algs(dsp, n_algs, mem->base + pos, len);
(be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff0000) >> 16, if (IS_ERR(adsp1_alg))
(be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff00) >> 8, return PTR_ERR(adsp1_alg);
be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff,
be32_to_cpu(adsp1_alg[i].dm), for (i = 0; i < n_algs; i++) {
be32_to_cpu(adsp1_alg[i].zm)); adsp_info(dsp, "%d: ID %x v%d.%d.%d DM@%x ZM@%x\n",
i, be32_to_cpu(adsp1_alg[i].alg.id),
region = kzalloc(sizeof(*region), GFP_KERNEL); (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff0000) >> 16,
if (!region) { (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff00) >> 8,
ret = -ENOMEM; be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff,
goto out; be32_to_cpu(adsp1_alg[i].dm),
} be32_to_cpu(adsp1_alg[i].zm));
region->type = WMFW_ADSP1_DM;
region->alg = be32_to_cpu(adsp1_alg[i].alg.id); alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_DM,
region->base = be32_to_cpu(adsp1_alg[i].dm); adsp1_alg[i].alg.id,
region->len = 0; adsp1_alg[i].dm);
list_add_tail(&region->list, &dsp->alg_regions); if (IS_ERR(alg_region)) {
if (i + 1 < algs) { ret = PTR_ERR(alg_region);
region->len = be32_to_cpu(adsp1_alg[i + 1].dm); goto out;
region->len -= be32_to_cpu(adsp1_alg[i].dm); }
region->len *= 4; if (dsp->fw_ver == 0) {
wm_adsp_create_control(dsp, region); if (i + 1 < n_algs) {
len = be32_to_cpu(adsp1_alg[i + 1].dm);
len -= be32_to_cpu(adsp1_alg[i].dm);
len *= 4;
wm_adsp_create_control(dsp, alg_region, 0,
len, NULL, 0, 0);
} else { } else {
adsp_warn(dsp, "Missing length info for region DM with ID %x\n", adsp_warn(dsp, "Missing length info for region DM with ID %x\n",
be32_to_cpu(adsp1_alg[i].alg.id)); be32_to_cpu(adsp1_alg[i].alg.id));
} }
}
region = kzalloc(sizeof(*region), GFP_KERNEL); alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_ZM,
if (!region) { adsp1_alg[i].alg.id,
ret = -ENOMEM; adsp1_alg[i].zm);
goto out; if (IS_ERR(alg_region)) {
} ret = PTR_ERR(alg_region);
region->type = WMFW_ADSP1_ZM; goto out;
region->alg = be32_to_cpu(adsp1_alg[i].alg.id); }
region->base = be32_to_cpu(adsp1_alg[i].zm); if (dsp->fw_ver == 0) {
region->len = 0; if (i + 1 < n_algs) {
list_add_tail(&region->list, &dsp->alg_regions); len = be32_to_cpu(adsp1_alg[i + 1].zm);
if (i + 1 < algs) { len -= be32_to_cpu(adsp1_alg[i].zm);
region->len = be32_to_cpu(adsp1_alg[i + 1].zm); len *= 4;
region->len -= be32_to_cpu(adsp1_alg[i].zm); wm_adsp_create_control(dsp, alg_region, 0,
region->len *= 4; len, NULL, 0, 0);
wm_adsp_create_control(dsp, region);
} else { } else {
adsp_warn(dsp, "Missing length info for region ZM with ID %x\n", adsp_warn(dsp, "Missing length info for region ZM with ID %x\n",
be32_to_cpu(adsp1_alg[i].alg.id)); be32_to_cpu(adsp1_alg[i].alg.id));
} }
break; }
}
case WMFW_ADSP2: out:
adsp_info(dsp, kfree(adsp1_alg);
"%d: ID %x v%d.%d.%d XM@%x YM@%x ZM@%x\n", return ret;
i, be32_to_cpu(adsp2_alg[i].alg.id), }
(be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff0000) >> 16,
(be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff00) >> 8, static int wm_adsp2_setup_algs(struct wm_adsp *dsp)
be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff, {
be32_to_cpu(adsp2_alg[i].xm), struct wmfw_adsp2_id_hdr adsp2_id;
be32_to_cpu(adsp2_alg[i].ym), struct wmfw_adsp2_alg_hdr *adsp2_alg;
be32_to_cpu(adsp2_alg[i].zm)); struct wm_adsp_alg_region *alg_region;
const struct wm_adsp_region *mem;
region = kzalloc(sizeof(*region), GFP_KERNEL); unsigned int pos, len;
if (!region) { size_t n_algs;
ret = -ENOMEM; int i, ret;
goto out;
} mem = wm_adsp_find_region(dsp, WMFW_ADSP2_XM);
region->type = WMFW_ADSP2_XM; if (WARN_ON(!mem))
region->alg = be32_to_cpu(adsp2_alg[i].alg.id); return -EINVAL;
region->base = be32_to_cpu(adsp2_alg[i].xm);
region->len = 0; ret = regmap_raw_read(dsp->regmap, mem->base, &adsp2_id,
list_add_tail(&region->list, &dsp->alg_regions); sizeof(adsp2_id));
if (i + 1 < algs) { if (ret != 0) {
region->len = be32_to_cpu(adsp2_alg[i + 1].xm); adsp_err(dsp, "Failed to read algorithm info: %d\n",
region->len -= be32_to_cpu(adsp2_alg[i].xm); ret);
region->len *= 4; return ret;
wm_adsp_create_control(dsp, region); }
n_algs = be32_to_cpu(adsp2_id.n_algs);
dsp->fw_id = be32_to_cpu(adsp2_id.fw.id);
adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n",
dsp->fw_id,
(be32_to_cpu(adsp2_id.fw.ver) & 0xff0000) >> 16,
(be32_to_cpu(adsp2_id.fw.ver) & 0xff00) >> 8,
be32_to_cpu(adsp2_id.fw.ver) & 0xff,
n_algs);
alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_XM,
adsp2_id.fw.id, adsp2_id.xm);
if (IS_ERR(alg_region))
return PTR_ERR(alg_region);
alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_YM,
adsp2_id.fw.id, adsp2_id.ym);
if (IS_ERR(alg_region))
return PTR_ERR(alg_region);
alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_ZM,
adsp2_id.fw.id, adsp2_id.zm);
if (IS_ERR(alg_region))
return PTR_ERR(alg_region);
pos = sizeof(adsp2_id) / 2;
len = (sizeof(*adsp2_alg) * n_algs) / 2;
adsp2_alg = wm_adsp_read_algs(dsp, n_algs, mem->base + pos, len);
if (IS_ERR(adsp2_alg))
return PTR_ERR(adsp2_alg);
for (i = 0; i < n_algs; i++) {
adsp_info(dsp,
"%d: ID %x v%d.%d.%d XM@%x YM@%x ZM@%x\n",
i, be32_to_cpu(adsp2_alg[i].alg.id),
(be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff0000) >> 16,
(be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff00) >> 8,
be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff,
be32_to_cpu(adsp2_alg[i].xm),
be32_to_cpu(adsp2_alg[i].ym),
be32_to_cpu(adsp2_alg[i].zm));
alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_XM,
adsp2_alg[i].alg.id,
adsp2_alg[i].xm);
if (IS_ERR(alg_region)) {
ret = PTR_ERR(alg_region);
goto out;
}
if (dsp->fw_ver == 0) {
if (i + 1 < n_algs) {
len = be32_to_cpu(adsp2_alg[i + 1].xm);
len -= be32_to_cpu(adsp2_alg[i].xm);
len *= 4;
wm_adsp_create_control(dsp, alg_region, 0,
len, NULL, 0, 0);
} else { } else {
adsp_warn(dsp, "Missing length info for region XM with ID %x\n", adsp_warn(dsp, "Missing length info for region XM with ID %x\n",
be32_to_cpu(adsp2_alg[i].alg.id)); be32_to_cpu(adsp2_alg[i].alg.id));
} }
}
region = kzalloc(sizeof(*region), GFP_KERNEL); alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_YM,
if (!region) { adsp2_alg[i].alg.id,
ret = -ENOMEM; adsp2_alg[i].ym);
goto out; if (IS_ERR(alg_region)) {
} ret = PTR_ERR(alg_region);
region->type = WMFW_ADSP2_YM; goto out;
region->alg = be32_to_cpu(adsp2_alg[i].alg.id); }
region->base = be32_to_cpu(adsp2_alg[i].ym); if (dsp->fw_ver == 0) {
region->len = 0; if (i + 1 < n_algs) {
list_add_tail(&region->list, &dsp->alg_regions); len = be32_to_cpu(adsp2_alg[i + 1].ym);
if (i + 1 < algs) { len -= be32_to_cpu(adsp2_alg[i].ym);
region->len = be32_to_cpu(adsp2_alg[i + 1].ym); len *= 4;
region->len -= be32_to_cpu(adsp2_alg[i].ym); wm_adsp_create_control(dsp, alg_region, 0,
region->len *= 4; len, NULL, 0, 0);
wm_adsp_create_control(dsp, region);
} else { } else {
adsp_warn(dsp, "Missing length info for region YM with ID %x\n", adsp_warn(dsp, "Missing length info for region YM with ID %x\n",
be32_to_cpu(adsp2_alg[i].alg.id)); be32_to_cpu(adsp2_alg[i].alg.id));
} }
}
region = kzalloc(sizeof(*region), GFP_KERNEL); alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_ZM,
if (!region) { adsp2_alg[i].alg.id,
ret = -ENOMEM; adsp2_alg[i].zm);
goto out; if (IS_ERR(alg_region)) {
} ret = PTR_ERR(alg_region);
region->type = WMFW_ADSP2_ZM; goto out;
region->alg = be32_to_cpu(adsp2_alg[i].alg.id); }
region->base = be32_to_cpu(adsp2_alg[i].zm); if (dsp->fw_ver == 0) {
region->len = 0; if (i + 1 < n_algs) {
list_add_tail(&region->list, &dsp->alg_regions); len = be32_to_cpu(adsp2_alg[i + 1].zm);
if (i + 1 < algs) { len -= be32_to_cpu(adsp2_alg[i].zm);
region->len = be32_to_cpu(adsp2_alg[i + 1].zm); len *= 4;
region->len -= be32_to_cpu(adsp2_alg[i].zm); wm_adsp_create_control(dsp, alg_region, 0,
region->len *= 4; len, NULL, 0, 0);
wm_adsp_create_control(dsp, region);
} else { } else {
adsp_warn(dsp, "Missing length info for region ZM with ID %x\n", adsp_warn(dsp, "Missing length info for region ZM with ID %x\n",
be32_to_cpu(adsp2_alg[i].alg.id)); be32_to_cpu(adsp2_alg[i].alg.id));
} }
break;
} }
} }
out: out:
kfree(alg); kfree(adsp2_alg);
return ret; return ret;
} }
...@@ -1354,9 +1634,9 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp) ...@@ -1354,9 +1634,9 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp)
return ret; return ret;
} }
int wm_adsp1_init(struct wm_adsp *adsp) int wm_adsp1_init(struct wm_adsp *dsp)
{ {
INIT_LIST_HEAD(&adsp->alg_regions); INIT_LIST_HEAD(&dsp->alg_regions);
return 0; return 0;
} }
...@@ -1410,7 +1690,7 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w, ...@@ -1410,7 +1690,7 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w,
if (ret != 0) if (ret != 0)
goto err; goto err;
ret = wm_adsp_setup_algs(dsp); ret = wm_adsp1_setup_algs(dsp);
if (ret != 0) if (ret != 0)
goto err; goto err;
...@@ -1568,7 +1848,7 @@ static void wm_adsp2_boot_work(struct work_struct *work) ...@@ -1568,7 +1848,7 @@ static void wm_adsp2_boot_work(struct work_struct *work)
if (ret != 0) if (ret != 0)
goto err; goto err;
ret = wm_adsp_setup_algs(dsp); ret = wm_adsp2_setup_algs(dsp);
if (ret != 0) if (ret != 0)
goto err; goto err;
...@@ -1642,6 +1922,9 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w, ...@@ -1642,6 +1922,9 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
break; break;
case SND_SOC_DAPM_PRE_PMD: case SND_SOC_DAPM_PRE_PMD:
/* Log firmware state, it can be useful for analysis */
wm_adsp2_show_fw_status(dsp);
dsp->running = false; dsp->running = false;
regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
...@@ -1694,7 +1977,7 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w, ...@@ -1694,7 +1977,7 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
} }
EXPORT_SYMBOL_GPL(wm_adsp2_event); EXPORT_SYMBOL_GPL(wm_adsp2_event);
int wm_adsp2_init(struct wm_adsp *adsp, bool dvfs) int wm_adsp2_init(struct wm_adsp *dsp, bool dvfs)
{ {
int ret; int ret;
...@@ -1702,40 +1985,40 @@ int wm_adsp2_init(struct wm_adsp *adsp, bool dvfs) ...@@ -1702,40 +1985,40 @@ int wm_adsp2_init(struct wm_adsp *adsp, bool dvfs)
* Disable the DSP memory by default when in reset for a small * Disable the DSP memory by default when in reset for a small
* power saving. * power saving.
*/ */
ret = regmap_update_bits(adsp->regmap, adsp->base + ADSP2_CONTROL, ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
ADSP2_MEM_ENA, 0); ADSP2_MEM_ENA, 0);
if (ret != 0) { if (ret != 0) {
adsp_err(adsp, "Failed to clear memory retention: %d\n", ret); adsp_err(dsp, "Failed to clear memory retention: %d\n", ret);
return ret; return ret;
} }
INIT_LIST_HEAD(&adsp->alg_regions); INIT_LIST_HEAD(&dsp->alg_regions);
INIT_LIST_HEAD(&adsp->ctl_list); INIT_LIST_HEAD(&dsp->ctl_list);
INIT_WORK(&adsp->boot_work, wm_adsp2_boot_work); INIT_WORK(&dsp->boot_work, wm_adsp2_boot_work);
if (dvfs) { if (dvfs) {
adsp->dvfs = devm_regulator_get(adsp->dev, "DCVDD"); dsp->dvfs = devm_regulator_get(dsp->dev, "DCVDD");
if (IS_ERR(adsp->dvfs)) { if (IS_ERR(dsp->dvfs)) {
ret = PTR_ERR(adsp->dvfs); ret = PTR_ERR(dsp->dvfs);
adsp_err(adsp, "Failed to get DCVDD: %d\n", ret); adsp_err(dsp, "Failed to get DCVDD: %d\n", ret);
return ret; return ret;
} }
ret = regulator_enable(adsp->dvfs); ret = regulator_enable(dsp->dvfs);
if (ret != 0) { if (ret != 0) {
adsp_err(adsp, "Failed to enable DCVDD: %d\n", ret); adsp_err(dsp, "Failed to enable DCVDD: %d\n", ret);
return ret; return ret;
} }
ret = regulator_set_voltage(adsp->dvfs, 1200000, 1800000); ret = regulator_set_voltage(dsp->dvfs, 1200000, 1800000);
if (ret != 0) { if (ret != 0) {
adsp_err(adsp, "Failed to initialise DVFS: %d\n", ret); adsp_err(dsp, "Failed to initialise DVFS: %d\n", ret);
return ret; return ret;
} }
ret = regulator_disable(adsp->dvfs); ret = regulator_disable(dsp->dvfs);
if (ret != 0) { if (ret != 0) {
adsp_err(adsp, "Failed to disable DCVDD: %d\n", ret); adsp_err(dsp, "Failed to disable DCVDD: %d\n", ret);
return ret; return ret;
} }
} }
......
...@@ -30,7 +30,6 @@ struct wm_adsp_alg_region { ...@@ -30,7 +30,6 @@ struct wm_adsp_alg_region {
unsigned int alg; unsigned int alg;
int type; int type;
unsigned int base; unsigned int base;
size_t len;
}; };
struct wm_adsp { struct wm_adsp {
...@@ -54,6 +53,7 @@ struct wm_adsp { ...@@ -54,6 +53,7 @@ struct wm_adsp {
int num_mems; int num_mems;
int fw; int fw;
int fw_ver;
bool running; bool running;
struct regulator *dvfs; struct regulator *dvfs;
...@@ -78,8 +78,8 @@ struct wm_adsp { ...@@ -78,8 +78,8 @@ struct wm_adsp {
extern const struct snd_kcontrol_new wm_adsp1_fw_controls[]; extern const struct snd_kcontrol_new wm_adsp1_fw_controls[];
extern const struct snd_kcontrol_new wm_adsp2_fw_controls[]; extern const struct snd_kcontrol_new wm_adsp2_fw_controls[];
int wm_adsp1_init(struct wm_adsp *adsp); int wm_adsp1_init(struct wm_adsp *dsp);
int wm_adsp2_init(struct wm_adsp *adsp, bool dvfs); int wm_adsp2_init(struct wm_adsp *dsp, bool dvfs);
int wm_adsp1_event(struct snd_soc_dapm_widget *w, int wm_adsp1_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event); struct snd_kcontrol *kcontrol, int event);
int wm_adsp2_early_event(struct snd_soc_dapm_widget *w, int wm_adsp2_early_event(struct snd_soc_dapm_widget *w,
......
...@@ -15,6 +15,17 @@ ...@@ -15,6 +15,17 @@
#include <linux/types.h> #include <linux/types.h>
#define WMFW_MAX_ALG_NAME 256
#define WMFW_MAX_ALG_DESCR_NAME 256
#define WMFW_MAX_COEFF_NAME 256
#define WMFW_MAX_COEFF_DESCR_NAME 256
#define WMFW_CTL_FLAG_SYS 0x8000
#define WMFW_CTL_FLAG_VOLATILE 0x0004
#define WMFW_CTL_FLAG_WRITEABLE 0x0002
#define WMFW_CTL_FLAG_READABLE 0x0001
struct wmfw_header { struct wmfw_header {
char magic[4]; char magic[4];
__le32 len; __le32 len;
...@@ -61,7 +72,7 @@ struct wmfw_adsp1_id_hdr { ...@@ -61,7 +72,7 @@ struct wmfw_adsp1_id_hdr {
struct wmfw_id_hdr fw; struct wmfw_id_hdr fw;
__be32 zm; __be32 zm;
__be32 dm; __be32 dm;
__be32 algs; __be32 n_algs;
} __packed; } __packed;
struct wmfw_adsp2_id_hdr { struct wmfw_adsp2_id_hdr {
...@@ -69,7 +80,7 @@ struct wmfw_adsp2_id_hdr { ...@@ -69,7 +80,7 @@ struct wmfw_adsp2_id_hdr {
__be32 zm; __be32 zm;
__be32 xm; __be32 xm;
__be32 ym; __be32 ym;
__be32 algs; __be32 n_algs;
} __packed; } __packed;
struct wmfw_alg_hdr { struct wmfw_alg_hdr {
...@@ -90,6 +101,28 @@ struct wmfw_adsp2_alg_hdr { ...@@ -90,6 +101,28 @@ struct wmfw_adsp2_alg_hdr {
__be32 ym; __be32 ym;
} __packed; } __packed;
struct wmfw_adsp_alg_data {
__le32 id;
u8 name[WMFW_MAX_ALG_NAME];
u8 descr[WMFW_MAX_ALG_DESCR_NAME];
__le32 ncoeff;
u8 data[];
} __packed;
struct wmfw_adsp_coeff_data {
struct {
__le16 offset;
__le16 type;
__le32 size;
} hdr;
u8 name[WMFW_MAX_COEFF_NAME];
u8 descr[WMFW_MAX_COEFF_DESCR_NAME];
__le16 ctl_type;
__le16 flags;
__le32 len;
u8 data[];
} __packed;
struct wmfw_coeff_hdr { struct wmfw_coeff_hdr {
u8 magic[4]; u8 magic[4];
__le32 len; __le32 len;
...@@ -117,9 +150,10 @@ struct wmfw_coeff_item { ...@@ -117,9 +150,10 @@ struct wmfw_coeff_item {
#define WMFW_ADSP1 1 #define WMFW_ADSP1 1
#define WMFW_ADSP2 2 #define WMFW_ADSP2 2
#define WMFW_ABSOLUTE 0xf0 #define WMFW_ABSOLUTE 0xf0
#define WMFW_NAME_TEXT 0xfe #define WMFW_ALGORITHM_DATA 0xf2
#define WMFW_INFO_TEXT 0xff #define WMFW_NAME_TEXT 0xfe
#define WMFW_INFO_TEXT 0xff
#define WMFW_ADSP1_PM 2 #define WMFW_ADSP1_PM 2
#define WMFW_ADSP1_DM 3 #define WMFW_ADSP1_DM 3
......
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