diff options
author | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2013-01-30 01:37:23 -0500 |
---|---|---|
committer | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2013-02-04 14:52:35 -0500 |
commit | cf17c83c4ac2de13a7b158c1c27fffb30ce109c3 (patch) | |
tree | 7a617680ebf87bbc3a7f6435dad63205a050debb /sound/soc/codecs/wm_adsp.c | |
parent | 4c47c2b0f82c5ebe2187768b8ed05281d13b438e (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/codecs/wm_adsp.c')
-rw-r--r-- | sound/soc/codecs/wm_adsp.c | 82 |
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 @@ | |||
15 | #include <linux/init.h> | 15 | #include <linux/init.h> |
16 | #include <linux/delay.h> | 16 | #include <linux/delay.h> |
17 | #include <linux/firmware.h> | 17 | #include <linux/firmware.h> |
18 | #include <linux/list.h> | ||
18 | #include <linux/pm.h> | 19 | #include <linux/pm.h> |
19 | #include <linux/pm_runtime.h> | 20 | #include <linux/pm_runtime.h> |
20 | #include <linux/regmap.h> | 21 | #include <linux/regmap.h> |
@@ -153,6 +154,43 @@ | |||
153 | #define ADSP2_RAM_RDY_SHIFT 0 | 154 | #define ADSP2_RAM_RDY_SHIFT 0 |
154 | #define ADSP2_RAM_RDY_WIDTH 1 | 155 | #define ADSP2_RAM_RDY_WIDTH 1 |
155 | 156 | ||
157 | struct wm_adsp_buf { | ||
158 | struct list_head list; | ||
159 | void *buf; | ||
160 | }; | ||
161 | |||
162 | static struct wm_adsp_buf *wm_adsp_buf_alloc(const void *src, size_t len, | ||
163 | struct list_head *list) | ||
164 | { | ||
165 | struct wm_adsp_buf *buf = kzalloc(sizeof(*buf), GFP_KERNEL); | ||
166 | |||
167 | if (buf == NULL) | ||
168 | return NULL; | ||
169 | |||
170 | buf->buf = kmemdup(src, len, GFP_KERNEL | GFP_DMA); | ||
171 | if (!buf->buf) { | ||
172 | kfree(buf); | ||
173 | return NULL; | ||
174 | } | ||
175 | |||
176 | if (list) | ||
177 | list_add_tail(&buf->list, list); | ||
178 | |||
179 | return buf; | ||
180 | } | ||
181 | |||
182 | static void wm_adsp_buf_free(struct list_head *list) | ||
183 | { | ||
184 | while (!list_empty(list)) { | ||
185 | struct wm_adsp_buf *buf = list_first_entry(list, | ||
186 | struct wm_adsp_buf, | ||
187 | list); | ||
188 | list_del(&buf->list); | ||
189 | kfree(buf->buf); | ||
190 | kfree(buf); | ||
191 | } | ||
192 | } | ||
193 | |||
156 | #define WM_ADSP_NUM_FW 4 | 194 | #define WM_ADSP_NUM_FW 4 |
157 | 195 | ||
158 | static const char *wm_adsp_fw_text[WM_ADSP_NUM_FW] = { | 196 | 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, | |||
254 | 292 | ||
255 | static int wm_adsp_load(struct wm_adsp *dsp) | 293 | static int wm_adsp_load(struct wm_adsp *dsp) |
256 | { | 294 | { |
295 | LIST_HEAD(buf_list); | ||
257 | const struct firmware *firmware; | 296 | const struct firmware *firmware; |
258 | struct regmap *regmap = dsp->regmap; | 297 | struct regmap *regmap = dsp->regmap; |
259 | unsigned int pos = 0; | 298 | unsigned int pos = 0; |
@@ -265,7 +304,7 @@ static int wm_adsp_load(struct wm_adsp *dsp) | |||
265 | const struct wm_adsp_region *mem; | 304 | const struct wm_adsp_region *mem; |
266 | const char *region_name; | 305 | const char *region_name; |
267 | char *file, *text; | 306 | char *file, *text; |
268 | void *buf; | 307 | struct wm_adsp_buf *buf; |
269 | unsigned int reg; | 308 | unsigned int reg; |
270 | int regions = 0; | 309 | int regions = 0; |
271 | int ret, offset, type, sizes; | 310 | int ret, offset, type, sizes; |
@@ -420,18 +459,16 @@ static int wm_adsp_load(struct wm_adsp *dsp) | |||
420 | } | 459 | } |
421 | 460 | ||
422 | if (reg) { | 461 | if (reg) { |
423 | buf = kmemdup(region->data, le32_to_cpu(region->len), | 462 | buf = wm_adsp_buf_alloc(region->data, |
424 | GFP_KERNEL | GFP_DMA); | 463 | le32_to_cpu(region->len), |
464 | &buf_list); | ||
425 | if (!buf) { | 465 | if (!buf) { |
426 | adsp_err(dsp, "Out of memory\n"); | 466 | adsp_err(dsp, "Out of memory\n"); |
427 | return -ENOMEM; | 467 | return -ENOMEM; |
428 | } | 468 | } |
429 | 469 | ||
430 | ret = regmap_raw_write(regmap, reg, buf, | 470 | ret = regmap_raw_write_async(regmap, reg, buf->buf, |
431 | le32_to_cpu(region->len)); | 471 | le32_to_cpu(region->len)); |
432 | |||
433 | kfree(buf); | ||
434 | |||
435 | if (ret != 0) { | 472 | if (ret != 0) { |
436 | adsp_err(dsp, | 473 | adsp_err(dsp, |
437 | "%s.%d: Failed to write %d bytes at %d in %s: %d\n", | 474 | "%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) | |||
445 | pos += le32_to_cpu(region->len) + sizeof(*region); | 482 | pos += le32_to_cpu(region->len) + sizeof(*region); |
446 | regions++; | 483 | regions++; |
447 | } | 484 | } |
448 | 485 | ||
486 | ret = regmap_async_complete(regmap); | ||
487 | if (ret != 0) { | ||
488 | adsp_err(dsp, "Failed to complete async write: %d\n", ret); | ||
489 | goto out_fw; | ||
490 | } | ||
491 | |||
449 | if (pos > firmware->size) | 492 | if (pos > firmware->size) |
450 | adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n", | 493 | adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n", |
451 | file, regions, pos - firmware->size); | 494 | file, regions, pos - firmware->size); |
452 | 495 | ||
453 | out_fw: | 496 | out_fw: |
497 | regmap_async_complete(regmap); | ||
498 | wm_adsp_buf_free(&buf_list); | ||
454 | release_firmware(firmware); | 499 | release_firmware(firmware); |
455 | out: | 500 | out: |
456 | kfree(file); | 501 | kfree(file); |
@@ -655,6 +700,7 @@ out: | |||
655 | 700 | ||
656 | static int wm_adsp_load_coeff(struct wm_adsp *dsp) | 701 | static int wm_adsp_load_coeff(struct wm_adsp *dsp) |
657 | { | 702 | { |
703 | LIST_HEAD(buf_list); | ||
658 | struct regmap *regmap = dsp->regmap; | 704 | struct regmap *regmap = dsp->regmap; |
659 | struct wmfw_coeff_hdr *hdr; | 705 | struct wmfw_coeff_hdr *hdr; |
660 | struct wmfw_coeff_item *blk; | 706 | struct wmfw_coeff_item *blk; |
@@ -664,7 +710,7 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp) | |||
664 | const char *region_name; | 710 | const char *region_name; |
665 | int ret, pos, blocks, type, offset, reg; | 711 | int ret, pos, blocks, type, offset, reg; |
666 | char *file; | 712 | char *file; |
667 | void *buf; | 713 | struct wm_adsp_buf *buf; |
668 | 714 | ||
669 | file = kzalloc(PAGE_SIZE, GFP_KERNEL); | 715 | file = kzalloc(PAGE_SIZE, GFP_KERNEL); |
670 | if (file == NULL) | 716 | if (file == NULL) |
@@ -776,8 +822,9 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp) | |||
776 | } | 822 | } |
777 | 823 | ||
778 | if (reg) { | 824 | if (reg) { |
779 | buf = kmemdup(blk->data, le32_to_cpu(blk->len), | 825 | buf = wm_adsp_buf_alloc(blk->data, |
780 | GFP_KERNEL | GFP_DMA); | 826 | le32_to_cpu(blk->len), |
827 | &buf_list); | ||
781 | if (!buf) { | 828 | if (!buf) { |
782 | adsp_err(dsp, "Out of memory\n"); | 829 | adsp_err(dsp, "Out of memory\n"); |
783 | return -ENOMEM; | 830 | return -ENOMEM; |
@@ -786,27 +833,30 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp) | |||
786 | adsp_dbg(dsp, "%s.%d: Writing %d bytes at %x\n", | 833 | adsp_dbg(dsp, "%s.%d: Writing %d bytes at %x\n", |
787 | file, blocks, le32_to_cpu(blk->len), | 834 | file, blocks, le32_to_cpu(blk->len), |
788 | reg); | 835 | reg); |
789 | ret = regmap_raw_write(regmap, reg, blk->data, | 836 | ret = regmap_raw_write_async(regmap, reg, buf->buf, |
790 | le32_to_cpu(blk->len)); | 837 | le32_to_cpu(blk->len)); |
791 | if (ret != 0) { | 838 | if (ret != 0) { |
792 | adsp_err(dsp, | 839 | adsp_err(dsp, |
793 | "%s.%d: Failed to write to %x in %s\n", | 840 | "%s.%d: Failed to write to %x in %s\n", |
794 | file, blocks, reg, region_name); | 841 | file, blocks, reg, region_name); |
795 | } | 842 | } |
796 | |||
797 | kfree(buf); | ||
798 | } | 843 | } |
799 | 844 | ||
800 | pos += le32_to_cpu(blk->len) + sizeof(*blk); | 845 | pos += le32_to_cpu(blk->len) + sizeof(*blk); |
801 | blocks++; | 846 | blocks++; |
802 | } | 847 | } |
803 | 848 | ||
849 | ret = regmap_async_complete(regmap); | ||
850 | if (ret != 0) | ||
851 | adsp_err(dsp, "Failed to complete async write: %d\n", ret); | ||
852 | |||
804 | if (pos > firmware->size) | 853 | if (pos > firmware->size) |
805 | adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n", | 854 | adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n", |
806 | file, blocks, pos - firmware->size); | 855 | file, blocks, pos - firmware->size); |
807 | 856 | ||
808 | out_fw: | 857 | out_fw: |
809 | release_firmware(firmware); | 858 | release_firmware(firmware); |
859 | wm_adsp_buf_free(&buf_list); | ||
810 | out: | 860 | out: |
811 | kfree(file); | 861 | kfree(file); |
812 | return 0; | 862 | return 0; |