summaryrefslogtreecommitdiff
path: root/sound/soc
diff options
context:
space:
mode:
authorMark Brown <broonie@opensource.wolfsonmicro.com>2013-01-30 14:37:23 +0800
committerMark Brown <broonie@opensource.wolfsonmicro.com>2013-02-04 19:52:35 +0000
commitcf17c83c4ac2de13a7b158c1c27fffb30ce109c3 (patch)
tree7a617680ebf87bbc3a7f6435dad63205a050debb /sound/soc
parent4c47c2b0f82c5ebe2187768b8ed05281d13b438e (diff)
ASoC: wm_adsp: Use asynchronous I/O to write firmware and coefficients
Allow the regmap API to use asynchronous I/O where supported to minimise the delay between transfers, reducing firmware download times. Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Diffstat (limited to 'sound/soc')
-rw-r--r--sound/soc/codecs/wm_adsp.c82
1 files changed, 66 insertions, 16 deletions
diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c
index 5487a94f4595..be45e2b8b59e 100644
--- a/sound/soc/codecs/wm_adsp.c
+++ b/sound/soc/codecs/wm_adsp.c
@@ -15,6 +15,7 @@
#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>
@@ -153,6 +154,43 @@
#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
static const char *wm_adsp_fw_text[WM_ADSP_NUM_FW] = {
@@ -254,6 +292,7 @@ static unsigned int wm_adsp_region_to_reg(struct wm_adsp_region const *region,
static int wm_adsp_load(struct wm_adsp *dsp)
{
+ LIST_HEAD(buf_list);
const struct firmware *firmware;
struct regmap *regmap = dsp->regmap;
unsigned int pos = 0;
@@ -265,7 +304,7 @@ static int wm_adsp_load(struct wm_adsp *dsp)
const struct wm_adsp_region *mem;
const char *region_name;
char *file, *text;
- void *buf;
+ struct wm_adsp_buf *buf;
unsigned int reg;
int regions = 0;
int ret, offset, type, sizes;
@@ -420,18 +459,16 @@ static int wm_adsp_load(struct wm_adsp *dsp)
}
if (reg) {
- buf = kmemdup(region->data, le32_to_cpu(region->len),
- GFP_KERNEL | GFP_DMA);
+ buf = wm_adsp_buf_alloc(region->data,
+ le32_to_cpu(region->len),
+ &buf_list);
if (!buf) {
adsp_err(dsp, "Out of memory\n");
return -ENOMEM;
}
- ret = regmap_raw_write(regmap, reg, buf,
- le32_to_cpu(region->len));
-
- kfree(buf);
-
+ ret = regmap_raw_write_async(regmap, reg, buf->buf,
+ le32_to_cpu(region->len));
if (ret != 0) {
adsp_err(dsp,
"%s.%d: Failed to write %d bytes at %d in %s: %d\n",
@@ -445,12 +482,20 @@ static int wm_adsp_load(struct wm_adsp *dsp)
pos += le32_to_cpu(region->len) + sizeof(*region);
regions++;
}
-
+
+ ret = regmap_async_complete(regmap);
+ if (ret != 0) {
+ adsp_err(dsp, "Failed to complete async write: %d\n", ret);
+ goto out_fw;
+ }
+
if (pos > firmware->size)
adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n",
file, regions, pos - firmware->size);
out_fw:
+ regmap_async_complete(regmap);
+ wm_adsp_buf_free(&buf_list);
release_firmware(firmware);
out:
kfree(file);
@@ -655,6 +700,7 @@ out:
static int wm_adsp_load_coeff(struct wm_adsp *dsp)
{
+ LIST_HEAD(buf_list);
struct regmap *regmap = dsp->regmap;
struct wmfw_coeff_hdr *hdr;
struct wmfw_coeff_item *blk;
@@ -664,7 +710,7 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp)
const char *region_name;
int ret, pos, blocks, type, offset, reg;
char *file;
- void *buf;
+ struct wm_adsp_buf *buf;
file = kzalloc(PAGE_SIZE, GFP_KERNEL);
if (file == NULL)
@@ -776,8 +822,9 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp)
}
if (reg) {
- buf = kmemdup(blk->data, le32_to_cpu(blk->len),
- GFP_KERNEL | GFP_DMA);
+ buf = wm_adsp_buf_alloc(blk->data,
+ le32_to_cpu(blk->len),
+ &buf_list);
if (!buf) {
adsp_err(dsp, "Out of memory\n");
return -ENOMEM;
@@ -786,27 +833,30 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp)
adsp_dbg(dsp, "%s.%d: Writing %d bytes at %x\n",
file, blocks, le32_to_cpu(blk->len),
reg);
- ret = regmap_raw_write(regmap, reg, blk->data,
- le32_to_cpu(blk->len));
+ ret = regmap_raw_write_async(regmap, reg, buf->buf,
+ le32_to_cpu(blk->len));
if (ret != 0) {
adsp_err(dsp,
"%s.%d: Failed to write to %x in %s\n",
file, blocks, reg, region_name);
}
-
- kfree(buf);
}
pos += le32_to_cpu(blk->len) + sizeof(*blk);
blocks++;
}
+ ret = regmap_async_complete(regmap);
+ if (ret != 0)
+ adsp_err(dsp, "Failed to complete async write: %d\n", ret);
+
if (pos > firmware->size)
adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n",
file, blocks, pos - firmware->size);
out_fw:
release_firmware(firmware);
+ wm_adsp_buf_free(&buf_list);
out:
kfree(file);
return 0;