12159ad93SMark Brown /* 22159ad93SMark Brown * wm_adsp.c -- Wolfson ADSP support 32159ad93SMark Brown * 42159ad93SMark Brown * Copyright 2012 Wolfson Microelectronics plc 52159ad93SMark Brown * 62159ad93SMark Brown * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> 72159ad93SMark Brown * 82159ad93SMark Brown * This program is free software; you can redistribute it and/or modify 92159ad93SMark Brown * it under the terms of the GNU General Public License version 2 as 102159ad93SMark Brown * published by the Free Software Foundation. 112159ad93SMark Brown */ 122159ad93SMark Brown 132159ad93SMark Brown #include <linux/module.h> 142159ad93SMark Brown #include <linux/moduleparam.h> 152159ad93SMark Brown #include <linux/init.h> 162159ad93SMark Brown #include <linux/delay.h> 172159ad93SMark Brown #include <linux/firmware.h> 18cf17c83cSMark Brown #include <linux/list.h> 192159ad93SMark Brown #include <linux/pm.h> 202159ad93SMark Brown #include <linux/pm_runtime.h> 212159ad93SMark Brown #include <linux/regmap.h> 22973838a0SMark Brown #include <linux/regulator/consumer.h> 232159ad93SMark Brown #include <linux/slab.h> 24cdcd7f72SCharles Keepax #include <linux/vmalloc.h> 256ab2b7b4SDimitris Papastamos #include <linux/workqueue.h> 26f9f55e31SRichard Fitzgerald #include <linux/debugfs.h> 272159ad93SMark Brown #include <sound/core.h> 282159ad93SMark Brown #include <sound/pcm.h> 292159ad93SMark Brown #include <sound/pcm_params.h> 302159ad93SMark Brown #include <sound/soc.h> 312159ad93SMark Brown #include <sound/jack.h> 322159ad93SMark Brown #include <sound/initval.h> 332159ad93SMark Brown #include <sound/tlv.h> 342159ad93SMark Brown 352159ad93SMark Brown #include <linux/mfd/arizona/registers.h> 362159ad93SMark Brown 37dc91428aSMark Brown #include "arizona.h" 382159ad93SMark Brown #include "wm_adsp.h" 392159ad93SMark Brown 402159ad93SMark Brown #define adsp_crit(_dsp, fmt, ...) \ 412159ad93SMark Brown dev_crit(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__) 422159ad93SMark Brown #define adsp_err(_dsp, fmt, ...) \ 432159ad93SMark Brown dev_err(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__) 442159ad93SMark Brown #define adsp_warn(_dsp, fmt, ...) \ 452159ad93SMark Brown dev_warn(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__) 462159ad93SMark Brown #define adsp_info(_dsp, fmt, ...) \ 472159ad93SMark Brown dev_info(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__) 482159ad93SMark Brown #define adsp_dbg(_dsp, fmt, ...) \ 492159ad93SMark Brown dev_dbg(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__) 502159ad93SMark Brown 512159ad93SMark Brown #define ADSP1_CONTROL_1 0x00 522159ad93SMark Brown #define ADSP1_CONTROL_2 0x02 532159ad93SMark Brown #define ADSP1_CONTROL_3 0x03 542159ad93SMark Brown #define ADSP1_CONTROL_4 0x04 552159ad93SMark Brown #define ADSP1_CONTROL_5 0x06 562159ad93SMark Brown #define ADSP1_CONTROL_6 0x07 572159ad93SMark Brown #define ADSP1_CONTROL_7 0x08 582159ad93SMark Brown #define ADSP1_CONTROL_8 0x09 592159ad93SMark Brown #define ADSP1_CONTROL_9 0x0A 602159ad93SMark Brown #define ADSP1_CONTROL_10 0x0B 612159ad93SMark Brown #define ADSP1_CONTROL_11 0x0C 622159ad93SMark Brown #define ADSP1_CONTROL_12 0x0D 632159ad93SMark Brown #define ADSP1_CONTROL_13 0x0F 642159ad93SMark Brown #define ADSP1_CONTROL_14 0x10 652159ad93SMark Brown #define ADSP1_CONTROL_15 0x11 662159ad93SMark Brown #define ADSP1_CONTROL_16 0x12 672159ad93SMark Brown #define ADSP1_CONTROL_17 0x13 682159ad93SMark Brown #define ADSP1_CONTROL_18 0x14 692159ad93SMark Brown #define ADSP1_CONTROL_19 0x16 702159ad93SMark Brown #define ADSP1_CONTROL_20 0x17 712159ad93SMark Brown #define ADSP1_CONTROL_21 0x18 722159ad93SMark Brown #define ADSP1_CONTROL_22 0x1A 732159ad93SMark Brown #define ADSP1_CONTROL_23 0x1B 742159ad93SMark Brown #define ADSP1_CONTROL_24 0x1C 752159ad93SMark Brown #define ADSP1_CONTROL_25 0x1E 762159ad93SMark Brown #define ADSP1_CONTROL_26 0x20 772159ad93SMark Brown #define ADSP1_CONTROL_27 0x21 782159ad93SMark Brown #define ADSP1_CONTROL_28 0x22 792159ad93SMark Brown #define ADSP1_CONTROL_29 0x23 802159ad93SMark Brown #define ADSP1_CONTROL_30 0x24 812159ad93SMark Brown #define ADSP1_CONTROL_31 0x26 822159ad93SMark Brown 832159ad93SMark Brown /* 842159ad93SMark Brown * ADSP1 Control 19 852159ad93SMark Brown */ 862159ad93SMark Brown #define ADSP1_WDMA_BUFFER_LENGTH_MASK 0x00FF /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */ 872159ad93SMark Brown #define ADSP1_WDMA_BUFFER_LENGTH_SHIFT 0 /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */ 882159ad93SMark Brown #define ADSP1_WDMA_BUFFER_LENGTH_WIDTH 8 /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */ 892159ad93SMark Brown 902159ad93SMark Brown 912159ad93SMark Brown /* 922159ad93SMark Brown * ADSP1 Control 30 932159ad93SMark Brown */ 942159ad93SMark Brown #define ADSP1_DBG_CLK_ENA 0x0008 /* DSP1_DBG_CLK_ENA */ 952159ad93SMark Brown #define ADSP1_DBG_CLK_ENA_MASK 0x0008 /* DSP1_DBG_CLK_ENA */ 962159ad93SMark Brown #define ADSP1_DBG_CLK_ENA_SHIFT 3 /* DSP1_DBG_CLK_ENA */ 972159ad93SMark Brown #define ADSP1_DBG_CLK_ENA_WIDTH 1 /* DSP1_DBG_CLK_ENA */ 982159ad93SMark Brown #define ADSP1_SYS_ENA 0x0004 /* DSP1_SYS_ENA */ 992159ad93SMark Brown #define ADSP1_SYS_ENA_MASK 0x0004 /* DSP1_SYS_ENA */ 1002159ad93SMark Brown #define ADSP1_SYS_ENA_SHIFT 2 /* DSP1_SYS_ENA */ 1012159ad93SMark Brown #define ADSP1_SYS_ENA_WIDTH 1 /* DSP1_SYS_ENA */ 1022159ad93SMark Brown #define ADSP1_CORE_ENA 0x0002 /* DSP1_CORE_ENA */ 1032159ad93SMark Brown #define ADSP1_CORE_ENA_MASK 0x0002 /* DSP1_CORE_ENA */ 1042159ad93SMark Brown #define ADSP1_CORE_ENA_SHIFT 1 /* DSP1_CORE_ENA */ 1052159ad93SMark Brown #define ADSP1_CORE_ENA_WIDTH 1 /* DSP1_CORE_ENA */ 1062159ad93SMark Brown #define ADSP1_START 0x0001 /* DSP1_START */ 1072159ad93SMark Brown #define ADSP1_START_MASK 0x0001 /* DSP1_START */ 1082159ad93SMark Brown #define ADSP1_START_SHIFT 0 /* DSP1_START */ 1092159ad93SMark Brown #define ADSP1_START_WIDTH 1 /* DSP1_START */ 1102159ad93SMark Brown 11194e205bfSChris Rattray /* 11294e205bfSChris Rattray * ADSP1 Control 31 11394e205bfSChris Rattray */ 11494e205bfSChris Rattray #define ADSP1_CLK_SEL_MASK 0x0007 /* CLK_SEL_ENA */ 11594e205bfSChris Rattray #define ADSP1_CLK_SEL_SHIFT 0 /* CLK_SEL_ENA */ 11694e205bfSChris Rattray #define ADSP1_CLK_SEL_WIDTH 3 /* CLK_SEL_ENA */ 11794e205bfSChris Rattray 1182d30b575SMark Brown #define ADSP2_CONTROL 0x0 1192d30b575SMark Brown #define ADSP2_CLOCKING 0x1 1202d30b575SMark Brown #define ADSP2_STATUS1 0x4 1212d30b575SMark Brown #define ADSP2_WDMA_CONFIG_1 0x30 1222d30b575SMark Brown #define ADSP2_WDMA_CONFIG_2 0x31 1232d30b575SMark Brown #define ADSP2_RDMA_CONFIG_1 0x34 1242159ad93SMark Brown 12510337b07SRichard Fitzgerald #define ADSP2_SCRATCH0 0x40 12610337b07SRichard Fitzgerald #define ADSP2_SCRATCH1 0x41 12710337b07SRichard Fitzgerald #define ADSP2_SCRATCH2 0x42 12810337b07SRichard Fitzgerald #define ADSP2_SCRATCH3 0x43 12910337b07SRichard Fitzgerald 1302159ad93SMark Brown /* 1312159ad93SMark Brown * ADSP2 Control 1322159ad93SMark Brown */ 1332159ad93SMark Brown 1342159ad93SMark Brown #define ADSP2_MEM_ENA 0x0010 /* DSP1_MEM_ENA */ 1352159ad93SMark Brown #define ADSP2_MEM_ENA_MASK 0x0010 /* DSP1_MEM_ENA */ 1362159ad93SMark Brown #define ADSP2_MEM_ENA_SHIFT 4 /* DSP1_MEM_ENA */ 1372159ad93SMark Brown #define ADSP2_MEM_ENA_WIDTH 1 /* DSP1_MEM_ENA */ 1382159ad93SMark Brown #define ADSP2_SYS_ENA 0x0004 /* DSP1_SYS_ENA */ 1392159ad93SMark Brown #define ADSP2_SYS_ENA_MASK 0x0004 /* DSP1_SYS_ENA */ 1402159ad93SMark Brown #define ADSP2_SYS_ENA_SHIFT 2 /* DSP1_SYS_ENA */ 1412159ad93SMark Brown #define ADSP2_SYS_ENA_WIDTH 1 /* DSP1_SYS_ENA */ 1422159ad93SMark Brown #define ADSP2_CORE_ENA 0x0002 /* DSP1_CORE_ENA */ 1432159ad93SMark Brown #define ADSP2_CORE_ENA_MASK 0x0002 /* DSP1_CORE_ENA */ 1442159ad93SMark Brown #define ADSP2_CORE_ENA_SHIFT 1 /* DSP1_CORE_ENA */ 1452159ad93SMark Brown #define ADSP2_CORE_ENA_WIDTH 1 /* DSP1_CORE_ENA */ 1462159ad93SMark Brown #define ADSP2_START 0x0001 /* DSP1_START */ 1472159ad93SMark Brown #define ADSP2_START_MASK 0x0001 /* DSP1_START */ 1482159ad93SMark Brown #define ADSP2_START_SHIFT 0 /* DSP1_START */ 1492159ad93SMark Brown #define ADSP2_START_WIDTH 1 /* DSP1_START */ 1502159ad93SMark Brown 1512159ad93SMark Brown /* 152973838a0SMark Brown * ADSP2 clocking 153973838a0SMark Brown */ 154973838a0SMark Brown #define ADSP2_CLK_SEL_MASK 0x0007 /* CLK_SEL_ENA */ 155973838a0SMark Brown #define ADSP2_CLK_SEL_SHIFT 0 /* CLK_SEL_ENA */ 156973838a0SMark Brown #define ADSP2_CLK_SEL_WIDTH 3 /* CLK_SEL_ENA */ 157973838a0SMark Brown 158973838a0SMark Brown /* 1592159ad93SMark Brown * ADSP2 Status 1 1602159ad93SMark Brown */ 1612159ad93SMark Brown #define ADSP2_RAM_RDY 0x0001 1622159ad93SMark Brown #define ADSP2_RAM_RDY_MASK 0x0001 1632159ad93SMark Brown #define ADSP2_RAM_RDY_SHIFT 0 1642159ad93SMark Brown #define ADSP2_RAM_RDY_WIDTH 1 1652159ad93SMark Brown 166cf17c83cSMark Brown struct wm_adsp_buf { 167cf17c83cSMark Brown struct list_head list; 168cf17c83cSMark Brown void *buf; 169cf17c83cSMark Brown }; 170cf17c83cSMark Brown 171cf17c83cSMark Brown static struct wm_adsp_buf *wm_adsp_buf_alloc(const void *src, size_t len, 172cf17c83cSMark Brown struct list_head *list) 173cf17c83cSMark Brown { 174cf17c83cSMark Brown struct wm_adsp_buf *buf = kzalloc(sizeof(*buf), GFP_KERNEL); 175cf17c83cSMark Brown 176cf17c83cSMark Brown if (buf == NULL) 177cf17c83cSMark Brown return NULL; 178cf17c83cSMark Brown 179cdcd7f72SCharles Keepax buf->buf = vmalloc(len); 180cf17c83cSMark Brown if (!buf->buf) { 181cdcd7f72SCharles Keepax vfree(buf); 182cf17c83cSMark Brown return NULL; 183cf17c83cSMark Brown } 184cdcd7f72SCharles Keepax memcpy(buf->buf, src, len); 185cf17c83cSMark Brown 186cf17c83cSMark Brown if (list) 187cf17c83cSMark Brown list_add_tail(&buf->list, list); 188cf17c83cSMark Brown 189cf17c83cSMark Brown return buf; 190cf17c83cSMark Brown } 191cf17c83cSMark Brown 192cf17c83cSMark Brown static void wm_adsp_buf_free(struct list_head *list) 193cf17c83cSMark Brown { 194cf17c83cSMark Brown while (!list_empty(list)) { 195cf17c83cSMark Brown struct wm_adsp_buf *buf = list_first_entry(list, 196cf17c83cSMark Brown struct wm_adsp_buf, 197cf17c83cSMark Brown list); 198cf17c83cSMark Brown list_del(&buf->list); 199cdcd7f72SCharles Keepax vfree(buf->buf); 200cf17c83cSMark Brown kfree(buf); 201cf17c83cSMark Brown } 202cf17c83cSMark Brown } 203cf17c83cSMark Brown 204dd84f925SMark Brown #define WM_ADSP_FW_MBC_VSS 0 20504d1300fSCharles Keepax #define WM_ADSP_FW_HIFI 1 20604d1300fSCharles Keepax #define WM_ADSP_FW_TX 2 20704d1300fSCharles Keepax #define WM_ADSP_FW_TX_SPK 3 20804d1300fSCharles Keepax #define WM_ADSP_FW_RX 4 20904d1300fSCharles Keepax #define WM_ADSP_FW_RX_ANC 5 21004d1300fSCharles Keepax #define WM_ADSP_FW_CTRL 6 21104d1300fSCharles Keepax #define WM_ADSP_FW_ASR 7 21204d1300fSCharles Keepax #define WM_ADSP_FW_TRACE 8 21304d1300fSCharles Keepax #define WM_ADSP_FW_SPK_PROT 9 21404d1300fSCharles Keepax #define WM_ADSP_FW_MISC 10 21504d1300fSCharles Keepax 21604d1300fSCharles Keepax #define WM_ADSP_NUM_FW 11 217dd84f925SMark Brown 2181023dbd9SMark Brown static const char *wm_adsp_fw_text[WM_ADSP_NUM_FW] = { 219dd84f925SMark Brown [WM_ADSP_FW_MBC_VSS] = "MBC/VSS", 22004d1300fSCharles Keepax [WM_ADSP_FW_HIFI] = "MasterHiFi", 221dd84f925SMark Brown [WM_ADSP_FW_TX] = "Tx", 222dd84f925SMark Brown [WM_ADSP_FW_TX_SPK] = "Tx Speaker", 22304d1300fSCharles Keepax [WM_ADSP_FW_RX] = "Rx", 224dd84f925SMark Brown [WM_ADSP_FW_RX_ANC] = "Rx ANC", 22504d1300fSCharles Keepax [WM_ADSP_FW_CTRL] = "Voice Ctrl", 22604d1300fSCharles Keepax [WM_ADSP_FW_ASR] = "ASR Assist", 22704d1300fSCharles Keepax [WM_ADSP_FW_TRACE] = "Dbg Trace", 22804d1300fSCharles Keepax [WM_ADSP_FW_SPK_PROT] = "Protection", 22904d1300fSCharles Keepax [WM_ADSP_FW_MISC] = "Misc", 2301023dbd9SMark Brown }; 2311023dbd9SMark Brown 2321023dbd9SMark Brown static struct { 2331023dbd9SMark Brown const char *file; 2341023dbd9SMark Brown } wm_adsp_fw[WM_ADSP_NUM_FW] = { 235dd84f925SMark Brown [WM_ADSP_FW_MBC_VSS] = { .file = "mbc-vss" }, 23604d1300fSCharles Keepax [WM_ADSP_FW_HIFI] = { .file = "hifi" }, 237dd84f925SMark Brown [WM_ADSP_FW_TX] = { .file = "tx" }, 238dd84f925SMark Brown [WM_ADSP_FW_TX_SPK] = { .file = "tx-spk" }, 23904d1300fSCharles Keepax [WM_ADSP_FW_RX] = { .file = "rx" }, 240dd84f925SMark Brown [WM_ADSP_FW_RX_ANC] = { .file = "rx-anc" }, 24104d1300fSCharles Keepax [WM_ADSP_FW_CTRL] = { .file = "ctrl" }, 24204d1300fSCharles Keepax [WM_ADSP_FW_ASR] = { .file = "asr" }, 24304d1300fSCharles Keepax [WM_ADSP_FW_TRACE] = { .file = "trace" }, 24404d1300fSCharles Keepax [WM_ADSP_FW_SPK_PROT] = { .file = "spk-prot" }, 24504d1300fSCharles Keepax [WM_ADSP_FW_MISC] = { .file = "misc" }, 2461023dbd9SMark Brown }; 2471023dbd9SMark Brown 2486ab2b7b4SDimitris Papastamos struct wm_coeff_ctl_ops { 2496ab2b7b4SDimitris Papastamos int (*xget)(struct snd_kcontrol *kcontrol, 2506ab2b7b4SDimitris Papastamos struct snd_ctl_elem_value *ucontrol); 2516ab2b7b4SDimitris Papastamos int (*xput)(struct snd_kcontrol *kcontrol, 2526ab2b7b4SDimitris Papastamos struct snd_ctl_elem_value *ucontrol); 2536ab2b7b4SDimitris Papastamos int (*xinfo)(struct snd_kcontrol *kcontrol, 2546ab2b7b4SDimitris Papastamos struct snd_ctl_elem_info *uinfo); 2556ab2b7b4SDimitris Papastamos }; 2566ab2b7b4SDimitris Papastamos 2576ab2b7b4SDimitris Papastamos struct wm_coeff_ctl { 2586ab2b7b4SDimitris Papastamos const char *name; 2592323736dSCharles Keepax const char *fw_name; 2603809f001SCharles Keepax struct wm_adsp_alg_region alg_region; 2616ab2b7b4SDimitris Papastamos struct wm_coeff_ctl_ops ops; 2623809f001SCharles Keepax struct wm_adsp *dsp; 2636ab2b7b4SDimitris Papastamos unsigned int enabled:1; 2646ab2b7b4SDimitris Papastamos struct list_head list; 2656ab2b7b4SDimitris Papastamos void *cache; 2662323736dSCharles Keepax unsigned int offset; 2676ab2b7b4SDimitris Papastamos size_t len; 2680c2e3f34SDimitris Papastamos unsigned int set:1; 2696ab2b7b4SDimitris Papastamos struct snd_kcontrol *kcontrol; 27026c22a19SCharles Keepax unsigned int flags; 2716ab2b7b4SDimitris Papastamos }; 2726ab2b7b4SDimitris Papastamos 273f9f55e31SRichard Fitzgerald #ifdef CONFIG_DEBUG_FS 274f9f55e31SRichard Fitzgerald static void wm_adsp_debugfs_save_wmfwname(struct wm_adsp *dsp, const char *s) 275f9f55e31SRichard Fitzgerald { 276f9f55e31SRichard Fitzgerald char *tmp = kasprintf(GFP_KERNEL, "%s\n", s); 277f9f55e31SRichard Fitzgerald 278f9f55e31SRichard Fitzgerald kfree(dsp->wmfw_file_name); 279f9f55e31SRichard Fitzgerald dsp->wmfw_file_name = tmp; 280f9f55e31SRichard Fitzgerald } 281f9f55e31SRichard Fitzgerald 282f9f55e31SRichard Fitzgerald static void wm_adsp_debugfs_save_binname(struct wm_adsp *dsp, const char *s) 283f9f55e31SRichard Fitzgerald { 284f9f55e31SRichard Fitzgerald char *tmp = kasprintf(GFP_KERNEL, "%s\n", s); 285f9f55e31SRichard Fitzgerald 286f9f55e31SRichard Fitzgerald kfree(dsp->bin_file_name); 287f9f55e31SRichard Fitzgerald dsp->bin_file_name = tmp; 288f9f55e31SRichard Fitzgerald } 289f9f55e31SRichard Fitzgerald 290f9f55e31SRichard Fitzgerald static void wm_adsp_debugfs_clear(struct wm_adsp *dsp) 291f9f55e31SRichard Fitzgerald { 292f9f55e31SRichard Fitzgerald kfree(dsp->wmfw_file_name); 293f9f55e31SRichard Fitzgerald kfree(dsp->bin_file_name); 294f9f55e31SRichard Fitzgerald dsp->wmfw_file_name = NULL; 295f9f55e31SRichard Fitzgerald dsp->bin_file_name = NULL; 296f9f55e31SRichard Fitzgerald } 297f9f55e31SRichard Fitzgerald 298f9f55e31SRichard Fitzgerald static ssize_t wm_adsp_debugfs_wmfw_read(struct file *file, 299f9f55e31SRichard Fitzgerald char __user *user_buf, 300f9f55e31SRichard Fitzgerald size_t count, loff_t *ppos) 301f9f55e31SRichard Fitzgerald { 302f9f55e31SRichard Fitzgerald struct wm_adsp *dsp = file->private_data; 303f9f55e31SRichard Fitzgerald ssize_t ret; 304f9f55e31SRichard Fitzgerald 305078e7183SCharles Keepax mutex_lock(&dsp->pwr_lock); 306f9f55e31SRichard Fitzgerald 307f9f55e31SRichard Fitzgerald if (!dsp->wmfw_file_name || !dsp->running) 308f9f55e31SRichard Fitzgerald ret = 0; 309f9f55e31SRichard Fitzgerald else 310f9f55e31SRichard Fitzgerald ret = simple_read_from_buffer(user_buf, count, ppos, 311f9f55e31SRichard Fitzgerald dsp->wmfw_file_name, 312f9f55e31SRichard Fitzgerald strlen(dsp->wmfw_file_name)); 313f9f55e31SRichard Fitzgerald 314078e7183SCharles Keepax mutex_unlock(&dsp->pwr_lock); 315f9f55e31SRichard Fitzgerald return ret; 316f9f55e31SRichard Fitzgerald } 317f9f55e31SRichard Fitzgerald 318f9f55e31SRichard Fitzgerald static ssize_t wm_adsp_debugfs_bin_read(struct file *file, 319f9f55e31SRichard Fitzgerald char __user *user_buf, 320f9f55e31SRichard Fitzgerald size_t count, loff_t *ppos) 321f9f55e31SRichard Fitzgerald { 322f9f55e31SRichard Fitzgerald struct wm_adsp *dsp = file->private_data; 323f9f55e31SRichard Fitzgerald ssize_t ret; 324f9f55e31SRichard Fitzgerald 325078e7183SCharles Keepax mutex_lock(&dsp->pwr_lock); 326f9f55e31SRichard Fitzgerald 327f9f55e31SRichard Fitzgerald if (!dsp->bin_file_name || !dsp->running) 328f9f55e31SRichard Fitzgerald ret = 0; 329f9f55e31SRichard Fitzgerald else 330f9f55e31SRichard Fitzgerald ret = simple_read_from_buffer(user_buf, count, ppos, 331f9f55e31SRichard Fitzgerald dsp->bin_file_name, 332f9f55e31SRichard Fitzgerald strlen(dsp->bin_file_name)); 333f9f55e31SRichard Fitzgerald 334078e7183SCharles Keepax mutex_unlock(&dsp->pwr_lock); 335f9f55e31SRichard Fitzgerald return ret; 336f9f55e31SRichard Fitzgerald } 337f9f55e31SRichard Fitzgerald 338f9f55e31SRichard Fitzgerald static const struct { 339f9f55e31SRichard Fitzgerald const char *name; 340f9f55e31SRichard Fitzgerald const struct file_operations fops; 341f9f55e31SRichard Fitzgerald } wm_adsp_debugfs_fops[] = { 342f9f55e31SRichard Fitzgerald { 343f9f55e31SRichard Fitzgerald .name = "wmfw_file_name", 344f9f55e31SRichard Fitzgerald .fops = { 345f9f55e31SRichard Fitzgerald .open = simple_open, 346f9f55e31SRichard Fitzgerald .read = wm_adsp_debugfs_wmfw_read, 347f9f55e31SRichard Fitzgerald }, 348f9f55e31SRichard Fitzgerald }, 349f9f55e31SRichard Fitzgerald { 350f9f55e31SRichard Fitzgerald .name = "bin_file_name", 351f9f55e31SRichard Fitzgerald .fops = { 352f9f55e31SRichard Fitzgerald .open = simple_open, 353f9f55e31SRichard Fitzgerald .read = wm_adsp_debugfs_bin_read, 354f9f55e31SRichard Fitzgerald }, 355f9f55e31SRichard Fitzgerald }, 356f9f55e31SRichard Fitzgerald }; 357f9f55e31SRichard Fitzgerald 358f9f55e31SRichard Fitzgerald static void wm_adsp2_init_debugfs(struct wm_adsp *dsp, 359f9f55e31SRichard Fitzgerald struct snd_soc_codec *codec) 360f9f55e31SRichard Fitzgerald { 361f9f55e31SRichard Fitzgerald struct dentry *root = NULL; 362f9f55e31SRichard Fitzgerald char *root_name; 363f9f55e31SRichard Fitzgerald int i; 364f9f55e31SRichard Fitzgerald 365f9f55e31SRichard Fitzgerald if (!codec->component.debugfs_root) { 366f9f55e31SRichard Fitzgerald adsp_err(dsp, "No codec debugfs root\n"); 367f9f55e31SRichard Fitzgerald goto err; 368f9f55e31SRichard Fitzgerald } 369f9f55e31SRichard Fitzgerald 370f9f55e31SRichard Fitzgerald root_name = kmalloc(PAGE_SIZE, GFP_KERNEL); 371f9f55e31SRichard Fitzgerald if (!root_name) 372f9f55e31SRichard Fitzgerald goto err; 373f9f55e31SRichard Fitzgerald 374f9f55e31SRichard Fitzgerald snprintf(root_name, PAGE_SIZE, "dsp%d", dsp->num); 375f9f55e31SRichard Fitzgerald root = debugfs_create_dir(root_name, codec->component.debugfs_root); 376f9f55e31SRichard Fitzgerald kfree(root_name); 377f9f55e31SRichard Fitzgerald 378f9f55e31SRichard Fitzgerald if (!root) 379f9f55e31SRichard Fitzgerald goto err; 380f9f55e31SRichard Fitzgerald 381f9f55e31SRichard Fitzgerald if (!debugfs_create_bool("running", S_IRUGO, root, &dsp->running)) 382f9f55e31SRichard Fitzgerald goto err; 383f9f55e31SRichard Fitzgerald 384f9f55e31SRichard Fitzgerald if (!debugfs_create_x32("fw_id", S_IRUGO, root, &dsp->fw_id)) 385f9f55e31SRichard Fitzgerald goto err; 386f9f55e31SRichard Fitzgerald 387f9f55e31SRichard Fitzgerald if (!debugfs_create_x32("fw_version", S_IRUGO, root, 388f9f55e31SRichard Fitzgerald &dsp->fw_id_version)) 389f9f55e31SRichard Fitzgerald goto err; 390f9f55e31SRichard Fitzgerald 391f9f55e31SRichard Fitzgerald for (i = 0; i < ARRAY_SIZE(wm_adsp_debugfs_fops); ++i) { 392f9f55e31SRichard Fitzgerald if (!debugfs_create_file(wm_adsp_debugfs_fops[i].name, 393f9f55e31SRichard Fitzgerald S_IRUGO, root, dsp, 394f9f55e31SRichard Fitzgerald &wm_adsp_debugfs_fops[i].fops)) 395f9f55e31SRichard Fitzgerald goto err; 396f9f55e31SRichard Fitzgerald } 397f9f55e31SRichard Fitzgerald 398f9f55e31SRichard Fitzgerald dsp->debugfs_root = root; 399f9f55e31SRichard Fitzgerald return; 400f9f55e31SRichard Fitzgerald 401f9f55e31SRichard Fitzgerald err: 402f9f55e31SRichard Fitzgerald debugfs_remove_recursive(root); 403f9f55e31SRichard Fitzgerald adsp_err(dsp, "Failed to create debugfs\n"); 404f9f55e31SRichard Fitzgerald } 405f9f55e31SRichard Fitzgerald 406f9f55e31SRichard Fitzgerald static void wm_adsp2_cleanup_debugfs(struct wm_adsp *dsp) 407f9f55e31SRichard Fitzgerald { 408f9f55e31SRichard Fitzgerald wm_adsp_debugfs_clear(dsp); 409f9f55e31SRichard Fitzgerald debugfs_remove_recursive(dsp->debugfs_root); 410f9f55e31SRichard Fitzgerald } 411f9f55e31SRichard Fitzgerald #else 412f9f55e31SRichard Fitzgerald static inline void wm_adsp2_init_debugfs(struct wm_adsp *dsp, 413f9f55e31SRichard Fitzgerald struct snd_soc_codec *codec) 414f9f55e31SRichard Fitzgerald { 415f9f55e31SRichard Fitzgerald } 416f9f55e31SRichard Fitzgerald 417f9f55e31SRichard Fitzgerald static inline void wm_adsp2_cleanup_debugfs(struct wm_adsp *dsp) 418f9f55e31SRichard Fitzgerald { 419f9f55e31SRichard Fitzgerald } 420f9f55e31SRichard Fitzgerald 421f9f55e31SRichard Fitzgerald static inline void wm_adsp_debugfs_save_wmfwname(struct wm_adsp *dsp, 422f9f55e31SRichard Fitzgerald const char *s) 423f9f55e31SRichard Fitzgerald { 424f9f55e31SRichard Fitzgerald } 425f9f55e31SRichard Fitzgerald 426f9f55e31SRichard Fitzgerald static inline void wm_adsp_debugfs_save_binname(struct wm_adsp *dsp, 427f9f55e31SRichard Fitzgerald const char *s) 428f9f55e31SRichard Fitzgerald { 429f9f55e31SRichard Fitzgerald } 430f9f55e31SRichard Fitzgerald 431f9f55e31SRichard Fitzgerald static inline void wm_adsp_debugfs_clear(struct wm_adsp *dsp) 432f9f55e31SRichard Fitzgerald { 433f9f55e31SRichard Fitzgerald } 434f9f55e31SRichard Fitzgerald #endif 435f9f55e31SRichard Fitzgerald 4361023dbd9SMark Brown static int wm_adsp_fw_get(struct snd_kcontrol *kcontrol, 4371023dbd9SMark Brown struct snd_ctl_elem_value *ucontrol) 4381023dbd9SMark Brown { 439ea53bf77SLars-Peter Clausen struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); 4401023dbd9SMark Brown struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; 4413809f001SCharles Keepax struct wm_adsp *dsp = snd_soc_codec_get_drvdata(codec); 4421023dbd9SMark Brown 4433809f001SCharles Keepax ucontrol->value.integer.value[0] = dsp[e->shift_l].fw; 4441023dbd9SMark Brown 4451023dbd9SMark Brown return 0; 4461023dbd9SMark Brown } 4471023dbd9SMark Brown 4481023dbd9SMark Brown static int wm_adsp_fw_put(struct snd_kcontrol *kcontrol, 4491023dbd9SMark Brown struct snd_ctl_elem_value *ucontrol) 4501023dbd9SMark Brown { 451ea53bf77SLars-Peter Clausen struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); 4521023dbd9SMark Brown struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; 4533809f001SCharles Keepax struct wm_adsp *dsp = snd_soc_codec_get_drvdata(codec); 454d27c5e15SCharles Keepax int ret = 0; 4551023dbd9SMark Brown 4563809f001SCharles Keepax if (ucontrol->value.integer.value[0] == dsp[e->shift_l].fw) 4571023dbd9SMark Brown return 0; 4581023dbd9SMark Brown 4591023dbd9SMark Brown if (ucontrol->value.integer.value[0] >= WM_ADSP_NUM_FW) 4601023dbd9SMark Brown return -EINVAL; 4611023dbd9SMark Brown 462d27c5e15SCharles Keepax mutex_lock(&dsp[e->shift_l].pwr_lock); 4631023dbd9SMark Brown 464d27c5e15SCharles Keepax if (dsp[e->shift_l].running) 465d27c5e15SCharles Keepax ret = -EBUSY; 466d27c5e15SCharles Keepax else 4673809f001SCharles Keepax dsp[e->shift_l].fw = ucontrol->value.integer.value[0]; 4681023dbd9SMark Brown 469d27c5e15SCharles Keepax mutex_unlock(&dsp[e->shift_l].pwr_lock); 470d27c5e15SCharles Keepax 471d27c5e15SCharles Keepax return ret; 4721023dbd9SMark Brown } 4731023dbd9SMark Brown 4741023dbd9SMark Brown static const struct soc_enum wm_adsp_fw_enum[] = { 4751023dbd9SMark Brown SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 4761023dbd9SMark Brown SOC_ENUM_SINGLE(0, 1, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 4771023dbd9SMark Brown SOC_ENUM_SINGLE(0, 2, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 4781023dbd9SMark Brown SOC_ENUM_SINGLE(0, 3, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 4791023dbd9SMark Brown }; 4801023dbd9SMark Brown 481336d0442SRichard Fitzgerald const struct snd_kcontrol_new wm_adsp_fw_controls[] = { 4821023dbd9SMark Brown SOC_ENUM_EXT("DSP1 Firmware", wm_adsp_fw_enum[0], 4831023dbd9SMark Brown wm_adsp_fw_get, wm_adsp_fw_put), 4841023dbd9SMark Brown SOC_ENUM_EXT("DSP2 Firmware", wm_adsp_fw_enum[1], 4851023dbd9SMark Brown wm_adsp_fw_get, wm_adsp_fw_put), 4861023dbd9SMark Brown SOC_ENUM_EXT("DSP3 Firmware", wm_adsp_fw_enum[2], 4871023dbd9SMark Brown wm_adsp_fw_get, wm_adsp_fw_put), 4881023dbd9SMark Brown SOC_ENUM_EXT("DSP4 Firmware", wm_adsp_fw_enum[3], 4891023dbd9SMark Brown wm_adsp_fw_get, wm_adsp_fw_put), 4901023dbd9SMark Brown }; 491336d0442SRichard Fitzgerald EXPORT_SYMBOL_GPL(wm_adsp_fw_controls); 4922159ad93SMark Brown 4932159ad93SMark Brown static struct wm_adsp_region const *wm_adsp_find_region(struct wm_adsp *dsp, 4942159ad93SMark Brown int type) 4952159ad93SMark Brown { 4962159ad93SMark Brown int i; 4972159ad93SMark Brown 4982159ad93SMark Brown for (i = 0; i < dsp->num_mems; i++) 4992159ad93SMark Brown if (dsp->mem[i].type == type) 5002159ad93SMark Brown return &dsp->mem[i]; 5012159ad93SMark Brown 5022159ad93SMark Brown return NULL; 5032159ad93SMark Brown } 5042159ad93SMark Brown 5053809f001SCharles Keepax static unsigned int wm_adsp_region_to_reg(struct wm_adsp_region const *mem, 50645b9ee72SMark Brown unsigned int offset) 50745b9ee72SMark Brown { 5083809f001SCharles Keepax if (WARN_ON(!mem)) 5096c452bdaSTakashi Iwai return offset; 5103809f001SCharles Keepax switch (mem->type) { 51145b9ee72SMark Brown case WMFW_ADSP1_PM: 5123809f001SCharles Keepax return mem->base + (offset * 3); 51345b9ee72SMark Brown case WMFW_ADSP1_DM: 5143809f001SCharles Keepax return mem->base + (offset * 2); 51545b9ee72SMark Brown case WMFW_ADSP2_XM: 5163809f001SCharles Keepax return mem->base + (offset * 2); 51745b9ee72SMark Brown case WMFW_ADSP2_YM: 5183809f001SCharles Keepax return mem->base + (offset * 2); 51945b9ee72SMark Brown case WMFW_ADSP1_ZM: 5203809f001SCharles Keepax return mem->base + (offset * 2); 52145b9ee72SMark Brown default: 5226c452bdaSTakashi Iwai WARN(1, "Unknown memory region type"); 52345b9ee72SMark Brown return offset; 52445b9ee72SMark Brown } 52545b9ee72SMark Brown } 52645b9ee72SMark Brown 52710337b07SRichard Fitzgerald static void wm_adsp2_show_fw_status(struct wm_adsp *dsp) 52810337b07SRichard Fitzgerald { 52910337b07SRichard Fitzgerald u16 scratch[4]; 53010337b07SRichard Fitzgerald int ret; 53110337b07SRichard Fitzgerald 53210337b07SRichard Fitzgerald ret = regmap_raw_read(dsp->regmap, dsp->base + ADSP2_SCRATCH0, 53310337b07SRichard Fitzgerald scratch, sizeof(scratch)); 53410337b07SRichard Fitzgerald if (ret) { 53510337b07SRichard Fitzgerald adsp_err(dsp, "Failed to read SCRATCH regs: %d\n", ret); 53610337b07SRichard Fitzgerald return; 53710337b07SRichard Fitzgerald } 53810337b07SRichard Fitzgerald 53910337b07SRichard Fitzgerald adsp_dbg(dsp, "FW SCRATCH 0:0x%x 1:0x%x 2:0x%x 3:0x%x\n", 54010337b07SRichard Fitzgerald be16_to_cpu(scratch[0]), 54110337b07SRichard Fitzgerald be16_to_cpu(scratch[1]), 54210337b07SRichard Fitzgerald be16_to_cpu(scratch[2]), 54310337b07SRichard Fitzgerald be16_to_cpu(scratch[3])); 54410337b07SRichard Fitzgerald } 54510337b07SRichard Fitzgerald 5467585a5b0SCharles Keepax static int wm_coeff_info(struct snd_kcontrol *kctl, 5476ab2b7b4SDimitris Papastamos struct snd_ctl_elem_info *uinfo) 5486ab2b7b4SDimitris Papastamos { 5497585a5b0SCharles Keepax struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kctl->private_value; 5506ab2b7b4SDimitris Papastamos 5516ab2b7b4SDimitris Papastamos uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; 5526ab2b7b4SDimitris Papastamos uinfo->count = ctl->len; 5536ab2b7b4SDimitris Papastamos return 0; 5546ab2b7b4SDimitris Papastamos } 5556ab2b7b4SDimitris Papastamos 556c9f8dd71SCharles Keepax static int wm_coeff_write_control(struct wm_coeff_ctl *ctl, 5576ab2b7b4SDimitris Papastamos const void *buf, size_t len) 5586ab2b7b4SDimitris Papastamos { 5593809f001SCharles Keepax struct wm_adsp_alg_region *alg_region = &ctl->alg_region; 5606ab2b7b4SDimitris Papastamos const struct wm_adsp_region *mem; 5613809f001SCharles Keepax struct wm_adsp *dsp = ctl->dsp; 5626ab2b7b4SDimitris Papastamos void *scratch; 5636ab2b7b4SDimitris Papastamos int ret; 5646ab2b7b4SDimitris Papastamos unsigned int reg; 5656ab2b7b4SDimitris Papastamos 5663809f001SCharles Keepax mem = wm_adsp_find_region(dsp, alg_region->type); 5676ab2b7b4SDimitris Papastamos if (!mem) { 5683809f001SCharles Keepax adsp_err(dsp, "No base for region %x\n", 5693809f001SCharles Keepax alg_region->type); 5706ab2b7b4SDimitris Papastamos return -EINVAL; 5716ab2b7b4SDimitris Papastamos } 5726ab2b7b4SDimitris Papastamos 5732323736dSCharles Keepax reg = ctl->alg_region.base + ctl->offset; 5746ab2b7b4SDimitris Papastamos reg = wm_adsp_region_to_reg(mem, reg); 5756ab2b7b4SDimitris Papastamos 5766ab2b7b4SDimitris Papastamos scratch = kmemdup(buf, ctl->len, GFP_KERNEL | GFP_DMA); 5776ab2b7b4SDimitris Papastamos if (!scratch) 5786ab2b7b4SDimitris Papastamos return -ENOMEM; 5796ab2b7b4SDimitris Papastamos 5803809f001SCharles Keepax ret = regmap_raw_write(dsp->regmap, reg, scratch, 5816ab2b7b4SDimitris Papastamos ctl->len); 5826ab2b7b4SDimitris Papastamos if (ret) { 5833809f001SCharles Keepax adsp_err(dsp, "Failed to write %zu bytes to %x: %d\n", 58443bc3bf6SDimitris Papastamos ctl->len, reg, ret); 5856ab2b7b4SDimitris Papastamos kfree(scratch); 5866ab2b7b4SDimitris Papastamos return ret; 5876ab2b7b4SDimitris Papastamos } 5883809f001SCharles Keepax adsp_dbg(dsp, "Wrote %zu bytes to %x\n", ctl->len, reg); 5896ab2b7b4SDimitris Papastamos 5906ab2b7b4SDimitris Papastamos kfree(scratch); 5916ab2b7b4SDimitris Papastamos 5926ab2b7b4SDimitris Papastamos return 0; 5936ab2b7b4SDimitris Papastamos } 5946ab2b7b4SDimitris Papastamos 5957585a5b0SCharles Keepax static int wm_coeff_put(struct snd_kcontrol *kctl, 5966ab2b7b4SDimitris Papastamos struct snd_ctl_elem_value *ucontrol) 5976ab2b7b4SDimitris Papastamos { 5987585a5b0SCharles Keepax struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kctl->private_value; 5996ab2b7b4SDimitris Papastamos char *p = ucontrol->value.bytes.data; 600168d10e7SCharles Keepax int ret = 0; 601168d10e7SCharles Keepax 602168d10e7SCharles Keepax mutex_lock(&ctl->dsp->pwr_lock); 6036ab2b7b4SDimitris Papastamos 6046ab2b7b4SDimitris Papastamos memcpy(ctl->cache, p, ctl->len); 6056ab2b7b4SDimitris Papastamos 6060c2e3f34SDimitris Papastamos ctl->set = 1; 607168d10e7SCharles Keepax if (ctl->enabled) 608168d10e7SCharles Keepax ret = wm_coeff_write_control(ctl, p, ctl->len); 6096ab2b7b4SDimitris Papastamos 610168d10e7SCharles Keepax mutex_unlock(&ctl->dsp->pwr_lock); 611168d10e7SCharles Keepax 612168d10e7SCharles Keepax return ret; 6136ab2b7b4SDimitris Papastamos } 6146ab2b7b4SDimitris Papastamos 615c9f8dd71SCharles Keepax static int wm_coeff_read_control(struct wm_coeff_ctl *ctl, 6166ab2b7b4SDimitris Papastamos void *buf, size_t len) 6176ab2b7b4SDimitris Papastamos { 6183809f001SCharles Keepax struct wm_adsp_alg_region *alg_region = &ctl->alg_region; 6196ab2b7b4SDimitris Papastamos const struct wm_adsp_region *mem; 6203809f001SCharles Keepax struct wm_adsp *dsp = ctl->dsp; 6216ab2b7b4SDimitris Papastamos void *scratch; 6226ab2b7b4SDimitris Papastamos int ret; 6236ab2b7b4SDimitris Papastamos unsigned int reg; 6246ab2b7b4SDimitris Papastamos 6253809f001SCharles Keepax mem = wm_adsp_find_region(dsp, alg_region->type); 6266ab2b7b4SDimitris Papastamos if (!mem) { 6273809f001SCharles Keepax adsp_err(dsp, "No base for region %x\n", 6283809f001SCharles Keepax alg_region->type); 6296ab2b7b4SDimitris Papastamos return -EINVAL; 6306ab2b7b4SDimitris Papastamos } 6316ab2b7b4SDimitris Papastamos 6322323736dSCharles Keepax reg = ctl->alg_region.base + ctl->offset; 6336ab2b7b4SDimitris Papastamos reg = wm_adsp_region_to_reg(mem, reg); 6346ab2b7b4SDimitris Papastamos 6356ab2b7b4SDimitris Papastamos scratch = kmalloc(ctl->len, GFP_KERNEL | GFP_DMA); 6366ab2b7b4SDimitris Papastamos if (!scratch) 6376ab2b7b4SDimitris Papastamos return -ENOMEM; 6386ab2b7b4SDimitris Papastamos 6393809f001SCharles Keepax ret = regmap_raw_read(dsp->regmap, reg, scratch, ctl->len); 6406ab2b7b4SDimitris Papastamos if (ret) { 6413809f001SCharles Keepax adsp_err(dsp, "Failed to read %zu bytes from %x: %d\n", 64243bc3bf6SDimitris Papastamos ctl->len, reg, ret); 6436ab2b7b4SDimitris Papastamos kfree(scratch); 6446ab2b7b4SDimitris Papastamos return ret; 6456ab2b7b4SDimitris Papastamos } 6463809f001SCharles Keepax adsp_dbg(dsp, "Read %zu bytes from %x\n", ctl->len, reg); 6476ab2b7b4SDimitris Papastamos 6486ab2b7b4SDimitris Papastamos memcpy(buf, scratch, ctl->len); 6496ab2b7b4SDimitris Papastamos kfree(scratch); 6506ab2b7b4SDimitris Papastamos 6516ab2b7b4SDimitris Papastamos return 0; 6526ab2b7b4SDimitris Papastamos } 6536ab2b7b4SDimitris Papastamos 6547585a5b0SCharles Keepax static int wm_coeff_get(struct snd_kcontrol *kctl, 6556ab2b7b4SDimitris Papastamos struct snd_ctl_elem_value *ucontrol) 6566ab2b7b4SDimitris Papastamos { 6577585a5b0SCharles Keepax struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kctl->private_value; 6586ab2b7b4SDimitris Papastamos char *p = ucontrol->value.bytes.data; 659168d10e7SCharles Keepax int ret = 0; 660168d10e7SCharles Keepax 661168d10e7SCharles Keepax mutex_lock(&ctl->dsp->pwr_lock); 6626ab2b7b4SDimitris Papastamos 66326c22a19SCharles Keepax if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) { 66426c22a19SCharles Keepax if (ctl->enabled) 665168d10e7SCharles Keepax ret = wm_coeff_read_control(ctl, p, ctl->len); 66626c22a19SCharles Keepax else 667168d10e7SCharles Keepax ret = -EPERM; 668168d10e7SCharles Keepax } else { 669bc1765d6SCharles Keepax if (!ctl->flags && ctl->enabled) 670bc1765d6SCharles Keepax ret = wm_coeff_read_control(ctl, ctl->cache, ctl->len); 671bc1765d6SCharles Keepax 672168d10e7SCharles Keepax memcpy(p, ctl->cache, ctl->len); 67326c22a19SCharles Keepax } 67426c22a19SCharles Keepax 675168d10e7SCharles Keepax mutex_unlock(&ctl->dsp->pwr_lock); 67626c22a19SCharles Keepax 677168d10e7SCharles Keepax return ret; 6786ab2b7b4SDimitris Papastamos } 6796ab2b7b4SDimitris Papastamos 6806ab2b7b4SDimitris Papastamos struct wmfw_ctl_work { 6813809f001SCharles Keepax struct wm_adsp *dsp; 6826ab2b7b4SDimitris Papastamos struct wm_coeff_ctl *ctl; 6836ab2b7b4SDimitris Papastamos struct work_struct work; 6846ab2b7b4SDimitris Papastamos }; 6856ab2b7b4SDimitris Papastamos 6863809f001SCharles Keepax static int wmfw_add_ctl(struct wm_adsp *dsp, struct wm_coeff_ctl *ctl) 6876ab2b7b4SDimitris Papastamos { 6886ab2b7b4SDimitris Papastamos struct snd_kcontrol_new *kcontrol; 6896ab2b7b4SDimitris Papastamos int ret; 6906ab2b7b4SDimitris Papastamos 69192bb4c32SDimitris Papastamos if (!ctl || !ctl->name) 6926ab2b7b4SDimitris Papastamos return -EINVAL; 6936ab2b7b4SDimitris Papastamos 6946ab2b7b4SDimitris Papastamos kcontrol = kzalloc(sizeof(*kcontrol), GFP_KERNEL); 6956ab2b7b4SDimitris Papastamos if (!kcontrol) 6966ab2b7b4SDimitris Papastamos return -ENOMEM; 6976ab2b7b4SDimitris Papastamos kcontrol->iface = SNDRV_CTL_ELEM_IFACE_MIXER; 6986ab2b7b4SDimitris Papastamos 6996ab2b7b4SDimitris Papastamos kcontrol->name = ctl->name; 7006ab2b7b4SDimitris Papastamos kcontrol->info = wm_coeff_info; 7016ab2b7b4SDimitris Papastamos kcontrol->get = wm_coeff_get; 7026ab2b7b4SDimitris Papastamos kcontrol->put = wm_coeff_put; 7036ab2b7b4SDimitris Papastamos kcontrol->private_value = (unsigned long)ctl; 7046ab2b7b4SDimitris Papastamos 70526c22a19SCharles Keepax if (ctl->flags) { 70626c22a19SCharles Keepax if (ctl->flags & WMFW_CTL_FLAG_WRITEABLE) 70726c22a19SCharles Keepax kcontrol->access |= SNDRV_CTL_ELEM_ACCESS_WRITE; 70826c22a19SCharles Keepax if (ctl->flags & WMFW_CTL_FLAG_READABLE) 70926c22a19SCharles Keepax kcontrol->access |= SNDRV_CTL_ELEM_ACCESS_READ; 71026c22a19SCharles Keepax if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) 71126c22a19SCharles Keepax kcontrol->access |= SNDRV_CTL_ELEM_ACCESS_VOLATILE; 71226c22a19SCharles Keepax } 71326c22a19SCharles Keepax 7143809f001SCharles Keepax ret = snd_soc_add_card_controls(dsp->card, 71581ad93ecSDimitris Papastamos kcontrol, 1); 7166ab2b7b4SDimitris Papastamos if (ret < 0) 7176ab2b7b4SDimitris Papastamos goto err_kcontrol; 7186ab2b7b4SDimitris Papastamos 7196ab2b7b4SDimitris Papastamos kfree(kcontrol); 7206ab2b7b4SDimitris Papastamos 7213809f001SCharles Keepax ctl->kcontrol = snd_soc_card_get_kcontrol(dsp->card, 72281ad93ecSDimitris Papastamos ctl->name); 72381ad93ecSDimitris Papastamos 7246ab2b7b4SDimitris Papastamos return 0; 7256ab2b7b4SDimitris Papastamos 7266ab2b7b4SDimitris Papastamos err_kcontrol: 7276ab2b7b4SDimitris Papastamos kfree(kcontrol); 7286ab2b7b4SDimitris Papastamos return ret; 7296ab2b7b4SDimitris Papastamos } 7306ab2b7b4SDimitris Papastamos 731b21acc1cSCharles Keepax static int wm_coeff_init_control_caches(struct wm_adsp *dsp) 732b21acc1cSCharles Keepax { 733b21acc1cSCharles Keepax struct wm_coeff_ctl *ctl; 734b21acc1cSCharles Keepax int ret; 735b21acc1cSCharles Keepax 736b21acc1cSCharles Keepax list_for_each_entry(ctl, &dsp->ctl_list, list) { 737b21acc1cSCharles Keepax if (!ctl->enabled || ctl->set) 738b21acc1cSCharles Keepax continue; 73926c22a19SCharles Keepax if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) 74026c22a19SCharles Keepax continue; 74126c22a19SCharles Keepax 742b21acc1cSCharles Keepax ret = wm_coeff_read_control(ctl, 743b21acc1cSCharles Keepax ctl->cache, 744b21acc1cSCharles Keepax ctl->len); 745b21acc1cSCharles Keepax if (ret < 0) 746b21acc1cSCharles Keepax return ret; 747b21acc1cSCharles Keepax } 748b21acc1cSCharles Keepax 749b21acc1cSCharles Keepax return 0; 750b21acc1cSCharles Keepax } 751b21acc1cSCharles Keepax 752b21acc1cSCharles Keepax static int wm_coeff_sync_controls(struct wm_adsp *dsp) 753b21acc1cSCharles Keepax { 754b21acc1cSCharles Keepax struct wm_coeff_ctl *ctl; 755b21acc1cSCharles Keepax int ret; 756b21acc1cSCharles Keepax 757b21acc1cSCharles Keepax list_for_each_entry(ctl, &dsp->ctl_list, list) { 758b21acc1cSCharles Keepax if (!ctl->enabled) 759b21acc1cSCharles Keepax continue; 76026c22a19SCharles Keepax if (ctl->set && !(ctl->flags & WMFW_CTL_FLAG_VOLATILE)) { 761b21acc1cSCharles Keepax ret = wm_coeff_write_control(ctl, 762b21acc1cSCharles Keepax ctl->cache, 763b21acc1cSCharles Keepax ctl->len); 764b21acc1cSCharles Keepax if (ret < 0) 765b21acc1cSCharles Keepax return ret; 766b21acc1cSCharles Keepax } 767b21acc1cSCharles Keepax } 768b21acc1cSCharles Keepax 769b21acc1cSCharles Keepax return 0; 770b21acc1cSCharles Keepax } 771b21acc1cSCharles Keepax 772b21acc1cSCharles Keepax static void wm_adsp_ctl_work(struct work_struct *work) 773b21acc1cSCharles Keepax { 774b21acc1cSCharles Keepax struct wmfw_ctl_work *ctl_work = container_of(work, 775b21acc1cSCharles Keepax struct wmfw_ctl_work, 776b21acc1cSCharles Keepax work); 777b21acc1cSCharles Keepax 778b21acc1cSCharles Keepax wmfw_add_ctl(ctl_work->dsp, ctl_work->ctl); 779b21acc1cSCharles Keepax kfree(ctl_work); 780b21acc1cSCharles Keepax } 781b21acc1cSCharles Keepax 782b21acc1cSCharles Keepax static int wm_adsp_create_control(struct wm_adsp *dsp, 783b21acc1cSCharles Keepax const struct wm_adsp_alg_region *alg_region, 7842323736dSCharles Keepax unsigned int offset, unsigned int len, 78526c22a19SCharles Keepax const char *subname, unsigned int subname_len, 78626c22a19SCharles Keepax unsigned int flags) 787b21acc1cSCharles Keepax { 788b21acc1cSCharles Keepax struct wm_coeff_ctl *ctl; 789b21acc1cSCharles Keepax struct wmfw_ctl_work *ctl_work; 790b21acc1cSCharles Keepax char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; 791b21acc1cSCharles Keepax char *region_name; 792b21acc1cSCharles Keepax int ret; 793b21acc1cSCharles Keepax 79426c22a19SCharles Keepax if (flags & WMFW_CTL_FLAG_SYS) 79526c22a19SCharles Keepax return 0; 79626c22a19SCharles Keepax 797b21acc1cSCharles Keepax switch (alg_region->type) { 798b21acc1cSCharles Keepax case WMFW_ADSP1_PM: 799b21acc1cSCharles Keepax region_name = "PM"; 800b21acc1cSCharles Keepax break; 801b21acc1cSCharles Keepax case WMFW_ADSP1_DM: 802b21acc1cSCharles Keepax region_name = "DM"; 803b21acc1cSCharles Keepax break; 804b21acc1cSCharles Keepax case WMFW_ADSP2_XM: 805b21acc1cSCharles Keepax region_name = "XM"; 806b21acc1cSCharles Keepax break; 807b21acc1cSCharles Keepax case WMFW_ADSP2_YM: 808b21acc1cSCharles Keepax region_name = "YM"; 809b21acc1cSCharles Keepax break; 810b21acc1cSCharles Keepax case WMFW_ADSP1_ZM: 811b21acc1cSCharles Keepax region_name = "ZM"; 812b21acc1cSCharles Keepax break; 813b21acc1cSCharles Keepax default: 8142323736dSCharles Keepax adsp_err(dsp, "Unknown region type: %d\n", alg_region->type); 815b21acc1cSCharles Keepax return -EINVAL; 816b21acc1cSCharles Keepax } 817b21acc1cSCharles Keepax 818cb5b57a9SCharles Keepax switch (dsp->fw_ver) { 819cb5b57a9SCharles Keepax case 0: 820cb5b57a9SCharles Keepax case 1: 821b21acc1cSCharles Keepax snprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "DSP%d %s %x", 822b21acc1cSCharles Keepax dsp->num, region_name, alg_region->alg); 823cb5b57a9SCharles Keepax break; 824cb5b57a9SCharles Keepax default: 825cb5b57a9SCharles Keepax ret = snprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, 826cb5b57a9SCharles Keepax "DSP%d%c %.12s %x", dsp->num, *region_name, 827cb5b57a9SCharles Keepax wm_adsp_fw_text[dsp->fw], alg_region->alg); 828cb5b57a9SCharles Keepax 829cb5b57a9SCharles Keepax /* Truncate the subname from the start if it is too long */ 830cb5b57a9SCharles Keepax if (subname) { 831cb5b57a9SCharles Keepax int avail = SNDRV_CTL_ELEM_ID_NAME_MAXLEN - ret - 2; 832cb5b57a9SCharles Keepax int skip = 0; 833cb5b57a9SCharles Keepax 834cb5b57a9SCharles Keepax if (subname_len > avail) 835cb5b57a9SCharles Keepax skip = subname_len - avail; 836cb5b57a9SCharles Keepax 837cb5b57a9SCharles Keepax snprintf(name + ret, 838cb5b57a9SCharles Keepax SNDRV_CTL_ELEM_ID_NAME_MAXLEN - ret, " %.*s", 839cb5b57a9SCharles Keepax subname_len - skip, subname + skip); 840cb5b57a9SCharles Keepax } 841cb5b57a9SCharles Keepax break; 842cb5b57a9SCharles Keepax } 843b21acc1cSCharles Keepax 8447585a5b0SCharles Keepax list_for_each_entry(ctl, &dsp->ctl_list, list) { 845b21acc1cSCharles Keepax if (!strcmp(ctl->name, name)) { 846b21acc1cSCharles Keepax if (!ctl->enabled) 847b21acc1cSCharles Keepax ctl->enabled = 1; 848b21acc1cSCharles Keepax return 0; 849b21acc1cSCharles Keepax } 850b21acc1cSCharles Keepax } 851b21acc1cSCharles Keepax 852b21acc1cSCharles Keepax ctl = kzalloc(sizeof(*ctl), GFP_KERNEL); 853b21acc1cSCharles Keepax if (!ctl) 854b21acc1cSCharles Keepax return -ENOMEM; 8552323736dSCharles Keepax ctl->fw_name = wm_adsp_fw_text[dsp->fw]; 856b21acc1cSCharles Keepax ctl->alg_region = *alg_region; 857b21acc1cSCharles Keepax ctl->name = kmemdup(name, strlen(name) + 1, GFP_KERNEL); 858b21acc1cSCharles Keepax if (!ctl->name) { 859b21acc1cSCharles Keepax ret = -ENOMEM; 860b21acc1cSCharles Keepax goto err_ctl; 861b21acc1cSCharles Keepax } 862b21acc1cSCharles Keepax ctl->enabled = 1; 863b21acc1cSCharles Keepax ctl->set = 0; 864b21acc1cSCharles Keepax ctl->ops.xget = wm_coeff_get; 865b21acc1cSCharles Keepax ctl->ops.xput = wm_coeff_put; 866b21acc1cSCharles Keepax ctl->dsp = dsp; 867b21acc1cSCharles Keepax 86826c22a19SCharles Keepax ctl->flags = flags; 8692323736dSCharles Keepax ctl->offset = offset; 870b21acc1cSCharles Keepax if (len > 512) { 871b21acc1cSCharles Keepax adsp_warn(dsp, "Truncating control %s from %d\n", 872b21acc1cSCharles Keepax ctl->name, len); 873b21acc1cSCharles Keepax len = 512; 874b21acc1cSCharles Keepax } 875b21acc1cSCharles Keepax ctl->len = len; 876b21acc1cSCharles Keepax ctl->cache = kzalloc(ctl->len, GFP_KERNEL); 877b21acc1cSCharles Keepax if (!ctl->cache) { 878b21acc1cSCharles Keepax ret = -ENOMEM; 879b21acc1cSCharles Keepax goto err_ctl_name; 880b21acc1cSCharles Keepax } 881b21acc1cSCharles Keepax 8822323736dSCharles Keepax list_add(&ctl->list, &dsp->ctl_list); 8832323736dSCharles Keepax 884b21acc1cSCharles Keepax ctl_work = kzalloc(sizeof(*ctl_work), GFP_KERNEL); 885b21acc1cSCharles Keepax if (!ctl_work) { 886b21acc1cSCharles Keepax ret = -ENOMEM; 887b21acc1cSCharles Keepax goto err_ctl_cache; 888b21acc1cSCharles Keepax } 889b21acc1cSCharles Keepax 890b21acc1cSCharles Keepax ctl_work->dsp = dsp; 891b21acc1cSCharles Keepax ctl_work->ctl = ctl; 892b21acc1cSCharles Keepax INIT_WORK(&ctl_work->work, wm_adsp_ctl_work); 893b21acc1cSCharles Keepax schedule_work(&ctl_work->work); 894b21acc1cSCharles Keepax 895b21acc1cSCharles Keepax return 0; 896b21acc1cSCharles Keepax 897b21acc1cSCharles Keepax err_ctl_cache: 898b21acc1cSCharles Keepax kfree(ctl->cache); 899b21acc1cSCharles Keepax err_ctl_name: 900b21acc1cSCharles Keepax kfree(ctl->name); 901b21acc1cSCharles Keepax err_ctl: 902b21acc1cSCharles Keepax kfree(ctl); 903b21acc1cSCharles Keepax 904b21acc1cSCharles Keepax return ret; 905b21acc1cSCharles Keepax } 906b21acc1cSCharles Keepax 9072323736dSCharles Keepax struct wm_coeff_parsed_alg { 9082323736dSCharles Keepax int id; 9092323736dSCharles Keepax const u8 *name; 9102323736dSCharles Keepax int name_len; 9112323736dSCharles Keepax int ncoeff; 9122323736dSCharles Keepax }; 9132323736dSCharles Keepax 9142323736dSCharles Keepax struct wm_coeff_parsed_coeff { 9152323736dSCharles Keepax int offset; 9162323736dSCharles Keepax int mem_type; 9172323736dSCharles Keepax const u8 *name; 9182323736dSCharles Keepax int name_len; 9192323736dSCharles Keepax int ctl_type; 9202323736dSCharles Keepax int flags; 9212323736dSCharles Keepax int len; 9222323736dSCharles Keepax }; 9232323736dSCharles Keepax 924cb5b57a9SCharles Keepax static int wm_coeff_parse_string(int bytes, const u8 **pos, const u8 **str) 925cb5b57a9SCharles Keepax { 926cb5b57a9SCharles Keepax int length; 927cb5b57a9SCharles Keepax 928cb5b57a9SCharles Keepax switch (bytes) { 929cb5b57a9SCharles Keepax case 1: 930cb5b57a9SCharles Keepax length = **pos; 931cb5b57a9SCharles Keepax break; 932cb5b57a9SCharles Keepax case 2: 9338299ee81SCharles Keepax length = le16_to_cpu(*((__le16 *)*pos)); 934cb5b57a9SCharles Keepax break; 935cb5b57a9SCharles Keepax default: 936cb5b57a9SCharles Keepax return 0; 937cb5b57a9SCharles Keepax } 938cb5b57a9SCharles Keepax 939cb5b57a9SCharles Keepax if (str) 940cb5b57a9SCharles Keepax *str = *pos + bytes; 941cb5b57a9SCharles Keepax 942cb5b57a9SCharles Keepax *pos += ((length + bytes) + 3) & ~0x03; 943cb5b57a9SCharles Keepax 944cb5b57a9SCharles Keepax return length; 945cb5b57a9SCharles Keepax } 946cb5b57a9SCharles Keepax 947cb5b57a9SCharles Keepax static int wm_coeff_parse_int(int bytes, const u8 **pos) 948cb5b57a9SCharles Keepax { 949cb5b57a9SCharles Keepax int val = 0; 950cb5b57a9SCharles Keepax 951cb5b57a9SCharles Keepax switch (bytes) { 952cb5b57a9SCharles Keepax case 2: 9538299ee81SCharles Keepax val = le16_to_cpu(*((__le16 *)*pos)); 954cb5b57a9SCharles Keepax break; 955cb5b57a9SCharles Keepax case 4: 9568299ee81SCharles Keepax val = le32_to_cpu(*((__le32 *)*pos)); 957cb5b57a9SCharles Keepax break; 958cb5b57a9SCharles Keepax default: 959cb5b57a9SCharles Keepax break; 960cb5b57a9SCharles Keepax } 961cb5b57a9SCharles Keepax 962cb5b57a9SCharles Keepax *pos += bytes; 963cb5b57a9SCharles Keepax 964cb5b57a9SCharles Keepax return val; 965cb5b57a9SCharles Keepax } 966cb5b57a9SCharles Keepax 9672323736dSCharles Keepax static inline void wm_coeff_parse_alg(struct wm_adsp *dsp, const u8 **data, 9682323736dSCharles Keepax struct wm_coeff_parsed_alg *blk) 9692323736dSCharles Keepax { 9702323736dSCharles Keepax const struct wmfw_adsp_alg_data *raw; 9712323736dSCharles Keepax 972cb5b57a9SCharles Keepax switch (dsp->fw_ver) { 973cb5b57a9SCharles Keepax case 0: 974cb5b57a9SCharles Keepax case 1: 9752323736dSCharles Keepax raw = (const struct wmfw_adsp_alg_data *)*data; 9762323736dSCharles Keepax *data = raw->data; 9772323736dSCharles Keepax 9782323736dSCharles Keepax blk->id = le32_to_cpu(raw->id); 9792323736dSCharles Keepax blk->name = raw->name; 9802323736dSCharles Keepax blk->name_len = strlen(raw->name); 9812323736dSCharles Keepax blk->ncoeff = le32_to_cpu(raw->ncoeff); 982cb5b57a9SCharles Keepax break; 983cb5b57a9SCharles Keepax default: 984cb5b57a9SCharles Keepax blk->id = wm_coeff_parse_int(sizeof(raw->id), data); 985cb5b57a9SCharles Keepax blk->name_len = wm_coeff_parse_string(sizeof(u8), data, 986cb5b57a9SCharles Keepax &blk->name); 987cb5b57a9SCharles Keepax wm_coeff_parse_string(sizeof(u16), data, NULL); 988cb5b57a9SCharles Keepax blk->ncoeff = wm_coeff_parse_int(sizeof(raw->ncoeff), data); 989cb5b57a9SCharles Keepax break; 990cb5b57a9SCharles Keepax } 9912323736dSCharles Keepax 9922323736dSCharles Keepax adsp_dbg(dsp, "Algorithm ID: %#x\n", blk->id); 9932323736dSCharles Keepax adsp_dbg(dsp, "Algorithm name: %.*s\n", blk->name_len, blk->name); 9942323736dSCharles Keepax adsp_dbg(dsp, "# of coefficient descriptors: %#x\n", blk->ncoeff); 9952323736dSCharles Keepax } 9962323736dSCharles Keepax 9972323736dSCharles Keepax static inline void wm_coeff_parse_coeff(struct wm_adsp *dsp, const u8 **data, 9982323736dSCharles Keepax struct wm_coeff_parsed_coeff *blk) 9992323736dSCharles Keepax { 10002323736dSCharles Keepax const struct wmfw_adsp_coeff_data *raw; 1001cb5b57a9SCharles Keepax const u8 *tmp; 1002cb5b57a9SCharles Keepax int length; 10032323736dSCharles Keepax 1004cb5b57a9SCharles Keepax switch (dsp->fw_ver) { 1005cb5b57a9SCharles Keepax case 0: 1006cb5b57a9SCharles Keepax case 1: 10072323736dSCharles Keepax raw = (const struct wmfw_adsp_coeff_data *)*data; 10082323736dSCharles Keepax *data = *data + sizeof(raw->hdr) + le32_to_cpu(raw->hdr.size); 10092323736dSCharles Keepax 10102323736dSCharles Keepax blk->offset = le16_to_cpu(raw->hdr.offset); 10112323736dSCharles Keepax blk->mem_type = le16_to_cpu(raw->hdr.type); 10122323736dSCharles Keepax blk->name = raw->name; 10132323736dSCharles Keepax blk->name_len = strlen(raw->name); 10142323736dSCharles Keepax blk->ctl_type = le16_to_cpu(raw->ctl_type); 10152323736dSCharles Keepax blk->flags = le16_to_cpu(raw->flags); 10162323736dSCharles Keepax blk->len = le32_to_cpu(raw->len); 1017cb5b57a9SCharles Keepax break; 1018cb5b57a9SCharles Keepax default: 1019cb5b57a9SCharles Keepax tmp = *data; 1020cb5b57a9SCharles Keepax blk->offset = wm_coeff_parse_int(sizeof(raw->hdr.offset), &tmp); 1021cb5b57a9SCharles Keepax blk->mem_type = wm_coeff_parse_int(sizeof(raw->hdr.type), &tmp); 1022cb5b57a9SCharles Keepax length = wm_coeff_parse_int(sizeof(raw->hdr.size), &tmp); 1023cb5b57a9SCharles Keepax blk->name_len = wm_coeff_parse_string(sizeof(u8), &tmp, 1024cb5b57a9SCharles Keepax &blk->name); 1025cb5b57a9SCharles Keepax wm_coeff_parse_string(sizeof(u8), &tmp, NULL); 1026cb5b57a9SCharles Keepax wm_coeff_parse_string(sizeof(u16), &tmp, NULL); 1027cb5b57a9SCharles Keepax blk->ctl_type = wm_coeff_parse_int(sizeof(raw->ctl_type), &tmp); 1028cb5b57a9SCharles Keepax blk->flags = wm_coeff_parse_int(sizeof(raw->flags), &tmp); 1029cb5b57a9SCharles Keepax blk->len = wm_coeff_parse_int(sizeof(raw->len), &tmp); 1030cb5b57a9SCharles Keepax 1031cb5b57a9SCharles Keepax *data = *data + sizeof(raw->hdr) + length; 1032cb5b57a9SCharles Keepax break; 1033cb5b57a9SCharles Keepax } 10342323736dSCharles Keepax 10352323736dSCharles Keepax adsp_dbg(dsp, "\tCoefficient type: %#x\n", blk->mem_type); 10362323736dSCharles Keepax adsp_dbg(dsp, "\tCoefficient offset: %#x\n", blk->offset); 10372323736dSCharles Keepax adsp_dbg(dsp, "\tCoefficient name: %.*s\n", blk->name_len, blk->name); 10382323736dSCharles Keepax adsp_dbg(dsp, "\tCoefficient flags: %#x\n", blk->flags); 10392323736dSCharles Keepax adsp_dbg(dsp, "\tALSA control type: %#x\n", blk->ctl_type); 10402323736dSCharles Keepax adsp_dbg(dsp, "\tALSA control len: %#x\n", blk->len); 10412323736dSCharles Keepax } 10422323736dSCharles Keepax 10432323736dSCharles Keepax static int wm_adsp_parse_coeff(struct wm_adsp *dsp, 10442323736dSCharles Keepax const struct wmfw_region *region) 10452323736dSCharles Keepax { 10462323736dSCharles Keepax struct wm_adsp_alg_region alg_region = {}; 10472323736dSCharles Keepax struct wm_coeff_parsed_alg alg_blk; 10482323736dSCharles Keepax struct wm_coeff_parsed_coeff coeff_blk; 10492323736dSCharles Keepax const u8 *data = region->data; 10502323736dSCharles Keepax int i, ret; 10512323736dSCharles Keepax 10522323736dSCharles Keepax wm_coeff_parse_alg(dsp, &data, &alg_blk); 10532323736dSCharles Keepax for (i = 0; i < alg_blk.ncoeff; i++) { 10542323736dSCharles Keepax wm_coeff_parse_coeff(dsp, &data, &coeff_blk); 10552323736dSCharles Keepax 10562323736dSCharles Keepax switch (coeff_blk.ctl_type) { 10572323736dSCharles Keepax case SNDRV_CTL_ELEM_TYPE_BYTES: 10582323736dSCharles Keepax break; 10592323736dSCharles Keepax default: 10602323736dSCharles Keepax adsp_err(dsp, "Unknown control type: %d\n", 10612323736dSCharles Keepax coeff_blk.ctl_type); 10622323736dSCharles Keepax return -EINVAL; 10632323736dSCharles Keepax } 10642323736dSCharles Keepax 10652323736dSCharles Keepax alg_region.type = coeff_blk.mem_type; 10662323736dSCharles Keepax alg_region.alg = alg_blk.id; 10672323736dSCharles Keepax 10682323736dSCharles Keepax ret = wm_adsp_create_control(dsp, &alg_region, 10692323736dSCharles Keepax coeff_blk.offset, 10702323736dSCharles Keepax coeff_blk.len, 10712323736dSCharles Keepax coeff_blk.name, 107226c22a19SCharles Keepax coeff_blk.name_len, 107326c22a19SCharles Keepax coeff_blk.flags); 10742323736dSCharles Keepax if (ret < 0) 10752323736dSCharles Keepax adsp_err(dsp, "Failed to create control: %.*s, %d\n", 10762323736dSCharles Keepax coeff_blk.name_len, coeff_blk.name, ret); 10772323736dSCharles Keepax } 10782323736dSCharles Keepax 10792323736dSCharles Keepax return 0; 10802323736dSCharles Keepax } 10812323736dSCharles Keepax 10822159ad93SMark Brown static int wm_adsp_load(struct wm_adsp *dsp) 10832159ad93SMark Brown { 1084cf17c83cSMark Brown LIST_HEAD(buf_list); 10852159ad93SMark Brown const struct firmware *firmware; 10862159ad93SMark Brown struct regmap *regmap = dsp->regmap; 10872159ad93SMark Brown unsigned int pos = 0; 10882159ad93SMark Brown const struct wmfw_header *header; 10892159ad93SMark Brown const struct wmfw_adsp1_sizes *adsp1_sizes; 10902159ad93SMark Brown const struct wmfw_adsp2_sizes *adsp2_sizes; 10912159ad93SMark Brown const struct wmfw_footer *footer; 10922159ad93SMark Brown const struct wmfw_region *region; 10932159ad93SMark Brown const struct wm_adsp_region *mem; 10942159ad93SMark Brown const char *region_name; 10952159ad93SMark Brown char *file, *text; 1096cf17c83cSMark Brown struct wm_adsp_buf *buf; 10972159ad93SMark Brown unsigned int reg; 10982159ad93SMark Brown int regions = 0; 10992159ad93SMark Brown int ret, offset, type, sizes; 11002159ad93SMark Brown 11012159ad93SMark Brown file = kzalloc(PAGE_SIZE, GFP_KERNEL); 11022159ad93SMark Brown if (file == NULL) 11032159ad93SMark Brown return -ENOMEM; 11042159ad93SMark Brown 11051023dbd9SMark Brown snprintf(file, PAGE_SIZE, "%s-dsp%d-%s.wmfw", dsp->part, dsp->num, 11061023dbd9SMark Brown wm_adsp_fw[dsp->fw].file); 11072159ad93SMark Brown file[PAGE_SIZE - 1] = '\0'; 11082159ad93SMark Brown 11092159ad93SMark Brown ret = request_firmware(&firmware, file, dsp->dev); 11102159ad93SMark Brown if (ret != 0) { 11112159ad93SMark Brown adsp_err(dsp, "Failed to request '%s'\n", file); 11122159ad93SMark Brown goto out; 11132159ad93SMark Brown } 11142159ad93SMark Brown ret = -EINVAL; 11152159ad93SMark Brown 11162159ad93SMark Brown pos = sizeof(*header) + sizeof(*adsp1_sizes) + sizeof(*footer); 11172159ad93SMark Brown if (pos >= firmware->size) { 11182159ad93SMark Brown adsp_err(dsp, "%s: file too short, %zu bytes\n", 11192159ad93SMark Brown file, firmware->size); 11202159ad93SMark Brown goto out_fw; 11212159ad93SMark Brown } 11222159ad93SMark Brown 11232159ad93SMark Brown header = (void *)&firmware->data[0]; 11242159ad93SMark Brown 11252159ad93SMark Brown if (memcmp(&header->magic[0], "WMFW", 4) != 0) { 11262159ad93SMark Brown adsp_err(dsp, "%s: invalid magic\n", file); 11272159ad93SMark Brown goto out_fw; 11282159ad93SMark Brown } 11292159ad93SMark Brown 11302323736dSCharles Keepax switch (header->ver) { 11312323736dSCharles Keepax case 0: 1132c61e59feSCharles Keepax adsp_warn(dsp, "%s: Depreciated file format %d\n", 1133c61e59feSCharles Keepax file, header->ver); 1134c61e59feSCharles Keepax break; 11352323736dSCharles Keepax case 1: 1136cb5b57a9SCharles Keepax case 2: 11372323736dSCharles Keepax break; 11382323736dSCharles Keepax default: 11392159ad93SMark Brown adsp_err(dsp, "%s: unknown file format %d\n", 11402159ad93SMark Brown file, header->ver); 11412159ad93SMark Brown goto out_fw; 11422159ad93SMark Brown } 11432323736dSCharles Keepax 11443626992aSDimitris Papastamos adsp_info(dsp, "Firmware version: %d\n", header->ver); 11452323736dSCharles Keepax dsp->fw_ver = header->ver; 11462159ad93SMark Brown 11472159ad93SMark Brown if (header->core != dsp->type) { 11482159ad93SMark Brown adsp_err(dsp, "%s: invalid core %d != %d\n", 11492159ad93SMark Brown file, header->core, dsp->type); 11502159ad93SMark Brown goto out_fw; 11512159ad93SMark Brown } 11522159ad93SMark Brown 11532159ad93SMark Brown switch (dsp->type) { 11542159ad93SMark Brown case WMFW_ADSP1: 11552159ad93SMark Brown pos = sizeof(*header) + sizeof(*adsp1_sizes) + sizeof(*footer); 11562159ad93SMark Brown adsp1_sizes = (void *)&(header[1]); 11572159ad93SMark Brown footer = (void *)&(adsp1_sizes[1]); 11582159ad93SMark Brown sizes = sizeof(*adsp1_sizes); 11592159ad93SMark Brown 11602159ad93SMark Brown adsp_dbg(dsp, "%s: %d DM, %d PM, %d ZM\n", 11612159ad93SMark Brown file, le32_to_cpu(adsp1_sizes->dm), 11622159ad93SMark Brown le32_to_cpu(adsp1_sizes->pm), 11632159ad93SMark Brown le32_to_cpu(adsp1_sizes->zm)); 11642159ad93SMark Brown break; 11652159ad93SMark Brown 11662159ad93SMark Brown case WMFW_ADSP2: 11672159ad93SMark Brown pos = sizeof(*header) + sizeof(*adsp2_sizes) + sizeof(*footer); 11682159ad93SMark Brown adsp2_sizes = (void *)&(header[1]); 11692159ad93SMark Brown footer = (void *)&(adsp2_sizes[1]); 11702159ad93SMark Brown sizes = sizeof(*adsp2_sizes); 11712159ad93SMark Brown 11722159ad93SMark Brown adsp_dbg(dsp, "%s: %d XM, %d YM %d PM, %d ZM\n", 11732159ad93SMark Brown file, le32_to_cpu(adsp2_sizes->xm), 11742159ad93SMark Brown le32_to_cpu(adsp2_sizes->ym), 11752159ad93SMark Brown le32_to_cpu(adsp2_sizes->pm), 11762159ad93SMark Brown le32_to_cpu(adsp2_sizes->zm)); 11772159ad93SMark Brown break; 11782159ad93SMark Brown 11792159ad93SMark Brown default: 11806c452bdaSTakashi Iwai WARN(1, "Unknown DSP type"); 11812159ad93SMark Brown goto out_fw; 11822159ad93SMark Brown } 11832159ad93SMark Brown 11842159ad93SMark Brown if (le32_to_cpu(header->len) != sizeof(*header) + 11852159ad93SMark Brown sizes + sizeof(*footer)) { 11862159ad93SMark Brown adsp_err(dsp, "%s: unexpected header length %d\n", 11872159ad93SMark Brown file, le32_to_cpu(header->len)); 11882159ad93SMark Brown goto out_fw; 11892159ad93SMark Brown } 11902159ad93SMark Brown 11912159ad93SMark Brown adsp_dbg(dsp, "%s: timestamp %llu\n", file, 11922159ad93SMark Brown le64_to_cpu(footer->timestamp)); 11932159ad93SMark Brown 11942159ad93SMark Brown while (pos < firmware->size && 11952159ad93SMark Brown pos - firmware->size > sizeof(*region)) { 11962159ad93SMark Brown region = (void *)&(firmware->data[pos]); 11972159ad93SMark Brown region_name = "Unknown"; 11982159ad93SMark Brown reg = 0; 11992159ad93SMark Brown text = NULL; 12002159ad93SMark Brown offset = le32_to_cpu(region->offset) & 0xffffff; 12012159ad93SMark Brown type = be32_to_cpu(region->type) & 0xff; 12022159ad93SMark Brown mem = wm_adsp_find_region(dsp, type); 12032159ad93SMark Brown 12042159ad93SMark Brown switch (type) { 12052159ad93SMark Brown case WMFW_NAME_TEXT: 12062159ad93SMark Brown region_name = "Firmware name"; 12072159ad93SMark Brown text = kzalloc(le32_to_cpu(region->len) + 1, 12082159ad93SMark Brown GFP_KERNEL); 12092159ad93SMark Brown break; 12102323736dSCharles Keepax case WMFW_ALGORITHM_DATA: 12112323736dSCharles Keepax region_name = "Algorithm"; 12122323736dSCharles Keepax ret = wm_adsp_parse_coeff(dsp, region); 12132323736dSCharles Keepax if (ret != 0) 12142323736dSCharles Keepax goto out_fw; 12152323736dSCharles Keepax break; 12162159ad93SMark Brown case WMFW_INFO_TEXT: 12172159ad93SMark Brown region_name = "Information"; 12182159ad93SMark Brown text = kzalloc(le32_to_cpu(region->len) + 1, 12192159ad93SMark Brown GFP_KERNEL); 12202159ad93SMark Brown break; 12212159ad93SMark Brown case WMFW_ABSOLUTE: 12222159ad93SMark Brown region_name = "Absolute"; 12232159ad93SMark Brown reg = offset; 12242159ad93SMark Brown break; 12252159ad93SMark Brown case WMFW_ADSP1_PM: 12262159ad93SMark Brown region_name = "PM"; 122745b9ee72SMark Brown reg = wm_adsp_region_to_reg(mem, offset); 12282159ad93SMark Brown break; 12292159ad93SMark Brown case WMFW_ADSP1_DM: 12302159ad93SMark Brown region_name = "DM"; 123145b9ee72SMark Brown reg = wm_adsp_region_to_reg(mem, offset); 12322159ad93SMark Brown break; 12332159ad93SMark Brown case WMFW_ADSP2_XM: 12342159ad93SMark Brown region_name = "XM"; 123545b9ee72SMark Brown reg = wm_adsp_region_to_reg(mem, offset); 12362159ad93SMark Brown break; 12372159ad93SMark Brown case WMFW_ADSP2_YM: 12382159ad93SMark Brown region_name = "YM"; 123945b9ee72SMark Brown reg = wm_adsp_region_to_reg(mem, offset); 12402159ad93SMark Brown break; 12412159ad93SMark Brown case WMFW_ADSP1_ZM: 12422159ad93SMark Brown region_name = "ZM"; 124345b9ee72SMark Brown reg = wm_adsp_region_to_reg(mem, offset); 12442159ad93SMark Brown break; 12452159ad93SMark Brown default: 12462159ad93SMark Brown adsp_warn(dsp, 12472159ad93SMark Brown "%s.%d: Unknown region type %x at %d(%x)\n", 12482159ad93SMark Brown file, regions, type, pos, pos); 12492159ad93SMark Brown break; 12502159ad93SMark Brown } 12512159ad93SMark Brown 12522159ad93SMark Brown adsp_dbg(dsp, "%s.%d: %d bytes at %d in %s\n", file, 12532159ad93SMark Brown regions, le32_to_cpu(region->len), offset, 12542159ad93SMark Brown region_name); 12552159ad93SMark Brown 12562159ad93SMark Brown if (text) { 12572159ad93SMark Brown memcpy(text, region->data, le32_to_cpu(region->len)); 12582159ad93SMark Brown adsp_info(dsp, "%s: %s\n", file, text); 12592159ad93SMark Brown kfree(text); 12602159ad93SMark Brown } 12612159ad93SMark Brown 12622159ad93SMark Brown if (reg) { 1263cdcd7f72SCharles Keepax buf = wm_adsp_buf_alloc(region->data, 1264cdcd7f72SCharles Keepax le32_to_cpu(region->len), 1265cf17c83cSMark Brown &buf_list); 1266a76fefabSMark Brown if (!buf) { 1267a76fefabSMark Brown adsp_err(dsp, "Out of memory\n"); 12687328823dSDimitris Papastamos ret = -ENOMEM; 12697328823dSDimitris Papastamos goto out_fw; 1270a76fefabSMark Brown } 1271a76fefabSMark Brown 1272cdcd7f72SCharles Keepax ret = regmap_raw_write_async(regmap, reg, buf->buf, 1273cdcd7f72SCharles Keepax le32_to_cpu(region->len)); 12742159ad93SMark Brown if (ret != 0) { 12752159ad93SMark Brown adsp_err(dsp, 1276cdcd7f72SCharles Keepax "%s.%d: Failed to write %d bytes at %d in %s: %d\n", 12772159ad93SMark Brown file, regions, 1278cdcd7f72SCharles Keepax le32_to_cpu(region->len), offset, 12792159ad93SMark Brown region_name, ret); 12802159ad93SMark Brown goto out_fw; 12812159ad93SMark Brown } 12822159ad93SMark Brown } 12832159ad93SMark Brown 12842159ad93SMark Brown pos += le32_to_cpu(region->len) + sizeof(*region); 12852159ad93SMark Brown regions++; 12862159ad93SMark Brown } 12872159ad93SMark Brown 1288cf17c83cSMark Brown ret = regmap_async_complete(regmap); 1289cf17c83cSMark Brown if (ret != 0) { 1290cf17c83cSMark Brown adsp_err(dsp, "Failed to complete async write: %d\n", ret); 1291cf17c83cSMark Brown goto out_fw; 1292cf17c83cSMark Brown } 1293cf17c83cSMark Brown 12942159ad93SMark Brown if (pos > firmware->size) 12952159ad93SMark Brown adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n", 12962159ad93SMark Brown file, regions, pos - firmware->size); 12972159ad93SMark Brown 1298f9f55e31SRichard Fitzgerald wm_adsp_debugfs_save_wmfwname(dsp, file); 1299f9f55e31SRichard Fitzgerald 13002159ad93SMark Brown out_fw: 1301cf17c83cSMark Brown regmap_async_complete(regmap); 1302cf17c83cSMark Brown wm_adsp_buf_free(&buf_list); 13032159ad93SMark Brown release_firmware(firmware); 13042159ad93SMark Brown out: 13052159ad93SMark Brown kfree(file); 13062159ad93SMark Brown 13072159ad93SMark Brown return ret; 13082159ad93SMark Brown } 13092159ad93SMark Brown 13102323736dSCharles Keepax static void wm_adsp_ctl_fixup_base(struct wm_adsp *dsp, 13112323736dSCharles Keepax const struct wm_adsp_alg_region *alg_region) 13122323736dSCharles Keepax { 13132323736dSCharles Keepax struct wm_coeff_ctl *ctl; 13142323736dSCharles Keepax 13152323736dSCharles Keepax list_for_each_entry(ctl, &dsp->ctl_list, list) { 13162323736dSCharles Keepax if (ctl->fw_name == wm_adsp_fw_text[dsp->fw] && 13172323736dSCharles Keepax alg_region->alg == ctl->alg_region.alg && 13182323736dSCharles Keepax alg_region->type == ctl->alg_region.type) { 13192323736dSCharles Keepax ctl->alg_region.base = alg_region->base; 13202323736dSCharles Keepax } 13212323736dSCharles Keepax } 13222323736dSCharles Keepax } 13232323736dSCharles Keepax 13243809f001SCharles Keepax static void *wm_adsp_read_algs(struct wm_adsp *dsp, size_t n_algs, 1325b618a185SCharles Keepax unsigned int pos, unsigned int len) 1326db40517cSMark Brown { 1327b618a185SCharles Keepax void *alg; 1328b618a185SCharles Keepax int ret; 1329db40517cSMark Brown __be32 val; 1330db40517cSMark Brown 13313809f001SCharles Keepax if (n_algs == 0) { 1332b618a185SCharles Keepax adsp_err(dsp, "No algorithms\n"); 1333b618a185SCharles Keepax return ERR_PTR(-EINVAL); 1334db40517cSMark Brown } 1335db40517cSMark Brown 13363809f001SCharles Keepax if (n_algs > 1024) { 13373809f001SCharles Keepax adsp_err(dsp, "Algorithm count %zx excessive\n", n_algs); 1338b618a185SCharles Keepax return ERR_PTR(-EINVAL); 1339b618a185SCharles Keepax } 1340b618a185SCharles Keepax 1341b618a185SCharles Keepax /* Read the terminator first to validate the length */ 1342b618a185SCharles Keepax ret = regmap_raw_read(dsp->regmap, pos + len, &val, sizeof(val)); 1343b618a185SCharles Keepax if (ret != 0) { 1344b618a185SCharles Keepax adsp_err(dsp, "Failed to read algorithm list end: %d\n", 1345b618a185SCharles Keepax ret); 1346b618a185SCharles Keepax return ERR_PTR(ret); 1347b618a185SCharles Keepax } 1348b618a185SCharles Keepax 1349b618a185SCharles Keepax if (be32_to_cpu(val) != 0xbedead) 1350b618a185SCharles Keepax adsp_warn(dsp, "Algorithm list end %x 0x%x != 0xbeadead\n", 1351b618a185SCharles Keepax pos + len, be32_to_cpu(val)); 1352b618a185SCharles Keepax 1353b618a185SCharles Keepax alg = kzalloc(len * 2, GFP_KERNEL | GFP_DMA); 1354b618a185SCharles Keepax if (!alg) 1355b618a185SCharles Keepax return ERR_PTR(-ENOMEM); 1356b618a185SCharles Keepax 1357b618a185SCharles Keepax ret = regmap_raw_read(dsp->regmap, pos, alg, len * 2); 1358b618a185SCharles Keepax if (ret != 0) { 1359b618a185SCharles Keepax adsp_err(dsp, "Failed to read algorithm list: %d\n", 1360b618a185SCharles Keepax ret); 1361b618a185SCharles Keepax kfree(alg); 1362b618a185SCharles Keepax return ERR_PTR(ret); 1363b618a185SCharles Keepax } 1364b618a185SCharles Keepax 1365b618a185SCharles Keepax return alg; 1366b618a185SCharles Keepax } 1367b618a185SCharles Keepax 136814197095SCharles Keepax static struct wm_adsp_alg_region * 136914197095SCharles Keepax wm_adsp_find_alg_region(struct wm_adsp *dsp, int type, unsigned int id) 137014197095SCharles Keepax { 137114197095SCharles Keepax struct wm_adsp_alg_region *alg_region; 137214197095SCharles Keepax 137314197095SCharles Keepax list_for_each_entry(alg_region, &dsp->alg_regions, list) { 137414197095SCharles Keepax if (id == alg_region->alg && type == alg_region->type) 137514197095SCharles Keepax return alg_region; 137614197095SCharles Keepax } 137714197095SCharles Keepax 137814197095SCharles Keepax return NULL; 137914197095SCharles Keepax } 138014197095SCharles Keepax 1381d9d20e17SCharles Keepax static struct wm_adsp_alg_region *wm_adsp_create_region(struct wm_adsp *dsp, 1382d9d20e17SCharles Keepax int type, __be32 id, 1383d9d20e17SCharles Keepax __be32 base) 1384d9d20e17SCharles Keepax { 1385d9d20e17SCharles Keepax struct wm_adsp_alg_region *alg_region; 1386d9d20e17SCharles Keepax 1387d9d20e17SCharles Keepax alg_region = kzalloc(sizeof(*alg_region), GFP_KERNEL); 1388d9d20e17SCharles Keepax if (!alg_region) 1389d9d20e17SCharles Keepax return ERR_PTR(-ENOMEM); 1390d9d20e17SCharles Keepax 1391d9d20e17SCharles Keepax alg_region->type = type; 1392d9d20e17SCharles Keepax alg_region->alg = be32_to_cpu(id); 1393d9d20e17SCharles Keepax alg_region->base = be32_to_cpu(base); 1394d9d20e17SCharles Keepax 1395d9d20e17SCharles Keepax list_add_tail(&alg_region->list, &dsp->alg_regions); 1396d9d20e17SCharles Keepax 13972323736dSCharles Keepax if (dsp->fw_ver > 0) 13982323736dSCharles Keepax wm_adsp_ctl_fixup_base(dsp, alg_region); 13992323736dSCharles Keepax 1400d9d20e17SCharles Keepax return alg_region; 1401d9d20e17SCharles Keepax } 1402d9d20e17SCharles Keepax 1403b618a185SCharles Keepax static int wm_adsp1_setup_algs(struct wm_adsp *dsp) 1404b618a185SCharles Keepax { 1405b618a185SCharles Keepax struct wmfw_adsp1_id_hdr adsp1_id; 1406b618a185SCharles Keepax struct wmfw_adsp1_alg_hdr *adsp1_alg; 14073809f001SCharles Keepax struct wm_adsp_alg_region *alg_region; 1408b618a185SCharles Keepax const struct wm_adsp_region *mem; 1409b618a185SCharles Keepax unsigned int pos, len; 14103809f001SCharles Keepax size_t n_algs; 1411b618a185SCharles Keepax int i, ret; 1412b618a185SCharles Keepax 1413b618a185SCharles Keepax mem = wm_adsp_find_region(dsp, WMFW_ADSP1_DM); 14146c452bdaSTakashi Iwai if (WARN_ON(!mem)) 1415db40517cSMark Brown return -EINVAL; 1416db40517cSMark Brown 1417b618a185SCharles Keepax ret = regmap_raw_read(dsp->regmap, mem->base, &adsp1_id, 1418db40517cSMark Brown sizeof(adsp1_id)); 1419db40517cSMark Brown if (ret != 0) { 1420db40517cSMark Brown adsp_err(dsp, "Failed to read algorithm info: %d\n", 1421db40517cSMark Brown ret); 1422db40517cSMark Brown return ret; 1423db40517cSMark Brown } 1424db40517cSMark Brown 14253809f001SCharles Keepax n_algs = be32_to_cpu(adsp1_id.n_algs); 1426f395a218SMark Brown dsp->fw_id = be32_to_cpu(adsp1_id.fw.id); 1427db40517cSMark Brown adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n", 1428f395a218SMark Brown dsp->fw_id, 1429db40517cSMark Brown (be32_to_cpu(adsp1_id.fw.ver) & 0xff0000) >> 16, 1430db40517cSMark Brown (be32_to_cpu(adsp1_id.fw.ver) & 0xff00) >> 8, 1431db40517cSMark Brown be32_to_cpu(adsp1_id.fw.ver) & 0xff, 14323809f001SCharles Keepax n_algs); 1433db40517cSMark Brown 1434d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_ZM, 1435d9d20e17SCharles Keepax adsp1_id.fw.id, adsp1_id.zm); 1436d9d20e17SCharles Keepax if (IS_ERR(alg_region)) 1437d9d20e17SCharles Keepax return PTR_ERR(alg_region); 1438ac50009fSMark Brown 1439d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_DM, 1440d9d20e17SCharles Keepax adsp1_id.fw.id, adsp1_id.dm); 1441d9d20e17SCharles Keepax if (IS_ERR(alg_region)) 1442d9d20e17SCharles Keepax return PTR_ERR(alg_region); 1443ac50009fSMark Brown 1444db40517cSMark Brown pos = sizeof(adsp1_id) / 2; 14453809f001SCharles Keepax len = (sizeof(*adsp1_alg) * n_algs) / 2; 1446db40517cSMark Brown 14473809f001SCharles Keepax adsp1_alg = wm_adsp_read_algs(dsp, n_algs, mem->base + pos, len); 1448b618a185SCharles Keepax if (IS_ERR(adsp1_alg)) 1449b618a185SCharles Keepax return PTR_ERR(adsp1_alg); 1450db40517cSMark Brown 14513809f001SCharles Keepax for (i = 0; i < n_algs; i++) { 1452471f4885SMark Brown adsp_info(dsp, "%d: ID %x v%d.%d.%d DM@%x ZM@%x\n", 1453db40517cSMark Brown i, be32_to_cpu(adsp1_alg[i].alg.id), 1454db40517cSMark Brown (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff0000) >> 16, 1455db40517cSMark Brown (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff00) >> 8, 1456471f4885SMark Brown be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff, 1457471f4885SMark Brown be32_to_cpu(adsp1_alg[i].dm), 1458471f4885SMark Brown be32_to_cpu(adsp1_alg[i].zm)); 1459471f4885SMark Brown 1460d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_DM, 1461d9d20e17SCharles Keepax adsp1_alg[i].alg.id, 1462d9d20e17SCharles Keepax adsp1_alg[i].dm); 1463d9d20e17SCharles Keepax if (IS_ERR(alg_region)) { 1464d9d20e17SCharles Keepax ret = PTR_ERR(alg_region); 1465d6d52179SJS Park goto out; 1466d6d52179SJS Park } 14672323736dSCharles Keepax if (dsp->fw_ver == 0) { 14683809f001SCharles Keepax if (i + 1 < n_algs) { 14696958eb2aSCharles Keepax len = be32_to_cpu(adsp1_alg[i + 1].dm); 14706958eb2aSCharles Keepax len -= be32_to_cpu(adsp1_alg[i].dm); 14716958eb2aSCharles Keepax len *= 4; 14722323736dSCharles Keepax wm_adsp_create_control(dsp, alg_region, 0, 147326c22a19SCharles Keepax len, NULL, 0, 0); 14746ab2b7b4SDimitris Papastamos } else { 14756ab2b7b4SDimitris Papastamos adsp_warn(dsp, "Missing length info for region DM with ID %x\n", 14766ab2b7b4SDimitris Papastamos be32_to_cpu(adsp1_alg[i].alg.id)); 14776ab2b7b4SDimitris Papastamos } 14782323736dSCharles Keepax } 1479471f4885SMark Brown 1480d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_ZM, 1481d9d20e17SCharles Keepax adsp1_alg[i].alg.id, 1482d9d20e17SCharles Keepax adsp1_alg[i].zm); 1483d9d20e17SCharles Keepax if (IS_ERR(alg_region)) { 1484d9d20e17SCharles Keepax ret = PTR_ERR(alg_region); 1485d6d52179SJS Park goto out; 1486d6d52179SJS Park } 14872323736dSCharles Keepax if (dsp->fw_ver == 0) { 14883809f001SCharles Keepax if (i + 1 < n_algs) { 14896958eb2aSCharles Keepax len = be32_to_cpu(adsp1_alg[i + 1].zm); 14906958eb2aSCharles Keepax len -= be32_to_cpu(adsp1_alg[i].zm); 14916958eb2aSCharles Keepax len *= 4; 14922323736dSCharles Keepax wm_adsp_create_control(dsp, alg_region, 0, 149326c22a19SCharles Keepax len, NULL, 0, 0); 14946ab2b7b4SDimitris Papastamos } else { 14956ab2b7b4SDimitris Papastamos adsp_warn(dsp, "Missing length info for region ZM with ID %x\n", 14966ab2b7b4SDimitris Papastamos be32_to_cpu(adsp1_alg[i].alg.id)); 14976ab2b7b4SDimitris Papastamos } 1498b618a185SCharles Keepax } 14992323736dSCharles Keepax } 1500db40517cSMark Brown 1501b618a185SCharles Keepax out: 1502b618a185SCharles Keepax kfree(adsp1_alg); 1503b618a185SCharles Keepax return ret; 1504b618a185SCharles Keepax } 1505b618a185SCharles Keepax 1506b618a185SCharles Keepax static int wm_adsp2_setup_algs(struct wm_adsp *dsp) 1507b618a185SCharles Keepax { 1508b618a185SCharles Keepax struct wmfw_adsp2_id_hdr adsp2_id; 1509b618a185SCharles Keepax struct wmfw_adsp2_alg_hdr *adsp2_alg; 15103809f001SCharles Keepax struct wm_adsp_alg_region *alg_region; 1511b618a185SCharles Keepax const struct wm_adsp_region *mem; 1512b618a185SCharles Keepax unsigned int pos, len; 15133809f001SCharles Keepax size_t n_algs; 1514b618a185SCharles Keepax int i, ret; 1515b618a185SCharles Keepax 1516b618a185SCharles Keepax mem = wm_adsp_find_region(dsp, WMFW_ADSP2_XM); 1517b618a185SCharles Keepax if (WARN_ON(!mem)) 1518b618a185SCharles Keepax return -EINVAL; 1519b618a185SCharles Keepax 1520b618a185SCharles Keepax ret = regmap_raw_read(dsp->regmap, mem->base, &adsp2_id, 1521b618a185SCharles Keepax sizeof(adsp2_id)); 1522b618a185SCharles Keepax if (ret != 0) { 1523b618a185SCharles Keepax adsp_err(dsp, "Failed to read algorithm info: %d\n", 1524b618a185SCharles Keepax ret); 1525b618a185SCharles Keepax return ret; 1526b618a185SCharles Keepax } 1527b618a185SCharles Keepax 15283809f001SCharles Keepax n_algs = be32_to_cpu(adsp2_id.n_algs); 1529b618a185SCharles Keepax dsp->fw_id = be32_to_cpu(adsp2_id.fw.id); 1530f9f55e31SRichard Fitzgerald dsp->fw_id_version = be32_to_cpu(adsp2_id.fw.ver); 1531b618a185SCharles Keepax adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n", 1532b618a185SCharles Keepax dsp->fw_id, 1533f9f55e31SRichard Fitzgerald (dsp->fw_id_version & 0xff0000) >> 16, 1534f9f55e31SRichard Fitzgerald (dsp->fw_id_version & 0xff00) >> 8, 1535f9f55e31SRichard Fitzgerald dsp->fw_id_version & 0xff, 15363809f001SCharles Keepax n_algs); 1537b618a185SCharles Keepax 1538d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_XM, 1539d9d20e17SCharles Keepax adsp2_id.fw.id, adsp2_id.xm); 1540d9d20e17SCharles Keepax if (IS_ERR(alg_region)) 1541d9d20e17SCharles Keepax return PTR_ERR(alg_region); 1542b618a185SCharles Keepax 1543d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_YM, 1544d9d20e17SCharles Keepax adsp2_id.fw.id, adsp2_id.ym); 1545d9d20e17SCharles Keepax if (IS_ERR(alg_region)) 1546d9d20e17SCharles Keepax return PTR_ERR(alg_region); 1547b618a185SCharles Keepax 1548d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_ZM, 1549d9d20e17SCharles Keepax adsp2_id.fw.id, adsp2_id.zm); 1550d9d20e17SCharles Keepax if (IS_ERR(alg_region)) 1551d9d20e17SCharles Keepax return PTR_ERR(alg_region); 1552b618a185SCharles Keepax 1553b618a185SCharles Keepax pos = sizeof(adsp2_id) / 2; 15543809f001SCharles Keepax len = (sizeof(*adsp2_alg) * n_algs) / 2; 1555b618a185SCharles Keepax 15563809f001SCharles Keepax adsp2_alg = wm_adsp_read_algs(dsp, n_algs, mem->base + pos, len); 1557b618a185SCharles Keepax if (IS_ERR(adsp2_alg)) 1558b618a185SCharles Keepax return PTR_ERR(adsp2_alg); 1559b618a185SCharles Keepax 15603809f001SCharles Keepax for (i = 0; i < n_algs; i++) { 1561471f4885SMark Brown adsp_info(dsp, 1562471f4885SMark Brown "%d: ID %x v%d.%d.%d XM@%x YM@%x ZM@%x\n", 1563db40517cSMark Brown i, be32_to_cpu(adsp2_alg[i].alg.id), 1564db40517cSMark Brown (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff0000) >> 16, 1565db40517cSMark Brown (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff00) >> 8, 1566471f4885SMark Brown be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff, 1567471f4885SMark Brown be32_to_cpu(adsp2_alg[i].xm), 1568471f4885SMark Brown be32_to_cpu(adsp2_alg[i].ym), 1569471f4885SMark Brown be32_to_cpu(adsp2_alg[i].zm)); 1570471f4885SMark Brown 1571d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_XM, 1572d9d20e17SCharles Keepax adsp2_alg[i].alg.id, 1573d9d20e17SCharles Keepax adsp2_alg[i].xm); 1574d9d20e17SCharles Keepax if (IS_ERR(alg_region)) { 1575d9d20e17SCharles Keepax ret = PTR_ERR(alg_region); 1576d6d52179SJS Park goto out; 1577d6d52179SJS Park } 15782323736dSCharles Keepax if (dsp->fw_ver == 0) { 15793809f001SCharles Keepax if (i + 1 < n_algs) { 15806958eb2aSCharles Keepax len = be32_to_cpu(adsp2_alg[i + 1].xm); 15816958eb2aSCharles Keepax len -= be32_to_cpu(adsp2_alg[i].xm); 15826958eb2aSCharles Keepax len *= 4; 15832323736dSCharles Keepax wm_adsp_create_control(dsp, alg_region, 0, 158426c22a19SCharles Keepax len, NULL, 0, 0); 15856ab2b7b4SDimitris Papastamos } else { 15866ab2b7b4SDimitris Papastamos adsp_warn(dsp, "Missing length info for region XM with ID %x\n", 15876ab2b7b4SDimitris Papastamos be32_to_cpu(adsp2_alg[i].alg.id)); 15886ab2b7b4SDimitris Papastamos } 15892323736dSCharles Keepax } 1590471f4885SMark Brown 1591d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_YM, 1592d9d20e17SCharles Keepax adsp2_alg[i].alg.id, 1593d9d20e17SCharles Keepax adsp2_alg[i].ym); 1594d9d20e17SCharles Keepax if (IS_ERR(alg_region)) { 1595d9d20e17SCharles Keepax ret = PTR_ERR(alg_region); 1596d6d52179SJS Park goto out; 1597d6d52179SJS Park } 15982323736dSCharles Keepax if (dsp->fw_ver == 0) { 15993809f001SCharles Keepax if (i + 1 < n_algs) { 16006958eb2aSCharles Keepax len = be32_to_cpu(adsp2_alg[i + 1].ym); 16016958eb2aSCharles Keepax len -= be32_to_cpu(adsp2_alg[i].ym); 16026958eb2aSCharles Keepax len *= 4; 16032323736dSCharles Keepax wm_adsp_create_control(dsp, alg_region, 0, 160426c22a19SCharles Keepax len, NULL, 0, 0); 16056ab2b7b4SDimitris Papastamos } else { 16066ab2b7b4SDimitris Papastamos adsp_warn(dsp, "Missing length info for region YM with ID %x\n", 16076ab2b7b4SDimitris Papastamos be32_to_cpu(adsp2_alg[i].alg.id)); 16086ab2b7b4SDimitris Papastamos } 16092323736dSCharles Keepax } 1610471f4885SMark Brown 1611d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_ZM, 1612d9d20e17SCharles Keepax adsp2_alg[i].alg.id, 1613d9d20e17SCharles Keepax adsp2_alg[i].zm); 1614d9d20e17SCharles Keepax if (IS_ERR(alg_region)) { 1615d9d20e17SCharles Keepax ret = PTR_ERR(alg_region); 1616d6d52179SJS Park goto out; 1617d6d52179SJS Park } 16182323736dSCharles Keepax if (dsp->fw_ver == 0) { 16193809f001SCharles Keepax if (i + 1 < n_algs) { 16206958eb2aSCharles Keepax len = be32_to_cpu(adsp2_alg[i + 1].zm); 16216958eb2aSCharles Keepax len -= be32_to_cpu(adsp2_alg[i].zm); 16226958eb2aSCharles Keepax len *= 4; 16232323736dSCharles Keepax wm_adsp_create_control(dsp, alg_region, 0, 162426c22a19SCharles Keepax len, NULL, 0, 0); 16256ab2b7b4SDimitris Papastamos } else { 16266ab2b7b4SDimitris Papastamos adsp_warn(dsp, "Missing length info for region ZM with ID %x\n", 16276ab2b7b4SDimitris Papastamos be32_to_cpu(adsp2_alg[i].alg.id)); 16286ab2b7b4SDimitris Papastamos } 1629db40517cSMark Brown } 16302323736dSCharles Keepax } 1631db40517cSMark Brown 1632db40517cSMark Brown out: 1633b618a185SCharles Keepax kfree(adsp2_alg); 1634db40517cSMark Brown return ret; 1635db40517cSMark Brown } 1636db40517cSMark Brown 16372159ad93SMark Brown static int wm_adsp_load_coeff(struct wm_adsp *dsp) 16382159ad93SMark Brown { 1639cf17c83cSMark Brown LIST_HEAD(buf_list); 16402159ad93SMark Brown struct regmap *regmap = dsp->regmap; 16412159ad93SMark Brown struct wmfw_coeff_hdr *hdr; 16422159ad93SMark Brown struct wmfw_coeff_item *blk; 16432159ad93SMark Brown const struct firmware *firmware; 1644471f4885SMark Brown const struct wm_adsp_region *mem; 1645471f4885SMark Brown struct wm_adsp_alg_region *alg_region; 16462159ad93SMark Brown const char *region_name; 16472159ad93SMark Brown int ret, pos, blocks, type, offset, reg; 16482159ad93SMark Brown char *file; 1649cf17c83cSMark Brown struct wm_adsp_buf *buf; 16502159ad93SMark Brown 16512159ad93SMark Brown file = kzalloc(PAGE_SIZE, GFP_KERNEL); 16522159ad93SMark Brown if (file == NULL) 16532159ad93SMark Brown return -ENOMEM; 16542159ad93SMark Brown 16551023dbd9SMark Brown snprintf(file, PAGE_SIZE, "%s-dsp%d-%s.bin", dsp->part, dsp->num, 16561023dbd9SMark Brown wm_adsp_fw[dsp->fw].file); 16572159ad93SMark Brown file[PAGE_SIZE - 1] = '\0'; 16582159ad93SMark Brown 16592159ad93SMark Brown ret = request_firmware(&firmware, file, dsp->dev); 16602159ad93SMark Brown if (ret != 0) { 16612159ad93SMark Brown adsp_warn(dsp, "Failed to request '%s'\n", file); 16622159ad93SMark Brown ret = 0; 16632159ad93SMark Brown goto out; 16642159ad93SMark Brown } 16652159ad93SMark Brown ret = -EINVAL; 16662159ad93SMark Brown 16672159ad93SMark Brown if (sizeof(*hdr) >= firmware->size) { 16682159ad93SMark Brown adsp_err(dsp, "%s: file too short, %zu bytes\n", 16692159ad93SMark Brown file, firmware->size); 16702159ad93SMark Brown goto out_fw; 16712159ad93SMark Brown } 16722159ad93SMark Brown 16732159ad93SMark Brown hdr = (void *)&firmware->data[0]; 16742159ad93SMark Brown if (memcmp(hdr->magic, "WMDR", 4) != 0) { 16752159ad93SMark Brown adsp_err(dsp, "%s: invalid magic\n", file); 1676a4cdbec7SCharles Keepax goto out_fw; 16772159ad93SMark Brown } 16782159ad93SMark Brown 1679c712326dSMark Brown switch (be32_to_cpu(hdr->rev) & 0xff) { 1680c712326dSMark Brown case 1: 1681c712326dSMark Brown break; 1682c712326dSMark Brown default: 1683c712326dSMark Brown adsp_err(dsp, "%s: Unsupported coefficient file format %d\n", 1684c712326dSMark Brown file, be32_to_cpu(hdr->rev) & 0xff); 1685c712326dSMark Brown ret = -EINVAL; 1686c712326dSMark Brown goto out_fw; 1687c712326dSMark Brown } 1688c712326dSMark Brown 16892159ad93SMark Brown adsp_dbg(dsp, "%s: v%d.%d.%d\n", file, 16902159ad93SMark Brown (le32_to_cpu(hdr->ver) >> 16) & 0xff, 16912159ad93SMark Brown (le32_to_cpu(hdr->ver) >> 8) & 0xff, 16922159ad93SMark Brown le32_to_cpu(hdr->ver) & 0xff); 16932159ad93SMark Brown 16942159ad93SMark Brown pos = le32_to_cpu(hdr->len); 16952159ad93SMark Brown 16962159ad93SMark Brown blocks = 0; 16972159ad93SMark Brown while (pos < firmware->size && 16982159ad93SMark Brown pos - firmware->size > sizeof(*blk)) { 16992159ad93SMark Brown blk = (void *)(&firmware->data[pos]); 17002159ad93SMark Brown 1701c712326dSMark Brown type = le16_to_cpu(blk->type); 1702c712326dSMark Brown offset = le16_to_cpu(blk->offset); 17032159ad93SMark Brown 17042159ad93SMark Brown adsp_dbg(dsp, "%s.%d: %x v%d.%d.%d\n", 17052159ad93SMark Brown file, blocks, le32_to_cpu(blk->id), 17062159ad93SMark Brown (le32_to_cpu(blk->ver) >> 16) & 0xff, 17072159ad93SMark Brown (le32_to_cpu(blk->ver) >> 8) & 0xff, 17082159ad93SMark Brown le32_to_cpu(blk->ver) & 0xff); 17092159ad93SMark Brown adsp_dbg(dsp, "%s.%d: %d bytes at 0x%x in %x\n", 17102159ad93SMark Brown file, blocks, le32_to_cpu(blk->len), offset, type); 17112159ad93SMark Brown 17122159ad93SMark Brown reg = 0; 17132159ad93SMark Brown region_name = "Unknown"; 17142159ad93SMark Brown switch (type) { 1715c712326dSMark Brown case (WMFW_NAME_TEXT << 8): 1716c712326dSMark Brown case (WMFW_INFO_TEXT << 8): 17172159ad93SMark Brown break; 1718c712326dSMark Brown case (WMFW_ABSOLUTE << 8): 1719f395a218SMark Brown /* 1720f395a218SMark Brown * Old files may use this for global 1721f395a218SMark Brown * coefficients. 1722f395a218SMark Brown */ 1723f395a218SMark Brown if (le32_to_cpu(blk->id) == dsp->fw_id && 1724f395a218SMark Brown offset == 0) { 1725f395a218SMark Brown region_name = "global coefficients"; 1726f395a218SMark Brown mem = wm_adsp_find_region(dsp, type); 1727f395a218SMark Brown if (!mem) { 1728f395a218SMark Brown adsp_err(dsp, "No ZM\n"); 1729f395a218SMark Brown break; 1730f395a218SMark Brown } 1731f395a218SMark Brown reg = wm_adsp_region_to_reg(mem, 0); 1732f395a218SMark Brown 1733f395a218SMark Brown } else { 17342159ad93SMark Brown region_name = "register"; 17352159ad93SMark Brown reg = offset; 1736f395a218SMark Brown } 17372159ad93SMark Brown break; 1738471f4885SMark Brown 1739471f4885SMark Brown case WMFW_ADSP1_DM: 1740471f4885SMark Brown case WMFW_ADSP1_ZM: 1741471f4885SMark Brown case WMFW_ADSP2_XM: 1742471f4885SMark Brown case WMFW_ADSP2_YM: 1743471f4885SMark Brown adsp_dbg(dsp, "%s.%d: %d bytes in %x for %x\n", 1744471f4885SMark Brown file, blocks, le32_to_cpu(blk->len), 1745471f4885SMark Brown type, le32_to_cpu(blk->id)); 1746471f4885SMark Brown 1747471f4885SMark Brown mem = wm_adsp_find_region(dsp, type); 1748471f4885SMark Brown if (!mem) { 1749471f4885SMark Brown adsp_err(dsp, "No base for region %x\n", type); 1750471f4885SMark Brown break; 1751471f4885SMark Brown } 1752471f4885SMark Brown 175314197095SCharles Keepax alg_region = wm_adsp_find_alg_region(dsp, type, 175414197095SCharles Keepax le32_to_cpu(blk->id)); 175514197095SCharles Keepax if (alg_region) { 1756338c5188SMark Brown reg = alg_region->base; 175714197095SCharles Keepax reg = wm_adsp_region_to_reg(mem, reg); 1758338c5188SMark Brown reg += offset; 175914197095SCharles Keepax } else { 1760471f4885SMark Brown adsp_err(dsp, "No %x for algorithm %x\n", 1761471f4885SMark Brown type, le32_to_cpu(blk->id)); 176214197095SCharles Keepax } 1763471f4885SMark Brown break; 1764471f4885SMark Brown 17652159ad93SMark Brown default: 176625c62f7eSMark Brown adsp_err(dsp, "%s.%d: Unknown region type %x at %d\n", 176725c62f7eSMark Brown file, blocks, type, pos); 17682159ad93SMark Brown break; 17692159ad93SMark Brown } 17702159ad93SMark Brown 17712159ad93SMark Brown if (reg) { 1772cf17c83cSMark Brown buf = wm_adsp_buf_alloc(blk->data, 1773cf17c83cSMark Brown le32_to_cpu(blk->len), 1774cf17c83cSMark Brown &buf_list); 1775a76fefabSMark Brown if (!buf) { 1776a76fefabSMark Brown adsp_err(dsp, "Out of memory\n"); 1777f4b82812SWei Yongjun ret = -ENOMEM; 1778f4b82812SWei Yongjun goto out_fw; 1779a76fefabSMark Brown } 1780a76fefabSMark Brown 178120da6d5aSMark Brown adsp_dbg(dsp, "%s.%d: Writing %d bytes at %x\n", 178220da6d5aSMark Brown file, blocks, le32_to_cpu(blk->len), 178320da6d5aSMark Brown reg); 1784cf17c83cSMark Brown ret = regmap_raw_write_async(regmap, reg, buf->buf, 17852159ad93SMark Brown le32_to_cpu(blk->len)); 17862159ad93SMark Brown if (ret != 0) { 17872159ad93SMark Brown adsp_err(dsp, 178843bc3bf6SDimitris Papastamos "%s.%d: Failed to write to %x in %s: %d\n", 178943bc3bf6SDimitris Papastamos file, blocks, reg, region_name, ret); 17902159ad93SMark Brown } 17912159ad93SMark Brown } 17922159ad93SMark Brown 1793be951017SCharles Keepax pos += (le32_to_cpu(blk->len) + sizeof(*blk) + 3) & ~0x03; 17942159ad93SMark Brown blocks++; 17952159ad93SMark Brown } 17962159ad93SMark Brown 1797cf17c83cSMark Brown ret = regmap_async_complete(regmap); 1798cf17c83cSMark Brown if (ret != 0) 1799cf17c83cSMark Brown adsp_err(dsp, "Failed to complete async write: %d\n", ret); 1800cf17c83cSMark Brown 18012159ad93SMark Brown if (pos > firmware->size) 18022159ad93SMark Brown adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n", 18032159ad93SMark Brown file, blocks, pos - firmware->size); 18042159ad93SMark Brown 1805f9f55e31SRichard Fitzgerald wm_adsp_debugfs_save_binname(dsp, file); 1806f9f55e31SRichard Fitzgerald 18072159ad93SMark Brown out_fw: 18089da7a5a9SCharles Keepax regmap_async_complete(regmap); 18092159ad93SMark Brown release_firmware(firmware); 1810cf17c83cSMark Brown wm_adsp_buf_free(&buf_list); 18112159ad93SMark Brown out: 18122159ad93SMark Brown kfree(file); 1813f4b82812SWei Yongjun return ret; 18142159ad93SMark Brown } 18152159ad93SMark Brown 18163809f001SCharles Keepax int wm_adsp1_init(struct wm_adsp *dsp) 18175e7a7a22SMark Brown { 18183809f001SCharles Keepax INIT_LIST_HEAD(&dsp->alg_regions); 18195e7a7a22SMark Brown 1820078e7183SCharles Keepax mutex_init(&dsp->pwr_lock); 1821078e7183SCharles Keepax 18225e7a7a22SMark Brown return 0; 18235e7a7a22SMark Brown } 18245e7a7a22SMark Brown EXPORT_SYMBOL_GPL(wm_adsp1_init); 18255e7a7a22SMark Brown 18262159ad93SMark Brown int wm_adsp1_event(struct snd_soc_dapm_widget *w, 18272159ad93SMark Brown struct snd_kcontrol *kcontrol, 18282159ad93SMark Brown int event) 18292159ad93SMark Brown { 183072718517SLars-Peter Clausen struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); 18312159ad93SMark Brown struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec); 18322159ad93SMark Brown struct wm_adsp *dsp = &dsps[w->shift]; 1833b0101b4fSDimitris Papastamos struct wm_adsp_alg_region *alg_region; 18346ab2b7b4SDimitris Papastamos struct wm_coeff_ctl *ctl; 18352159ad93SMark Brown int ret; 18367585a5b0SCharles Keepax unsigned int val; 18372159ad93SMark Brown 183800200107SLars-Peter Clausen dsp->card = codec->component.card; 183992bb4c32SDimitris Papastamos 1840078e7183SCharles Keepax mutex_lock(&dsp->pwr_lock); 1841078e7183SCharles Keepax 18422159ad93SMark Brown switch (event) { 18432159ad93SMark Brown case SND_SOC_DAPM_POST_PMU: 18442159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 18452159ad93SMark Brown ADSP1_SYS_ENA, ADSP1_SYS_ENA); 18462159ad93SMark Brown 184794e205bfSChris Rattray /* 184894e205bfSChris Rattray * For simplicity set the DSP clock rate to be the 184994e205bfSChris Rattray * SYSCLK rate rather than making it configurable. 185094e205bfSChris Rattray */ 185194e205bfSChris Rattray if (dsp->sysclk_reg) { 185294e205bfSChris Rattray ret = regmap_read(dsp->regmap, dsp->sysclk_reg, &val); 185394e205bfSChris Rattray if (ret != 0) { 185494e205bfSChris Rattray adsp_err(dsp, "Failed to read SYSCLK state: %d\n", 185594e205bfSChris Rattray ret); 1856078e7183SCharles Keepax goto err_mutex; 185794e205bfSChris Rattray } 185894e205bfSChris Rattray 185994e205bfSChris Rattray val = (val & dsp->sysclk_mask) 186094e205bfSChris Rattray >> dsp->sysclk_shift; 186194e205bfSChris Rattray 186294e205bfSChris Rattray ret = regmap_update_bits(dsp->regmap, 186394e205bfSChris Rattray dsp->base + ADSP1_CONTROL_31, 186494e205bfSChris Rattray ADSP1_CLK_SEL_MASK, val); 186594e205bfSChris Rattray if (ret != 0) { 186694e205bfSChris Rattray adsp_err(dsp, "Failed to set clock rate: %d\n", 186794e205bfSChris Rattray ret); 1868078e7183SCharles Keepax goto err_mutex; 186994e205bfSChris Rattray } 187094e205bfSChris Rattray } 187194e205bfSChris Rattray 18722159ad93SMark Brown ret = wm_adsp_load(dsp); 18732159ad93SMark Brown if (ret != 0) 1874078e7183SCharles Keepax goto err_ena; 18752159ad93SMark Brown 1876b618a185SCharles Keepax ret = wm_adsp1_setup_algs(dsp); 1877db40517cSMark Brown if (ret != 0) 1878078e7183SCharles Keepax goto err_ena; 1879db40517cSMark Brown 18802159ad93SMark Brown ret = wm_adsp_load_coeff(dsp); 18812159ad93SMark Brown if (ret != 0) 1882078e7183SCharles Keepax goto err_ena; 18832159ad93SMark Brown 18840c2e3f34SDimitris Papastamos /* Initialize caches for enabled and unset controls */ 188581ad93ecSDimitris Papastamos ret = wm_coeff_init_control_caches(dsp); 18866ab2b7b4SDimitris Papastamos if (ret != 0) 1887078e7183SCharles Keepax goto err_ena; 18886ab2b7b4SDimitris Papastamos 18890c2e3f34SDimitris Papastamos /* Sync set controls */ 189081ad93ecSDimitris Papastamos ret = wm_coeff_sync_controls(dsp); 18916ab2b7b4SDimitris Papastamos if (ret != 0) 1892078e7183SCharles Keepax goto err_ena; 18936ab2b7b4SDimitris Papastamos 18942159ad93SMark Brown /* Start the core running */ 18952159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 18962159ad93SMark Brown ADSP1_CORE_ENA | ADSP1_START, 18972159ad93SMark Brown ADSP1_CORE_ENA | ADSP1_START); 18982159ad93SMark Brown break; 18992159ad93SMark Brown 19002159ad93SMark Brown case SND_SOC_DAPM_PRE_PMD: 19012159ad93SMark Brown /* Halt the core */ 19022159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 19032159ad93SMark Brown ADSP1_CORE_ENA | ADSP1_START, 0); 19042159ad93SMark Brown 19052159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_19, 19062159ad93SMark Brown ADSP1_WDMA_BUFFER_LENGTH_MASK, 0); 19072159ad93SMark Brown 19082159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 19092159ad93SMark Brown ADSP1_SYS_ENA, 0); 19106ab2b7b4SDimitris Papastamos 191181ad93ecSDimitris Papastamos list_for_each_entry(ctl, &dsp->ctl_list, list) 19126ab2b7b4SDimitris Papastamos ctl->enabled = 0; 1913b0101b4fSDimitris Papastamos 1914b0101b4fSDimitris Papastamos while (!list_empty(&dsp->alg_regions)) { 1915b0101b4fSDimitris Papastamos alg_region = list_first_entry(&dsp->alg_regions, 1916b0101b4fSDimitris Papastamos struct wm_adsp_alg_region, 1917b0101b4fSDimitris Papastamos list); 1918b0101b4fSDimitris Papastamos list_del(&alg_region->list); 1919b0101b4fSDimitris Papastamos kfree(alg_region); 1920b0101b4fSDimitris Papastamos } 19212159ad93SMark Brown break; 19222159ad93SMark Brown 19232159ad93SMark Brown default: 19242159ad93SMark Brown break; 19252159ad93SMark Brown } 19262159ad93SMark Brown 1927078e7183SCharles Keepax mutex_unlock(&dsp->pwr_lock); 1928078e7183SCharles Keepax 19292159ad93SMark Brown return 0; 19302159ad93SMark Brown 1931078e7183SCharles Keepax err_ena: 19322159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 19332159ad93SMark Brown ADSP1_SYS_ENA, 0); 1934078e7183SCharles Keepax err_mutex: 1935078e7183SCharles Keepax mutex_unlock(&dsp->pwr_lock); 1936078e7183SCharles Keepax 19372159ad93SMark Brown return ret; 19382159ad93SMark Brown } 19392159ad93SMark Brown EXPORT_SYMBOL_GPL(wm_adsp1_event); 19402159ad93SMark Brown 19412159ad93SMark Brown static int wm_adsp2_ena(struct wm_adsp *dsp) 19422159ad93SMark Brown { 19432159ad93SMark Brown unsigned int val; 19442159ad93SMark Brown int ret, count; 19452159ad93SMark Brown 19461552c325SMark Brown ret = regmap_update_bits_async(dsp->regmap, dsp->base + ADSP2_CONTROL, 19472159ad93SMark Brown ADSP2_SYS_ENA, ADSP2_SYS_ENA); 19482159ad93SMark Brown if (ret != 0) 19492159ad93SMark Brown return ret; 19502159ad93SMark Brown 19512159ad93SMark Brown /* Wait for the RAM to start, should be near instantaneous */ 1952939fd1e8SCharles Keepax for (count = 0; count < 10; ++count) { 19532159ad93SMark Brown ret = regmap_read(dsp->regmap, dsp->base + ADSP2_STATUS1, 19542159ad93SMark Brown &val); 19552159ad93SMark Brown if (ret != 0) 19562159ad93SMark Brown return ret; 1957939fd1e8SCharles Keepax 1958939fd1e8SCharles Keepax if (val & ADSP2_RAM_RDY) 1959939fd1e8SCharles Keepax break; 1960939fd1e8SCharles Keepax 1961939fd1e8SCharles Keepax msleep(1); 1962939fd1e8SCharles Keepax } 19632159ad93SMark Brown 19642159ad93SMark Brown if (!(val & ADSP2_RAM_RDY)) { 19652159ad93SMark Brown adsp_err(dsp, "Failed to start DSP RAM\n"); 19662159ad93SMark Brown return -EBUSY; 19672159ad93SMark Brown } 19682159ad93SMark Brown 19692159ad93SMark Brown adsp_dbg(dsp, "RAM ready after %d polls\n", count); 19702159ad93SMark Brown 19712159ad93SMark Brown return 0; 19722159ad93SMark Brown } 19732159ad93SMark Brown 197418b1a902SCharles Keepax static void wm_adsp2_boot_work(struct work_struct *work) 19752159ad93SMark Brown { 1976d8a64d6aSCharles Keepax struct wm_adsp *dsp = container_of(work, 1977d8a64d6aSCharles Keepax struct wm_adsp, 1978d8a64d6aSCharles Keepax boot_work); 19792159ad93SMark Brown int ret; 1980d8a64d6aSCharles Keepax unsigned int val; 19812159ad93SMark Brown 1982078e7183SCharles Keepax mutex_lock(&dsp->pwr_lock); 1983078e7183SCharles Keepax 1984dd49e2c8SMark Brown /* 1985dd49e2c8SMark Brown * For simplicity set the DSP clock rate to be the 1986dd49e2c8SMark Brown * SYSCLK rate rather than making it configurable. 1987dd49e2c8SMark Brown */ 1988dd49e2c8SMark Brown ret = regmap_read(dsp->regmap, ARIZONA_SYSTEM_CLOCK_1, &val); 1989dd49e2c8SMark Brown if (ret != 0) { 1990d8a64d6aSCharles Keepax adsp_err(dsp, "Failed to read SYSCLK state: %d\n", ret); 1991078e7183SCharles Keepax goto err_mutex; 1992dd49e2c8SMark Brown } 1993dd49e2c8SMark Brown val = (val & ARIZONA_SYSCLK_FREQ_MASK) 1994dd49e2c8SMark Brown >> ARIZONA_SYSCLK_FREQ_SHIFT; 1995dd49e2c8SMark Brown 19961552c325SMark Brown ret = regmap_update_bits_async(dsp->regmap, 1997dd49e2c8SMark Brown dsp->base + ADSP2_CLOCKING, 1998dd49e2c8SMark Brown ADSP2_CLK_SEL_MASK, val); 1999dd49e2c8SMark Brown if (ret != 0) { 2000d8a64d6aSCharles Keepax adsp_err(dsp, "Failed to set clock rate: %d\n", ret); 2001078e7183SCharles Keepax goto err_mutex; 2002dd49e2c8SMark Brown } 2003dd49e2c8SMark Brown 20042159ad93SMark Brown ret = wm_adsp2_ena(dsp); 20052159ad93SMark Brown if (ret != 0) 2006078e7183SCharles Keepax goto err_mutex; 20072159ad93SMark Brown 20082159ad93SMark Brown ret = wm_adsp_load(dsp); 20092159ad93SMark Brown if (ret != 0) 2010078e7183SCharles Keepax goto err_ena; 20112159ad93SMark Brown 2012b618a185SCharles Keepax ret = wm_adsp2_setup_algs(dsp); 2013db40517cSMark Brown if (ret != 0) 2014078e7183SCharles Keepax goto err_ena; 2015db40517cSMark Brown 20162159ad93SMark Brown ret = wm_adsp_load_coeff(dsp); 20172159ad93SMark Brown if (ret != 0) 2018078e7183SCharles Keepax goto err_ena; 20192159ad93SMark Brown 20200c2e3f34SDimitris Papastamos /* Initialize caches for enabled and unset controls */ 202181ad93ecSDimitris Papastamos ret = wm_coeff_init_control_caches(dsp); 20226ab2b7b4SDimitris Papastamos if (ret != 0) 2023078e7183SCharles Keepax goto err_ena; 20246ab2b7b4SDimitris Papastamos 20250c2e3f34SDimitris Papastamos /* Sync set controls */ 202681ad93ecSDimitris Papastamos ret = wm_coeff_sync_controls(dsp); 20276ab2b7b4SDimitris Papastamos if (ret != 0) 2028078e7183SCharles Keepax goto err_ena; 20296ab2b7b4SDimitris Papastamos 20301023dbd9SMark Brown dsp->running = true; 2031d8a64d6aSCharles Keepax 2032078e7183SCharles Keepax mutex_unlock(&dsp->pwr_lock); 2033078e7183SCharles Keepax 2034d8a64d6aSCharles Keepax return; 2035d8a64d6aSCharles Keepax 2036078e7183SCharles Keepax err_ena: 2037d8a64d6aSCharles Keepax regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 2038d8a64d6aSCharles Keepax ADSP2_SYS_ENA | ADSP2_CORE_ENA | ADSP2_START, 0); 2039078e7183SCharles Keepax err_mutex: 2040078e7183SCharles Keepax mutex_unlock(&dsp->pwr_lock); 2041d8a64d6aSCharles Keepax } 2042d8a64d6aSCharles Keepax 204312db5eddSCharles Keepax int wm_adsp2_early_event(struct snd_soc_dapm_widget *w, 204412db5eddSCharles Keepax struct snd_kcontrol *kcontrol, int event) 204512db5eddSCharles Keepax { 204672718517SLars-Peter Clausen struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); 204712db5eddSCharles Keepax struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec); 204812db5eddSCharles Keepax struct wm_adsp *dsp = &dsps[w->shift]; 204912db5eddSCharles Keepax 205000200107SLars-Peter Clausen dsp->card = codec->component.card; 205112db5eddSCharles Keepax 205212db5eddSCharles Keepax switch (event) { 205312db5eddSCharles Keepax case SND_SOC_DAPM_PRE_PMU: 205412db5eddSCharles Keepax queue_work(system_unbound_wq, &dsp->boot_work); 205512db5eddSCharles Keepax break; 205612db5eddSCharles Keepax default: 205712db5eddSCharles Keepax break; 2058cab27258SCharles Keepax } 205912db5eddSCharles Keepax 206012db5eddSCharles Keepax return 0; 206112db5eddSCharles Keepax } 206212db5eddSCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp2_early_event); 206312db5eddSCharles Keepax 2064d8a64d6aSCharles Keepax int wm_adsp2_event(struct snd_soc_dapm_widget *w, 2065d8a64d6aSCharles Keepax struct snd_kcontrol *kcontrol, int event) 2066d8a64d6aSCharles Keepax { 206772718517SLars-Peter Clausen struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); 2068d8a64d6aSCharles Keepax struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec); 2069d8a64d6aSCharles Keepax struct wm_adsp *dsp = &dsps[w->shift]; 2070d8a64d6aSCharles Keepax struct wm_adsp_alg_region *alg_region; 2071d8a64d6aSCharles Keepax struct wm_coeff_ctl *ctl; 2072d8a64d6aSCharles Keepax int ret; 2073d8a64d6aSCharles Keepax 2074d8a64d6aSCharles Keepax switch (event) { 2075d8a64d6aSCharles Keepax case SND_SOC_DAPM_POST_PMU: 2076d8a64d6aSCharles Keepax flush_work(&dsp->boot_work); 2077d8a64d6aSCharles Keepax 2078d8a64d6aSCharles Keepax if (!dsp->running) 2079d8a64d6aSCharles Keepax return -EIO; 2080d8a64d6aSCharles Keepax 2081d8a64d6aSCharles Keepax ret = regmap_update_bits(dsp->regmap, 2082d8a64d6aSCharles Keepax dsp->base + ADSP2_CONTROL, 208300e4c3b6SCharles Keepax ADSP2_CORE_ENA | ADSP2_START, 208400e4c3b6SCharles Keepax ADSP2_CORE_ENA | ADSP2_START); 2085d8a64d6aSCharles Keepax if (ret != 0) 2086d8a64d6aSCharles Keepax goto err; 20872159ad93SMark Brown break; 20882159ad93SMark Brown 20892159ad93SMark Brown case SND_SOC_DAPM_PRE_PMD: 209010337b07SRichard Fitzgerald /* Log firmware state, it can be useful for analysis */ 209110337b07SRichard Fitzgerald wm_adsp2_show_fw_status(dsp); 209210337b07SRichard Fitzgerald 2093078e7183SCharles Keepax mutex_lock(&dsp->pwr_lock); 2094078e7183SCharles Keepax 2095f9f55e31SRichard Fitzgerald wm_adsp_debugfs_clear(dsp); 2096f9f55e31SRichard Fitzgerald 2097f9f55e31SRichard Fitzgerald dsp->fw_id = 0; 2098f9f55e31SRichard Fitzgerald dsp->fw_id_version = 0; 20991023dbd9SMark Brown dsp->running = false; 21001023dbd9SMark Brown 21012159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 2102a7f9be7eSMark Brown ADSP2_SYS_ENA | ADSP2_CORE_ENA | 2103a7f9be7eSMark Brown ADSP2_START, 0); 2104973838a0SMark Brown 21052d30b575SMark Brown /* Make sure DMAs are quiesced */ 21062d30b575SMark Brown regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_1, 0); 21072d30b575SMark Brown regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_2, 0); 21082d30b575SMark Brown regmap_write(dsp->regmap, dsp->base + ADSP2_RDMA_CONFIG_1, 0); 21092d30b575SMark Brown 211081ad93ecSDimitris Papastamos list_for_each_entry(ctl, &dsp->ctl_list, list) 21116ab2b7b4SDimitris Papastamos ctl->enabled = 0; 21126ab2b7b4SDimitris Papastamos 2113471f4885SMark Brown while (!list_empty(&dsp->alg_regions)) { 2114471f4885SMark Brown alg_region = list_first_entry(&dsp->alg_regions, 2115471f4885SMark Brown struct wm_adsp_alg_region, 2116471f4885SMark Brown list); 2117471f4885SMark Brown list_del(&alg_region->list); 2118471f4885SMark Brown kfree(alg_region); 2119471f4885SMark Brown } 2120ddbc5efeSCharles Keepax 2121078e7183SCharles Keepax mutex_unlock(&dsp->pwr_lock); 2122078e7183SCharles Keepax 2123ddbc5efeSCharles Keepax adsp_dbg(dsp, "Shutdown complete\n"); 21242159ad93SMark Brown break; 21252159ad93SMark Brown 21262159ad93SMark Brown default: 21272159ad93SMark Brown break; 21282159ad93SMark Brown } 21292159ad93SMark Brown 21302159ad93SMark Brown return 0; 21312159ad93SMark Brown err: 21322159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 2133a7f9be7eSMark Brown ADSP2_SYS_ENA | ADSP2_CORE_ENA | ADSP2_START, 0); 21342159ad93SMark Brown return ret; 21352159ad93SMark Brown } 21362159ad93SMark Brown EXPORT_SYMBOL_GPL(wm_adsp2_event); 2137973838a0SMark Brown 2138f5e2ce92SRichard Fitzgerald int wm_adsp2_codec_probe(struct wm_adsp *dsp, struct snd_soc_codec *codec) 2139f5e2ce92SRichard Fitzgerald { 2140f9f55e31SRichard Fitzgerald wm_adsp2_init_debugfs(dsp, codec); 2141f9f55e31SRichard Fitzgerald 2142218e5087SRichard Fitzgerald return snd_soc_add_codec_controls(codec, 2143336d0442SRichard Fitzgerald &wm_adsp_fw_controls[dsp->num - 1], 2144336d0442SRichard Fitzgerald 1); 2145f5e2ce92SRichard Fitzgerald } 2146f5e2ce92SRichard Fitzgerald EXPORT_SYMBOL_GPL(wm_adsp2_codec_probe); 2147f5e2ce92SRichard Fitzgerald 2148f5e2ce92SRichard Fitzgerald int wm_adsp2_codec_remove(struct wm_adsp *dsp, struct snd_soc_codec *codec) 2149f5e2ce92SRichard Fitzgerald { 2150f9f55e31SRichard Fitzgerald wm_adsp2_cleanup_debugfs(dsp); 2151f9f55e31SRichard Fitzgerald 2152f5e2ce92SRichard Fitzgerald return 0; 2153f5e2ce92SRichard Fitzgerald } 2154f5e2ce92SRichard Fitzgerald EXPORT_SYMBOL_GPL(wm_adsp2_codec_remove); 2155f5e2ce92SRichard Fitzgerald 215681ac58b1SRichard Fitzgerald int wm_adsp2_init(struct wm_adsp *dsp) 2157973838a0SMark Brown { 2158973838a0SMark Brown int ret; 2159973838a0SMark Brown 216010a2b662SMark Brown /* 216110a2b662SMark Brown * Disable the DSP memory by default when in reset for a small 216210a2b662SMark Brown * power saving. 216310a2b662SMark Brown */ 21643809f001SCharles Keepax ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 216510a2b662SMark Brown ADSP2_MEM_ENA, 0); 216610a2b662SMark Brown if (ret != 0) { 21673809f001SCharles Keepax adsp_err(dsp, "Failed to clear memory retention: %d\n", ret); 216810a2b662SMark Brown return ret; 216910a2b662SMark Brown } 217010a2b662SMark Brown 21713809f001SCharles Keepax INIT_LIST_HEAD(&dsp->alg_regions); 21723809f001SCharles Keepax INIT_LIST_HEAD(&dsp->ctl_list); 21733809f001SCharles Keepax INIT_WORK(&dsp->boot_work, wm_adsp2_boot_work); 21746ab2b7b4SDimitris Papastamos 2175078e7183SCharles Keepax mutex_init(&dsp->pwr_lock); 2176078e7183SCharles Keepax 2177973838a0SMark Brown return 0; 2178973838a0SMark Brown } 2179973838a0SMark Brown EXPORT_SYMBOL_GPL(wm_adsp2_init); 21800a37c6efSPraveen Diwakar 21810a37c6efSPraveen Diwakar MODULE_LICENSE("GPL v2"); 2182