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 "wm_adsp.h" 362159ad93SMark Brown 372159ad93SMark Brown #define adsp_crit(_dsp, fmt, ...) \ 382159ad93SMark Brown dev_crit(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__) 392159ad93SMark Brown #define adsp_err(_dsp, fmt, ...) \ 402159ad93SMark Brown dev_err(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__) 412159ad93SMark Brown #define adsp_warn(_dsp, fmt, ...) \ 422159ad93SMark Brown dev_warn(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__) 432159ad93SMark Brown #define adsp_info(_dsp, fmt, ...) \ 442159ad93SMark Brown dev_info(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__) 452159ad93SMark Brown #define adsp_dbg(_dsp, fmt, ...) \ 462159ad93SMark Brown dev_dbg(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__) 472159ad93SMark Brown 482159ad93SMark Brown #define ADSP1_CONTROL_1 0x00 492159ad93SMark Brown #define ADSP1_CONTROL_2 0x02 502159ad93SMark Brown #define ADSP1_CONTROL_3 0x03 512159ad93SMark Brown #define ADSP1_CONTROL_4 0x04 522159ad93SMark Brown #define ADSP1_CONTROL_5 0x06 532159ad93SMark Brown #define ADSP1_CONTROL_6 0x07 542159ad93SMark Brown #define ADSP1_CONTROL_7 0x08 552159ad93SMark Brown #define ADSP1_CONTROL_8 0x09 562159ad93SMark Brown #define ADSP1_CONTROL_9 0x0A 572159ad93SMark Brown #define ADSP1_CONTROL_10 0x0B 582159ad93SMark Brown #define ADSP1_CONTROL_11 0x0C 592159ad93SMark Brown #define ADSP1_CONTROL_12 0x0D 602159ad93SMark Brown #define ADSP1_CONTROL_13 0x0F 612159ad93SMark Brown #define ADSP1_CONTROL_14 0x10 622159ad93SMark Brown #define ADSP1_CONTROL_15 0x11 632159ad93SMark Brown #define ADSP1_CONTROL_16 0x12 642159ad93SMark Brown #define ADSP1_CONTROL_17 0x13 652159ad93SMark Brown #define ADSP1_CONTROL_18 0x14 662159ad93SMark Brown #define ADSP1_CONTROL_19 0x16 672159ad93SMark Brown #define ADSP1_CONTROL_20 0x17 682159ad93SMark Brown #define ADSP1_CONTROL_21 0x18 692159ad93SMark Brown #define ADSP1_CONTROL_22 0x1A 702159ad93SMark Brown #define ADSP1_CONTROL_23 0x1B 712159ad93SMark Brown #define ADSP1_CONTROL_24 0x1C 722159ad93SMark Brown #define ADSP1_CONTROL_25 0x1E 732159ad93SMark Brown #define ADSP1_CONTROL_26 0x20 742159ad93SMark Brown #define ADSP1_CONTROL_27 0x21 752159ad93SMark Brown #define ADSP1_CONTROL_28 0x22 762159ad93SMark Brown #define ADSP1_CONTROL_29 0x23 772159ad93SMark Brown #define ADSP1_CONTROL_30 0x24 782159ad93SMark Brown #define ADSP1_CONTROL_31 0x26 792159ad93SMark Brown 802159ad93SMark Brown /* 812159ad93SMark Brown * ADSP1 Control 19 822159ad93SMark Brown */ 832159ad93SMark Brown #define ADSP1_WDMA_BUFFER_LENGTH_MASK 0x00FF /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */ 842159ad93SMark Brown #define ADSP1_WDMA_BUFFER_LENGTH_SHIFT 0 /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */ 852159ad93SMark Brown #define ADSP1_WDMA_BUFFER_LENGTH_WIDTH 8 /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */ 862159ad93SMark Brown 872159ad93SMark Brown 882159ad93SMark Brown /* 892159ad93SMark Brown * ADSP1 Control 30 902159ad93SMark Brown */ 912159ad93SMark Brown #define ADSP1_DBG_CLK_ENA 0x0008 /* DSP1_DBG_CLK_ENA */ 922159ad93SMark Brown #define ADSP1_DBG_CLK_ENA_MASK 0x0008 /* DSP1_DBG_CLK_ENA */ 932159ad93SMark Brown #define ADSP1_DBG_CLK_ENA_SHIFT 3 /* DSP1_DBG_CLK_ENA */ 942159ad93SMark Brown #define ADSP1_DBG_CLK_ENA_WIDTH 1 /* DSP1_DBG_CLK_ENA */ 952159ad93SMark Brown #define ADSP1_SYS_ENA 0x0004 /* DSP1_SYS_ENA */ 962159ad93SMark Brown #define ADSP1_SYS_ENA_MASK 0x0004 /* DSP1_SYS_ENA */ 972159ad93SMark Brown #define ADSP1_SYS_ENA_SHIFT 2 /* DSP1_SYS_ENA */ 982159ad93SMark Brown #define ADSP1_SYS_ENA_WIDTH 1 /* DSP1_SYS_ENA */ 992159ad93SMark Brown #define ADSP1_CORE_ENA 0x0002 /* DSP1_CORE_ENA */ 1002159ad93SMark Brown #define ADSP1_CORE_ENA_MASK 0x0002 /* DSP1_CORE_ENA */ 1012159ad93SMark Brown #define ADSP1_CORE_ENA_SHIFT 1 /* DSP1_CORE_ENA */ 1022159ad93SMark Brown #define ADSP1_CORE_ENA_WIDTH 1 /* DSP1_CORE_ENA */ 1032159ad93SMark Brown #define ADSP1_START 0x0001 /* DSP1_START */ 1042159ad93SMark Brown #define ADSP1_START_MASK 0x0001 /* DSP1_START */ 1052159ad93SMark Brown #define ADSP1_START_SHIFT 0 /* DSP1_START */ 1062159ad93SMark Brown #define ADSP1_START_WIDTH 1 /* DSP1_START */ 1072159ad93SMark Brown 10894e205bfSChris Rattray /* 10994e205bfSChris Rattray * ADSP1 Control 31 11094e205bfSChris Rattray */ 11194e205bfSChris Rattray #define ADSP1_CLK_SEL_MASK 0x0007 /* CLK_SEL_ENA */ 11294e205bfSChris Rattray #define ADSP1_CLK_SEL_SHIFT 0 /* CLK_SEL_ENA */ 11394e205bfSChris Rattray #define ADSP1_CLK_SEL_WIDTH 3 /* CLK_SEL_ENA */ 11494e205bfSChris Rattray 1152d30b575SMark Brown #define ADSP2_CONTROL 0x0 1162d30b575SMark Brown #define ADSP2_CLOCKING 0x1 1172d30b575SMark Brown #define ADSP2_STATUS1 0x4 1182d30b575SMark Brown #define ADSP2_WDMA_CONFIG_1 0x30 1192d30b575SMark Brown #define ADSP2_WDMA_CONFIG_2 0x31 1202d30b575SMark Brown #define ADSP2_RDMA_CONFIG_1 0x34 1212159ad93SMark Brown 12210337b07SRichard Fitzgerald #define ADSP2_SCRATCH0 0x40 12310337b07SRichard Fitzgerald #define ADSP2_SCRATCH1 0x41 12410337b07SRichard Fitzgerald #define ADSP2_SCRATCH2 0x42 12510337b07SRichard Fitzgerald #define ADSP2_SCRATCH3 0x43 12610337b07SRichard Fitzgerald 1272159ad93SMark Brown /* 1282159ad93SMark Brown * ADSP2 Control 1292159ad93SMark Brown */ 1302159ad93SMark Brown 1312159ad93SMark Brown #define ADSP2_MEM_ENA 0x0010 /* DSP1_MEM_ENA */ 1322159ad93SMark Brown #define ADSP2_MEM_ENA_MASK 0x0010 /* DSP1_MEM_ENA */ 1332159ad93SMark Brown #define ADSP2_MEM_ENA_SHIFT 4 /* DSP1_MEM_ENA */ 1342159ad93SMark Brown #define ADSP2_MEM_ENA_WIDTH 1 /* DSP1_MEM_ENA */ 1352159ad93SMark Brown #define ADSP2_SYS_ENA 0x0004 /* DSP1_SYS_ENA */ 1362159ad93SMark Brown #define ADSP2_SYS_ENA_MASK 0x0004 /* DSP1_SYS_ENA */ 1372159ad93SMark Brown #define ADSP2_SYS_ENA_SHIFT 2 /* DSP1_SYS_ENA */ 1382159ad93SMark Brown #define ADSP2_SYS_ENA_WIDTH 1 /* DSP1_SYS_ENA */ 1392159ad93SMark Brown #define ADSP2_CORE_ENA 0x0002 /* DSP1_CORE_ENA */ 1402159ad93SMark Brown #define ADSP2_CORE_ENA_MASK 0x0002 /* DSP1_CORE_ENA */ 1412159ad93SMark Brown #define ADSP2_CORE_ENA_SHIFT 1 /* DSP1_CORE_ENA */ 1422159ad93SMark Brown #define ADSP2_CORE_ENA_WIDTH 1 /* DSP1_CORE_ENA */ 1432159ad93SMark Brown #define ADSP2_START 0x0001 /* DSP1_START */ 1442159ad93SMark Brown #define ADSP2_START_MASK 0x0001 /* DSP1_START */ 1452159ad93SMark Brown #define ADSP2_START_SHIFT 0 /* DSP1_START */ 1462159ad93SMark Brown #define ADSP2_START_WIDTH 1 /* DSP1_START */ 1472159ad93SMark Brown 1482159ad93SMark Brown /* 149973838a0SMark Brown * ADSP2 clocking 150973838a0SMark Brown */ 151973838a0SMark Brown #define ADSP2_CLK_SEL_MASK 0x0007 /* CLK_SEL_ENA */ 152973838a0SMark Brown #define ADSP2_CLK_SEL_SHIFT 0 /* CLK_SEL_ENA */ 153973838a0SMark Brown #define ADSP2_CLK_SEL_WIDTH 3 /* CLK_SEL_ENA */ 154973838a0SMark Brown 155973838a0SMark Brown /* 1562159ad93SMark Brown * ADSP2 Status 1 1572159ad93SMark Brown */ 1582159ad93SMark Brown #define ADSP2_RAM_RDY 0x0001 1592159ad93SMark Brown #define ADSP2_RAM_RDY_MASK 0x0001 1602159ad93SMark Brown #define ADSP2_RAM_RDY_SHIFT 0 1612159ad93SMark Brown #define ADSP2_RAM_RDY_WIDTH 1 1622159ad93SMark Brown 1639ee78757SCharles Keepax #define ADSP_MAX_STD_CTRL_SIZE 512 1649ee78757SCharles Keepax 165f4f0c4c6SRichard Fitzgerald #define WM_ADSP_ACKED_CTL_TIMEOUT_MS 100 166f4f0c4c6SRichard Fitzgerald #define WM_ADSP_ACKED_CTL_N_QUICKPOLLS 10 167f4f0c4c6SRichard Fitzgerald 168f4f0c4c6SRichard Fitzgerald /* 169f4f0c4c6SRichard Fitzgerald * Event control messages 170f4f0c4c6SRichard Fitzgerald */ 171f4f0c4c6SRichard Fitzgerald #define WM_ADSP_FW_EVENT_SHUTDOWN 0x000001 172f4f0c4c6SRichard Fitzgerald 173cf17c83cSMark Brown struct wm_adsp_buf { 174cf17c83cSMark Brown struct list_head list; 175cf17c83cSMark Brown void *buf; 176cf17c83cSMark Brown }; 177cf17c83cSMark Brown 178cf17c83cSMark Brown static struct wm_adsp_buf *wm_adsp_buf_alloc(const void *src, size_t len, 179cf17c83cSMark Brown struct list_head *list) 180cf17c83cSMark Brown { 181cf17c83cSMark Brown struct wm_adsp_buf *buf = kzalloc(sizeof(*buf), GFP_KERNEL); 182cf17c83cSMark Brown 183cf17c83cSMark Brown if (buf == NULL) 184cf17c83cSMark Brown return NULL; 185cf17c83cSMark Brown 186cdcd7f72SCharles Keepax buf->buf = vmalloc(len); 187cf17c83cSMark Brown if (!buf->buf) { 188cdcd7f72SCharles Keepax vfree(buf); 189cf17c83cSMark Brown return NULL; 190cf17c83cSMark Brown } 191cdcd7f72SCharles Keepax memcpy(buf->buf, src, len); 192cf17c83cSMark Brown 193cf17c83cSMark Brown if (list) 194cf17c83cSMark Brown list_add_tail(&buf->list, list); 195cf17c83cSMark Brown 196cf17c83cSMark Brown return buf; 197cf17c83cSMark Brown } 198cf17c83cSMark Brown 199cf17c83cSMark Brown static void wm_adsp_buf_free(struct list_head *list) 200cf17c83cSMark Brown { 201cf17c83cSMark Brown while (!list_empty(list)) { 202cf17c83cSMark Brown struct wm_adsp_buf *buf = list_first_entry(list, 203cf17c83cSMark Brown struct wm_adsp_buf, 204cf17c83cSMark Brown list); 205cf17c83cSMark Brown list_del(&buf->list); 206cdcd7f72SCharles Keepax vfree(buf->buf); 207cf17c83cSMark Brown kfree(buf); 208cf17c83cSMark Brown } 209cf17c83cSMark Brown } 210cf17c83cSMark Brown 211dd84f925SMark Brown #define WM_ADSP_FW_MBC_VSS 0 21204d1300fSCharles Keepax #define WM_ADSP_FW_HIFI 1 21304d1300fSCharles Keepax #define WM_ADSP_FW_TX 2 21404d1300fSCharles Keepax #define WM_ADSP_FW_TX_SPK 3 21504d1300fSCharles Keepax #define WM_ADSP_FW_RX 4 21604d1300fSCharles Keepax #define WM_ADSP_FW_RX_ANC 5 21704d1300fSCharles Keepax #define WM_ADSP_FW_CTRL 6 21804d1300fSCharles Keepax #define WM_ADSP_FW_ASR 7 21904d1300fSCharles Keepax #define WM_ADSP_FW_TRACE 8 22004d1300fSCharles Keepax #define WM_ADSP_FW_SPK_PROT 9 22104d1300fSCharles Keepax #define WM_ADSP_FW_MISC 10 22204d1300fSCharles Keepax 22304d1300fSCharles Keepax #define WM_ADSP_NUM_FW 11 224dd84f925SMark Brown 2251023dbd9SMark Brown static const char *wm_adsp_fw_text[WM_ADSP_NUM_FW] = { 226dd84f925SMark Brown [WM_ADSP_FW_MBC_VSS] = "MBC/VSS", 22704d1300fSCharles Keepax [WM_ADSP_FW_HIFI] = "MasterHiFi", 228dd84f925SMark Brown [WM_ADSP_FW_TX] = "Tx", 229dd84f925SMark Brown [WM_ADSP_FW_TX_SPK] = "Tx Speaker", 23004d1300fSCharles Keepax [WM_ADSP_FW_RX] = "Rx", 231dd84f925SMark Brown [WM_ADSP_FW_RX_ANC] = "Rx ANC", 23204d1300fSCharles Keepax [WM_ADSP_FW_CTRL] = "Voice Ctrl", 23304d1300fSCharles Keepax [WM_ADSP_FW_ASR] = "ASR Assist", 23404d1300fSCharles Keepax [WM_ADSP_FW_TRACE] = "Dbg Trace", 23504d1300fSCharles Keepax [WM_ADSP_FW_SPK_PROT] = "Protection", 23604d1300fSCharles Keepax [WM_ADSP_FW_MISC] = "Misc", 2371023dbd9SMark Brown }; 2381023dbd9SMark Brown 2392cd19bdbSCharles Keepax struct wm_adsp_system_config_xm_hdr { 2402cd19bdbSCharles Keepax __be32 sys_enable; 2412cd19bdbSCharles Keepax __be32 fw_id; 2422cd19bdbSCharles Keepax __be32 fw_rev; 2432cd19bdbSCharles Keepax __be32 boot_status; 2442cd19bdbSCharles Keepax __be32 watchdog; 2452cd19bdbSCharles Keepax __be32 dma_buffer_size; 2462cd19bdbSCharles Keepax __be32 rdma[6]; 2472cd19bdbSCharles Keepax __be32 wdma[8]; 2482cd19bdbSCharles Keepax __be32 build_job_name[3]; 2492cd19bdbSCharles Keepax __be32 build_job_number; 2502cd19bdbSCharles Keepax }; 2512cd19bdbSCharles Keepax 2522cd19bdbSCharles Keepax struct wm_adsp_alg_xm_struct { 2532cd19bdbSCharles Keepax __be32 magic; 2542cd19bdbSCharles Keepax __be32 smoothing; 2552cd19bdbSCharles Keepax __be32 threshold; 2562cd19bdbSCharles Keepax __be32 host_buf_ptr; 2572cd19bdbSCharles Keepax __be32 start_seq; 2582cd19bdbSCharles Keepax __be32 high_water_mark; 2592cd19bdbSCharles Keepax __be32 low_water_mark; 2602cd19bdbSCharles Keepax __be64 smoothed_power; 2612cd19bdbSCharles Keepax }; 2622cd19bdbSCharles Keepax 2632cd19bdbSCharles Keepax struct wm_adsp_buffer { 2642cd19bdbSCharles Keepax __be32 X_buf_base; /* XM base addr of first X area */ 2652cd19bdbSCharles Keepax __be32 X_buf_size; /* Size of 1st X area in words */ 2662cd19bdbSCharles Keepax __be32 X_buf_base2; /* XM base addr of 2nd X area */ 2672cd19bdbSCharles Keepax __be32 X_buf_brk; /* Total X size in words */ 2682cd19bdbSCharles Keepax __be32 Y_buf_base; /* YM base addr of Y area */ 2692cd19bdbSCharles Keepax __be32 wrap; /* Total size X and Y in words */ 2702cd19bdbSCharles Keepax __be32 high_water_mark; /* Point at which IRQ is asserted */ 2712cd19bdbSCharles Keepax __be32 irq_count; /* bits 1-31 count IRQ assertions */ 2722cd19bdbSCharles Keepax __be32 irq_ack; /* acked IRQ count, bit 0 enables IRQ */ 2732cd19bdbSCharles Keepax __be32 next_write_index; /* word index of next write */ 2742cd19bdbSCharles Keepax __be32 next_read_index; /* word index of next read */ 2752cd19bdbSCharles Keepax __be32 error; /* error if any */ 2762cd19bdbSCharles Keepax __be32 oldest_block_index; /* word index of oldest surviving */ 2772cd19bdbSCharles Keepax __be32 requested_rewind; /* how many blocks rewind was done */ 2782cd19bdbSCharles Keepax __be32 reserved_space; /* internal */ 2792cd19bdbSCharles Keepax __be32 min_free; /* min free space since stream start */ 2802cd19bdbSCharles Keepax __be32 blocks_written[2]; /* total blocks written (64 bit) */ 2812cd19bdbSCharles Keepax __be32 words_written[2]; /* total words written (64 bit) */ 2822cd19bdbSCharles Keepax }; 2832cd19bdbSCharles Keepax 284721be3beSCharles Keepax struct wm_adsp_compr; 285721be3beSCharles Keepax 2862cd19bdbSCharles Keepax struct wm_adsp_compr_buf { 2872cd19bdbSCharles Keepax struct wm_adsp *dsp; 288721be3beSCharles Keepax struct wm_adsp_compr *compr; 2892cd19bdbSCharles Keepax 2902cd19bdbSCharles Keepax struct wm_adsp_buffer_region *regions; 2912cd19bdbSCharles Keepax u32 host_buf_ptr; 292565ace46SCharles Keepax 293565ace46SCharles Keepax u32 error; 294565ace46SCharles Keepax u32 irq_count; 295565ace46SCharles Keepax int read_index; 296565ace46SCharles Keepax int avail; 2972cd19bdbSCharles Keepax }; 2982cd19bdbSCharles Keepax 299406abc95SCharles Keepax struct wm_adsp_compr { 300406abc95SCharles Keepax struct wm_adsp *dsp; 30195fe9597SCharles Keepax struct wm_adsp_compr_buf *buf; 302406abc95SCharles Keepax 303406abc95SCharles Keepax struct snd_compr_stream *stream; 304406abc95SCharles Keepax struct snd_compressed_buffer size; 305565ace46SCharles Keepax 30683a40ce9SCharles Keepax u32 *raw_buf; 307565ace46SCharles Keepax unsigned int copied_total; 308da2b3358SCharles Keepax 309da2b3358SCharles Keepax unsigned int sample_rate; 310406abc95SCharles Keepax }; 311406abc95SCharles Keepax 312406abc95SCharles Keepax #define WM_ADSP_DATA_WORD_SIZE 3 313406abc95SCharles Keepax 314406abc95SCharles Keepax #define WM_ADSP_MIN_FRAGMENTS 1 315406abc95SCharles Keepax #define WM_ADSP_MAX_FRAGMENTS 256 316406abc95SCharles Keepax #define WM_ADSP_MIN_FRAGMENT_SIZE (64 * WM_ADSP_DATA_WORD_SIZE) 317406abc95SCharles Keepax #define WM_ADSP_MAX_FRAGMENT_SIZE (4096 * WM_ADSP_DATA_WORD_SIZE) 318406abc95SCharles Keepax 3192cd19bdbSCharles Keepax #define WM_ADSP_ALG_XM_STRUCT_MAGIC 0x49aec7 3202cd19bdbSCharles Keepax 3212cd19bdbSCharles Keepax #define HOST_BUFFER_FIELD(field) \ 3222cd19bdbSCharles Keepax (offsetof(struct wm_adsp_buffer, field) / sizeof(__be32)) 3232cd19bdbSCharles Keepax 3242cd19bdbSCharles Keepax #define ALG_XM_FIELD(field) \ 3252cd19bdbSCharles Keepax (offsetof(struct wm_adsp_alg_xm_struct, field) / sizeof(__be32)) 3262cd19bdbSCharles Keepax 3272cd19bdbSCharles Keepax static int wm_adsp_buffer_init(struct wm_adsp *dsp); 3282cd19bdbSCharles Keepax static int wm_adsp_buffer_free(struct wm_adsp *dsp); 3292cd19bdbSCharles Keepax 3302cd19bdbSCharles Keepax struct wm_adsp_buffer_region { 3312cd19bdbSCharles Keepax unsigned int offset; 3322cd19bdbSCharles Keepax unsigned int cumulative_size; 3332cd19bdbSCharles Keepax unsigned int mem_type; 3342cd19bdbSCharles Keepax unsigned int base_addr; 3352cd19bdbSCharles Keepax }; 3362cd19bdbSCharles Keepax 3372cd19bdbSCharles Keepax struct wm_adsp_buffer_region_def { 3382cd19bdbSCharles Keepax unsigned int mem_type; 3392cd19bdbSCharles Keepax unsigned int base_offset; 3402cd19bdbSCharles Keepax unsigned int size_offset; 3412cd19bdbSCharles Keepax }; 3422cd19bdbSCharles Keepax 3433a9686c4SCharles Keepax static const struct wm_adsp_buffer_region_def default_regions[] = { 3442cd19bdbSCharles Keepax { 3452cd19bdbSCharles Keepax .mem_type = WMFW_ADSP2_XM, 3462cd19bdbSCharles Keepax .base_offset = HOST_BUFFER_FIELD(X_buf_base), 3472cd19bdbSCharles Keepax .size_offset = HOST_BUFFER_FIELD(X_buf_size), 3482cd19bdbSCharles Keepax }, 3492cd19bdbSCharles Keepax { 3502cd19bdbSCharles Keepax .mem_type = WMFW_ADSP2_XM, 3512cd19bdbSCharles Keepax .base_offset = HOST_BUFFER_FIELD(X_buf_base2), 3522cd19bdbSCharles Keepax .size_offset = HOST_BUFFER_FIELD(X_buf_brk), 3532cd19bdbSCharles Keepax }, 3542cd19bdbSCharles Keepax { 3552cd19bdbSCharles Keepax .mem_type = WMFW_ADSP2_YM, 3562cd19bdbSCharles Keepax .base_offset = HOST_BUFFER_FIELD(Y_buf_base), 3572cd19bdbSCharles Keepax .size_offset = HOST_BUFFER_FIELD(wrap), 3582cd19bdbSCharles Keepax }, 3592cd19bdbSCharles Keepax }; 3602cd19bdbSCharles Keepax 361406abc95SCharles Keepax struct wm_adsp_fw_caps { 362406abc95SCharles Keepax u32 id; 363406abc95SCharles Keepax struct snd_codec_desc desc; 3642cd19bdbSCharles Keepax int num_regions; 3653a9686c4SCharles Keepax const struct wm_adsp_buffer_region_def *region_defs; 366406abc95SCharles Keepax }; 367406abc95SCharles Keepax 368e6d00f34SCharles Keepax static const struct wm_adsp_fw_caps ctrl_caps[] = { 369406abc95SCharles Keepax { 370406abc95SCharles Keepax .id = SND_AUDIOCODEC_BESPOKE, 371406abc95SCharles Keepax .desc = { 372406abc95SCharles Keepax .max_ch = 1, 373406abc95SCharles Keepax .sample_rates = { 16000 }, 374406abc95SCharles Keepax .num_sample_rates = 1, 375406abc95SCharles Keepax .formats = SNDRV_PCM_FMTBIT_S16_LE, 376406abc95SCharles Keepax }, 377e6d00f34SCharles Keepax .num_regions = ARRAY_SIZE(default_regions), 378e6d00f34SCharles Keepax .region_defs = default_regions, 379406abc95SCharles Keepax }, 380406abc95SCharles Keepax }; 381406abc95SCharles Keepax 3827ce4283cSCharles Keepax static const struct wm_adsp_fw_caps trace_caps[] = { 3837ce4283cSCharles Keepax { 3847ce4283cSCharles Keepax .id = SND_AUDIOCODEC_BESPOKE, 3857ce4283cSCharles Keepax .desc = { 3867ce4283cSCharles Keepax .max_ch = 8, 3877ce4283cSCharles Keepax .sample_rates = { 3887ce4283cSCharles Keepax 4000, 8000, 11025, 12000, 16000, 22050, 3897ce4283cSCharles Keepax 24000, 32000, 44100, 48000, 64000, 88200, 3907ce4283cSCharles Keepax 96000, 176400, 192000 3917ce4283cSCharles Keepax }, 3927ce4283cSCharles Keepax .num_sample_rates = 15, 3937ce4283cSCharles Keepax .formats = SNDRV_PCM_FMTBIT_S16_LE, 3947ce4283cSCharles Keepax }, 3957ce4283cSCharles Keepax .num_regions = ARRAY_SIZE(default_regions), 3967ce4283cSCharles Keepax .region_defs = default_regions, 397406abc95SCharles Keepax }, 398406abc95SCharles Keepax }; 399406abc95SCharles Keepax 400406abc95SCharles Keepax static const struct { 4011023dbd9SMark Brown const char *file; 402406abc95SCharles Keepax int compr_direction; 403406abc95SCharles Keepax int num_caps; 404406abc95SCharles Keepax const struct wm_adsp_fw_caps *caps; 40520b7f7c5SCharles Keepax bool voice_trigger; 4061023dbd9SMark Brown } wm_adsp_fw[WM_ADSP_NUM_FW] = { 407dd84f925SMark Brown [WM_ADSP_FW_MBC_VSS] = { .file = "mbc-vss" }, 40804d1300fSCharles Keepax [WM_ADSP_FW_HIFI] = { .file = "hifi" }, 409dd84f925SMark Brown [WM_ADSP_FW_TX] = { .file = "tx" }, 410dd84f925SMark Brown [WM_ADSP_FW_TX_SPK] = { .file = "tx-spk" }, 41104d1300fSCharles Keepax [WM_ADSP_FW_RX] = { .file = "rx" }, 412dd84f925SMark Brown [WM_ADSP_FW_RX_ANC] = { .file = "rx-anc" }, 413406abc95SCharles Keepax [WM_ADSP_FW_CTRL] = { 414406abc95SCharles Keepax .file = "ctrl", 415406abc95SCharles Keepax .compr_direction = SND_COMPRESS_CAPTURE, 416e6d00f34SCharles Keepax .num_caps = ARRAY_SIZE(ctrl_caps), 417e6d00f34SCharles Keepax .caps = ctrl_caps, 41820b7f7c5SCharles Keepax .voice_trigger = true, 419406abc95SCharles Keepax }, 42004d1300fSCharles Keepax [WM_ADSP_FW_ASR] = { .file = "asr" }, 4217ce4283cSCharles Keepax [WM_ADSP_FW_TRACE] = { 4227ce4283cSCharles Keepax .file = "trace", 4237ce4283cSCharles Keepax .compr_direction = SND_COMPRESS_CAPTURE, 4247ce4283cSCharles Keepax .num_caps = ARRAY_SIZE(trace_caps), 4257ce4283cSCharles Keepax .caps = trace_caps, 4267ce4283cSCharles Keepax }, 42704d1300fSCharles Keepax [WM_ADSP_FW_SPK_PROT] = { .file = "spk-prot" }, 42804d1300fSCharles Keepax [WM_ADSP_FW_MISC] = { .file = "misc" }, 4291023dbd9SMark Brown }; 4301023dbd9SMark Brown 4316ab2b7b4SDimitris Papastamos struct wm_coeff_ctl_ops { 4326ab2b7b4SDimitris Papastamos int (*xget)(struct snd_kcontrol *kcontrol, 4336ab2b7b4SDimitris Papastamos struct snd_ctl_elem_value *ucontrol); 4346ab2b7b4SDimitris Papastamos int (*xput)(struct snd_kcontrol *kcontrol, 4356ab2b7b4SDimitris Papastamos struct snd_ctl_elem_value *ucontrol); 4366ab2b7b4SDimitris Papastamos int (*xinfo)(struct snd_kcontrol *kcontrol, 4376ab2b7b4SDimitris Papastamos struct snd_ctl_elem_info *uinfo); 4386ab2b7b4SDimitris Papastamos }; 4396ab2b7b4SDimitris Papastamos 4406ab2b7b4SDimitris Papastamos struct wm_coeff_ctl { 4416ab2b7b4SDimitris Papastamos const char *name; 4422323736dSCharles Keepax const char *fw_name; 4433809f001SCharles Keepax struct wm_adsp_alg_region alg_region; 4446ab2b7b4SDimitris Papastamos struct wm_coeff_ctl_ops ops; 4453809f001SCharles Keepax struct wm_adsp *dsp; 4466ab2b7b4SDimitris Papastamos unsigned int enabled:1; 4476ab2b7b4SDimitris Papastamos struct list_head list; 4486ab2b7b4SDimitris Papastamos void *cache; 4492323736dSCharles Keepax unsigned int offset; 4506ab2b7b4SDimitris Papastamos size_t len; 4510c2e3f34SDimitris Papastamos unsigned int set:1; 4526ab2b7b4SDimitris Papastamos struct snd_kcontrol *kcontrol; 4539ee78757SCharles Keepax struct soc_bytes_ext bytes_ext; 45426c22a19SCharles Keepax unsigned int flags; 4558eb084d0SStuart Henderson unsigned int type; 4566ab2b7b4SDimitris Papastamos }; 4576ab2b7b4SDimitris Papastamos 4589ce5e6e6SRichard Fitzgerald static const char *wm_adsp_mem_region_name(unsigned int type) 4599ce5e6e6SRichard Fitzgerald { 4609ce5e6e6SRichard Fitzgerald switch (type) { 4619ce5e6e6SRichard Fitzgerald case WMFW_ADSP1_PM: 4629ce5e6e6SRichard Fitzgerald return "PM"; 4639ce5e6e6SRichard Fitzgerald case WMFW_ADSP1_DM: 4649ce5e6e6SRichard Fitzgerald return "DM"; 4659ce5e6e6SRichard Fitzgerald case WMFW_ADSP2_XM: 4669ce5e6e6SRichard Fitzgerald return "XM"; 4679ce5e6e6SRichard Fitzgerald case WMFW_ADSP2_YM: 4689ce5e6e6SRichard Fitzgerald return "YM"; 4699ce5e6e6SRichard Fitzgerald case WMFW_ADSP1_ZM: 4709ce5e6e6SRichard Fitzgerald return "ZM"; 4719ce5e6e6SRichard Fitzgerald default: 4729ce5e6e6SRichard Fitzgerald return NULL; 4739ce5e6e6SRichard Fitzgerald } 4749ce5e6e6SRichard Fitzgerald } 4759ce5e6e6SRichard Fitzgerald 476f9f55e31SRichard Fitzgerald #ifdef CONFIG_DEBUG_FS 477f9f55e31SRichard Fitzgerald static void wm_adsp_debugfs_save_wmfwname(struct wm_adsp *dsp, const char *s) 478f9f55e31SRichard Fitzgerald { 479f9f55e31SRichard Fitzgerald char *tmp = kasprintf(GFP_KERNEL, "%s\n", s); 480f9f55e31SRichard Fitzgerald 481f9f55e31SRichard Fitzgerald kfree(dsp->wmfw_file_name); 482f9f55e31SRichard Fitzgerald dsp->wmfw_file_name = tmp; 483f9f55e31SRichard Fitzgerald } 484f9f55e31SRichard Fitzgerald 485f9f55e31SRichard Fitzgerald static void wm_adsp_debugfs_save_binname(struct wm_adsp *dsp, const char *s) 486f9f55e31SRichard Fitzgerald { 487f9f55e31SRichard Fitzgerald char *tmp = kasprintf(GFP_KERNEL, "%s\n", s); 488f9f55e31SRichard Fitzgerald 489f9f55e31SRichard Fitzgerald kfree(dsp->bin_file_name); 490f9f55e31SRichard Fitzgerald dsp->bin_file_name = tmp; 491f9f55e31SRichard Fitzgerald } 492f9f55e31SRichard Fitzgerald 493f9f55e31SRichard Fitzgerald static void wm_adsp_debugfs_clear(struct wm_adsp *dsp) 494f9f55e31SRichard Fitzgerald { 495f9f55e31SRichard Fitzgerald kfree(dsp->wmfw_file_name); 496f9f55e31SRichard Fitzgerald kfree(dsp->bin_file_name); 497f9f55e31SRichard Fitzgerald dsp->wmfw_file_name = NULL; 498f9f55e31SRichard Fitzgerald dsp->bin_file_name = NULL; 499f9f55e31SRichard Fitzgerald } 500f9f55e31SRichard Fitzgerald 501f9f55e31SRichard Fitzgerald static ssize_t wm_adsp_debugfs_wmfw_read(struct file *file, 502f9f55e31SRichard Fitzgerald char __user *user_buf, 503f9f55e31SRichard Fitzgerald size_t count, loff_t *ppos) 504f9f55e31SRichard Fitzgerald { 505f9f55e31SRichard Fitzgerald struct wm_adsp *dsp = file->private_data; 506f9f55e31SRichard Fitzgerald ssize_t ret; 507f9f55e31SRichard Fitzgerald 508078e7183SCharles Keepax mutex_lock(&dsp->pwr_lock); 509f9f55e31SRichard Fitzgerald 51028823ebaSCharles Keepax if (!dsp->wmfw_file_name || !dsp->booted) 511f9f55e31SRichard Fitzgerald ret = 0; 512f9f55e31SRichard Fitzgerald else 513f9f55e31SRichard Fitzgerald ret = simple_read_from_buffer(user_buf, count, ppos, 514f9f55e31SRichard Fitzgerald dsp->wmfw_file_name, 515f9f55e31SRichard Fitzgerald strlen(dsp->wmfw_file_name)); 516f9f55e31SRichard Fitzgerald 517078e7183SCharles Keepax mutex_unlock(&dsp->pwr_lock); 518f9f55e31SRichard Fitzgerald return ret; 519f9f55e31SRichard Fitzgerald } 520f9f55e31SRichard Fitzgerald 521f9f55e31SRichard Fitzgerald static ssize_t wm_adsp_debugfs_bin_read(struct file *file, 522f9f55e31SRichard Fitzgerald char __user *user_buf, 523f9f55e31SRichard Fitzgerald size_t count, loff_t *ppos) 524f9f55e31SRichard Fitzgerald { 525f9f55e31SRichard Fitzgerald struct wm_adsp *dsp = file->private_data; 526f9f55e31SRichard Fitzgerald ssize_t ret; 527f9f55e31SRichard Fitzgerald 528078e7183SCharles Keepax mutex_lock(&dsp->pwr_lock); 529f9f55e31SRichard Fitzgerald 53028823ebaSCharles Keepax if (!dsp->bin_file_name || !dsp->booted) 531f9f55e31SRichard Fitzgerald ret = 0; 532f9f55e31SRichard Fitzgerald else 533f9f55e31SRichard Fitzgerald ret = simple_read_from_buffer(user_buf, count, ppos, 534f9f55e31SRichard Fitzgerald dsp->bin_file_name, 535f9f55e31SRichard Fitzgerald strlen(dsp->bin_file_name)); 536f9f55e31SRichard Fitzgerald 537078e7183SCharles Keepax mutex_unlock(&dsp->pwr_lock); 538f9f55e31SRichard Fitzgerald return ret; 539f9f55e31SRichard Fitzgerald } 540f9f55e31SRichard Fitzgerald 541f9f55e31SRichard Fitzgerald static const struct { 542f9f55e31SRichard Fitzgerald const char *name; 543f9f55e31SRichard Fitzgerald const struct file_operations fops; 544f9f55e31SRichard Fitzgerald } wm_adsp_debugfs_fops[] = { 545f9f55e31SRichard Fitzgerald { 546f9f55e31SRichard Fitzgerald .name = "wmfw_file_name", 547f9f55e31SRichard Fitzgerald .fops = { 548f9f55e31SRichard Fitzgerald .open = simple_open, 549f9f55e31SRichard Fitzgerald .read = wm_adsp_debugfs_wmfw_read, 550f9f55e31SRichard Fitzgerald }, 551f9f55e31SRichard Fitzgerald }, 552f9f55e31SRichard Fitzgerald { 553f9f55e31SRichard Fitzgerald .name = "bin_file_name", 554f9f55e31SRichard Fitzgerald .fops = { 555f9f55e31SRichard Fitzgerald .open = simple_open, 556f9f55e31SRichard Fitzgerald .read = wm_adsp_debugfs_bin_read, 557f9f55e31SRichard Fitzgerald }, 558f9f55e31SRichard Fitzgerald }, 559f9f55e31SRichard Fitzgerald }; 560f9f55e31SRichard Fitzgerald 561f9f55e31SRichard Fitzgerald static void wm_adsp2_init_debugfs(struct wm_adsp *dsp, 562f9f55e31SRichard Fitzgerald struct snd_soc_codec *codec) 563f9f55e31SRichard Fitzgerald { 564f9f55e31SRichard Fitzgerald struct dentry *root = NULL; 565f9f55e31SRichard Fitzgerald char *root_name; 566f9f55e31SRichard Fitzgerald int i; 567f9f55e31SRichard Fitzgerald 568f9f55e31SRichard Fitzgerald if (!codec->component.debugfs_root) { 569f9f55e31SRichard Fitzgerald adsp_err(dsp, "No codec debugfs root\n"); 570f9f55e31SRichard Fitzgerald goto err; 571f9f55e31SRichard Fitzgerald } 572f9f55e31SRichard Fitzgerald 573f9f55e31SRichard Fitzgerald root_name = kmalloc(PAGE_SIZE, GFP_KERNEL); 574f9f55e31SRichard Fitzgerald if (!root_name) 575f9f55e31SRichard Fitzgerald goto err; 576f9f55e31SRichard Fitzgerald 577f9f55e31SRichard Fitzgerald snprintf(root_name, PAGE_SIZE, "dsp%d", dsp->num); 578f9f55e31SRichard Fitzgerald root = debugfs_create_dir(root_name, codec->component.debugfs_root); 579f9f55e31SRichard Fitzgerald kfree(root_name); 580f9f55e31SRichard Fitzgerald 581f9f55e31SRichard Fitzgerald if (!root) 582f9f55e31SRichard Fitzgerald goto err; 583f9f55e31SRichard Fitzgerald 58428823ebaSCharles Keepax if (!debugfs_create_bool("booted", S_IRUGO, root, &dsp->booted)) 58528823ebaSCharles Keepax goto err; 58628823ebaSCharles Keepax 587f9f55e31SRichard Fitzgerald if (!debugfs_create_bool("running", S_IRUGO, root, &dsp->running)) 588f9f55e31SRichard Fitzgerald goto err; 589f9f55e31SRichard Fitzgerald 590f9f55e31SRichard Fitzgerald if (!debugfs_create_x32("fw_id", S_IRUGO, root, &dsp->fw_id)) 591f9f55e31SRichard Fitzgerald goto err; 592f9f55e31SRichard Fitzgerald 593f9f55e31SRichard Fitzgerald if (!debugfs_create_x32("fw_version", S_IRUGO, root, 594f9f55e31SRichard Fitzgerald &dsp->fw_id_version)) 595f9f55e31SRichard Fitzgerald goto err; 596f9f55e31SRichard Fitzgerald 597f9f55e31SRichard Fitzgerald for (i = 0; i < ARRAY_SIZE(wm_adsp_debugfs_fops); ++i) { 598f9f55e31SRichard Fitzgerald if (!debugfs_create_file(wm_adsp_debugfs_fops[i].name, 599f9f55e31SRichard Fitzgerald S_IRUGO, root, dsp, 600f9f55e31SRichard Fitzgerald &wm_adsp_debugfs_fops[i].fops)) 601f9f55e31SRichard Fitzgerald goto err; 602f9f55e31SRichard Fitzgerald } 603f9f55e31SRichard Fitzgerald 604f9f55e31SRichard Fitzgerald dsp->debugfs_root = root; 605f9f55e31SRichard Fitzgerald return; 606f9f55e31SRichard Fitzgerald 607f9f55e31SRichard Fitzgerald err: 608f9f55e31SRichard Fitzgerald debugfs_remove_recursive(root); 609f9f55e31SRichard Fitzgerald adsp_err(dsp, "Failed to create debugfs\n"); 610f9f55e31SRichard Fitzgerald } 611f9f55e31SRichard Fitzgerald 612f9f55e31SRichard Fitzgerald static void wm_adsp2_cleanup_debugfs(struct wm_adsp *dsp) 613f9f55e31SRichard Fitzgerald { 614f9f55e31SRichard Fitzgerald wm_adsp_debugfs_clear(dsp); 615f9f55e31SRichard Fitzgerald debugfs_remove_recursive(dsp->debugfs_root); 616f9f55e31SRichard Fitzgerald } 617f9f55e31SRichard Fitzgerald #else 618f9f55e31SRichard Fitzgerald static inline void wm_adsp2_init_debugfs(struct wm_adsp *dsp, 619f9f55e31SRichard Fitzgerald struct snd_soc_codec *codec) 620f9f55e31SRichard Fitzgerald { 621f9f55e31SRichard Fitzgerald } 622f9f55e31SRichard Fitzgerald 623f9f55e31SRichard Fitzgerald static inline void wm_adsp2_cleanup_debugfs(struct wm_adsp *dsp) 624f9f55e31SRichard Fitzgerald { 625f9f55e31SRichard Fitzgerald } 626f9f55e31SRichard Fitzgerald 627f9f55e31SRichard Fitzgerald static inline void wm_adsp_debugfs_save_wmfwname(struct wm_adsp *dsp, 628f9f55e31SRichard Fitzgerald const char *s) 629f9f55e31SRichard Fitzgerald { 630f9f55e31SRichard Fitzgerald } 631f9f55e31SRichard Fitzgerald 632f9f55e31SRichard Fitzgerald static inline void wm_adsp_debugfs_save_binname(struct wm_adsp *dsp, 633f9f55e31SRichard Fitzgerald const char *s) 634f9f55e31SRichard Fitzgerald { 635f9f55e31SRichard Fitzgerald } 636f9f55e31SRichard Fitzgerald 637f9f55e31SRichard Fitzgerald static inline void wm_adsp_debugfs_clear(struct wm_adsp *dsp) 638f9f55e31SRichard Fitzgerald { 639f9f55e31SRichard Fitzgerald } 640f9f55e31SRichard Fitzgerald #endif 641f9f55e31SRichard Fitzgerald 6421023dbd9SMark Brown static int wm_adsp_fw_get(struct snd_kcontrol *kcontrol, 6431023dbd9SMark Brown struct snd_ctl_elem_value *ucontrol) 6441023dbd9SMark Brown { 645ea53bf77SLars-Peter Clausen struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); 6461023dbd9SMark Brown struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; 6473809f001SCharles Keepax struct wm_adsp *dsp = snd_soc_codec_get_drvdata(codec); 6481023dbd9SMark Brown 64915c66570STakashi Iwai ucontrol->value.enumerated.item[0] = dsp[e->shift_l].fw; 6501023dbd9SMark Brown 6511023dbd9SMark Brown return 0; 6521023dbd9SMark Brown } 6531023dbd9SMark Brown 6541023dbd9SMark Brown static int wm_adsp_fw_put(struct snd_kcontrol *kcontrol, 6551023dbd9SMark Brown struct snd_ctl_elem_value *ucontrol) 6561023dbd9SMark Brown { 657ea53bf77SLars-Peter Clausen struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); 6581023dbd9SMark Brown struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; 6593809f001SCharles Keepax struct wm_adsp *dsp = snd_soc_codec_get_drvdata(codec); 660d27c5e15SCharles Keepax int ret = 0; 6611023dbd9SMark Brown 66215c66570STakashi Iwai if (ucontrol->value.enumerated.item[0] == dsp[e->shift_l].fw) 6631023dbd9SMark Brown return 0; 6641023dbd9SMark Brown 66515c66570STakashi Iwai if (ucontrol->value.enumerated.item[0] >= WM_ADSP_NUM_FW) 6661023dbd9SMark Brown return -EINVAL; 6671023dbd9SMark Brown 668d27c5e15SCharles Keepax mutex_lock(&dsp[e->shift_l].pwr_lock); 6691023dbd9SMark Brown 67028823ebaSCharles Keepax if (dsp[e->shift_l].booted || dsp[e->shift_l].compr) 671d27c5e15SCharles Keepax ret = -EBUSY; 672d27c5e15SCharles Keepax else 67315c66570STakashi Iwai dsp[e->shift_l].fw = ucontrol->value.enumerated.item[0]; 6741023dbd9SMark Brown 675d27c5e15SCharles Keepax mutex_unlock(&dsp[e->shift_l].pwr_lock); 676d27c5e15SCharles Keepax 677d27c5e15SCharles Keepax return ret; 6781023dbd9SMark Brown } 6791023dbd9SMark Brown 6801023dbd9SMark Brown static const struct soc_enum wm_adsp_fw_enum[] = { 6811023dbd9SMark Brown SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 6821023dbd9SMark Brown SOC_ENUM_SINGLE(0, 1, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 6831023dbd9SMark Brown SOC_ENUM_SINGLE(0, 2, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 6841023dbd9SMark Brown SOC_ENUM_SINGLE(0, 3, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 6851023dbd9SMark Brown }; 6861023dbd9SMark Brown 687336d0442SRichard Fitzgerald const struct snd_kcontrol_new wm_adsp_fw_controls[] = { 6881023dbd9SMark Brown SOC_ENUM_EXT("DSP1 Firmware", wm_adsp_fw_enum[0], 6891023dbd9SMark Brown wm_adsp_fw_get, wm_adsp_fw_put), 6901023dbd9SMark Brown SOC_ENUM_EXT("DSP2 Firmware", wm_adsp_fw_enum[1], 6911023dbd9SMark Brown wm_adsp_fw_get, wm_adsp_fw_put), 6921023dbd9SMark Brown SOC_ENUM_EXT("DSP3 Firmware", wm_adsp_fw_enum[2], 6931023dbd9SMark Brown wm_adsp_fw_get, wm_adsp_fw_put), 6941023dbd9SMark Brown SOC_ENUM_EXT("DSP4 Firmware", wm_adsp_fw_enum[3], 6951023dbd9SMark Brown wm_adsp_fw_get, wm_adsp_fw_put), 6961023dbd9SMark Brown }; 697336d0442SRichard Fitzgerald EXPORT_SYMBOL_GPL(wm_adsp_fw_controls); 6982159ad93SMark Brown 6992159ad93SMark Brown static struct wm_adsp_region const *wm_adsp_find_region(struct wm_adsp *dsp, 7002159ad93SMark Brown int type) 7012159ad93SMark Brown { 7022159ad93SMark Brown int i; 7032159ad93SMark Brown 7042159ad93SMark Brown for (i = 0; i < dsp->num_mems; i++) 7052159ad93SMark Brown if (dsp->mem[i].type == type) 7062159ad93SMark Brown return &dsp->mem[i]; 7072159ad93SMark Brown 7082159ad93SMark Brown return NULL; 7092159ad93SMark Brown } 7102159ad93SMark Brown 7113809f001SCharles Keepax static unsigned int wm_adsp_region_to_reg(struct wm_adsp_region const *mem, 71245b9ee72SMark Brown unsigned int offset) 71345b9ee72SMark Brown { 7143809f001SCharles Keepax if (WARN_ON(!mem)) 7156c452bdaSTakashi Iwai return offset; 7163809f001SCharles Keepax switch (mem->type) { 71745b9ee72SMark Brown case WMFW_ADSP1_PM: 7183809f001SCharles Keepax return mem->base + (offset * 3); 71945b9ee72SMark Brown case WMFW_ADSP1_DM: 7203809f001SCharles Keepax return mem->base + (offset * 2); 72145b9ee72SMark Brown case WMFW_ADSP2_XM: 7223809f001SCharles Keepax return mem->base + (offset * 2); 72345b9ee72SMark Brown case WMFW_ADSP2_YM: 7243809f001SCharles Keepax return mem->base + (offset * 2); 72545b9ee72SMark Brown case WMFW_ADSP1_ZM: 7263809f001SCharles Keepax return mem->base + (offset * 2); 72745b9ee72SMark Brown default: 7286c452bdaSTakashi Iwai WARN(1, "Unknown memory region type"); 72945b9ee72SMark Brown return offset; 73045b9ee72SMark Brown } 73145b9ee72SMark Brown } 73245b9ee72SMark Brown 73310337b07SRichard Fitzgerald static void wm_adsp2_show_fw_status(struct wm_adsp *dsp) 73410337b07SRichard Fitzgerald { 73510337b07SRichard Fitzgerald u16 scratch[4]; 73610337b07SRichard Fitzgerald int ret; 73710337b07SRichard Fitzgerald 73810337b07SRichard Fitzgerald ret = regmap_raw_read(dsp->regmap, dsp->base + ADSP2_SCRATCH0, 73910337b07SRichard Fitzgerald scratch, sizeof(scratch)); 74010337b07SRichard Fitzgerald if (ret) { 74110337b07SRichard Fitzgerald adsp_err(dsp, "Failed to read SCRATCH regs: %d\n", ret); 74210337b07SRichard Fitzgerald return; 74310337b07SRichard Fitzgerald } 74410337b07SRichard Fitzgerald 74510337b07SRichard Fitzgerald adsp_dbg(dsp, "FW SCRATCH 0:0x%x 1:0x%x 2:0x%x 3:0x%x\n", 74610337b07SRichard Fitzgerald be16_to_cpu(scratch[0]), 74710337b07SRichard Fitzgerald be16_to_cpu(scratch[1]), 74810337b07SRichard Fitzgerald be16_to_cpu(scratch[2]), 74910337b07SRichard Fitzgerald be16_to_cpu(scratch[3])); 75010337b07SRichard Fitzgerald } 75110337b07SRichard Fitzgerald 7529ee78757SCharles Keepax static inline struct wm_coeff_ctl *bytes_ext_to_ctl(struct soc_bytes_ext *ext) 7539ee78757SCharles Keepax { 7549ee78757SCharles Keepax return container_of(ext, struct wm_coeff_ctl, bytes_ext); 7559ee78757SCharles Keepax } 7569ee78757SCharles Keepax 7577585a5b0SCharles Keepax static int wm_coeff_info(struct snd_kcontrol *kctl, 7586ab2b7b4SDimitris Papastamos struct snd_ctl_elem_info *uinfo) 7596ab2b7b4SDimitris Papastamos { 7609ee78757SCharles Keepax struct soc_bytes_ext *bytes_ext = 7619ee78757SCharles Keepax (struct soc_bytes_ext *)kctl->private_value; 7629ee78757SCharles Keepax struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); 7636ab2b7b4SDimitris Papastamos 7646ab2b7b4SDimitris Papastamos uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; 7656ab2b7b4SDimitris Papastamos uinfo->count = ctl->len; 7666ab2b7b4SDimitris Papastamos return 0; 7676ab2b7b4SDimitris Papastamos } 7686ab2b7b4SDimitris Papastamos 769f4f0c4c6SRichard Fitzgerald static int wm_coeff_write_acked_control(struct wm_coeff_ctl *ctl, 770f4f0c4c6SRichard Fitzgerald unsigned int event_id) 771f4f0c4c6SRichard Fitzgerald { 772f4f0c4c6SRichard Fitzgerald struct wm_adsp *dsp = ctl->dsp; 773f4f0c4c6SRichard Fitzgerald u32 val = cpu_to_be32(event_id); 774f4f0c4c6SRichard Fitzgerald unsigned int reg; 775f4f0c4c6SRichard Fitzgerald int i, ret; 776f4f0c4c6SRichard Fitzgerald 777f4f0c4c6SRichard Fitzgerald ret = wm_coeff_base_reg(ctl, ®); 778f4f0c4c6SRichard Fitzgerald if (ret) 779f4f0c4c6SRichard Fitzgerald return ret; 780f4f0c4c6SRichard Fitzgerald 781f4f0c4c6SRichard Fitzgerald adsp_dbg(dsp, "Sending 0x%x to acked control alg 0x%x %s:0x%x\n", 782f4f0c4c6SRichard Fitzgerald event_id, ctl->alg_region.alg, 783f4f0c4c6SRichard Fitzgerald wm_adsp_mem_region_name(ctl->alg_region.type), ctl->offset); 784f4f0c4c6SRichard Fitzgerald 785f4f0c4c6SRichard Fitzgerald ret = regmap_raw_write(dsp->regmap, reg, &val, sizeof(val)); 786f4f0c4c6SRichard Fitzgerald if (ret) { 787f4f0c4c6SRichard Fitzgerald adsp_err(dsp, "Failed to write %x: %d\n", reg, ret); 788f4f0c4c6SRichard Fitzgerald return ret; 789f4f0c4c6SRichard Fitzgerald } 790f4f0c4c6SRichard Fitzgerald 791f4f0c4c6SRichard Fitzgerald /* 792f4f0c4c6SRichard Fitzgerald * Poll for ack, we initially poll at ~1ms intervals for firmwares 793f4f0c4c6SRichard Fitzgerald * that respond quickly, then go to ~10ms polls. A firmware is unlikely 794f4f0c4c6SRichard Fitzgerald * to ack instantly so we do the first 1ms delay before reading the 795f4f0c4c6SRichard Fitzgerald * control to avoid a pointless bus transaction 796f4f0c4c6SRichard Fitzgerald */ 797f4f0c4c6SRichard Fitzgerald for (i = 0; i < WM_ADSP_ACKED_CTL_TIMEOUT_MS;) { 798f4f0c4c6SRichard Fitzgerald switch (i) { 799f4f0c4c6SRichard Fitzgerald case 0 ... WM_ADSP_ACKED_CTL_N_QUICKPOLLS - 1: 800f4f0c4c6SRichard Fitzgerald usleep_range(1000, 2000); 801f4f0c4c6SRichard Fitzgerald i++; 802f4f0c4c6SRichard Fitzgerald break; 803f4f0c4c6SRichard Fitzgerald default: 804f4f0c4c6SRichard Fitzgerald usleep_range(10000, 20000); 805f4f0c4c6SRichard Fitzgerald i += 10; 806f4f0c4c6SRichard Fitzgerald break; 807f4f0c4c6SRichard Fitzgerald } 808f4f0c4c6SRichard Fitzgerald 809f4f0c4c6SRichard Fitzgerald ret = regmap_raw_read(dsp->regmap, reg, &val, sizeof(val)); 810f4f0c4c6SRichard Fitzgerald if (ret) { 811f4f0c4c6SRichard Fitzgerald adsp_err(dsp, "Failed to read %x: %d\n", reg, ret); 812f4f0c4c6SRichard Fitzgerald return ret; 813f4f0c4c6SRichard Fitzgerald } 814f4f0c4c6SRichard Fitzgerald 815f4f0c4c6SRichard Fitzgerald if (val == 0) { 816f4f0c4c6SRichard Fitzgerald adsp_dbg(dsp, "Acked control ACKED at poll %u\n", i); 817f4f0c4c6SRichard Fitzgerald return 0; 818f4f0c4c6SRichard Fitzgerald } 819f4f0c4c6SRichard Fitzgerald } 820f4f0c4c6SRichard Fitzgerald 821f4f0c4c6SRichard Fitzgerald adsp_warn(dsp, "Acked control @0x%x alg:0x%x %s:0x%x timed out\n", 822f4f0c4c6SRichard Fitzgerald reg, ctl->alg_region.alg, 823f4f0c4c6SRichard Fitzgerald wm_adsp_mem_region_name(ctl->alg_region.type), 824f4f0c4c6SRichard Fitzgerald ctl->offset); 825f4f0c4c6SRichard Fitzgerald 826f4f0c4c6SRichard Fitzgerald return -ETIMEDOUT; 827f4f0c4c6SRichard Fitzgerald } 828f4f0c4c6SRichard Fitzgerald 829c9f8dd71SCharles Keepax static int wm_coeff_write_control(struct wm_coeff_ctl *ctl, 8306ab2b7b4SDimitris Papastamos const void *buf, size_t len) 8316ab2b7b4SDimitris Papastamos { 8323809f001SCharles Keepax struct wm_adsp_alg_region *alg_region = &ctl->alg_region; 8336ab2b7b4SDimitris Papastamos const struct wm_adsp_region *mem; 8343809f001SCharles Keepax struct wm_adsp *dsp = ctl->dsp; 8356ab2b7b4SDimitris Papastamos void *scratch; 8366ab2b7b4SDimitris Papastamos int ret; 8376ab2b7b4SDimitris Papastamos unsigned int reg; 8386ab2b7b4SDimitris Papastamos 8393809f001SCharles Keepax mem = wm_adsp_find_region(dsp, alg_region->type); 8406ab2b7b4SDimitris Papastamos if (!mem) { 8413809f001SCharles Keepax adsp_err(dsp, "No base for region %x\n", 8423809f001SCharles Keepax alg_region->type); 8436ab2b7b4SDimitris Papastamos return -EINVAL; 8446ab2b7b4SDimitris Papastamos } 8456ab2b7b4SDimitris Papastamos 8462323736dSCharles Keepax reg = ctl->alg_region.base + ctl->offset; 8476ab2b7b4SDimitris Papastamos reg = wm_adsp_region_to_reg(mem, reg); 8486ab2b7b4SDimitris Papastamos 8494f8ea6d7SCharles Keepax scratch = kmemdup(buf, len, GFP_KERNEL | GFP_DMA); 8506ab2b7b4SDimitris Papastamos if (!scratch) 8516ab2b7b4SDimitris Papastamos return -ENOMEM; 8526ab2b7b4SDimitris Papastamos 8533809f001SCharles Keepax ret = regmap_raw_write(dsp->regmap, reg, scratch, 8544f8ea6d7SCharles Keepax len); 8556ab2b7b4SDimitris Papastamos if (ret) { 8563809f001SCharles Keepax adsp_err(dsp, "Failed to write %zu bytes to %x: %d\n", 8574f8ea6d7SCharles Keepax len, reg, ret); 8586ab2b7b4SDimitris Papastamos kfree(scratch); 8596ab2b7b4SDimitris Papastamos return ret; 8606ab2b7b4SDimitris Papastamos } 8614f8ea6d7SCharles Keepax adsp_dbg(dsp, "Wrote %zu bytes to %x\n", len, reg); 8626ab2b7b4SDimitris Papastamos 8636ab2b7b4SDimitris Papastamos kfree(scratch); 8646ab2b7b4SDimitris Papastamos 8656ab2b7b4SDimitris Papastamos return 0; 8666ab2b7b4SDimitris Papastamos } 8676ab2b7b4SDimitris Papastamos 8687585a5b0SCharles Keepax static int wm_coeff_put(struct snd_kcontrol *kctl, 8696ab2b7b4SDimitris Papastamos struct snd_ctl_elem_value *ucontrol) 8706ab2b7b4SDimitris Papastamos { 8719ee78757SCharles Keepax struct soc_bytes_ext *bytes_ext = 8729ee78757SCharles Keepax (struct soc_bytes_ext *)kctl->private_value; 8739ee78757SCharles Keepax struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); 8746ab2b7b4SDimitris Papastamos char *p = ucontrol->value.bytes.data; 875168d10e7SCharles Keepax int ret = 0; 876168d10e7SCharles Keepax 877168d10e7SCharles Keepax mutex_lock(&ctl->dsp->pwr_lock); 8786ab2b7b4SDimitris Papastamos 8796ab2b7b4SDimitris Papastamos memcpy(ctl->cache, p, ctl->len); 8806ab2b7b4SDimitris Papastamos 8810c2e3f34SDimitris Papastamos ctl->set = 1; 882cef45771SCharles Keepax if (ctl->enabled && ctl->dsp->running) 883168d10e7SCharles Keepax ret = wm_coeff_write_control(ctl, p, ctl->len); 8846ab2b7b4SDimitris Papastamos 885168d10e7SCharles Keepax mutex_unlock(&ctl->dsp->pwr_lock); 886168d10e7SCharles Keepax 887168d10e7SCharles Keepax return ret; 8886ab2b7b4SDimitris Papastamos } 8896ab2b7b4SDimitris Papastamos 8909ee78757SCharles Keepax static int wm_coeff_tlv_put(struct snd_kcontrol *kctl, 8919ee78757SCharles Keepax const unsigned int __user *bytes, unsigned int size) 8929ee78757SCharles Keepax { 8939ee78757SCharles Keepax struct soc_bytes_ext *bytes_ext = 8949ee78757SCharles Keepax (struct soc_bytes_ext *)kctl->private_value; 8959ee78757SCharles Keepax struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); 8969ee78757SCharles Keepax int ret = 0; 8979ee78757SCharles Keepax 8989ee78757SCharles Keepax mutex_lock(&ctl->dsp->pwr_lock); 8999ee78757SCharles Keepax 9009ee78757SCharles Keepax if (copy_from_user(ctl->cache, bytes, size)) { 9019ee78757SCharles Keepax ret = -EFAULT; 9029ee78757SCharles Keepax } else { 9039ee78757SCharles Keepax ctl->set = 1; 904cef45771SCharles Keepax if (ctl->enabled && ctl->dsp->running) 9059ee78757SCharles Keepax ret = wm_coeff_write_control(ctl, ctl->cache, size); 9069ee78757SCharles Keepax } 9079ee78757SCharles Keepax 9089ee78757SCharles Keepax mutex_unlock(&ctl->dsp->pwr_lock); 9099ee78757SCharles Keepax 9109ee78757SCharles Keepax return ret; 9119ee78757SCharles Keepax } 9129ee78757SCharles Keepax 913c9f8dd71SCharles Keepax static int wm_coeff_read_control(struct wm_coeff_ctl *ctl, 9146ab2b7b4SDimitris Papastamos void *buf, size_t len) 9156ab2b7b4SDimitris Papastamos { 9163809f001SCharles Keepax struct wm_adsp_alg_region *alg_region = &ctl->alg_region; 9176ab2b7b4SDimitris Papastamos const struct wm_adsp_region *mem; 9183809f001SCharles Keepax struct wm_adsp *dsp = ctl->dsp; 9196ab2b7b4SDimitris Papastamos void *scratch; 9206ab2b7b4SDimitris Papastamos int ret; 9216ab2b7b4SDimitris Papastamos unsigned int reg; 9226ab2b7b4SDimitris Papastamos 9233809f001SCharles Keepax mem = wm_adsp_find_region(dsp, alg_region->type); 9246ab2b7b4SDimitris Papastamos if (!mem) { 9253809f001SCharles Keepax adsp_err(dsp, "No base for region %x\n", 9263809f001SCharles Keepax alg_region->type); 9276ab2b7b4SDimitris Papastamos return -EINVAL; 9286ab2b7b4SDimitris Papastamos } 9296ab2b7b4SDimitris Papastamos 9302323736dSCharles Keepax reg = ctl->alg_region.base + ctl->offset; 9316ab2b7b4SDimitris Papastamos reg = wm_adsp_region_to_reg(mem, reg); 9326ab2b7b4SDimitris Papastamos 9334f8ea6d7SCharles Keepax scratch = kmalloc(len, GFP_KERNEL | GFP_DMA); 9346ab2b7b4SDimitris Papastamos if (!scratch) 9356ab2b7b4SDimitris Papastamos return -ENOMEM; 9366ab2b7b4SDimitris Papastamos 9374f8ea6d7SCharles Keepax ret = regmap_raw_read(dsp->regmap, reg, scratch, len); 9386ab2b7b4SDimitris Papastamos if (ret) { 9393809f001SCharles Keepax adsp_err(dsp, "Failed to read %zu bytes from %x: %d\n", 9405602a643SCharles Keepax len, reg, ret); 9416ab2b7b4SDimitris Papastamos kfree(scratch); 9426ab2b7b4SDimitris Papastamos return ret; 9436ab2b7b4SDimitris Papastamos } 9444f8ea6d7SCharles Keepax adsp_dbg(dsp, "Read %zu bytes from %x\n", len, reg); 9456ab2b7b4SDimitris Papastamos 9464f8ea6d7SCharles Keepax memcpy(buf, scratch, len); 9476ab2b7b4SDimitris Papastamos kfree(scratch); 9486ab2b7b4SDimitris Papastamos 9496ab2b7b4SDimitris Papastamos return 0; 9506ab2b7b4SDimitris Papastamos } 9516ab2b7b4SDimitris Papastamos 9527585a5b0SCharles Keepax static int wm_coeff_get(struct snd_kcontrol *kctl, 9536ab2b7b4SDimitris Papastamos struct snd_ctl_elem_value *ucontrol) 9546ab2b7b4SDimitris Papastamos { 9559ee78757SCharles Keepax struct soc_bytes_ext *bytes_ext = 9569ee78757SCharles Keepax (struct soc_bytes_ext *)kctl->private_value; 9579ee78757SCharles Keepax struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); 9586ab2b7b4SDimitris Papastamos char *p = ucontrol->value.bytes.data; 959168d10e7SCharles Keepax int ret = 0; 960168d10e7SCharles Keepax 961168d10e7SCharles Keepax mutex_lock(&ctl->dsp->pwr_lock); 9626ab2b7b4SDimitris Papastamos 96326c22a19SCharles Keepax if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) { 964cef45771SCharles Keepax if (ctl->enabled && ctl->dsp->running) 965168d10e7SCharles Keepax ret = wm_coeff_read_control(ctl, p, ctl->len); 96626c22a19SCharles Keepax else 967168d10e7SCharles Keepax ret = -EPERM; 968168d10e7SCharles Keepax } else { 969cef45771SCharles Keepax if (!ctl->flags && ctl->enabled && ctl->dsp->running) 970bc1765d6SCharles Keepax ret = wm_coeff_read_control(ctl, ctl->cache, ctl->len); 971bc1765d6SCharles Keepax 972168d10e7SCharles Keepax memcpy(p, ctl->cache, ctl->len); 97326c22a19SCharles Keepax } 97426c22a19SCharles Keepax 975168d10e7SCharles Keepax mutex_unlock(&ctl->dsp->pwr_lock); 97626c22a19SCharles Keepax 977168d10e7SCharles Keepax return ret; 9786ab2b7b4SDimitris Papastamos } 9796ab2b7b4SDimitris Papastamos 9809ee78757SCharles Keepax static int wm_coeff_tlv_get(struct snd_kcontrol *kctl, 9819ee78757SCharles Keepax unsigned int __user *bytes, unsigned int size) 9829ee78757SCharles Keepax { 9839ee78757SCharles Keepax struct soc_bytes_ext *bytes_ext = 9849ee78757SCharles Keepax (struct soc_bytes_ext *)kctl->private_value; 9859ee78757SCharles Keepax struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); 9869ee78757SCharles Keepax int ret = 0; 9879ee78757SCharles Keepax 9889ee78757SCharles Keepax mutex_lock(&ctl->dsp->pwr_lock); 9899ee78757SCharles Keepax 9909ee78757SCharles Keepax if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) { 991cef45771SCharles Keepax if (ctl->enabled && ctl->dsp->running) 9929ee78757SCharles Keepax ret = wm_coeff_read_control(ctl, ctl->cache, size); 9939ee78757SCharles Keepax else 9949ee78757SCharles Keepax ret = -EPERM; 9959ee78757SCharles Keepax } else { 996cef45771SCharles Keepax if (!ctl->flags && ctl->enabled && ctl->dsp->running) 9979ee78757SCharles Keepax ret = wm_coeff_read_control(ctl, ctl->cache, size); 9989ee78757SCharles Keepax } 9999ee78757SCharles Keepax 10009ee78757SCharles Keepax if (!ret && copy_to_user(bytes, ctl->cache, size)) 10019ee78757SCharles Keepax ret = -EFAULT; 10029ee78757SCharles Keepax 10039ee78757SCharles Keepax mutex_unlock(&ctl->dsp->pwr_lock); 10049ee78757SCharles Keepax 10059ee78757SCharles Keepax return ret; 10069ee78757SCharles Keepax } 10079ee78757SCharles Keepax 10086ab2b7b4SDimitris Papastamos struct wmfw_ctl_work { 10093809f001SCharles Keepax struct wm_adsp *dsp; 10106ab2b7b4SDimitris Papastamos struct wm_coeff_ctl *ctl; 10116ab2b7b4SDimitris Papastamos struct work_struct work; 10126ab2b7b4SDimitris Papastamos }; 10136ab2b7b4SDimitris Papastamos 10149ee78757SCharles Keepax static unsigned int wmfw_convert_flags(unsigned int in, unsigned int len) 10159ee78757SCharles Keepax { 10169ee78757SCharles Keepax unsigned int out, rd, wr, vol; 10179ee78757SCharles Keepax 10189ee78757SCharles Keepax if (len > ADSP_MAX_STD_CTRL_SIZE) { 10199ee78757SCharles Keepax rd = SNDRV_CTL_ELEM_ACCESS_TLV_READ; 10209ee78757SCharles Keepax wr = SNDRV_CTL_ELEM_ACCESS_TLV_WRITE; 10219ee78757SCharles Keepax vol = SNDRV_CTL_ELEM_ACCESS_VOLATILE; 10229ee78757SCharles Keepax 10239ee78757SCharles Keepax out = SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK; 10249ee78757SCharles Keepax } else { 10259ee78757SCharles Keepax rd = SNDRV_CTL_ELEM_ACCESS_READ; 10269ee78757SCharles Keepax wr = SNDRV_CTL_ELEM_ACCESS_WRITE; 10279ee78757SCharles Keepax vol = SNDRV_CTL_ELEM_ACCESS_VOLATILE; 10289ee78757SCharles Keepax 10299ee78757SCharles Keepax out = 0; 10309ee78757SCharles Keepax } 10319ee78757SCharles Keepax 10329ee78757SCharles Keepax if (in) { 10339ee78757SCharles Keepax if (in & WMFW_CTL_FLAG_READABLE) 10349ee78757SCharles Keepax out |= rd; 10359ee78757SCharles Keepax if (in & WMFW_CTL_FLAG_WRITEABLE) 10369ee78757SCharles Keepax out |= wr; 10379ee78757SCharles Keepax if (in & WMFW_CTL_FLAG_VOLATILE) 10389ee78757SCharles Keepax out |= vol; 10399ee78757SCharles Keepax } else { 10409ee78757SCharles Keepax out |= rd | wr | vol; 10419ee78757SCharles Keepax } 10429ee78757SCharles Keepax 10439ee78757SCharles Keepax return out; 10449ee78757SCharles Keepax } 10459ee78757SCharles Keepax 10463809f001SCharles Keepax static int wmfw_add_ctl(struct wm_adsp *dsp, struct wm_coeff_ctl *ctl) 10476ab2b7b4SDimitris Papastamos { 10486ab2b7b4SDimitris Papastamos struct snd_kcontrol_new *kcontrol; 10496ab2b7b4SDimitris Papastamos int ret; 10506ab2b7b4SDimitris Papastamos 105192bb4c32SDimitris Papastamos if (!ctl || !ctl->name) 10526ab2b7b4SDimitris Papastamos return -EINVAL; 10536ab2b7b4SDimitris Papastamos 10546ab2b7b4SDimitris Papastamos kcontrol = kzalloc(sizeof(*kcontrol), GFP_KERNEL); 10556ab2b7b4SDimitris Papastamos if (!kcontrol) 10566ab2b7b4SDimitris Papastamos return -ENOMEM; 10576ab2b7b4SDimitris Papastamos 10586ab2b7b4SDimitris Papastamos kcontrol->name = ctl->name; 10596ab2b7b4SDimitris Papastamos kcontrol->info = wm_coeff_info; 10606ab2b7b4SDimitris Papastamos kcontrol->get = wm_coeff_get; 10616ab2b7b4SDimitris Papastamos kcontrol->put = wm_coeff_put; 10629ee78757SCharles Keepax kcontrol->iface = SNDRV_CTL_ELEM_IFACE_MIXER; 10639ee78757SCharles Keepax kcontrol->tlv.c = snd_soc_bytes_tlv_callback; 10649ee78757SCharles Keepax kcontrol->private_value = (unsigned long)&ctl->bytes_ext; 10656ab2b7b4SDimitris Papastamos 10669ee78757SCharles Keepax ctl->bytes_ext.max = ctl->len; 10679ee78757SCharles Keepax ctl->bytes_ext.get = wm_coeff_tlv_get; 10689ee78757SCharles Keepax ctl->bytes_ext.put = wm_coeff_tlv_put; 10699ee78757SCharles Keepax 10709ee78757SCharles Keepax kcontrol->access = wmfw_convert_flags(ctl->flags, ctl->len); 107126c22a19SCharles Keepax 10727d00cd97SCharles Keepax ret = snd_soc_add_card_controls(dsp->card, kcontrol, 1); 10736ab2b7b4SDimitris Papastamos if (ret < 0) 10746ab2b7b4SDimitris Papastamos goto err_kcontrol; 10756ab2b7b4SDimitris Papastamos 10766ab2b7b4SDimitris Papastamos kfree(kcontrol); 10776ab2b7b4SDimitris Papastamos 10787d00cd97SCharles Keepax ctl->kcontrol = snd_soc_card_get_kcontrol(dsp->card, ctl->name); 107981ad93ecSDimitris Papastamos 10806ab2b7b4SDimitris Papastamos return 0; 10816ab2b7b4SDimitris Papastamos 10826ab2b7b4SDimitris Papastamos err_kcontrol: 10836ab2b7b4SDimitris Papastamos kfree(kcontrol); 10846ab2b7b4SDimitris Papastamos return ret; 10856ab2b7b4SDimitris Papastamos } 10866ab2b7b4SDimitris Papastamos 1087b21acc1cSCharles Keepax static int wm_coeff_init_control_caches(struct wm_adsp *dsp) 1088b21acc1cSCharles Keepax { 1089b21acc1cSCharles Keepax struct wm_coeff_ctl *ctl; 1090b21acc1cSCharles Keepax int ret; 1091b21acc1cSCharles Keepax 1092b21acc1cSCharles Keepax list_for_each_entry(ctl, &dsp->ctl_list, list) { 1093b21acc1cSCharles Keepax if (!ctl->enabled || ctl->set) 1094b21acc1cSCharles Keepax continue; 109526c22a19SCharles Keepax if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) 109626c22a19SCharles Keepax continue; 109726c22a19SCharles Keepax 10987d00cd97SCharles Keepax ret = wm_coeff_read_control(ctl, ctl->cache, ctl->len); 1099b21acc1cSCharles Keepax if (ret < 0) 1100b21acc1cSCharles Keepax return ret; 1101b21acc1cSCharles Keepax } 1102b21acc1cSCharles Keepax 1103b21acc1cSCharles Keepax return 0; 1104b21acc1cSCharles Keepax } 1105b21acc1cSCharles Keepax 1106b21acc1cSCharles Keepax static int wm_coeff_sync_controls(struct wm_adsp *dsp) 1107b21acc1cSCharles Keepax { 1108b21acc1cSCharles Keepax struct wm_coeff_ctl *ctl; 1109b21acc1cSCharles Keepax int ret; 1110b21acc1cSCharles Keepax 1111b21acc1cSCharles Keepax list_for_each_entry(ctl, &dsp->ctl_list, list) { 1112b21acc1cSCharles Keepax if (!ctl->enabled) 1113b21acc1cSCharles Keepax continue; 111426c22a19SCharles Keepax if (ctl->set && !(ctl->flags & WMFW_CTL_FLAG_VOLATILE)) { 11157d00cd97SCharles Keepax ret = wm_coeff_write_control(ctl, ctl->cache, ctl->len); 1116b21acc1cSCharles Keepax if (ret < 0) 1117b21acc1cSCharles Keepax return ret; 1118b21acc1cSCharles Keepax } 1119b21acc1cSCharles Keepax } 1120b21acc1cSCharles Keepax 1121b21acc1cSCharles Keepax return 0; 1122b21acc1cSCharles Keepax } 1123b21acc1cSCharles Keepax 1124f4f0c4c6SRichard Fitzgerald static void wm_adsp_signal_event_controls(struct wm_adsp *dsp, 1125f4f0c4c6SRichard Fitzgerald unsigned int event) 1126f4f0c4c6SRichard Fitzgerald { 1127f4f0c4c6SRichard Fitzgerald struct wm_coeff_ctl *ctl; 1128f4f0c4c6SRichard Fitzgerald int ret; 1129f4f0c4c6SRichard Fitzgerald 1130f4f0c4c6SRichard Fitzgerald list_for_each_entry(ctl, &dsp->ctl_list, list) { 1131f4f0c4c6SRichard Fitzgerald if (ctl->type != WMFW_CTL_TYPE_HOSTEVENT) 1132f4f0c4c6SRichard Fitzgerald continue; 1133f4f0c4c6SRichard Fitzgerald 1134f4f0c4c6SRichard Fitzgerald ret = wm_coeff_write_acked_control(ctl, event); 1135f4f0c4c6SRichard Fitzgerald if (ret) 1136f4f0c4c6SRichard Fitzgerald adsp_warn(dsp, 1137f4f0c4c6SRichard Fitzgerald "Failed to send 0x%x event to alg 0x%x (%d)\n", 1138f4f0c4c6SRichard Fitzgerald event, ctl->alg_region.alg, ret); 1139f4f0c4c6SRichard Fitzgerald } 1140f4f0c4c6SRichard Fitzgerald } 1141f4f0c4c6SRichard Fitzgerald 1142b21acc1cSCharles Keepax static void wm_adsp_ctl_work(struct work_struct *work) 1143b21acc1cSCharles Keepax { 1144b21acc1cSCharles Keepax struct wmfw_ctl_work *ctl_work = container_of(work, 1145b21acc1cSCharles Keepax struct wmfw_ctl_work, 1146b21acc1cSCharles Keepax work); 1147b21acc1cSCharles Keepax 1148b21acc1cSCharles Keepax wmfw_add_ctl(ctl_work->dsp, ctl_work->ctl); 1149b21acc1cSCharles Keepax kfree(ctl_work); 1150b21acc1cSCharles Keepax } 1151b21acc1cSCharles Keepax 115266225e98SRichard Fitzgerald static void wm_adsp_free_ctl_blk(struct wm_coeff_ctl *ctl) 115366225e98SRichard Fitzgerald { 115466225e98SRichard Fitzgerald kfree(ctl->cache); 115566225e98SRichard Fitzgerald kfree(ctl->name); 115666225e98SRichard Fitzgerald kfree(ctl); 115766225e98SRichard Fitzgerald } 115866225e98SRichard Fitzgerald 1159b21acc1cSCharles Keepax static int wm_adsp_create_control(struct wm_adsp *dsp, 1160b21acc1cSCharles Keepax const struct wm_adsp_alg_region *alg_region, 11612323736dSCharles Keepax unsigned int offset, unsigned int len, 116226c22a19SCharles Keepax const char *subname, unsigned int subname_len, 11638eb084d0SStuart Henderson unsigned int flags, unsigned int type) 1164b21acc1cSCharles Keepax { 1165b21acc1cSCharles Keepax struct wm_coeff_ctl *ctl; 1166b21acc1cSCharles Keepax struct wmfw_ctl_work *ctl_work; 1167b21acc1cSCharles Keepax char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; 11689ce5e6e6SRichard Fitzgerald const char *region_name; 1169b21acc1cSCharles Keepax int ret; 1170b21acc1cSCharles Keepax 11719ce5e6e6SRichard Fitzgerald region_name = wm_adsp_mem_region_name(alg_region->type); 11729ce5e6e6SRichard Fitzgerald if (!region_name) { 11732323736dSCharles Keepax adsp_err(dsp, "Unknown region type: %d\n", alg_region->type); 1174b21acc1cSCharles Keepax return -EINVAL; 1175b21acc1cSCharles Keepax } 1176b21acc1cSCharles Keepax 1177cb5b57a9SCharles Keepax switch (dsp->fw_ver) { 1178cb5b57a9SCharles Keepax case 0: 1179cb5b57a9SCharles Keepax case 1: 1180b21acc1cSCharles Keepax snprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "DSP%d %s %x", 1181b21acc1cSCharles Keepax dsp->num, region_name, alg_region->alg); 1182cb5b57a9SCharles Keepax break; 1183cb5b57a9SCharles Keepax default: 1184cb5b57a9SCharles Keepax ret = snprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, 1185cb5b57a9SCharles Keepax "DSP%d%c %.12s %x", dsp->num, *region_name, 1186cb5b57a9SCharles Keepax wm_adsp_fw_text[dsp->fw], alg_region->alg); 1187cb5b57a9SCharles Keepax 1188cb5b57a9SCharles Keepax /* Truncate the subname from the start if it is too long */ 1189cb5b57a9SCharles Keepax if (subname) { 1190cb5b57a9SCharles Keepax int avail = SNDRV_CTL_ELEM_ID_NAME_MAXLEN - ret - 2; 1191cb5b57a9SCharles Keepax int skip = 0; 1192cb5b57a9SCharles Keepax 1193cb5b57a9SCharles Keepax if (subname_len > avail) 1194cb5b57a9SCharles Keepax skip = subname_len - avail; 1195cb5b57a9SCharles Keepax 1196cb5b57a9SCharles Keepax snprintf(name + ret, 1197cb5b57a9SCharles Keepax SNDRV_CTL_ELEM_ID_NAME_MAXLEN - ret, " %.*s", 1198cb5b57a9SCharles Keepax subname_len - skip, subname + skip); 1199cb5b57a9SCharles Keepax } 1200cb5b57a9SCharles Keepax break; 1201cb5b57a9SCharles Keepax } 1202b21acc1cSCharles Keepax 12037585a5b0SCharles Keepax list_for_each_entry(ctl, &dsp->ctl_list, list) { 1204b21acc1cSCharles Keepax if (!strcmp(ctl->name, name)) { 1205b21acc1cSCharles Keepax if (!ctl->enabled) 1206b21acc1cSCharles Keepax ctl->enabled = 1; 1207b21acc1cSCharles Keepax return 0; 1208b21acc1cSCharles Keepax } 1209b21acc1cSCharles Keepax } 1210b21acc1cSCharles Keepax 1211b21acc1cSCharles Keepax ctl = kzalloc(sizeof(*ctl), GFP_KERNEL); 1212b21acc1cSCharles Keepax if (!ctl) 1213b21acc1cSCharles Keepax return -ENOMEM; 12142323736dSCharles Keepax ctl->fw_name = wm_adsp_fw_text[dsp->fw]; 1215b21acc1cSCharles Keepax ctl->alg_region = *alg_region; 1216b21acc1cSCharles Keepax ctl->name = kmemdup(name, strlen(name) + 1, GFP_KERNEL); 1217b21acc1cSCharles Keepax if (!ctl->name) { 1218b21acc1cSCharles Keepax ret = -ENOMEM; 1219b21acc1cSCharles Keepax goto err_ctl; 1220b21acc1cSCharles Keepax } 1221b21acc1cSCharles Keepax ctl->enabled = 1; 1222b21acc1cSCharles Keepax ctl->set = 0; 1223b21acc1cSCharles Keepax ctl->ops.xget = wm_coeff_get; 1224b21acc1cSCharles Keepax ctl->ops.xput = wm_coeff_put; 1225b21acc1cSCharles Keepax ctl->dsp = dsp; 1226b21acc1cSCharles Keepax 122726c22a19SCharles Keepax ctl->flags = flags; 12288eb084d0SStuart Henderson ctl->type = type; 12292323736dSCharles Keepax ctl->offset = offset; 1230b21acc1cSCharles Keepax ctl->len = len; 1231b21acc1cSCharles Keepax ctl->cache = kzalloc(ctl->len, GFP_KERNEL); 1232b21acc1cSCharles Keepax if (!ctl->cache) { 1233b21acc1cSCharles Keepax ret = -ENOMEM; 1234b21acc1cSCharles Keepax goto err_ctl_name; 1235b21acc1cSCharles Keepax } 1236b21acc1cSCharles Keepax 12372323736dSCharles Keepax list_add(&ctl->list, &dsp->ctl_list); 12382323736dSCharles Keepax 12398eb084d0SStuart Henderson if (flags & WMFW_CTL_FLAG_SYS) 12408eb084d0SStuart Henderson return 0; 12418eb084d0SStuart Henderson 1242b21acc1cSCharles Keepax ctl_work = kzalloc(sizeof(*ctl_work), GFP_KERNEL); 1243b21acc1cSCharles Keepax if (!ctl_work) { 1244b21acc1cSCharles Keepax ret = -ENOMEM; 1245b21acc1cSCharles Keepax goto err_ctl_cache; 1246b21acc1cSCharles Keepax } 1247b21acc1cSCharles Keepax 1248b21acc1cSCharles Keepax ctl_work->dsp = dsp; 1249b21acc1cSCharles Keepax ctl_work->ctl = ctl; 1250b21acc1cSCharles Keepax INIT_WORK(&ctl_work->work, wm_adsp_ctl_work); 1251b21acc1cSCharles Keepax schedule_work(&ctl_work->work); 1252b21acc1cSCharles Keepax 1253b21acc1cSCharles Keepax return 0; 1254b21acc1cSCharles Keepax 1255b21acc1cSCharles Keepax err_ctl_cache: 1256b21acc1cSCharles Keepax kfree(ctl->cache); 1257b21acc1cSCharles Keepax err_ctl_name: 1258b21acc1cSCharles Keepax kfree(ctl->name); 1259b21acc1cSCharles Keepax err_ctl: 1260b21acc1cSCharles Keepax kfree(ctl); 1261b21acc1cSCharles Keepax 1262b21acc1cSCharles Keepax return ret; 1263b21acc1cSCharles Keepax } 1264b21acc1cSCharles Keepax 12652323736dSCharles Keepax struct wm_coeff_parsed_alg { 12662323736dSCharles Keepax int id; 12672323736dSCharles Keepax const u8 *name; 12682323736dSCharles Keepax int name_len; 12692323736dSCharles Keepax int ncoeff; 12702323736dSCharles Keepax }; 12712323736dSCharles Keepax 12722323736dSCharles Keepax struct wm_coeff_parsed_coeff { 12732323736dSCharles Keepax int offset; 12742323736dSCharles Keepax int mem_type; 12752323736dSCharles Keepax const u8 *name; 12762323736dSCharles Keepax int name_len; 12772323736dSCharles Keepax int ctl_type; 12782323736dSCharles Keepax int flags; 12792323736dSCharles Keepax int len; 12802323736dSCharles Keepax }; 12812323736dSCharles Keepax 1282cb5b57a9SCharles Keepax static int wm_coeff_parse_string(int bytes, const u8 **pos, const u8 **str) 1283cb5b57a9SCharles Keepax { 1284cb5b57a9SCharles Keepax int length; 1285cb5b57a9SCharles Keepax 1286cb5b57a9SCharles Keepax switch (bytes) { 1287cb5b57a9SCharles Keepax case 1: 1288cb5b57a9SCharles Keepax length = **pos; 1289cb5b57a9SCharles Keepax break; 1290cb5b57a9SCharles Keepax case 2: 12918299ee81SCharles Keepax length = le16_to_cpu(*((__le16 *)*pos)); 1292cb5b57a9SCharles Keepax break; 1293cb5b57a9SCharles Keepax default: 1294cb5b57a9SCharles Keepax return 0; 1295cb5b57a9SCharles Keepax } 1296cb5b57a9SCharles Keepax 1297cb5b57a9SCharles Keepax if (str) 1298cb5b57a9SCharles Keepax *str = *pos + bytes; 1299cb5b57a9SCharles Keepax 1300cb5b57a9SCharles Keepax *pos += ((length + bytes) + 3) & ~0x03; 1301cb5b57a9SCharles Keepax 1302cb5b57a9SCharles Keepax return length; 1303cb5b57a9SCharles Keepax } 1304cb5b57a9SCharles Keepax 1305cb5b57a9SCharles Keepax static int wm_coeff_parse_int(int bytes, const u8 **pos) 1306cb5b57a9SCharles Keepax { 1307cb5b57a9SCharles Keepax int val = 0; 1308cb5b57a9SCharles Keepax 1309cb5b57a9SCharles Keepax switch (bytes) { 1310cb5b57a9SCharles Keepax case 2: 13118299ee81SCharles Keepax val = le16_to_cpu(*((__le16 *)*pos)); 1312cb5b57a9SCharles Keepax break; 1313cb5b57a9SCharles Keepax case 4: 13148299ee81SCharles Keepax val = le32_to_cpu(*((__le32 *)*pos)); 1315cb5b57a9SCharles Keepax break; 1316cb5b57a9SCharles Keepax default: 1317cb5b57a9SCharles Keepax break; 1318cb5b57a9SCharles Keepax } 1319cb5b57a9SCharles Keepax 1320cb5b57a9SCharles Keepax *pos += bytes; 1321cb5b57a9SCharles Keepax 1322cb5b57a9SCharles Keepax return val; 1323cb5b57a9SCharles Keepax } 1324cb5b57a9SCharles Keepax 13252323736dSCharles Keepax static inline void wm_coeff_parse_alg(struct wm_adsp *dsp, const u8 **data, 13262323736dSCharles Keepax struct wm_coeff_parsed_alg *blk) 13272323736dSCharles Keepax { 13282323736dSCharles Keepax const struct wmfw_adsp_alg_data *raw; 13292323736dSCharles Keepax 1330cb5b57a9SCharles Keepax switch (dsp->fw_ver) { 1331cb5b57a9SCharles Keepax case 0: 1332cb5b57a9SCharles Keepax case 1: 13332323736dSCharles Keepax raw = (const struct wmfw_adsp_alg_data *)*data; 13342323736dSCharles Keepax *data = raw->data; 13352323736dSCharles Keepax 13362323736dSCharles Keepax blk->id = le32_to_cpu(raw->id); 13372323736dSCharles Keepax blk->name = raw->name; 13382323736dSCharles Keepax blk->name_len = strlen(raw->name); 13392323736dSCharles Keepax blk->ncoeff = le32_to_cpu(raw->ncoeff); 1340cb5b57a9SCharles Keepax break; 1341cb5b57a9SCharles Keepax default: 1342cb5b57a9SCharles Keepax blk->id = wm_coeff_parse_int(sizeof(raw->id), data); 1343cb5b57a9SCharles Keepax blk->name_len = wm_coeff_parse_string(sizeof(u8), data, 1344cb5b57a9SCharles Keepax &blk->name); 1345cb5b57a9SCharles Keepax wm_coeff_parse_string(sizeof(u16), data, NULL); 1346cb5b57a9SCharles Keepax blk->ncoeff = wm_coeff_parse_int(sizeof(raw->ncoeff), data); 1347cb5b57a9SCharles Keepax break; 1348cb5b57a9SCharles Keepax } 13492323736dSCharles Keepax 13502323736dSCharles Keepax adsp_dbg(dsp, "Algorithm ID: %#x\n", blk->id); 13512323736dSCharles Keepax adsp_dbg(dsp, "Algorithm name: %.*s\n", blk->name_len, blk->name); 13522323736dSCharles Keepax adsp_dbg(dsp, "# of coefficient descriptors: %#x\n", blk->ncoeff); 13532323736dSCharles Keepax } 13542323736dSCharles Keepax 13552323736dSCharles Keepax static inline void wm_coeff_parse_coeff(struct wm_adsp *dsp, const u8 **data, 13562323736dSCharles Keepax struct wm_coeff_parsed_coeff *blk) 13572323736dSCharles Keepax { 13582323736dSCharles Keepax const struct wmfw_adsp_coeff_data *raw; 1359cb5b57a9SCharles Keepax const u8 *tmp; 1360cb5b57a9SCharles Keepax int length; 13612323736dSCharles Keepax 1362cb5b57a9SCharles Keepax switch (dsp->fw_ver) { 1363cb5b57a9SCharles Keepax case 0: 1364cb5b57a9SCharles Keepax case 1: 13652323736dSCharles Keepax raw = (const struct wmfw_adsp_coeff_data *)*data; 13662323736dSCharles Keepax *data = *data + sizeof(raw->hdr) + le32_to_cpu(raw->hdr.size); 13672323736dSCharles Keepax 13682323736dSCharles Keepax blk->offset = le16_to_cpu(raw->hdr.offset); 13692323736dSCharles Keepax blk->mem_type = le16_to_cpu(raw->hdr.type); 13702323736dSCharles Keepax blk->name = raw->name; 13712323736dSCharles Keepax blk->name_len = strlen(raw->name); 13722323736dSCharles Keepax blk->ctl_type = le16_to_cpu(raw->ctl_type); 13732323736dSCharles Keepax blk->flags = le16_to_cpu(raw->flags); 13742323736dSCharles Keepax blk->len = le32_to_cpu(raw->len); 1375cb5b57a9SCharles Keepax break; 1376cb5b57a9SCharles Keepax default: 1377cb5b57a9SCharles Keepax tmp = *data; 1378cb5b57a9SCharles Keepax blk->offset = wm_coeff_parse_int(sizeof(raw->hdr.offset), &tmp); 1379cb5b57a9SCharles Keepax blk->mem_type = wm_coeff_parse_int(sizeof(raw->hdr.type), &tmp); 1380cb5b57a9SCharles Keepax length = wm_coeff_parse_int(sizeof(raw->hdr.size), &tmp); 1381cb5b57a9SCharles Keepax blk->name_len = wm_coeff_parse_string(sizeof(u8), &tmp, 1382cb5b57a9SCharles Keepax &blk->name); 1383cb5b57a9SCharles Keepax wm_coeff_parse_string(sizeof(u8), &tmp, NULL); 1384cb5b57a9SCharles Keepax wm_coeff_parse_string(sizeof(u16), &tmp, NULL); 1385cb5b57a9SCharles Keepax blk->ctl_type = wm_coeff_parse_int(sizeof(raw->ctl_type), &tmp); 1386cb5b57a9SCharles Keepax blk->flags = wm_coeff_parse_int(sizeof(raw->flags), &tmp); 1387cb5b57a9SCharles Keepax blk->len = wm_coeff_parse_int(sizeof(raw->len), &tmp); 1388cb5b57a9SCharles Keepax 1389cb5b57a9SCharles Keepax *data = *data + sizeof(raw->hdr) + length; 1390cb5b57a9SCharles Keepax break; 1391cb5b57a9SCharles Keepax } 13922323736dSCharles Keepax 13932323736dSCharles Keepax adsp_dbg(dsp, "\tCoefficient type: %#x\n", blk->mem_type); 13942323736dSCharles Keepax adsp_dbg(dsp, "\tCoefficient offset: %#x\n", blk->offset); 13952323736dSCharles Keepax adsp_dbg(dsp, "\tCoefficient name: %.*s\n", blk->name_len, blk->name); 13962323736dSCharles Keepax adsp_dbg(dsp, "\tCoefficient flags: %#x\n", blk->flags); 13972323736dSCharles Keepax adsp_dbg(dsp, "\tALSA control type: %#x\n", blk->ctl_type); 13982323736dSCharles Keepax adsp_dbg(dsp, "\tALSA control len: %#x\n", blk->len); 13992323736dSCharles Keepax } 14002323736dSCharles Keepax 1401f4f0c4c6SRichard Fitzgerald static int wm_adsp_check_coeff_flags(struct wm_adsp *dsp, 1402f4f0c4c6SRichard Fitzgerald const struct wm_coeff_parsed_coeff *coeff_blk, 1403f4f0c4c6SRichard Fitzgerald unsigned int f_required, 1404f4f0c4c6SRichard Fitzgerald unsigned int f_illegal) 1405f4f0c4c6SRichard Fitzgerald { 1406f4f0c4c6SRichard Fitzgerald if ((coeff_blk->flags & f_illegal) || 1407f4f0c4c6SRichard Fitzgerald ((coeff_blk->flags & f_required) != f_required)) { 1408f4f0c4c6SRichard Fitzgerald adsp_err(dsp, "Illegal flags 0x%x for control type 0x%x\n", 1409f4f0c4c6SRichard Fitzgerald coeff_blk->flags, coeff_blk->ctl_type); 1410f4f0c4c6SRichard Fitzgerald return -EINVAL; 1411f4f0c4c6SRichard Fitzgerald } 1412f4f0c4c6SRichard Fitzgerald 1413f4f0c4c6SRichard Fitzgerald return 0; 1414f4f0c4c6SRichard Fitzgerald } 1415f4f0c4c6SRichard Fitzgerald 14162323736dSCharles Keepax static int wm_adsp_parse_coeff(struct wm_adsp *dsp, 14172323736dSCharles Keepax const struct wmfw_region *region) 14182323736dSCharles Keepax { 14192323736dSCharles Keepax struct wm_adsp_alg_region alg_region = {}; 14202323736dSCharles Keepax struct wm_coeff_parsed_alg alg_blk; 14212323736dSCharles Keepax struct wm_coeff_parsed_coeff coeff_blk; 14222323736dSCharles Keepax const u8 *data = region->data; 14232323736dSCharles Keepax int i, ret; 14242323736dSCharles Keepax 14252323736dSCharles Keepax wm_coeff_parse_alg(dsp, &data, &alg_blk); 14262323736dSCharles Keepax for (i = 0; i < alg_blk.ncoeff; i++) { 14272323736dSCharles Keepax wm_coeff_parse_coeff(dsp, &data, &coeff_blk); 14282323736dSCharles Keepax 14292323736dSCharles Keepax switch (coeff_blk.ctl_type) { 14302323736dSCharles Keepax case SNDRV_CTL_ELEM_TYPE_BYTES: 14312323736dSCharles Keepax break; 1432f4f0c4c6SRichard Fitzgerald case WMFW_CTL_TYPE_HOSTEVENT: 1433f4f0c4c6SRichard Fitzgerald ret = wm_adsp_check_coeff_flags(dsp, &coeff_blk, 1434f4f0c4c6SRichard Fitzgerald WMFW_CTL_FLAG_SYS | 1435f4f0c4c6SRichard Fitzgerald WMFW_CTL_FLAG_VOLATILE | 1436f4f0c4c6SRichard Fitzgerald WMFW_CTL_FLAG_WRITEABLE | 1437f4f0c4c6SRichard Fitzgerald WMFW_CTL_FLAG_READABLE, 1438f4f0c4c6SRichard Fitzgerald 0); 1439f4f0c4c6SRichard Fitzgerald if (ret) 1440f4f0c4c6SRichard Fitzgerald return -EINVAL; 1441f4f0c4c6SRichard Fitzgerald break; 14422323736dSCharles Keepax default: 14432323736dSCharles Keepax adsp_err(dsp, "Unknown control type: %d\n", 14442323736dSCharles Keepax coeff_blk.ctl_type); 14452323736dSCharles Keepax return -EINVAL; 14462323736dSCharles Keepax } 14472323736dSCharles Keepax 14482323736dSCharles Keepax alg_region.type = coeff_blk.mem_type; 14492323736dSCharles Keepax alg_region.alg = alg_blk.id; 14502323736dSCharles Keepax 14512323736dSCharles Keepax ret = wm_adsp_create_control(dsp, &alg_region, 14522323736dSCharles Keepax coeff_blk.offset, 14532323736dSCharles Keepax coeff_blk.len, 14542323736dSCharles Keepax coeff_blk.name, 145526c22a19SCharles Keepax coeff_blk.name_len, 14568eb084d0SStuart Henderson coeff_blk.flags, 14578eb084d0SStuart Henderson coeff_blk.ctl_type); 14582323736dSCharles Keepax if (ret < 0) 14592323736dSCharles Keepax adsp_err(dsp, "Failed to create control: %.*s, %d\n", 14602323736dSCharles Keepax coeff_blk.name_len, coeff_blk.name, ret); 14612323736dSCharles Keepax } 14622323736dSCharles Keepax 14632323736dSCharles Keepax return 0; 14642323736dSCharles Keepax } 14652323736dSCharles Keepax 14662159ad93SMark Brown static int wm_adsp_load(struct wm_adsp *dsp) 14672159ad93SMark Brown { 1468cf17c83cSMark Brown LIST_HEAD(buf_list); 14692159ad93SMark Brown const struct firmware *firmware; 14702159ad93SMark Brown struct regmap *regmap = dsp->regmap; 14712159ad93SMark Brown unsigned int pos = 0; 14722159ad93SMark Brown const struct wmfw_header *header; 14732159ad93SMark Brown const struct wmfw_adsp1_sizes *adsp1_sizes; 14742159ad93SMark Brown const struct wmfw_adsp2_sizes *adsp2_sizes; 14752159ad93SMark Brown const struct wmfw_footer *footer; 14762159ad93SMark Brown const struct wmfw_region *region; 14772159ad93SMark Brown const struct wm_adsp_region *mem; 14782159ad93SMark Brown const char *region_name; 14792159ad93SMark Brown char *file, *text; 1480cf17c83cSMark Brown struct wm_adsp_buf *buf; 14812159ad93SMark Brown unsigned int reg; 14822159ad93SMark Brown int regions = 0; 14832159ad93SMark Brown int ret, offset, type, sizes; 14842159ad93SMark Brown 14852159ad93SMark Brown file = kzalloc(PAGE_SIZE, GFP_KERNEL); 14862159ad93SMark Brown if (file == NULL) 14872159ad93SMark Brown return -ENOMEM; 14882159ad93SMark Brown 14891023dbd9SMark Brown snprintf(file, PAGE_SIZE, "%s-dsp%d-%s.wmfw", dsp->part, dsp->num, 14901023dbd9SMark Brown wm_adsp_fw[dsp->fw].file); 14912159ad93SMark Brown file[PAGE_SIZE - 1] = '\0'; 14922159ad93SMark Brown 14932159ad93SMark Brown ret = request_firmware(&firmware, file, dsp->dev); 14942159ad93SMark Brown if (ret != 0) { 14952159ad93SMark Brown adsp_err(dsp, "Failed to request '%s'\n", file); 14962159ad93SMark Brown goto out; 14972159ad93SMark Brown } 14982159ad93SMark Brown ret = -EINVAL; 14992159ad93SMark Brown 15002159ad93SMark Brown pos = sizeof(*header) + sizeof(*adsp1_sizes) + sizeof(*footer); 15012159ad93SMark Brown if (pos >= firmware->size) { 15022159ad93SMark Brown adsp_err(dsp, "%s: file too short, %zu bytes\n", 15032159ad93SMark Brown file, firmware->size); 15042159ad93SMark Brown goto out_fw; 15052159ad93SMark Brown } 15062159ad93SMark Brown 15072159ad93SMark Brown header = (void *)&firmware->data[0]; 15082159ad93SMark Brown 15092159ad93SMark Brown if (memcmp(&header->magic[0], "WMFW", 4) != 0) { 15102159ad93SMark Brown adsp_err(dsp, "%s: invalid magic\n", file); 15112159ad93SMark Brown goto out_fw; 15122159ad93SMark Brown } 15132159ad93SMark Brown 15142323736dSCharles Keepax switch (header->ver) { 15152323736dSCharles Keepax case 0: 1516c61e59feSCharles Keepax adsp_warn(dsp, "%s: Depreciated file format %d\n", 1517c61e59feSCharles Keepax file, header->ver); 1518c61e59feSCharles Keepax break; 15192323736dSCharles Keepax case 1: 1520cb5b57a9SCharles Keepax case 2: 15212323736dSCharles Keepax break; 15222323736dSCharles Keepax default: 15232159ad93SMark Brown adsp_err(dsp, "%s: unknown file format %d\n", 15242159ad93SMark Brown file, header->ver); 15252159ad93SMark Brown goto out_fw; 15262159ad93SMark Brown } 15272323736dSCharles Keepax 15283626992aSDimitris Papastamos adsp_info(dsp, "Firmware version: %d\n", header->ver); 15292323736dSCharles Keepax dsp->fw_ver = header->ver; 15302159ad93SMark Brown 15312159ad93SMark Brown if (header->core != dsp->type) { 15322159ad93SMark Brown adsp_err(dsp, "%s: invalid core %d != %d\n", 15332159ad93SMark Brown file, header->core, dsp->type); 15342159ad93SMark Brown goto out_fw; 15352159ad93SMark Brown } 15362159ad93SMark Brown 15372159ad93SMark Brown switch (dsp->type) { 15382159ad93SMark Brown case WMFW_ADSP1: 15392159ad93SMark Brown pos = sizeof(*header) + sizeof(*adsp1_sizes) + sizeof(*footer); 15402159ad93SMark Brown adsp1_sizes = (void *)&(header[1]); 15412159ad93SMark Brown footer = (void *)&(adsp1_sizes[1]); 15422159ad93SMark Brown sizes = sizeof(*adsp1_sizes); 15432159ad93SMark Brown 15442159ad93SMark Brown adsp_dbg(dsp, "%s: %d DM, %d PM, %d ZM\n", 15452159ad93SMark Brown file, le32_to_cpu(adsp1_sizes->dm), 15462159ad93SMark Brown le32_to_cpu(adsp1_sizes->pm), 15472159ad93SMark Brown le32_to_cpu(adsp1_sizes->zm)); 15482159ad93SMark Brown break; 15492159ad93SMark Brown 15502159ad93SMark Brown case WMFW_ADSP2: 15512159ad93SMark Brown pos = sizeof(*header) + sizeof(*adsp2_sizes) + sizeof(*footer); 15522159ad93SMark Brown adsp2_sizes = (void *)&(header[1]); 15532159ad93SMark Brown footer = (void *)&(adsp2_sizes[1]); 15542159ad93SMark Brown sizes = sizeof(*adsp2_sizes); 15552159ad93SMark Brown 15562159ad93SMark Brown adsp_dbg(dsp, "%s: %d XM, %d YM %d PM, %d ZM\n", 15572159ad93SMark Brown file, le32_to_cpu(adsp2_sizes->xm), 15582159ad93SMark Brown le32_to_cpu(adsp2_sizes->ym), 15592159ad93SMark Brown le32_to_cpu(adsp2_sizes->pm), 15602159ad93SMark Brown le32_to_cpu(adsp2_sizes->zm)); 15612159ad93SMark Brown break; 15622159ad93SMark Brown 15632159ad93SMark Brown default: 15646c452bdaSTakashi Iwai WARN(1, "Unknown DSP type"); 15652159ad93SMark Brown goto out_fw; 15662159ad93SMark Brown } 15672159ad93SMark Brown 15682159ad93SMark Brown if (le32_to_cpu(header->len) != sizeof(*header) + 15692159ad93SMark Brown sizes + sizeof(*footer)) { 15702159ad93SMark Brown adsp_err(dsp, "%s: unexpected header length %d\n", 15712159ad93SMark Brown file, le32_to_cpu(header->len)); 15722159ad93SMark Brown goto out_fw; 15732159ad93SMark Brown } 15742159ad93SMark Brown 15752159ad93SMark Brown adsp_dbg(dsp, "%s: timestamp %llu\n", file, 15762159ad93SMark Brown le64_to_cpu(footer->timestamp)); 15772159ad93SMark Brown 15782159ad93SMark Brown while (pos < firmware->size && 15792159ad93SMark Brown pos - firmware->size > sizeof(*region)) { 15802159ad93SMark Brown region = (void *)&(firmware->data[pos]); 15812159ad93SMark Brown region_name = "Unknown"; 15822159ad93SMark Brown reg = 0; 15832159ad93SMark Brown text = NULL; 15842159ad93SMark Brown offset = le32_to_cpu(region->offset) & 0xffffff; 15852159ad93SMark Brown type = be32_to_cpu(region->type) & 0xff; 15862159ad93SMark Brown mem = wm_adsp_find_region(dsp, type); 15872159ad93SMark Brown 15882159ad93SMark Brown switch (type) { 15892159ad93SMark Brown case WMFW_NAME_TEXT: 15902159ad93SMark Brown region_name = "Firmware name"; 15912159ad93SMark Brown text = kzalloc(le32_to_cpu(region->len) + 1, 15922159ad93SMark Brown GFP_KERNEL); 15932159ad93SMark Brown break; 15942323736dSCharles Keepax case WMFW_ALGORITHM_DATA: 15952323736dSCharles Keepax region_name = "Algorithm"; 15962323736dSCharles Keepax ret = wm_adsp_parse_coeff(dsp, region); 15972323736dSCharles Keepax if (ret != 0) 15982323736dSCharles Keepax goto out_fw; 15992323736dSCharles Keepax break; 16002159ad93SMark Brown case WMFW_INFO_TEXT: 16012159ad93SMark Brown region_name = "Information"; 16022159ad93SMark Brown text = kzalloc(le32_to_cpu(region->len) + 1, 16032159ad93SMark Brown GFP_KERNEL); 16042159ad93SMark Brown break; 16052159ad93SMark Brown case WMFW_ABSOLUTE: 16062159ad93SMark Brown region_name = "Absolute"; 16072159ad93SMark Brown reg = offset; 16082159ad93SMark Brown break; 16092159ad93SMark Brown case WMFW_ADSP1_PM: 16102159ad93SMark Brown case WMFW_ADSP1_DM: 16112159ad93SMark Brown case WMFW_ADSP2_XM: 16122159ad93SMark Brown case WMFW_ADSP2_YM: 16132159ad93SMark Brown case WMFW_ADSP1_ZM: 16149ce5e6e6SRichard Fitzgerald region_name = wm_adsp_mem_region_name(type); 161545b9ee72SMark Brown reg = wm_adsp_region_to_reg(mem, offset); 16162159ad93SMark Brown break; 16172159ad93SMark Brown default: 16182159ad93SMark Brown adsp_warn(dsp, 16192159ad93SMark Brown "%s.%d: Unknown region type %x at %d(%x)\n", 16202159ad93SMark Brown file, regions, type, pos, pos); 16212159ad93SMark Brown break; 16222159ad93SMark Brown } 16232159ad93SMark Brown 16242159ad93SMark Brown adsp_dbg(dsp, "%s.%d: %d bytes at %d in %s\n", file, 16252159ad93SMark Brown regions, le32_to_cpu(region->len), offset, 16262159ad93SMark Brown region_name); 16272159ad93SMark Brown 16282159ad93SMark Brown if (text) { 16292159ad93SMark Brown memcpy(text, region->data, le32_to_cpu(region->len)); 16302159ad93SMark Brown adsp_info(dsp, "%s: %s\n", file, text); 16312159ad93SMark Brown kfree(text); 16322159ad93SMark Brown } 16332159ad93SMark Brown 16342159ad93SMark Brown if (reg) { 1635cdcd7f72SCharles Keepax buf = wm_adsp_buf_alloc(region->data, 1636cdcd7f72SCharles Keepax le32_to_cpu(region->len), 1637cf17c83cSMark Brown &buf_list); 1638a76fefabSMark Brown if (!buf) { 1639a76fefabSMark Brown adsp_err(dsp, "Out of memory\n"); 16407328823dSDimitris Papastamos ret = -ENOMEM; 16417328823dSDimitris Papastamos goto out_fw; 1642a76fefabSMark Brown } 1643a76fefabSMark Brown 1644cdcd7f72SCharles Keepax ret = regmap_raw_write_async(regmap, reg, buf->buf, 1645cdcd7f72SCharles Keepax le32_to_cpu(region->len)); 16462159ad93SMark Brown if (ret != 0) { 16472159ad93SMark Brown adsp_err(dsp, 1648cdcd7f72SCharles Keepax "%s.%d: Failed to write %d bytes at %d in %s: %d\n", 16492159ad93SMark Brown file, regions, 1650cdcd7f72SCharles Keepax le32_to_cpu(region->len), offset, 16512159ad93SMark Brown region_name, ret); 16522159ad93SMark Brown goto out_fw; 16532159ad93SMark Brown } 16542159ad93SMark Brown } 16552159ad93SMark Brown 16562159ad93SMark Brown pos += le32_to_cpu(region->len) + sizeof(*region); 16572159ad93SMark Brown regions++; 16582159ad93SMark Brown } 16592159ad93SMark Brown 1660cf17c83cSMark Brown ret = regmap_async_complete(regmap); 1661cf17c83cSMark Brown if (ret != 0) { 1662cf17c83cSMark Brown adsp_err(dsp, "Failed to complete async write: %d\n", ret); 1663cf17c83cSMark Brown goto out_fw; 1664cf17c83cSMark Brown } 1665cf17c83cSMark Brown 16662159ad93SMark Brown if (pos > firmware->size) 16672159ad93SMark Brown adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n", 16682159ad93SMark Brown file, regions, pos - firmware->size); 16692159ad93SMark Brown 1670f9f55e31SRichard Fitzgerald wm_adsp_debugfs_save_wmfwname(dsp, file); 1671f9f55e31SRichard Fitzgerald 16722159ad93SMark Brown out_fw: 1673cf17c83cSMark Brown regmap_async_complete(regmap); 1674cf17c83cSMark Brown wm_adsp_buf_free(&buf_list); 16752159ad93SMark Brown release_firmware(firmware); 16762159ad93SMark Brown out: 16772159ad93SMark Brown kfree(file); 16782159ad93SMark Brown 16792159ad93SMark Brown return ret; 16802159ad93SMark Brown } 16812159ad93SMark Brown 16822323736dSCharles Keepax static void wm_adsp_ctl_fixup_base(struct wm_adsp *dsp, 16832323736dSCharles Keepax const struct wm_adsp_alg_region *alg_region) 16842323736dSCharles Keepax { 16852323736dSCharles Keepax struct wm_coeff_ctl *ctl; 16862323736dSCharles Keepax 16872323736dSCharles Keepax list_for_each_entry(ctl, &dsp->ctl_list, list) { 16882323736dSCharles Keepax if (ctl->fw_name == wm_adsp_fw_text[dsp->fw] && 16892323736dSCharles Keepax alg_region->alg == ctl->alg_region.alg && 16902323736dSCharles Keepax alg_region->type == ctl->alg_region.type) { 16912323736dSCharles Keepax ctl->alg_region.base = alg_region->base; 16922323736dSCharles Keepax } 16932323736dSCharles Keepax } 16942323736dSCharles Keepax } 16952323736dSCharles Keepax 16963809f001SCharles Keepax static void *wm_adsp_read_algs(struct wm_adsp *dsp, size_t n_algs, 1697b618a185SCharles Keepax unsigned int pos, unsigned int len) 1698db40517cSMark Brown { 1699b618a185SCharles Keepax void *alg; 1700b618a185SCharles Keepax int ret; 1701db40517cSMark Brown __be32 val; 1702db40517cSMark Brown 17033809f001SCharles Keepax if (n_algs == 0) { 1704b618a185SCharles Keepax adsp_err(dsp, "No algorithms\n"); 1705b618a185SCharles Keepax return ERR_PTR(-EINVAL); 1706db40517cSMark Brown } 1707db40517cSMark Brown 17083809f001SCharles Keepax if (n_algs > 1024) { 17093809f001SCharles Keepax adsp_err(dsp, "Algorithm count %zx excessive\n", n_algs); 1710b618a185SCharles Keepax return ERR_PTR(-EINVAL); 1711b618a185SCharles Keepax } 1712b618a185SCharles Keepax 1713b618a185SCharles Keepax /* Read the terminator first to validate the length */ 1714b618a185SCharles Keepax ret = regmap_raw_read(dsp->regmap, pos + len, &val, sizeof(val)); 1715b618a185SCharles Keepax if (ret != 0) { 1716b618a185SCharles Keepax adsp_err(dsp, "Failed to read algorithm list end: %d\n", 1717b618a185SCharles Keepax ret); 1718b618a185SCharles Keepax return ERR_PTR(ret); 1719b618a185SCharles Keepax } 1720b618a185SCharles Keepax 1721b618a185SCharles Keepax if (be32_to_cpu(val) != 0xbedead) 1722b618a185SCharles Keepax adsp_warn(dsp, "Algorithm list end %x 0x%x != 0xbeadead\n", 1723b618a185SCharles Keepax pos + len, be32_to_cpu(val)); 1724b618a185SCharles Keepax 1725b618a185SCharles Keepax alg = kzalloc(len * 2, GFP_KERNEL | GFP_DMA); 1726b618a185SCharles Keepax if (!alg) 1727b618a185SCharles Keepax return ERR_PTR(-ENOMEM); 1728b618a185SCharles Keepax 1729b618a185SCharles Keepax ret = regmap_raw_read(dsp->regmap, pos, alg, len * 2); 1730b618a185SCharles Keepax if (ret != 0) { 17317d00cd97SCharles Keepax adsp_err(dsp, "Failed to read algorithm list: %d\n", ret); 1732b618a185SCharles Keepax kfree(alg); 1733b618a185SCharles Keepax return ERR_PTR(ret); 1734b618a185SCharles Keepax } 1735b618a185SCharles Keepax 1736b618a185SCharles Keepax return alg; 1737b618a185SCharles Keepax } 1738b618a185SCharles Keepax 173914197095SCharles Keepax static struct wm_adsp_alg_region * 174014197095SCharles Keepax wm_adsp_find_alg_region(struct wm_adsp *dsp, int type, unsigned int id) 174114197095SCharles Keepax { 174214197095SCharles Keepax struct wm_adsp_alg_region *alg_region; 174314197095SCharles Keepax 174414197095SCharles Keepax list_for_each_entry(alg_region, &dsp->alg_regions, list) { 174514197095SCharles Keepax if (id == alg_region->alg && type == alg_region->type) 174614197095SCharles Keepax return alg_region; 174714197095SCharles Keepax } 174814197095SCharles Keepax 174914197095SCharles Keepax return NULL; 175014197095SCharles Keepax } 175114197095SCharles Keepax 1752d9d20e17SCharles Keepax static struct wm_adsp_alg_region *wm_adsp_create_region(struct wm_adsp *dsp, 1753d9d20e17SCharles Keepax int type, __be32 id, 1754d9d20e17SCharles Keepax __be32 base) 1755d9d20e17SCharles Keepax { 1756d9d20e17SCharles Keepax struct wm_adsp_alg_region *alg_region; 1757d9d20e17SCharles Keepax 1758d9d20e17SCharles Keepax alg_region = kzalloc(sizeof(*alg_region), GFP_KERNEL); 1759d9d20e17SCharles Keepax if (!alg_region) 1760d9d20e17SCharles Keepax return ERR_PTR(-ENOMEM); 1761d9d20e17SCharles Keepax 1762d9d20e17SCharles Keepax alg_region->type = type; 1763d9d20e17SCharles Keepax alg_region->alg = be32_to_cpu(id); 1764d9d20e17SCharles Keepax alg_region->base = be32_to_cpu(base); 1765d9d20e17SCharles Keepax 1766d9d20e17SCharles Keepax list_add_tail(&alg_region->list, &dsp->alg_regions); 1767d9d20e17SCharles Keepax 17682323736dSCharles Keepax if (dsp->fw_ver > 0) 17692323736dSCharles Keepax wm_adsp_ctl_fixup_base(dsp, alg_region); 17702323736dSCharles Keepax 1771d9d20e17SCharles Keepax return alg_region; 1772d9d20e17SCharles Keepax } 1773d9d20e17SCharles Keepax 177456574d54SRichard Fitzgerald static void wm_adsp_free_alg_regions(struct wm_adsp *dsp) 177556574d54SRichard Fitzgerald { 177656574d54SRichard Fitzgerald struct wm_adsp_alg_region *alg_region; 177756574d54SRichard Fitzgerald 177856574d54SRichard Fitzgerald while (!list_empty(&dsp->alg_regions)) { 177956574d54SRichard Fitzgerald alg_region = list_first_entry(&dsp->alg_regions, 178056574d54SRichard Fitzgerald struct wm_adsp_alg_region, 178156574d54SRichard Fitzgerald list); 178256574d54SRichard Fitzgerald list_del(&alg_region->list); 178356574d54SRichard Fitzgerald kfree(alg_region); 178456574d54SRichard Fitzgerald } 178556574d54SRichard Fitzgerald } 178656574d54SRichard Fitzgerald 1787b618a185SCharles Keepax static int wm_adsp1_setup_algs(struct wm_adsp *dsp) 1788b618a185SCharles Keepax { 1789b618a185SCharles Keepax struct wmfw_adsp1_id_hdr adsp1_id; 1790b618a185SCharles Keepax struct wmfw_adsp1_alg_hdr *adsp1_alg; 17913809f001SCharles Keepax struct wm_adsp_alg_region *alg_region; 1792b618a185SCharles Keepax const struct wm_adsp_region *mem; 1793b618a185SCharles Keepax unsigned int pos, len; 17943809f001SCharles Keepax size_t n_algs; 1795b618a185SCharles Keepax int i, ret; 1796b618a185SCharles Keepax 1797b618a185SCharles Keepax mem = wm_adsp_find_region(dsp, WMFW_ADSP1_DM); 17986c452bdaSTakashi Iwai if (WARN_ON(!mem)) 1799db40517cSMark Brown return -EINVAL; 1800db40517cSMark Brown 1801b618a185SCharles Keepax ret = regmap_raw_read(dsp->regmap, mem->base, &adsp1_id, 1802db40517cSMark Brown sizeof(adsp1_id)); 1803db40517cSMark Brown if (ret != 0) { 1804db40517cSMark Brown adsp_err(dsp, "Failed to read algorithm info: %d\n", 1805db40517cSMark Brown ret); 1806db40517cSMark Brown return ret; 1807db40517cSMark Brown } 1808db40517cSMark Brown 18093809f001SCharles Keepax n_algs = be32_to_cpu(adsp1_id.n_algs); 1810f395a218SMark Brown dsp->fw_id = be32_to_cpu(adsp1_id.fw.id); 1811db40517cSMark Brown adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n", 1812f395a218SMark Brown dsp->fw_id, 1813db40517cSMark Brown (be32_to_cpu(adsp1_id.fw.ver) & 0xff0000) >> 16, 1814db40517cSMark Brown (be32_to_cpu(adsp1_id.fw.ver) & 0xff00) >> 8, 1815db40517cSMark Brown be32_to_cpu(adsp1_id.fw.ver) & 0xff, 18163809f001SCharles Keepax n_algs); 1817db40517cSMark Brown 1818d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_ZM, 1819d9d20e17SCharles Keepax adsp1_id.fw.id, adsp1_id.zm); 1820d9d20e17SCharles Keepax if (IS_ERR(alg_region)) 1821d9d20e17SCharles Keepax return PTR_ERR(alg_region); 1822ac50009fSMark Brown 1823d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_DM, 1824d9d20e17SCharles Keepax adsp1_id.fw.id, adsp1_id.dm); 1825d9d20e17SCharles Keepax if (IS_ERR(alg_region)) 1826d9d20e17SCharles Keepax return PTR_ERR(alg_region); 1827ac50009fSMark Brown 1828db40517cSMark Brown pos = sizeof(adsp1_id) / 2; 18293809f001SCharles Keepax len = (sizeof(*adsp1_alg) * n_algs) / 2; 1830db40517cSMark Brown 18313809f001SCharles Keepax adsp1_alg = wm_adsp_read_algs(dsp, n_algs, mem->base + pos, len); 1832b618a185SCharles Keepax if (IS_ERR(adsp1_alg)) 1833b618a185SCharles Keepax return PTR_ERR(adsp1_alg); 1834db40517cSMark Brown 18353809f001SCharles Keepax for (i = 0; i < n_algs; i++) { 1836471f4885SMark Brown adsp_info(dsp, "%d: ID %x v%d.%d.%d DM@%x ZM@%x\n", 1837db40517cSMark Brown i, be32_to_cpu(adsp1_alg[i].alg.id), 1838db40517cSMark Brown (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff0000) >> 16, 1839db40517cSMark Brown (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff00) >> 8, 1840471f4885SMark Brown be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff, 1841471f4885SMark Brown be32_to_cpu(adsp1_alg[i].dm), 1842471f4885SMark Brown be32_to_cpu(adsp1_alg[i].zm)); 1843471f4885SMark Brown 1844d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_DM, 1845d9d20e17SCharles Keepax adsp1_alg[i].alg.id, 1846d9d20e17SCharles Keepax adsp1_alg[i].dm); 1847d9d20e17SCharles Keepax if (IS_ERR(alg_region)) { 1848d9d20e17SCharles Keepax ret = PTR_ERR(alg_region); 1849d6d52179SJS Park goto out; 1850d6d52179SJS Park } 18512323736dSCharles Keepax if (dsp->fw_ver == 0) { 18523809f001SCharles Keepax if (i + 1 < n_algs) { 18536958eb2aSCharles Keepax len = be32_to_cpu(adsp1_alg[i + 1].dm); 18546958eb2aSCharles Keepax len -= be32_to_cpu(adsp1_alg[i].dm); 18556958eb2aSCharles Keepax len *= 4; 18562323736dSCharles Keepax wm_adsp_create_control(dsp, alg_region, 0, 18578eb084d0SStuart Henderson len, NULL, 0, 0, 18588eb084d0SStuart Henderson SNDRV_CTL_ELEM_TYPE_BYTES); 18596ab2b7b4SDimitris Papastamos } else { 18606ab2b7b4SDimitris Papastamos adsp_warn(dsp, "Missing length info for region DM with ID %x\n", 18616ab2b7b4SDimitris Papastamos be32_to_cpu(adsp1_alg[i].alg.id)); 18626ab2b7b4SDimitris Papastamos } 18632323736dSCharles Keepax } 1864471f4885SMark Brown 1865d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_ZM, 1866d9d20e17SCharles Keepax adsp1_alg[i].alg.id, 1867d9d20e17SCharles Keepax adsp1_alg[i].zm); 1868d9d20e17SCharles Keepax if (IS_ERR(alg_region)) { 1869d9d20e17SCharles Keepax ret = PTR_ERR(alg_region); 1870d6d52179SJS Park goto out; 1871d6d52179SJS Park } 18722323736dSCharles Keepax if (dsp->fw_ver == 0) { 18733809f001SCharles Keepax if (i + 1 < n_algs) { 18746958eb2aSCharles Keepax len = be32_to_cpu(adsp1_alg[i + 1].zm); 18756958eb2aSCharles Keepax len -= be32_to_cpu(adsp1_alg[i].zm); 18766958eb2aSCharles Keepax len *= 4; 18772323736dSCharles Keepax wm_adsp_create_control(dsp, alg_region, 0, 18788eb084d0SStuart Henderson len, NULL, 0, 0, 18798eb084d0SStuart Henderson SNDRV_CTL_ELEM_TYPE_BYTES); 18806ab2b7b4SDimitris Papastamos } else { 18816ab2b7b4SDimitris Papastamos adsp_warn(dsp, "Missing length info for region ZM with ID %x\n", 18826ab2b7b4SDimitris Papastamos be32_to_cpu(adsp1_alg[i].alg.id)); 18836ab2b7b4SDimitris Papastamos } 1884b618a185SCharles Keepax } 18852323736dSCharles Keepax } 1886db40517cSMark Brown 1887b618a185SCharles Keepax out: 1888b618a185SCharles Keepax kfree(adsp1_alg); 1889b618a185SCharles Keepax return ret; 1890b618a185SCharles Keepax } 1891b618a185SCharles Keepax 1892b618a185SCharles Keepax static int wm_adsp2_setup_algs(struct wm_adsp *dsp) 1893b618a185SCharles Keepax { 1894b618a185SCharles Keepax struct wmfw_adsp2_id_hdr adsp2_id; 1895b618a185SCharles Keepax struct wmfw_adsp2_alg_hdr *adsp2_alg; 18963809f001SCharles Keepax struct wm_adsp_alg_region *alg_region; 1897b618a185SCharles Keepax const struct wm_adsp_region *mem; 1898b618a185SCharles Keepax unsigned int pos, len; 18993809f001SCharles Keepax size_t n_algs; 1900b618a185SCharles Keepax int i, ret; 1901b618a185SCharles Keepax 1902b618a185SCharles Keepax mem = wm_adsp_find_region(dsp, WMFW_ADSP2_XM); 1903b618a185SCharles Keepax if (WARN_ON(!mem)) 1904b618a185SCharles Keepax return -EINVAL; 1905b618a185SCharles Keepax 1906b618a185SCharles Keepax ret = regmap_raw_read(dsp->regmap, mem->base, &adsp2_id, 1907b618a185SCharles Keepax sizeof(adsp2_id)); 1908b618a185SCharles Keepax if (ret != 0) { 1909b618a185SCharles Keepax adsp_err(dsp, "Failed to read algorithm info: %d\n", 1910b618a185SCharles Keepax ret); 1911b618a185SCharles Keepax return ret; 1912b618a185SCharles Keepax } 1913b618a185SCharles Keepax 19143809f001SCharles Keepax n_algs = be32_to_cpu(adsp2_id.n_algs); 1915b618a185SCharles Keepax dsp->fw_id = be32_to_cpu(adsp2_id.fw.id); 1916f9f55e31SRichard Fitzgerald dsp->fw_id_version = be32_to_cpu(adsp2_id.fw.ver); 1917b618a185SCharles Keepax adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n", 1918b618a185SCharles Keepax dsp->fw_id, 1919f9f55e31SRichard Fitzgerald (dsp->fw_id_version & 0xff0000) >> 16, 1920f9f55e31SRichard Fitzgerald (dsp->fw_id_version & 0xff00) >> 8, 1921f9f55e31SRichard Fitzgerald dsp->fw_id_version & 0xff, 19223809f001SCharles Keepax n_algs); 1923b618a185SCharles Keepax 1924d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_XM, 1925d9d20e17SCharles Keepax adsp2_id.fw.id, adsp2_id.xm); 1926d9d20e17SCharles Keepax if (IS_ERR(alg_region)) 1927d9d20e17SCharles Keepax return PTR_ERR(alg_region); 1928b618a185SCharles Keepax 1929d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_YM, 1930d9d20e17SCharles Keepax adsp2_id.fw.id, adsp2_id.ym); 1931d9d20e17SCharles Keepax if (IS_ERR(alg_region)) 1932d9d20e17SCharles Keepax return PTR_ERR(alg_region); 1933b618a185SCharles Keepax 1934d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_ZM, 1935d9d20e17SCharles Keepax adsp2_id.fw.id, adsp2_id.zm); 1936d9d20e17SCharles Keepax if (IS_ERR(alg_region)) 1937d9d20e17SCharles Keepax return PTR_ERR(alg_region); 1938b618a185SCharles Keepax 1939b618a185SCharles Keepax pos = sizeof(adsp2_id) / 2; 19403809f001SCharles Keepax len = (sizeof(*adsp2_alg) * n_algs) / 2; 1941b618a185SCharles Keepax 19423809f001SCharles Keepax adsp2_alg = wm_adsp_read_algs(dsp, n_algs, mem->base + pos, len); 1943b618a185SCharles Keepax if (IS_ERR(adsp2_alg)) 1944b618a185SCharles Keepax return PTR_ERR(adsp2_alg); 1945b618a185SCharles Keepax 19463809f001SCharles Keepax for (i = 0; i < n_algs; i++) { 1947471f4885SMark Brown adsp_info(dsp, 1948471f4885SMark Brown "%d: ID %x v%d.%d.%d XM@%x YM@%x ZM@%x\n", 1949db40517cSMark Brown i, be32_to_cpu(adsp2_alg[i].alg.id), 1950db40517cSMark Brown (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff0000) >> 16, 1951db40517cSMark Brown (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff00) >> 8, 1952471f4885SMark Brown be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff, 1953471f4885SMark Brown be32_to_cpu(adsp2_alg[i].xm), 1954471f4885SMark Brown be32_to_cpu(adsp2_alg[i].ym), 1955471f4885SMark Brown be32_to_cpu(adsp2_alg[i].zm)); 1956471f4885SMark Brown 1957d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_XM, 1958d9d20e17SCharles Keepax adsp2_alg[i].alg.id, 1959d9d20e17SCharles Keepax adsp2_alg[i].xm); 1960d9d20e17SCharles Keepax if (IS_ERR(alg_region)) { 1961d9d20e17SCharles Keepax ret = PTR_ERR(alg_region); 1962d6d52179SJS Park goto out; 1963d6d52179SJS Park } 19642323736dSCharles Keepax if (dsp->fw_ver == 0) { 19653809f001SCharles Keepax if (i + 1 < n_algs) { 19666958eb2aSCharles Keepax len = be32_to_cpu(adsp2_alg[i + 1].xm); 19676958eb2aSCharles Keepax len -= be32_to_cpu(adsp2_alg[i].xm); 19686958eb2aSCharles Keepax len *= 4; 19692323736dSCharles Keepax wm_adsp_create_control(dsp, alg_region, 0, 19708eb084d0SStuart Henderson len, NULL, 0, 0, 19718eb084d0SStuart Henderson SNDRV_CTL_ELEM_TYPE_BYTES); 19726ab2b7b4SDimitris Papastamos } else { 19736ab2b7b4SDimitris Papastamos adsp_warn(dsp, "Missing length info for region XM with ID %x\n", 19746ab2b7b4SDimitris Papastamos be32_to_cpu(adsp2_alg[i].alg.id)); 19756ab2b7b4SDimitris Papastamos } 19762323736dSCharles Keepax } 1977471f4885SMark Brown 1978d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_YM, 1979d9d20e17SCharles Keepax adsp2_alg[i].alg.id, 1980d9d20e17SCharles Keepax adsp2_alg[i].ym); 1981d9d20e17SCharles Keepax if (IS_ERR(alg_region)) { 1982d9d20e17SCharles Keepax ret = PTR_ERR(alg_region); 1983d6d52179SJS Park goto out; 1984d6d52179SJS Park } 19852323736dSCharles Keepax if (dsp->fw_ver == 0) { 19863809f001SCharles Keepax if (i + 1 < n_algs) { 19876958eb2aSCharles Keepax len = be32_to_cpu(adsp2_alg[i + 1].ym); 19886958eb2aSCharles Keepax len -= be32_to_cpu(adsp2_alg[i].ym); 19896958eb2aSCharles Keepax len *= 4; 19902323736dSCharles Keepax wm_adsp_create_control(dsp, alg_region, 0, 19918eb084d0SStuart Henderson len, NULL, 0, 0, 19928eb084d0SStuart Henderson SNDRV_CTL_ELEM_TYPE_BYTES); 19936ab2b7b4SDimitris Papastamos } else { 19946ab2b7b4SDimitris Papastamos adsp_warn(dsp, "Missing length info for region YM with ID %x\n", 19956ab2b7b4SDimitris Papastamos be32_to_cpu(adsp2_alg[i].alg.id)); 19966ab2b7b4SDimitris Papastamos } 19972323736dSCharles Keepax } 1998471f4885SMark Brown 1999d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_ZM, 2000d9d20e17SCharles Keepax adsp2_alg[i].alg.id, 2001d9d20e17SCharles Keepax adsp2_alg[i].zm); 2002d9d20e17SCharles Keepax if (IS_ERR(alg_region)) { 2003d9d20e17SCharles Keepax ret = PTR_ERR(alg_region); 2004d6d52179SJS Park goto out; 2005d6d52179SJS Park } 20062323736dSCharles Keepax if (dsp->fw_ver == 0) { 20073809f001SCharles Keepax if (i + 1 < n_algs) { 20086958eb2aSCharles Keepax len = be32_to_cpu(adsp2_alg[i + 1].zm); 20096958eb2aSCharles Keepax len -= be32_to_cpu(adsp2_alg[i].zm); 20106958eb2aSCharles Keepax len *= 4; 20112323736dSCharles Keepax wm_adsp_create_control(dsp, alg_region, 0, 20128eb084d0SStuart Henderson len, NULL, 0, 0, 20138eb084d0SStuart Henderson SNDRV_CTL_ELEM_TYPE_BYTES); 20146ab2b7b4SDimitris Papastamos } else { 20156ab2b7b4SDimitris Papastamos adsp_warn(dsp, "Missing length info for region ZM with ID %x\n", 20166ab2b7b4SDimitris Papastamos be32_to_cpu(adsp2_alg[i].alg.id)); 20176ab2b7b4SDimitris Papastamos } 2018db40517cSMark Brown } 20192323736dSCharles Keepax } 2020db40517cSMark Brown 2021db40517cSMark Brown out: 2022b618a185SCharles Keepax kfree(adsp2_alg); 2023db40517cSMark Brown return ret; 2024db40517cSMark Brown } 2025db40517cSMark Brown 20262159ad93SMark Brown static int wm_adsp_load_coeff(struct wm_adsp *dsp) 20272159ad93SMark Brown { 2028cf17c83cSMark Brown LIST_HEAD(buf_list); 20292159ad93SMark Brown struct regmap *regmap = dsp->regmap; 20302159ad93SMark Brown struct wmfw_coeff_hdr *hdr; 20312159ad93SMark Brown struct wmfw_coeff_item *blk; 20322159ad93SMark Brown const struct firmware *firmware; 2033471f4885SMark Brown const struct wm_adsp_region *mem; 2034471f4885SMark Brown struct wm_adsp_alg_region *alg_region; 20352159ad93SMark Brown const char *region_name; 20362159ad93SMark Brown int ret, pos, blocks, type, offset, reg; 20372159ad93SMark Brown char *file; 2038cf17c83cSMark Brown struct wm_adsp_buf *buf; 20392159ad93SMark Brown 20402159ad93SMark Brown file = kzalloc(PAGE_SIZE, GFP_KERNEL); 20412159ad93SMark Brown if (file == NULL) 20422159ad93SMark Brown return -ENOMEM; 20432159ad93SMark Brown 20441023dbd9SMark Brown snprintf(file, PAGE_SIZE, "%s-dsp%d-%s.bin", dsp->part, dsp->num, 20451023dbd9SMark Brown wm_adsp_fw[dsp->fw].file); 20462159ad93SMark Brown file[PAGE_SIZE - 1] = '\0'; 20472159ad93SMark Brown 20482159ad93SMark Brown ret = request_firmware(&firmware, file, dsp->dev); 20492159ad93SMark Brown if (ret != 0) { 20502159ad93SMark Brown adsp_warn(dsp, "Failed to request '%s'\n", file); 20512159ad93SMark Brown ret = 0; 20522159ad93SMark Brown goto out; 20532159ad93SMark Brown } 20542159ad93SMark Brown ret = -EINVAL; 20552159ad93SMark Brown 20562159ad93SMark Brown if (sizeof(*hdr) >= firmware->size) { 20572159ad93SMark Brown adsp_err(dsp, "%s: file too short, %zu bytes\n", 20582159ad93SMark Brown file, firmware->size); 20592159ad93SMark Brown goto out_fw; 20602159ad93SMark Brown } 20612159ad93SMark Brown 20622159ad93SMark Brown hdr = (void *)&firmware->data[0]; 20632159ad93SMark Brown if (memcmp(hdr->magic, "WMDR", 4) != 0) { 20642159ad93SMark Brown adsp_err(dsp, "%s: invalid magic\n", file); 2065a4cdbec7SCharles Keepax goto out_fw; 20662159ad93SMark Brown } 20672159ad93SMark Brown 2068c712326dSMark Brown switch (be32_to_cpu(hdr->rev) & 0xff) { 2069c712326dSMark Brown case 1: 2070c712326dSMark Brown break; 2071c712326dSMark Brown default: 2072c712326dSMark Brown adsp_err(dsp, "%s: Unsupported coefficient file format %d\n", 2073c712326dSMark Brown file, be32_to_cpu(hdr->rev) & 0xff); 2074c712326dSMark Brown ret = -EINVAL; 2075c712326dSMark Brown goto out_fw; 2076c712326dSMark Brown } 2077c712326dSMark Brown 20782159ad93SMark Brown adsp_dbg(dsp, "%s: v%d.%d.%d\n", file, 20792159ad93SMark Brown (le32_to_cpu(hdr->ver) >> 16) & 0xff, 20802159ad93SMark Brown (le32_to_cpu(hdr->ver) >> 8) & 0xff, 20812159ad93SMark Brown le32_to_cpu(hdr->ver) & 0xff); 20822159ad93SMark Brown 20832159ad93SMark Brown pos = le32_to_cpu(hdr->len); 20842159ad93SMark Brown 20852159ad93SMark Brown blocks = 0; 20862159ad93SMark Brown while (pos < firmware->size && 20872159ad93SMark Brown pos - firmware->size > sizeof(*blk)) { 20882159ad93SMark Brown blk = (void *)(&firmware->data[pos]); 20892159ad93SMark Brown 2090c712326dSMark Brown type = le16_to_cpu(blk->type); 2091c712326dSMark Brown offset = le16_to_cpu(blk->offset); 20922159ad93SMark Brown 20932159ad93SMark Brown adsp_dbg(dsp, "%s.%d: %x v%d.%d.%d\n", 20942159ad93SMark Brown file, blocks, le32_to_cpu(blk->id), 20952159ad93SMark Brown (le32_to_cpu(blk->ver) >> 16) & 0xff, 20962159ad93SMark Brown (le32_to_cpu(blk->ver) >> 8) & 0xff, 20972159ad93SMark Brown le32_to_cpu(blk->ver) & 0xff); 20982159ad93SMark Brown adsp_dbg(dsp, "%s.%d: %d bytes at 0x%x in %x\n", 20992159ad93SMark Brown file, blocks, le32_to_cpu(blk->len), offset, type); 21002159ad93SMark Brown 21012159ad93SMark Brown reg = 0; 21022159ad93SMark Brown region_name = "Unknown"; 21032159ad93SMark Brown switch (type) { 2104c712326dSMark Brown case (WMFW_NAME_TEXT << 8): 2105c712326dSMark Brown case (WMFW_INFO_TEXT << 8): 21062159ad93SMark Brown break; 2107c712326dSMark Brown case (WMFW_ABSOLUTE << 8): 2108f395a218SMark Brown /* 2109f395a218SMark Brown * Old files may use this for global 2110f395a218SMark Brown * coefficients. 2111f395a218SMark Brown */ 2112f395a218SMark Brown if (le32_to_cpu(blk->id) == dsp->fw_id && 2113f395a218SMark Brown offset == 0) { 2114f395a218SMark Brown region_name = "global coefficients"; 2115f395a218SMark Brown mem = wm_adsp_find_region(dsp, type); 2116f395a218SMark Brown if (!mem) { 2117f395a218SMark Brown adsp_err(dsp, "No ZM\n"); 2118f395a218SMark Brown break; 2119f395a218SMark Brown } 2120f395a218SMark Brown reg = wm_adsp_region_to_reg(mem, 0); 2121f395a218SMark Brown 2122f395a218SMark Brown } else { 21232159ad93SMark Brown region_name = "register"; 21242159ad93SMark Brown reg = offset; 2125f395a218SMark Brown } 21262159ad93SMark Brown break; 2127471f4885SMark Brown 2128471f4885SMark Brown case WMFW_ADSP1_DM: 2129471f4885SMark Brown case WMFW_ADSP1_ZM: 2130471f4885SMark Brown case WMFW_ADSP2_XM: 2131471f4885SMark Brown case WMFW_ADSP2_YM: 2132471f4885SMark Brown adsp_dbg(dsp, "%s.%d: %d bytes in %x for %x\n", 2133471f4885SMark Brown file, blocks, le32_to_cpu(blk->len), 2134471f4885SMark Brown type, le32_to_cpu(blk->id)); 2135471f4885SMark Brown 2136471f4885SMark Brown mem = wm_adsp_find_region(dsp, type); 2137471f4885SMark Brown if (!mem) { 2138471f4885SMark Brown adsp_err(dsp, "No base for region %x\n", type); 2139471f4885SMark Brown break; 2140471f4885SMark Brown } 2141471f4885SMark Brown 214214197095SCharles Keepax alg_region = wm_adsp_find_alg_region(dsp, type, 214314197095SCharles Keepax le32_to_cpu(blk->id)); 214414197095SCharles Keepax if (alg_region) { 2145338c5188SMark Brown reg = alg_region->base; 214614197095SCharles Keepax reg = wm_adsp_region_to_reg(mem, reg); 2147338c5188SMark Brown reg += offset; 214814197095SCharles Keepax } else { 2149471f4885SMark Brown adsp_err(dsp, "No %x for algorithm %x\n", 2150471f4885SMark Brown type, le32_to_cpu(blk->id)); 215114197095SCharles Keepax } 2152471f4885SMark Brown break; 2153471f4885SMark Brown 21542159ad93SMark Brown default: 215525c62f7eSMark Brown adsp_err(dsp, "%s.%d: Unknown region type %x at %d\n", 215625c62f7eSMark Brown file, blocks, type, pos); 21572159ad93SMark Brown break; 21582159ad93SMark Brown } 21592159ad93SMark Brown 21602159ad93SMark Brown if (reg) { 2161cf17c83cSMark Brown buf = wm_adsp_buf_alloc(blk->data, 2162cf17c83cSMark Brown le32_to_cpu(blk->len), 2163cf17c83cSMark Brown &buf_list); 2164a76fefabSMark Brown if (!buf) { 2165a76fefabSMark Brown adsp_err(dsp, "Out of memory\n"); 2166f4b82812SWei Yongjun ret = -ENOMEM; 2167f4b82812SWei Yongjun goto out_fw; 2168a76fefabSMark Brown } 2169a76fefabSMark Brown 217020da6d5aSMark Brown adsp_dbg(dsp, "%s.%d: Writing %d bytes at %x\n", 217120da6d5aSMark Brown file, blocks, le32_to_cpu(blk->len), 217220da6d5aSMark Brown reg); 2173cf17c83cSMark Brown ret = regmap_raw_write_async(regmap, reg, buf->buf, 21742159ad93SMark Brown le32_to_cpu(blk->len)); 21752159ad93SMark Brown if (ret != 0) { 21762159ad93SMark Brown adsp_err(dsp, 217743bc3bf6SDimitris Papastamos "%s.%d: Failed to write to %x in %s: %d\n", 217843bc3bf6SDimitris Papastamos file, blocks, reg, region_name, ret); 21792159ad93SMark Brown } 21802159ad93SMark Brown } 21812159ad93SMark Brown 2182be951017SCharles Keepax pos += (le32_to_cpu(blk->len) + sizeof(*blk) + 3) & ~0x03; 21832159ad93SMark Brown blocks++; 21842159ad93SMark Brown } 21852159ad93SMark Brown 2186cf17c83cSMark Brown ret = regmap_async_complete(regmap); 2187cf17c83cSMark Brown if (ret != 0) 2188cf17c83cSMark Brown adsp_err(dsp, "Failed to complete async write: %d\n", ret); 2189cf17c83cSMark Brown 21902159ad93SMark Brown if (pos > firmware->size) 21912159ad93SMark Brown adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n", 21922159ad93SMark Brown file, blocks, pos - firmware->size); 21932159ad93SMark Brown 2194f9f55e31SRichard Fitzgerald wm_adsp_debugfs_save_binname(dsp, file); 2195f9f55e31SRichard Fitzgerald 21962159ad93SMark Brown out_fw: 21979da7a5a9SCharles Keepax regmap_async_complete(regmap); 21982159ad93SMark Brown release_firmware(firmware); 2199cf17c83cSMark Brown wm_adsp_buf_free(&buf_list); 22002159ad93SMark Brown out: 22012159ad93SMark Brown kfree(file); 2202f4b82812SWei Yongjun return ret; 22032159ad93SMark Brown } 22042159ad93SMark Brown 22053809f001SCharles Keepax int wm_adsp1_init(struct wm_adsp *dsp) 22065e7a7a22SMark Brown { 22073809f001SCharles Keepax INIT_LIST_HEAD(&dsp->alg_regions); 22085e7a7a22SMark Brown 2209078e7183SCharles Keepax mutex_init(&dsp->pwr_lock); 2210078e7183SCharles Keepax 22115e7a7a22SMark Brown return 0; 22125e7a7a22SMark Brown } 22135e7a7a22SMark Brown EXPORT_SYMBOL_GPL(wm_adsp1_init); 22145e7a7a22SMark Brown 22152159ad93SMark Brown int wm_adsp1_event(struct snd_soc_dapm_widget *w, 22162159ad93SMark Brown struct snd_kcontrol *kcontrol, 22172159ad93SMark Brown int event) 22182159ad93SMark Brown { 221972718517SLars-Peter Clausen struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); 22202159ad93SMark Brown struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec); 22212159ad93SMark Brown struct wm_adsp *dsp = &dsps[w->shift]; 22226ab2b7b4SDimitris Papastamos struct wm_coeff_ctl *ctl; 22232159ad93SMark Brown int ret; 22247585a5b0SCharles Keepax unsigned int val; 22252159ad93SMark Brown 222600200107SLars-Peter Clausen dsp->card = codec->component.card; 222792bb4c32SDimitris Papastamos 2228078e7183SCharles Keepax mutex_lock(&dsp->pwr_lock); 2229078e7183SCharles Keepax 22302159ad93SMark Brown switch (event) { 22312159ad93SMark Brown case SND_SOC_DAPM_POST_PMU: 22322159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 22332159ad93SMark Brown ADSP1_SYS_ENA, ADSP1_SYS_ENA); 22342159ad93SMark Brown 223594e205bfSChris Rattray /* 223694e205bfSChris Rattray * For simplicity set the DSP clock rate to be the 223794e205bfSChris Rattray * SYSCLK rate rather than making it configurable. 223894e205bfSChris Rattray */ 223994e205bfSChris Rattray if (dsp->sysclk_reg) { 224094e205bfSChris Rattray ret = regmap_read(dsp->regmap, dsp->sysclk_reg, &val); 224194e205bfSChris Rattray if (ret != 0) { 224294e205bfSChris Rattray adsp_err(dsp, "Failed to read SYSCLK state: %d\n", 224394e205bfSChris Rattray ret); 2244078e7183SCharles Keepax goto err_mutex; 224594e205bfSChris Rattray } 224694e205bfSChris Rattray 22477d00cd97SCharles Keepax val = (val & dsp->sysclk_mask) >> dsp->sysclk_shift; 224894e205bfSChris Rattray 224994e205bfSChris Rattray ret = regmap_update_bits(dsp->regmap, 225094e205bfSChris Rattray dsp->base + ADSP1_CONTROL_31, 225194e205bfSChris Rattray ADSP1_CLK_SEL_MASK, val); 225294e205bfSChris Rattray if (ret != 0) { 225394e205bfSChris Rattray adsp_err(dsp, "Failed to set clock rate: %d\n", 225494e205bfSChris Rattray ret); 2255078e7183SCharles Keepax goto err_mutex; 225694e205bfSChris Rattray } 225794e205bfSChris Rattray } 225894e205bfSChris Rattray 22592159ad93SMark Brown ret = wm_adsp_load(dsp); 22602159ad93SMark Brown if (ret != 0) 2261078e7183SCharles Keepax goto err_ena; 22622159ad93SMark Brown 2263b618a185SCharles Keepax ret = wm_adsp1_setup_algs(dsp); 2264db40517cSMark Brown if (ret != 0) 2265078e7183SCharles Keepax goto err_ena; 2266db40517cSMark Brown 22672159ad93SMark Brown ret = wm_adsp_load_coeff(dsp); 22682159ad93SMark Brown if (ret != 0) 2269078e7183SCharles Keepax goto err_ena; 22702159ad93SMark Brown 22710c2e3f34SDimitris Papastamos /* Initialize caches for enabled and unset controls */ 227281ad93ecSDimitris Papastamos ret = wm_coeff_init_control_caches(dsp); 22736ab2b7b4SDimitris Papastamos if (ret != 0) 2274078e7183SCharles Keepax goto err_ena; 22756ab2b7b4SDimitris Papastamos 22760c2e3f34SDimitris Papastamos /* Sync set controls */ 227781ad93ecSDimitris Papastamos ret = wm_coeff_sync_controls(dsp); 22786ab2b7b4SDimitris Papastamos if (ret != 0) 2279078e7183SCharles Keepax goto err_ena; 22806ab2b7b4SDimitris Papastamos 228128823ebaSCharles Keepax dsp->booted = true; 228228823ebaSCharles Keepax 22832159ad93SMark Brown /* Start the core running */ 22842159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 22852159ad93SMark Brown ADSP1_CORE_ENA | ADSP1_START, 22862159ad93SMark Brown ADSP1_CORE_ENA | ADSP1_START); 228728823ebaSCharles Keepax 228828823ebaSCharles Keepax dsp->running = true; 22892159ad93SMark Brown break; 22902159ad93SMark Brown 22912159ad93SMark Brown case SND_SOC_DAPM_PRE_PMD: 229228823ebaSCharles Keepax dsp->running = false; 229328823ebaSCharles Keepax dsp->booted = false; 229428823ebaSCharles Keepax 22952159ad93SMark Brown /* Halt the core */ 22962159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 22972159ad93SMark Brown ADSP1_CORE_ENA | ADSP1_START, 0); 22982159ad93SMark Brown 22992159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_19, 23002159ad93SMark Brown ADSP1_WDMA_BUFFER_LENGTH_MASK, 0); 23012159ad93SMark Brown 23022159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 23032159ad93SMark Brown ADSP1_SYS_ENA, 0); 23046ab2b7b4SDimitris Papastamos 230581ad93ecSDimitris Papastamos list_for_each_entry(ctl, &dsp->ctl_list, list) 23066ab2b7b4SDimitris Papastamos ctl->enabled = 0; 2307b0101b4fSDimitris Papastamos 230856574d54SRichard Fitzgerald 230956574d54SRichard Fitzgerald wm_adsp_free_alg_regions(dsp); 23102159ad93SMark Brown break; 23112159ad93SMark Brown 23122159ad93SMark Brown default: 23132159ad93SMark Brown break; 23142159ad93SMark Brown } 23152159ad93SMark Brown 2316078e7183SCharles Keepax mutex_unlock(&dsp->pwr_lock); 2317078e7183SCharles Keepax 23182159ad93SMark Brown return 0; 23192159ad93SMark Brown 2320078e7183SCharles Keepax err_ena: 23212159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 23222159ad93SMark Brown ADSP1_SYS_ENA, 0); 2323078e7183SCharles Keepax err_mutex: 2324078e7183SCharles Keepax mutex_unlock(&dsp->pwr_lock); 2325078e7183SCharles Keepax 23262159ad93SMark Brown return ret; 23272159ad93SMark Brown } 23282159ad93SMark Brown EXPORT_SYMBOL_GPL(wm_adsp1_event); 23292159ad93SMark Brown 23302159ad93SMark Brown static int wm_adsp2_ena(struct wm_adsp *dsp) 23312159ad93SMark Brown { 23322159ad93SMark Brown unsigned int val; 23332159ad93SMark Brown int ret, count; 23342159ad93SMark Brown 23351552c325SMark Brown ret = regmap_update_bits_async(dsp->regmap, dsp->base + ADSP2_CONTROL, 23362159ad93SMark Brown ADSP2_SYS_ENA, ADSP2_SYS_ENA); 23372159ad93SMark Brown if (ret != 0) 23382159ad93SMark Brown return ret; 23392159ad93SMark Brown 23402159ad93SMark Brown /* Wait for the RAM to start, should be near instantaneous */ 2341939fd1e8SCharles Keepax for (count = 0; count < 10; ++count) { 23427d00cd97SCharles Keepax ret = regmap_read(dsp->regmap, dsp->base + ADSP2_STATUS1, &val); 23432159ad93SMark Brown if (ret != 0) 23442159ad93SMark Brown return ret; 2345939fd1e8SCharles Keepax 2346939fd1e8SCharles Keepax if (val & ADSP2_RAM_RDY) 2347939fd1e8SCharles Keepax break; 2348939fd1e8SCharles Keepax 23491fa96f3fSCharles Keepax usleep_range(250, 500); 2350939fd1e8SCharles Keepax } 23512159ad93SMark Brown 23522159ad93SMark Brown if (!(val & ADSP2_RAM_RDY)) { 23532159ad93SMark Brown adsp_err(dsp, "Failed to start DSP RAM\n"); 23542159ad93SMark Brown return -EBUSY; 23552159ad93SMark Brown } 23562159ad93SMark Brown 23572159ad93SMark Brown adsp_dbg(dsp, "RAM ready after %d polls\n", count); 23582159ad93SMark Brown 23592159ad93SMark Brown return 0; 23602159ad93SMark Brown } 23612159ad93SMark Brown 236218b1a902SCharles Keepax static void wm_adsp2_boot_work(struct work_struct *work) 23632159ad93SMark Brown { 2364d8a64d6aSCharles Keepax struct wm_adsp *dsp = container_of(work, 2365d8a64d6aSCharles Keepax struct wm_adsp, 2366d8a64d6aSCharles Keepax boot_work); 23672159ad93SMark Brown int ret; 23682159ad93SMark Brown 2369078e7183SCharles Keepax mutex_lock(&dsp->pwr_lock); 2370078e7183SCharles Keepax 237190d19ba5SCharles Keepax ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 237290d19ba5SCharles Keepax ADSP2_MEM_ENA, ADSP2_MEM_ENA); 237390d19ba5SCharles Keepax if (ret != 0) 237490d19ba5SCharles Keepax goto err_mutex; 237590d19ba5SCharles Keepax 23762159ad93SMark Brown ret = wm_adsp2_ena(dsp); 23772159ad93SMark Brown if (ret != 0) 2378078e7183SCharles Keepax goto err_mutex; 23792159ad93SMark Brown 23802159ad93SMark Brown ret = wm_adsp_load(dsp); 23812159ad93SMark Brown if (ret != 0) 2382078e7183SCharles Keepax goto err_ena; 23832159ad93SMark Brown 2384b618a185SCharles Keepax ret = wm_adsp2_setup_algs(dsp); 2385db40517cSMark Brown if (ret != 0) 2386078e7183SCharles Keepax goto err_ena; 2387db40517cSMark Brown 23882159ad93SMark Brown ret = wm_adsp_load_coeff(dsp); 23892159ad93SMark Brown if (ret != 0) 2390078e7183SCharles Keepax goto err_ena; 23912159ad93SMark Brown 23920c2e3f34SDimitris Papastamos /* Initialize caches for enabled and unset controls */ 239381ad93ecSDimitris Papastamos ret = wm_coeff_init_control_caches(dsp); 23946ab2b7b4SDimitris Papastamos if (ret != 0) 2395078e7183SCharles Keepax goto err_ena; 23966ab2b7b4SDimitris Papastamos 239728823ebaSCharles Keepax dsp->booted = true; 2398d8a64d6aSCharles Keepax 239990d19ba5SCharles Keepax /* Turn DSP back off until we are ready to run */ 240090d19ba5SCharles Keepax ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 240190d19ba5SCharles Keepax ADSP2_SYS_ENA, 0); 240290d19ba5SCharles Keepax if (ret != 0) 240390d19ba5SCharles Keepax goto err_ena; 240490d19ba5SCharles Keepax 2405078e7183SCharles Keepax mutex_unlock(&dsp->pwr_lock); 2406078e7183SCharles Keepax 2407d8a64d6aSCharles Keepax return; 2408d8a64d6aSCharles Keepax 2409078e7183SCharles Keepax err_ena: 2410d8a64d6aSCharles Keepax regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 2411d8a64d6aSCharles Keepax ADSP2_SYS_ENA | ADSP2_CORE_ENA | ADSP2_START, 0); 2412078e7183SCharles Keepax err_mutex: 2413078e7183SCharles Keepax mutex_unlock(&dsp->pwr_lock); 2414d8a64d6aSCharles Keepax } 2415d8a64d6aSCharles Keepax 2416d82d767fSCharles Keepax static void wm_adsp2_set_dspclk(struct wm_adsp *dsp, unsigned int freq) 2417d82d767fSCharles Keepax { 2418d82d767fSCharles Keepax int ret; 2419d82d767fSCharles Keepax 2420d82d767fSCharles Keepax ret = regmap_update_bits_async(dsp->regmap, 2421d82d767fSCharles Keepax dsp->base + ADSP2_CLOCKING, 2422d82d767fSCharles Keepax ADSP2_CLK_SEL_MASK, 2423d82d767fSCharles Keepax freq << ADSP2_CLK_SEL_SHIFT); 2424d82d767fSCharles Keepax if (ret != 0) 2425d82d767fSCharles Keepax adsp_err(dsp, "Failed to set clock rate: %d\n", ret); 2426d82d767fSCharles Keepax } 2427d82d767fSCharles Keepax 242812db5eddSCharles Keepax int wm_adsp2_early_event(struct snd_soc_dapm_widget *w, 2429d82d767fSCharles Keepax struct snd_kcontrol *kcontrol, int event, 2430d82d767fSCharles Keepax unsigned int freq) 243112db5eddSCharles Keepax { 243272718517SLars-Peter Clausen struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); 243312db5eddSCharles Keepax struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec); 243412db5eddSCharles Keepax struct wm_adsp *dsp = &dsps[w->shift]; 243557a60cc3SCharles Keepax struct wm_coeff_ctl *ctl; 243612db5eddSCharles Keepax 243700200107SLars-Peter Clausen dsp->card = codec->component.card; 243812db5eddSCharles Keepax 243912db5eddSCharles Keepax switch (event) { 244012db5eddSCharles Keepax case SND_SOC_DAPM_PRE_PMU: 2441d82d767fSCharles Keepax wm_adsp2_set_dspclk(dsp, freq); 244212db5eddSCharles Keepax queue_work(system_unbound_wq, &dsp->boot_work); 244312db5eddSCharles Keepax break; 244457a60cc3SCharles Keepax case SND_SOC_DAPM_PRE_PMD: 244557a60cc3SCharles Keepax wm_adsp_debugfs_clear(dsp); 244657a60cc3SCharles Keepax 244757a60cc3SCharles Keepax dsp->fw_id = 0; 244857a60cc3SCharles Keepax dsp->fw_id_version = 0; 244957a60cc3SCharles Keepax 245057a60cc3SCharles Keepax dsp->booted = false; 245157a60cc3SCharles Keepax 245257a60cc3SCharles Keepax regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 245357a60cc3SCharles Keepax ADSP2_MEM_ENA, 0); 245457a60cc3SCharles Keepax 245557a60cc3SCharles Keepax list_for_each_entry(ctl, &dsp->ctl_list, list) 245657a60cc3SCharles Keepax ctl->enabled = 0; 245757a60cc3SCharles Keepax 245857a60cc3SCharles Keepax wm_adsp_free_alg_regions(dsp); 245957a60cc3SCharles Keepax 246057a60cc3SCharles Keepax adsp_dbg(dsp, "Shutdown complete\n"); 246157a60cc3SCharles Keepax break; 246212db5eddSCharles Keepax default: 246312db5eddSCharles Keepax break; 2464cab27258SCharles Keepax } 246512db5eddSCharles Keepax 246612db5eddSCharles Keepax return 0; 246712db5eddSCharles Keepax } 246812db5eddSCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp2_early_event); 246912db5eddSCharles Keepax 2470d8a64d6aSCharles Keepax int wm_adsp2_event(struct snd_soc_dapm_widget *w, 2471d8a64d6aSCharles Keepax struct snd_kcontrol *kcontrol, int event) 2472d8a64d6aSCharles Keepax { 247372718517SLars-Peter Clausen struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); 2474d8a64d6aSCharles Keepax struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec); 2475d8a64d6aSCharles Keepax struct wm_adsp *dsp = &dsps[w->shift]; 2476d8a64d6aSCharles Keepax int ret; 2477d8a64d6aSCharles Keepax 2478d8a64d6aSCharles Keepax switch (event) { 2479d8a64d6aSCharles Keepax case SND_SOC_DAPM_POST_PMU: 2480d8a64d6aSCharles Keepax flush_work(&dsp->boot_work); 2481d8a64d6aSCharles Keepax 248228823ebaSCharles Keepax if (!dsp->booted) 2483d8a64d6aSCharles Keepax return -EIO; 2484d8a64d6aSCharles Keepax 248590d19ba5SCharles Keepax ret = wm_adsp2_ena(dsp); 248690d19ba5SCharles Keepax if (ret != 0) 248790d19ba5SCharles Keepax goto err; 248890d19ba5SCharles Keepax 2489cef45771SCharles Keepax /* Sync set controls */ 2490cef45771SCharles Keepax ret = wm_coeff_sync_controls(dsp); 2491cef45771SCharles Keepax if (ret != 0) 2492cef45771SCharles Keepax goto err; 2493cef45771SCharles Keepax 2494d8a64d6aSCharles Keepax ret = regmap_update_bits(dsp->regmap, 2495d8a64d6aSCharles Keepax dsp->base + ADSP2_CONTROL, 249600e4c3b6SCharles Keepax ADSP2_CORE_ENA | ADSP2_START, 249700e4c3b6SCharles Keepax ADSP2_CORE_ENA | ADSP2_START); 2498d8a64d6aSCharles Keepax if (ret != 0) 2499d8a64d6aSCharles Keepax goto err; 25002cd19bdbSCharles Keepax 250128823ebaSCharles Keepax dsp->running = true; 250228823ebaSCharles Keepax 2503612047f0SCharles Keepax mutex_lock(&dsp->pwr_lock); 2504612047f0SCharles Keepax 25052cd19bdbSCharles Keepax if (wm_adsp_fw[dsp->fw].num_caps != 0) 25062cd19bdbSCharles Keepax ret = wm_adsp_buffer_init(dsp); 25072cd19bdbSCharles Keepax 2508612047f0SCharles Keepax mutex_unlock(&dsp->pwr_lock); 2509612047f0SCharles Keepax 25102159ad93SMark Brown break; 25112159ad93SMark Brown 25122159ad93SMark Brown case SND_SOC_DAPM_PRE_PMD: 2513f4f0c4c6SRichard Fitzgerald /* Tell the firmware to cleanup */ 2514f4f0c4c6SRichard Fitzgerald wm_adsp_signal_event_controls(dsp, WM_ADSP_FW_EVENT_SHUTDOWN); 2515f4f0c4c6SRichard Fitzgerald 251610337b07SRichard Fitzgerald /* Log firmware state, it can be useful for analysis */ 251710337b07SRichard Fitzgerald wm_adsp2_show_fw_status(dsp); 251810337b07SRichard Fitzgerald 2519078e7183SCharles Keepax mutex_lock(&dsp->pwr_lock); 2520078e7183SCharles Keepax 25211023dbd9SMark Brown dsp->running = false; 25221023dbd9SMark Brown 25232159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 252457a60cc3SCharles Keepax ADSP2_CORE_ENA | ADSP2_START, 0); 2525973838a0SMark Brown 25262d30b575SMark Brown /* Make sure DMAs are quiesced */ 25276facd2d1SSimon Trimmer regmap_write(dsp->regmap, dsp->base + ADSP2_RDMA_CONFIG_1, 0); 25282d30b575SMark Brown regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_1, 0); 25292d30b575SMark Brown regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_2, 0); 25306facd2d1SSimon Trimmer 25316facd2d1SSimon Trimmer regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 25326facd2d1SSimon Trimmer ADSP2_SYS_ENA, 0); 25332d30b575SMark Brown 25342cd19bdbSCharles Keepax if (wm_adsp_fw[dsp->fw].num_caps != 0) 25352cd19bdbSCharles Keepax wm_adsp_buffer_free(dsp); 25362cd19bdbSCharles Keepax 2537078e7183SCharles Keepax mutex_unlock(&dsp->pwr_lock); 2538078e7183SCharles Keepax 253957a60cc3SCharles Keepax adsp_dbg(dsp, "Execution stopped\n"); 25402159ad93SMark Brown break; 25412159ad93SMark Brown 25422159ad93SMark Brown default: 25432159ad93SMark Brown break; 25442159ad93SMark Brown } 25452159ad93SMark Brown 25462159ad93SMark Brown return 0; 25472159ad93SMark Brown err: 25482159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 2549a7f9be7eSMark Brown ADSP2_SYS_ENA | ADSP2_CORE_ENA | ADSP2_START, 0); 25502159ad93SMark Brown return ret; 25512159ad93SMark Brown } 25522159ad93SMark Brown EXPORT_SYMBOL_GPL(wm_adsp2_event); 2553973838a0SMark Brown 2554f5e2ce92SRichard Fitzgerald int wm_adsp2_codec_probe(struct wm_adsp *dsp, struct snd_soc_codec *codec) 2555f5e2ce92SRichard Fitzgerald { 2556f9f55e31SRichard Fitzgerald wm_adsp2_init_debugfs(dsp, codec); 2557f9f55e31SRichard Fitzgerald 2558218e5087SRichard Fitzgerald return snd_soc_add_codec_controls(codec, 2559336d0442SRichard Fitzgerald &wm_adsp_fw_controls[dsp->num - 1], 2560336d0442SRichard Fitzgerald 1); 2561f5e2ce92SRichard Fitzgerald } 2562f5e2ce92SRichard Fitzgerald EXPORT_SYMBOL_GPL(wm_adsp2_codec_probe); 2563f5e2ce92SRichard Fitzgerald 2564f5e2ce92SRichard Fitzgerald int wm_adsp2_codec_remove(struct wm_adsp *dsp, struct snd_soc_codec *codec) 2565f5e2ce92SRichard Fitzgerald { 2566f9f55e31SRichard Fitzgerald wm_adsp2_cleanup_debugfs(dsp); 2567f9f55e31SRichard Fitzgerald 2568f5e2ce92SRichard Fitzgerald return 0; 2569f5e2ce92SRichard Fitzgerald } 2570f5e2ce92SRichard Fitzgerald EXPORT_SYMBOL_GPL(wm_adsp2_codec_remove); 2571f5e2ce92SRichard Fitzgerald 257281ac58b1SRichard Fitzgerald int wm_adsp2_init(struct wm_adsp *dsp) 2573973838a0SMark Brown { 2574973838a0SMark Brown int ret; 2575973838a0SMark Brown 257610a2b662SMark Brown /* 257710a2b662SMark Brown * Disable the DSP memory by default when in reset for a small 257810a2b662SMark Brown * power saving. 257910a2b662SMark Brown */ 25803809f001SCharles Keepax ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 258110a2b662SMark Brown ADSP2_MEM_ENA, 0); 258210a2b662SMark Brown if (ret != 0) { 25833809f001SCharles Keepax adsp_err(dsp, "Failed to clear memory retention: %d\n", ret); 258410a2b662SMark Brown return ret; 258510a2b662SMark Brown } 258610a2b662SMark Brown 25873809f001SCharles Keepax INIT_LIST_HEAD(&dsp->alg_regions); 25883809f001SCharles Keepax INIT_LIST_HEAD(&dsp->ctl_list); 25893809f001SCharles Keepax INIT_WORK(&dsp->boot_work, wm_adsp2_boot_work); 25906ab2b7b4SDimitris Papastamos 2591078e7183SCharles Keepax mutex_init(&dsp->pwr_lock); 2592078e7183SCharles Keepax 2593973838a0SMark Brown return 0; 2594973838a0SMark Brown } 2595973838a0SMark Brown EXPORT_SYMBOL_GPL(wm_adsp2_init); 25960a37c6efSPraveen Diwakar 259766225e98SRichard Fitzgerald void wm_adsp2_remove(struct wm_adsp *dsp) 259866225e98SRichard Fitzgerald { 259966225e98SRichard Fitzgerald struct wm_coeff_ctl *ctl; 260066225e98SRichard Fitzgerald 260166225e98SRichard Fitzgerald while (!list_empty(&dsp->ctl_list)) { 260266225e98SRichard Fitzgerald ctl = list_first_entry(&dsp->ctl_list, struct wm_coeff_ctl, 260366225e98SRichard Fitzgerald list); 260466225e98SRichard Fitzgerald list_del(&ctl->list); 260566225e98SRichard Fitzgerald wm_adsp_free_ctl_blk(ctl); 260666225e98SRichard Fitzgerald } 260766225e98SRichard Fitzgerald } 260866225e98SRichard Fitzgerald EXPORT_SYMBOL_GPL(wm_adsp2_remove); 260966225e98SRichard Fitzgerald 2610edd71350SCharles Keepax static inline int wm_adsp_compr_attached(struct wm_adsp_compr *compr) 2611edd71350SCharles Keepax { 2612edd71350SCharles Keepax return compr->buf != NULL; 2613edd71350SCharles Keepax } 2614edd71350SCharles Keepax 2615edd71350SCharles Keepax static int wm_adsp_compr_attach(struct wm_adsp_compr *compr) 2616edd71350SCharles Keepax { 2617edd71350SCharles Keepax /* 2618edd71350SCharles Keepax * Note this will be more complex once each DSP can support multiple 2619edd71350SCharles Keepax * streams 2620edd71350SCharles Keepax */ 2621edd71350SCharles Keepax if (!compr->dsp->buffer) 2622edd71350SCharles Keepax return -EINVAL; 2623edd71350SCharles Keepax 2624edd71350SCharles Keepax compr->buf = compr->dsp->buffer; 2625721be3beSCharles Keepax compr->buf->compr = compr; 2626edd71350SCharles Keepax 2627edd71350SCharles Keepax return 0; 2628edd71350SCharles Keepax } 2629edd71350SCharles Keepax 2630721be3beSCharles Keepax static void wm_adsp_compr_detach(struct wm_adsp_compr *compr) 2631721be3beSCharles Keepax { 2632721be3beSCharles Keepax if (!compr) 2633721be3beSCharles Keepax return; 2634721be3beSCharles Keepax 2635721be3beSCharles Keepax /* Wake the poll so it can see buffer is no longer attached */ 2636721be3beSCharles Keepax if (compr->stream) 2637721be3beSCharles Keepax snd_compr_fragment_elapsed(compr->stream); 2638721be3beSCharles Keepax 2639721be3beSCharles Keepax if (wm_adsp_compr_attached(compr)) { 2640721be3beSCharles Keepax compr->buf->compr = NULL; 2641721be3beSCharles Keepax compr->buf = NULL; 2642721be3beSCharles Keepax } 2643721be3beSCharles Keepax } 2644721be3beSCharles Keepax 2645406abc95SCharles Keepax int wm_adsp_compr_open(struct wm_adsp *dsp, struct snd_compr_stream *stream) 2646406abc95SCharles Keepax { 2647406abc95SCharles Keepax struct wm_adsp_compr *compr; 2648406abc95SCharles Keepax int ret = 0; 2649406abc95SCharles Keepax 2650406abc95SCharles Keepax mutex_lock(&dsp->pwr_lock); 2651406abc95SCharles Keepax 2652406abc95SCharles Keepax if (wm_adsp_fw[dsp->fw].num_caps == 0) { 2653406abc95SCharles Keepax adsp_err(dsp, "Firmware does not support compressed API\n"); 2654406abc95SCharles Keepax ret = -ENXIO; 2655406abc95SCharles Keepax goto out; 2656406abc95SCharles Keepax } 2657406abc95SCharles Keepax 2658406abc95SCharles Keepax if (wm_adsp_fw[dsp->fw].compr_direction != stream->direction) { 2659406abc95SCharles Keepax adsp_err(dsp, "Firmware does not support stream direction\n"); 2660406abc95SCharles Keepax ret = -EINVAL; 2661406abc95SCharles Keepax goto out; 2662406abc95SCharles Keepax } 2663406abc95SCharles Keepax 266495fe9597SCharles Keepax if (dsp->compr) { 266595fe9597SCharles Keepax /* It is expect this limitation will be removed in future */ 266695fe9597SCharles Keepax adsp_err(dsp, "Only a single stream supported per DSP\n"); 266795fe9597SCharles Keepax ret = -EBUSY; 266895fe9597SCharles Keepax goto out; 266995fe9597SCharles Keepax } 267095fe9597SCharles Keepax 2671406abc95SCharles Keepax compr = kzalloc(sizeof(*compr), GFP_KERNEL); 2672406abc95SCharles Keepax if (!compr) { 2673406abc95SCharles Keepax ret = -ENOMEM; 2674406abc95SCharles Keepax goto out; 2675406abc95SCharles Keepax } 2676406abc95SCharles Keepax 2677406abc95SCharles Keepax compr->dsp = dsp; 2678406abc95SCharles Keepax compr->stream = stream; 2679406abc95SCharles Keepax 2680406abc95SCharles Keepax dsp->compr = compr; 2681406abc95SCharles Keepax 2682406abc95SCharles Keepax stream->runtime->private_data = compr; 2683406abc95SCharles Keepax 2684406abc95SCharles Keepax out: 2685406abc95SCharles Keepax mutex_unlock(&dsp->pwr_lock); 2686406abc95SCharles Keepax 2687406abc95SCharles Keepax return ret; 2688406abc95SCharles Keepax } 2689406abc95SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_open); 2690406abc95SCharles Keepax 2691406abc95SCharles Keepax int wm_adsp_compr_free(struct snd_compr_stream *stream) 2692406abc95SCharles Keepax { 2693406abc95SCharles Keepax struct wm_adsp_compr *compr = stream->runtime->private_data; 2694406abc95SCharles Keepax struct wm_adsp *dsp = compr->dsp; 2695406abc95SCharles Keepax 2696406abc95SCharles Keepax mutex_lock(&dsp->pwr_lock); 2697406abc95SCharles Keepax 2698721be3beSCharles Keepax wm_adsp_compr_detach(compr); 2699406abc95SCharles Keepax dsp->compr = NULL; 2700406abc95SCharles Keepax 270183a40ce9SCharles Keepax kfree(compr->raw_buf); 2702406abc95SCharles Keepax kfree(compr); 2703406abc95SCharles Keepax 2704406abc95SCharles Keepax mutex_unlock(&dsp->pwr_lock); 2705406abc95SCharles Keepax 2706406abc95SCharles Keepax return 0; 2707406abc95SCharles Keepax } 2708406abc95SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_free); 2709406abc95SCharles Keepax 2710406abc95SCharles Keepax static int wm_adsp_compr_check_params(struct snd_compr_stream *stream, 2711406abc95SCharles Keepax struct snd_compr_params *params) 2712406abc95SCharles Keepax { 2713406abc95SCharles Keepax struct wm_adsp_compr *compr = stream->runtime->private_data; 2714406abc95SCharles Keepax struct wm_adsp *dsp = compr->dsp; 2715406abc95SCharles Keepax const struct wm_adsp_fw_caps *caps; 2716406abc95SCharles Keepax const struct snd_codec_desc *desc; 2717406abc95SCharles Keepax int i, j; 2718406abc95SCharles Keepax 2719406abc95SCharles Keepax if (params->buffer.fragment_size < WM_ADSP_MIN_FRAGMENT_SIZE || 2720406abc95SCharles Keepax params->buffer.fragment_size > WM_ADSP_MAX_FRAGMENT_SIZE || 2721406abc95SCharles Keepax params->buffer.fragments < WM_ADSP_MIN_FRAGMENTS || 2722406abc95SCharles Keepax params->buffer.fragments > WM_ADSP_MAX_FRAGMENTS || 2723406abc95SCharles Keepax params->buffer.fragment_size % WM_ADSP_DATA_WORD_SIZE) { 2724406abc95SCharles Keepax adsp_err(dsp, "Invalid buffer fragsize=%d fragments=%d\n", 2725406abc95SCharles Keepax params->buffer.fragment_size, 2726406abc95SCharles Keepax params->buffer.fragments); 2727406abc95SCharles Keepax 2728406abc95SCharles Keepax return -EINVAL; 2729406abc95SCharles Keepax } 2730406abc95SCharles Keepax 2731406abc95SCharles Keepax for (i = 0; i < wm_adsp_fw[dsp->fw].num_caps; i++) { 2732406abc95SCharles Keepax caps = &wm_adsp_fw[dsp->fw].caps[i]; 2733406abc95SCharles Keepax desc = &caps->desc; 2734406abc95SCharles Keepax 2735406abc95SCharles Keepax if (caps->id != params->codec.id) 2736406abc95SCharles Keepax continue; 2737406abc95SCharles Keepax 2738406abc95SCharles Keepax if (stream->direction == SND_COMPRESS_PLAYBACK) { 2739406abc95SCharles Keepax if (desc->max_ch < params->codec.ch_out) 2740406abc95SCharles Keepax continue; 2741406abc95SCharles Keepax } else { 2742406abc95SCharles Keepax if (desc->max_ch < params->codec.ch_in) 2743406abc95SCharles Keepax continue; 2744406abc95SCharles Keepax } 2745406abc95SCharles Keepax 2746406abc95SCharles Keepax if (!(desc->formats & (1 << params->codec.format))) 2747406abc95SCharles Keepax continue; 2748406abc95SCharles Keepax 2749406abc95SCharles Keepax for (j = 0; j < desc->num_sample_rates; ++j) 2750406abc95SCharles Keepax if (desc->sample_rates[j] == params->codec.sample_rate) 2751406abc95SCharles Keepax return 0; 2752406abc95SCharles Keepax } 2753406abc95SCharles Keepax 2754406abc95SCharles Keepax adsp_err(dsp, "Invalid params id=%u ch=%u,%u rate=%u fmt=%u\n", 2755406abc95SCharles Keepax params->codec.id, params->codec.ch_in, params->codec.ch_out, 2756406abc95SCharles Keepax params->codec.sample_rate, params->codec.format); 2757406abc95SCharles Keepax return -EINVAL; 2758406abc95SCharles Keepax } 2759406abc95SCharles Keepax 2760565ace46SCharles Keepax static inline unsigned int wm_adsp_compr_frag_words(struct wm_adsp_compr *compr) 2761565ace46SCharles Keepax { 2762565ace46SCharles Keepax return compr->size.fragment_size / WM_ADSP_DATA_WORD_SIZE; 2763565ace46SCharles Keepax } 2764565ace46SCharles Keepax 2765406abc95SCharles Keepax int wm_adsp_compr_set_params(struct snd_compr_stream *stream, 2766406abc95SCharles Keepax struct snd_compr_params *params) 2767406abc95SCharles Keepax { 2768406abc95SCharles Keepax struct wm_adsp_compr *compr = stream->runtime->private_data; 276983a40ce9SCharles Keepax unsigned int size; 2770406abc95SCharles Keepax int ret; 2771406abc95SCharles Keepax 2772406abc95SCharles Keepax ret = wm_adsp_compr_check_params(stream, params); 2773406abc95SCharles Keepax if (ret) 2774406abc95SCharles Keepax return ret; 2775406abc95SCharles Keepax 2776406abc95SCharles Keepax compr->size = params->buffer; 2777406abc95SCharles Keepax 2778406abc95SCharles Keepax adsp_dbg(compr->dsp, "fragment_size=%d fragments=%d\n", 2779406abc95SCharles Keepax compr->size.fragment_size, compr->size.fragments); 2780406abc95SCharles Keepax 278183a40ce9SCharles Keepax size = wm_adsp_compr_frag_words(compr) * sizeof(*compr->raw_buf); 278283a40ce9SCharles Keepax compr->raw_buf = kmalloc(size, GFP_DMA | GFP_KERNEL); 278383a40ce9SCharles Keepax if (!compr->raw_buf) 278483a40ce9SCharles Keepax return -ENOMEM; 278583a40ce9SCharles Keepax 2786da2b3358SCharles Keepax compr->sample_rate = params->codec.sample_rate; 2787da2b3358SCharles Keepax 2788406abc95SCharles Keepax return 0; 2789406abc95SCharles Keepax } 2790406abc95SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_set_params); 2791406abc95SCharles Keepax 2792406abc95SCharles Keepax int wm_adsp_compr_get_caps(struct snd_compr_stream *stream, 2793406abc95SCharles Keepax struct snd_compr_caps *caps) 2794406abc95SCharles Keepax { 2795406abc95SCharles Keepax struct wm_adsp_compr *compr = stream->runtime->private_data; 2796406abc95SCharles Keepax int fw = compr->dsp->fw; 2797406abc95SCharles Keepax int i; 2798406abc95SCharles Keepax 2799406abc95SCharles Keepax if (wm_adsp_fw[fw].caps) { 2800406abc95SCharles Keepax for (i = 0; i < wm_adsp_fw[fw].num_caps; i++) 2801406abc95SCharles Keepax caps->codecs[i] = wm_adsp_fw[fw].caps[i].id; 2802406abc95SCharles Keepax 2803406abc95SCharles Keepax caps->num_codecs = i; 2804406abc95SCharles Keepax caps->direction = wm_adsp_fw[fw].compr_direction; 2805406abc95SCharles Keepax 2806406abc95SCharles Keepax caps->min_fragment_size = WM_ADSP_MIN_FRAGMENT_SIZE; 2807406abc95SCharles Keepax caps->max_fragment_size = WM_ADSP_MAX_FRAGMENT_SIZE; 2808406abc95SCharles Keepax caps->min_fragments = WM_ADSP_MIN_FRAGMENTS; 2809406abc95SCharles Keepax caps->max_fragments = WM_ADSP_MAX_FRAGMENTS; 2810406abc95SCharles Keepax } 2811406abc95SCharles Keepax 2812406abc95SCharles Keepax return 0; 2813406abc95SCharles Keepax } 2814406abc95SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_get_caps); 2815406abc95SCharles Keepax 28162cd19bdbSCharles Keepax static int wm_adsp_read_data_block(struct wm_adsp *dsp, int mem_type, 28172cd19bdbSCharles Keepax unsigned int mem_addr, 28182cd19bdbSCharles Keepax unsigned int num_words, u32 *data) 28192cd19bdbSCharles Keepax { 28202cd19bdbSCharles Keepax struct wm_adsp_region const *mem = wm_adsp_find_region(dsp, mem_type); 28212cd19bdbSCharles Keepax unsigned int i, reg; 28222cd19bdbSCharles Keepax int ret; 28232cd19bdbSCharles Keepax 28242cd19bdbSCharles Keepax if (!mem) 28252cd19bdbSCharles Keepax return -EINVAL; 28262cd19bdbSCharles Keepax 28272cd19bdbSCharles Keepax reg = wm_adsp_region_to_reg(mem, mem_addr); 28282cd19bdbSCharles Keepax 28292cd19bdbSCharles Keepax ret = regmap_raw_read(dsp->regmap, reg, data, 28302cd19bdbSCharles Keepax sizeof(*data) * num_words); 28312cd19bdbSCharles Keepax if (ret < 0) 28322cd19bdbSCharles Keepax return ret; 28332cd19bdbSCharles Keepax 28342cd19bdbSCharles Keepax for (i = 0; i < num_words; ++i) 28352cd19bdbSCharles Keepax data[i] = be32_to_cpu(data[i]) & 0x00ffffffu; 28362cd19bdbSCharles Keepax 28372cd19bdbSCharles Keepax return 0; 28382cd19bdbSCharles Keepax } 28392cd19bdbSCharles Keepax 28402cd19bdbSCharles Keepax static inline int wm_adsp_read_data_word(struct wm_adsp *dsp, int mem_type, 28412cd19bdbSCharles Keepax unsigned int mem_addr, u32 *data) 28422cd19bdbSCharles Keepax { 28432cd19bdbSCharles Keepax return wm_adsp_read_data_block(dsp, mem_type, mem_addr, 1, data); 28442cd19bdbSCharles Keepax } 28452cd19bdbSCharles Keepax 28462cd19bdbSCharles Keepax static int wm_adsp_write_data_word(struct wm_adsp *dsp, int mem_type, 28472cd19bdbSCharles Keepax unsigned int mem_addr, u32 data) 28482cd19bdbSCharles Keepax { 28492cd19bdbSCharles Keepax struct wm_adsp_region const *mem = wm_adsp_find_region(dsp, mem_type); 28502cd19bdbSCharles Keepax unsigned int reg; 28512cd19bdbSCharles Keepax 28522cd19bdbSCharles Keepax if (!mem) 28532cd19bdbSCharles Keepax return -EINVAL; 28542cd19bdbSCharles Keepax 28552cd19bdbSCharles Keepax reg = wm_adsp_region_to_reg(mem, mem_addr); 28562cd19bdbSCharles Keepax 28572cd19bdbSCharles Keepax data = cpu_to_be32(data & 0x00ffffffu); 28582cd19bdbSCharles Keepax 28592cd19bdbSCharles Keepax return regmap_raw_write(dsp->regmap, reg, &data, sizeof(data)); 28602cd19bdbSCharles Keepax } 28612cd19bdbSCharles Keepax 28622cd19bdbSCharles Keepax static inline int wm_adsp_buffer_read(struct wm_adsp_compr_buf *buf, 28632cd19bdbSCharles Keepax unsigned int field_offset, u32 *data) 28642cd19bdbSCharles Keepax { 28652cd19bdbSCharles Keepax return wm_adsp_read_data_word(buf->dsp, WMFW_ADSP2_XM, 28662cd19bdbSCharles Keepax buf->host_buf_ptr + field_offset, data); 28672cd19bdbSCharles Keepax } 28682cd19bdbSCharles Keepax 28692cd19bdbSCharles Keepax static inline int wm_adsp_buffer_write(struct wm_adsp_compr_buf *buf, 28702cd19bdbSCharles Keepax unsigned int field_offset, u32 data) 28712cd19bdbSCharles Keepax { 28722cd19bdbSCharles Keepax return wm_adsp_write_data_word(buf->dsp, WMFW_ADSP2_XM, 28732cd19bdbSCharles Keepax buf->host_buf_ptr + field_offset, data); 28742cd19bdbSCharles Keepax } 28752cd19bdbSCharles Keepax 28762cd19bdbSCharles Keepax static int wm_adsp_buffer_locate(struct wm_adsp_compr_buf *buf) 28772cd19bdbSCharles Keepax { 28782cd19bdbSCharles Keepax struct wm_adsp_alg_region *alg_region; 28792cd19bdbSCharles Keepax struct wm_adsp *dsp = buf->dsp; 28802cd19bdbSCharles Keepax u32 xmalg, addr, magic; 28812cd19bdbSCharles Keepax int i, ret; 28822cd19bdbSCharles Keepax 28832cd19bdbSCharles Keepax alg_region = wm_adsp_find_alg_region(dsp, WMFW_ADSP2_XM, dsp->fw_id); 28842cd19bdbSCharles Keepax xmalg = sizeof(struct wm_adsp_system_config_xm_hdr) / sizeof(__be32); 28852cd19bdbSCharles Keepax 28862cd19bdbSCharles Keepax addr = alg_region->base + xmalg + ALG_XM_FIELD(magic); 28872cd19bdbSCharles Keepax ret = wm_adsp_read_data_word(dsp, WMFW_ADSP2_XM, addr, &magic); 28882cd19bdbSCharles Keepax if (ret < 0) 28892cd19bdbSCharles Keepax return ret; 28902cd19bdbSCharles Keepax 28912cd19bdbSCharles Keepax if (magic != WM_ADSP_ALG_XM_STRUCT_MAGIC) 28922cd19bdbSCharles Keepax return -EINVAL; 28932cd19bdbSCharles Keepax 28942cd19bdbSCharles Keepax addr = alg_region->base + xmalg + ALG_XM_FIELD(host_buf_ptr); 28952cd19bdbSCharles Keepax for (i = 0; i < 5; ++i) { 28962cd19bdbSCharles Keepax ret = wm_adsp_read_data_word(dsp, WMFW_ADSP2_XM, addr, 28972cd19bdbSCharles Keepax &buf->host_buf_ptr); 28982cd19bdbSCharles Keepax if (ret < 0) 28992cd19bdbSCharles Keepax return ret; 29002cd19bdbSCharles Keepax 29012cd19bdbSCharles Keepax if (buf->host_buf_ptr) 29022cd19bdbSCharles Keepax break; 29032cd19bdbSCharles Keepax 29042cd19bdbSCharles Keepax usleep_range(1000, 2000); 29052cd19bdbSCharles Keepax } 29062cd19bdbSCharles Keepax 29072cd19bdbSCharles Keepax if (!buf->host_buf_ptr) 29082cd19bdbSCharles Keepax return -EIO; 29092cd19bdbSCharles Keepax 29102cd19bdbSCharles Keepax adsp_dbg(dsp, "host_buf_ptr=%x\n", buf->host_buf_ptr); 29112cd19bdbSCharles Keepax 29122cd19bdbSCharles Keepax return 0; 29132cd19bdbSCharles Keepax } 29142cd19bdbSCharles Keepax 29152cd19bdbSCharles Keepax static int wm_adsp_buffer_populate(struct wm_adsp_compr_buf *buf) 29162cd19bdbSCharles Keepax { 29172cd19bdbSCharles Keepax const struct wm_adsp_fw_caps *caps = wm_adsp_fw[buf->dsp->fw].caps; 29182cd19bdbSCharles Keepax struct wm_adsp_buffer_region *region; 29192cd19bdbSCharles Keepax u32 offset = 0; 29202cd19bdbSCharles Keepax int i, ret; 29212cd19bdbSCharles Keepax 29222cd19bdbSCharles Keepax for (i = 0; i < caps->num_regions; ++i) { 29232cd19bdbSCharles Keepax region = &buf->regions[i]; 29242cd19bdbSCharles Keepax 29252cd19bdbSCharles Keepax region->offset = offset; 29262cd19bdbSCharles Keepax region->mem_type = caps->region_defs[i].mem_type; 29272cd19bdbSCharles Keepax 29282cd19bdbSCharles Keepax ret = wm_adsp_buffer_read(buf, caps->region_defs[i].base_offset, 29292cd19bdbSCharles Keepax ®ion->base_addr); 29302cd19bdbSCharles Keepax if (ret < 0) 29312cd19bdbSCharles Keepax return ret; 29322cd19bdbSCharles Keepax 29332cd19bdbSCharles Keepax ret = wm_adsp_buffer_read(buf, caps->region_defs[i].size_offset, 29342cd19bdbSCharles Keepax &offset); 29352cd19bdbSCharles Keepax if (ret < 0) 29362cd19bdbSCharles Keepax return ret; 29372cd19bdbSCharles Keepax 29382cd19bdbSCharles Keepax region->cumulative_size = offset; 29392cd19bdbSCharles Keepax 29402cd19bdbSCharles Keepax adsp_dbg(buf->dsp, 29412cd19bdbSCharles Keepax "region=%d type=%d base=%04x off=%04x size=%04x\n", 29422cd19bdbSCharles Keepax i, region->mem_type, region->base_addr, 29432cd19bdbSCharles Keepax region->offset, region->cumulative_size); 29442cd19bdbSCharles Keepax } 29452cd19bdbSCharles Keepax 29462cd19bdbSCharles Keepax return 0; 29472cd19bdbSCharles Keepax } 29482cd19bdbSCharles Keepax 29492cd19bdbSCharles Keepax static int wm_adsp_buffer_init(struct wm_adsp *dsp) 29502cd19bdbSCharles Keepax { 29512cd19bdbSCharles Keepax struct wm_adsp_compr_buf *buf; 29522cd19bdbSCharles Keepax int ret; 29532cd19bdbSCharles Keepax 29542cd19bdbSCharles Keepax buf = kzalloc(sizeof(*buf), GFP_KERNEL); 29552cd19bdbSCharles Keepax if (!buf) 29562cd19bdbSCharles Keepax return -ENOMEM; 29572cd19bdbSCharles Keepax 29582cd19bdbSCharles Keepax buf->dsp = dsp; 2959565ace46SCharles Keepax buf->read_index = -1; 2960565ace46SCharles Keepax buf->irq_count = 0xFFFFFFFF; 29612cd19bdbSCharles Keepax 29622cd19bdbSCharles Keepax ret = wm_adsp_buffer_locate(buf); 29632cd19bdbSCharles Keepax if (ret < 0) { 29642cd19bdbSCharles Keepax adsp_err(dsp, "Failed to acquire host buffer: %d\n", ret); 29652cd19bdbSCharles Keepax goto err_buffer; 29662cd19bdbSCharles Keepax } 29672cd19bdbSCharles Keepax 29682cd19bdbSCharles Keepax buf->regions = kcalloc(wm_adsp_fw[dsp->fw].caps->num_regions, 29692cd19bdbSCharles Keepax sizeof(*buf->regions), GFP_KERNEL); 29702cd19bdbSCharles Keepax if (!buf->regions) { 29712cd19bdbSCharles Keepax ret = -ENOMEM; 29722cd19bdbSCharles Keepax goto err_buffer; 29732cd19bdbSCharles Keepax } 29742cd19bdbSCharles Keepax 29752cd19bdbSCharles Keepax ret = wm_adsp_buffer_populate(buf); 29762cd19bdbSCharles Keepax if (ret < 0) { 29772cd19bdbSCharles Keepax adsp_err(dsp, "Failed to populate host buffer: %d\n", ret); 29782cd19bdbSCharles Keepax goto err_regions; 29792cd19bdbSCharles Keepax } 29802cd19bdbSCharles Keepax 29812cd19bdbSCharles Keepax dsp->buffer = buf; 29822cd19bdbSCharles Keepax 29832cd19bdbSCharles Keepax return 0; 29842cd19bdbSCharles Keepax 29852cd19bdbSCharles Keepax err_regions: 29862cd19bdbSCharles Keepax kfree(buf->regions); 29872cd19bdbSCharles Keepax err_buffer: 29882cd19bdbSCharles Keepax kfree(buf); 29892cd19bdbSCharles Keepax return ret; 29902cd19bdbSCharles Keepax } 29912cd19bdbSCharles Keepax 29922cd19bdbSCharles Keepax static int wm_adsp_buffer_free(struct wm_adsp *dsp) 29932cd19bdbSCharles Keepax { 29942cd19bdbSCharles Keepax if (dsp->buffer) { 2995721be3beSCharles Keepax wm_adsp_compr_detach(dsp->buffer->compr); 2996721be3beSCharles Keepax 29972cd19bdbSCharles Keepax kfree(dsp->buffer->regions); 29982cd19bdbSCharles Keepax kfree(dsp->buffer); 29992cd19bdbSCharles Keepax 30002cd19bdbSCharles Keepax dsp->buffer = NULL; 30012cd19bdbSCharles Keepax } 30022cd19bdbSCharles Keepax 30032cd19bdbSCharles Keepax return 0; 30042cd19bdbSCharles Keepax } 30052cd19bdbSCharles Keepax 300695fe9597SCharles Keepax int wm_adsp_compr_trigger(struct snd_compr_stream *stream, int cmd) 300795fe9597SCharles Keepax { 300895fe9597SCharles Keepax struct wm_adsp_compr *compr = stream->runtime->private_data; 300995fe9597SCharles Keepax struct wm_adsp *dsp = compr->dsp; 301095fe9597SCharles Keepax int ret = 0; 301195fe9597SCharles Keepax 301295fe9597SCharles Keepax adsp_dbg(dsp, "Trigger: %d\n", cmd); 301395fe9597SCharles Keepax 301495fe9597SCharles Keepax mutex_lock(&dsp->pwr_lock); 301595fe9597SCharles Keepax 301695fe9597SCharles Keepax switch (cmd) { 301795fe9597SCharles Keepax case SNDRV_PCM_TRIGGER_START: 301895fe9597SCharles Keepax if (wm_adsp_compr_attached(compr)) 301995fe9597SCharles Keepax break; 302095fe9597SCharles Keepax 302195fe9597SCharles Keepax ret = wm_adsp_compr_attach(compr); 302295fe9597SCharles Keepax if (ret < 0) { 302395fe9597SCharles Keepax adsp_err(dsp, "Failed to link buffer and stream: %d\n", 302495fe9597SCharles Keepax ret); 302595fe9597SCharles Keepax break; 302695fe9597SCharles Keepax } 3027565ace46SCharles Keepax 3028565ace46SCharles Keepax /* Trigger the IRQ at one fragment of data */ 3029565ace46SCharles Keepax ret = wm_adsp_buffer_write(compr->buf, 3030565ace46SCharles Keepax HOST_BUFFER_FIELD(high_water_mark), 3031565ace46SCharles Keepax wm_adsp_compr_frag_words(compr)); 3032565ace46SCharles Keepax if (ret < 0) { 3033565ace46SCharles Keepax adsp_err(dsp, "Failed to set high water mark: %d\n", 3034565ace46SCharles Keepax ret); 3035565ace46SCharles Keepax break; 3036565ace46SCharles Keepax } 303795fe9597SCharles Keepax break; 303895fe9597SCharles Keepax case SNDRV_PCM_TRIGGER_STOP: 303995fe9597SCharles Keepax break; 304095fe9597SCharles Keepax default: 304195fe9597SCharles Keepax ret = -EINVAL; 304295fe9597SCharles Keepax break; 304395fe9597SCharles Keepax } 304495fe9597SCharles Keepax 304595fe9597SCharles Keepax mutex_unlock(&dsp->pwr_lock); 304695fe9597SCharles Keepax 304795fe9597SCharles Keepax return ret; 304895fe9597SCharles Keepax } 304995fe9597SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_trigger); 305095fe9597SCharles Keepax 3051565ace46SCharles Keepax static inline int wm_adsp_buffer_size(struct wm_adsp_compr_buf *buf) 3052565ace46SCharles Keepax { 3053565ace46SCharles Keepax int last_region = wm_adsp_fw[buf->dsp->fw].caps->num_regions - 1; 3054565ace46SCharles Keepax 3055565ace46SCharles Keepax return buf->regions[last_region].cumulative_size; 3056565ace46SCharles Keepax } 3057565ace46SCharles Keepax 3058565ace46SCharles Keepax static int wm_adsp_buffer_update_avail(struct wm_adsp_compr_buf *buf) 3059565ace46SCharles Keepax { 3060565ace46SCharles Keepax u32 next_read_index, next_write_index; 3061565ace46SCharles Keepax int write_index, read_index, avail; 3062565ace46SCharles Keepax int ret; 3063565ace46SCharles Keepax 3064565ace46SCharles Keepax /* Only sync read index if we haven't already read a valid index */ 3065565ace46SCharles Keepax if (buf->read_index < 0) { 3066565ace46SCharles Keepax ret = wm_adsp_buffer_read(buf, 3067565ace46SCharles Keepax HOST_BUFFER_FIELD(next_read_index), 3068565ace46SCharles Keepax &next_read_index); 3069565ace46SCharles Keepax if (ret < 0) 3070565ace46SCharles Keepax return ret; 3071565ace46SCharles Keepax 3072565ace46SCharles Keepax read_index = sign_extend32(next_read_index, 23); 3073565ace46SCharles Keepax 3074565ace46SCharles Keepax if (read_index < 0) { 3075565ace46SCharles Keepax adsp_dbg(buf->dsp, "Avail check on unstarted stream\n"); 3076565ace46SCharles Keepax return 0; 3077565ace46SCharles Keepax } 3078565ace46SCharles Keepax 3079565ace46SCharles Keepax buf->read_index = read_index; 3080565ace46SCharles Keepax } 3081565ace46SCharles Keepax 3082565ace46SCharles Keepax ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(next_write_index), 3083565ace46SCharles Keepax &next_write_index); 3084565ace46SCharles Keepax if (ret < 0) 3085565ace46SCharles Keepax return ret; 3086565ace46SCharles Keepax 3087565ace46SCharles Keepax write_index = sign_extend32(next_write_index, 23); 3088565ace46SCharles Keepax 3089565ace46SCharles Keepax avail = write_index - buf->read_index; 3090565ace46SCharles Keepax if (avail < 0) 3091565ace46SCharles Keepax avail += wm_adsp_buffer_size(buf); 3092565ace46SCharles Keepax 3093565ace46SCharles Keepax adsp_dbg(buf->dsp, "readindex=0x%x, writeindex=0x%x, avail=%d\n", 309433d740e0SCharles Keepax buf->read_index, write_index, avail * WM_ADSP_DATA_WORD_SIZE); 3095565ace46SCharles Keepax 3096565ace46SCharles Keepax buf->avail = avail; 3097565ace46SCharles Keepax 3098565ace46SCharles Keepax return 0; 3099565ace46SCharles Keepax } 3100565ace46SCharles Keepax 31019771b18aSCharles Keepax static int wm_adsp_buffer_get_error(struct wm_adsp_compr_buf *buf) 31029771b18aSCharles Keepax { 31039771b18aSCharles Keepax int ret; 31049771b18aSCharles Keepax 31059771b18aSCharles Keepax ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(error), &buf->error); 31069771b18aSCharles Keepax if (ret < 0) { 31079771b18aSCharles Keepax adsp_err(buf->dsp, "Failed to check buffer error: %d\n", ret); 31089771b18aSCharles Keepax return ret; 31099771b18aSCharles Keepax } 31109771b18aSCharles Keepax if (buf->error != 0) { 31119771b18aSCharles Keepax adsp_err(buf->dsp, "Buffer error occurred: %d\n", buf->error); 31129771b18aSCharles Keepax return -EIO; 31139771b18aSCharles Keepax } 31149771b18aSCharles Keepax 31159771b18aSCharles Keepax return 0; 31169771b18aSCharles Keepax } 31179771b18aSCharles Keepax 3118565ace46SCharles Keepax int wm_adsp_compr_handle_irq(struct wm_adsp *dsp) 3119565ace46SCharles Keepax { 3120612047f0SCharles Keepax struct wm_adsp_compr_buf *buf; 3121612047f0SCharles Keepax struct wm_adsp_compr *compr; 3122565ace46SCharles Keepax int ret = 0; 3123565ace46SCharles Keepax 3124565ace46SCharles Keepax mutex_lock(&dsp->pwr_lock); 3125565ace46SCharles Keepax 3126612047f0SCharles Keepax buf = dsp->buffer; 3127612047f0SCharles Keepax compr = dsp->compr; 3128612047f0SCharles Keepax 3129565ace46SCharles Keepax if (!buf) { 3130565ace46SCharles Keepax ret = -ENODEV; 3131565ace46SCharles Keepax goto out; 3132565ace46SCharles Keepax } 3133565ace46SCharles Keepax 3134565ace46SCharles Keepax adsp_dbg(dsp, "Handling buffer IRQ\n"); 3135565ace46SCharles Keepax 31369771b18aSCharles Keepax ret = wm_adsp_buffer_get_error(buf); 31379771b18aSCharles Keepax if (ret < 0) 31385847609eSCharles Keepax goto out_notify; /* Wake poll to report error */ 3139565ace46SCharles Keepax 3140565ace46SCharles Keepax ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(irq_count), 3141565ace46SCharles Keepax &buf->irq_count); 3142565ace46SCharles Keepax if (ret < 0) { 3143565ace46SCharles Keepax adsp_err(dsp, "Failed to get irq_count: %d\n", ret); 3144565ace46SCharles Keepax goto out; 3145565ace46SCharles Keepax } 3146565ace46SCharles Keepax 3147565ace46SCharles Keepax ret = wm_adsp_buffer_update_avail(buf); 3148565ace46SCharles Keepax if (ret < 0) { 3149565ace46SCharles Keepax adsp_err(dsp, "Error reading avail: %d\n", ret); 3150565ace46SCharles Keepax goto out; 3151565ace46SCharles Keepax } 3152565ace46SCharles Keepax 315320b7f7c5SCharles Keepax if (wm_adsp_fw[dsp->fw].voice_trigger && buf->irq_count == 2) 315420b7f7c5SCharles Keepax ret = WM_ADSP_COMPR_VOICE_TRIGGER; 315520b7f7c5SCharles Keepax 31565847609eSCharles Keepax out_notify: 3157c7dae7c4SCharles Keepax if (compr && compr->stream) 315883a40ce9SCharles Keepax snd_compr_fragment_elapsed(compr->stream); 315983a40ce9SCharles Keepax 3160565ace46SCharles Keepax out: 3161565ace46SCharles Keepax mutex_unlock(&dsp->pwr_lock); 3162565ace46SCharles Keepax 3163565ace46SCharles Keepax return ret; 3164565ace46SCharles Keepax } 3165565ace46SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_handle_irq); 3166565ace46SCharles Keepax 3167565ace46SCharles Keepax static int wm_adsp_buffer_reenable_irq(struct wm_adsp_compr_buf *buf) 3168565ace46SCharles Keepax { 3169565ace46SCharles Keepax if (buf->irq_count & 0x01) 3170565ace46SCharles Keepax return 0; 3171565ace46SCharles Keepax 3172565ace46SCharles Keepax adsp_dbg(buf->dsp, "Enable IRQ(0x%x) for next fragment\n", 3173565ace46SCharles Keepax buf->irq_count); 3174565ace46SCharles Keepax 3175565ace46SCharles Keepax buf->irq_count |= 0x01; 3176565ace46SCharles Keepax 3177565ace46SCharles Keepax return wm_adsp_buffer_write(buf, HOST_BUFFER_FIELD(irq_ack), 3178565ace46SCharles Keepax buf->irq_count); 3179565ace46SCharles Keepax } 3180565ace46SCharles Keepax 3181565ace46SCharles Keepax int wm_adsp_compr_pointer(struct snd_compr_stream *stream, 3182565ace46SCharles Keepax struct snd_compr_tstamp *tstamp) 3183565ace46SCharles Keepax { 3184565ace46SCharles Keepax struct wm_adsp_compr *compr = stream->runtime->private_data; 3185565ace46SCharles Keepax struct wm_adsp *dsp = compr->dsp; 3186612047f0SCharles Keepax struct wm_adsp_compr_buf *buf; 3187565ace46SCharles Keepax int ret = 0; 3188565ace46SCharles Keepax 3189565ace46SCharles Keepax adsp_dbg(dsp, "Pointer request\n"); 3190565ace46SCharles Keepax 3191565ace46SCharles Keepax mutex_lock(&dsp->pwr_lock); 3192565ace46SCharles Keepax 3193612047f0SCharles Keepax buf = compr->buf; 3194612047f0SCharles Keepax 319528ee3d73SCharles Keepax if (!compr->buf || compr->buf->error) { 31968d280664SCharles Keepax snd_compr_stop_error(stream, SNDRV_PCM_STATE_XRUN); 3197565ace46SCharles Keepax ret = -EIO; 3198565ace46SCharles Keepax goto out; 3199565ace46SCharles Keepax } 3200565ace46SCharles Keepax 3201565ace46SCharles Keepax if (buf->avail < wm_adsp_compr_frag_words(compr)) { 3202565ace46SCharles Keepax ret = wm_adsp_buffer_update_avail(buf); 3203565ace46SCharles Keepax if (ret < 0) { 3204565ace46SCharles Keepax adsp_err(dsp, "Error reading avail: %d\n", ret); 3205565ace46SCharles Keepax goto out; 3206565ace46SCharles Keepax } 3207565ace46SCharles Keepax 3208565ace46SCharles Keepax /* 3209565ace46SCharles Keepax * If we really have less than 1 fragment available tell the 3210565ace46SCharles Keepax * DSP to inform us once a whole fragment is available. 3211565ace46SCharles Keepax */ 3212565ace46SCharles Keepax if (buf->avail < wm_adsp_compr_frag_words(compr)) { 32135847609eSCharles Keepax ret = wm_adsp_buffer_get_error(buf); 32148d280664SCharles Keepax if (ret < 0) { 32158d280664SCharles Keepax if (compr->buf->error) 32168d280664SCharles Keepax snd_compr_stop_error(stream, 32178d280664SCharles Keepax SNDRV_PCM_STATE_XRUN); 32185847609eSCharles Keepax goto out; 32198d280664SCharles Keepax } 32205847609eSCharles Keepax 3221565ace46SCharles Keepax ret = wm_adsp_buffer_reenable_irq(buf); 3222565ace46SCharles Keepax if (ret < 0) { 3223565ace46SCharles Keepax adsp_err(dsp, 3224565ace46SCharles Keepax "Failed to re-enable buffer IRQ: %d\n", 3225565ace46SCharles Keepax ret); 3226565ace46SCharles Keepax goto out; 3227565ace46SCharles Keepax } 3228565ace46SCharles Keepax } 3229565ace46SCharles Keepax } 3230565ace46SCharles Keepax 3231565ace46SCharles Keepax tstamp->copied_total = compr->copied_total; 3232565ace46SCharles Keepax tstamp->copied_total += buf->avail * WM_ADSP_DATA_WORD_SIZE; 3233da2b3358SCharles Keepax tstamp->sampling_rate = compr->sample_rate; 3234565ace46SCharles Keepax 3235565ace46SCharles Keepax out: 3236565ace46SCharles Keepax mutex_unlock(&dsp->pwr_lock); 3237565ace46SCharles Keepax 3238565ace46SCharles Keepax return ret; 3239565ace46SCharles Keepax } 3240565ace46SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_pointer); 3241565ace46SCharles Keepax 324283a40ce9SCharles Keepax static int wm_adsp_buffer_capture_block(struct wm_adsp_compr *compr, int target) 324383a40ce9SCharles Keepax { 324483a40ce9SCharles Keepax struct wm_adsp_compr_buf *buf = compr->buf; 324583a40ce9SCharles Keepax u8 *pack_in = (u8 *)compr->raw_buf; 324683a40ce9SCharles Keepax u8 *pack_out = (u8 *)compr->raw_buf; 324783a40ce9SCharles Keepax unsigned int adsp_addr; 324883a40ce9SCharles Keepax int mem_type, nwords, max_read; 324983a40ce9SCharles Keepax int i, j, ret; 325083a40ce9SCharles Keepax 325183a40ce9SCharles Keepax /* Calculate read parameters */ 325283a40ce9SCharles Keepax for (i = 0; i < wm_adsp_fw[buf->dsp->fw].caps->num_regions; ++i) 325383a40ce9SCharles Keepax if (buf->read_index < buf->regions[i].cumulative_size) 325483a40ce9SCharles Keepax break; 325583a40ce9SCharles Keepax 325683a40ce9SCharles Keepax if (i == wm_adsp_fw[buf->dsp->fw].caps->num_regions) 325783a40ce9SCharles Keepax return -EINVAL; 325883a40ce9SCharles Keepax 325983a40ce9SCharles Keepax mem_type = buf->regions[i].mem_type; 326083a40ce9SCharles Keepax adsp_addr = buf->regions[i].base_addr + 326183a40ce9SCharles Keepax (buf->read_index - buf->regions[i].offset); 326283a40ce9SCharles Keepax 326383a40ce9SCharles Keepax max_read = wm_adsp_compr_frag_words(compr); 326483a40ce9SCharles Keepax nwords = buf->regions[i].cumulative_size - buf->read_index; 326583a40ce9SCharles Keepax 326683a40ce9SCharles Keepax if (nwords > target) 326783a40ce9SCharles Keepax nwords = target; 326883a40ce9SCharles Keepax if (nwords > buf->avail) 326983a40ce9SCharles Keepax nwords = buf->avail; 327083a40ce9SCharles Keepax if (nwords > max_read) 327183a40ce9SCharles Keepax nwords = max_read; 327283a40ce9SCharles Keepax if (!nwords) 327383a40ce9SCharles Keepax return 0; 327483a40ce9SCharles Keepax 327583a40ce9SCharles Keepax /* Read data from DSP */ 327683a40ce9SCharles Keepax ret = wm_adsp_read_data_block(buf->dsp, mem_type, adsp_addr, 327783a40ce9SCharles Keepax nwords, compr->raw_buf); 327883a40ce9SCharles Keepax if (ret < 0) 327983a40ce9SCharles Keepax return ret; 328083a40ce9SCharles Keepax 328183a40ce9SCharles Keepax /* Remove the padding bytes from the data read from the DSP */ 328283a40ce9SCharles Keepax for (i = 0; i < nwords; i++) { 328383a40ce9SCharles Keepax for (j = 0; j < WM_ADSP_DATA_WORD_SIZE; j++) 328483a40ce9SCharles Keepax *pack_out++ = *pack_in++; 328583a40ce9SCharles Keepax 328683a40ce9SCharles Keepax pack_in += sizeof(*(compr->raw_buf)) - WM_ADSP_DATA_WORD_SIZE; 328783a40ce9SCharles Keepax } 328883a40ce9SCharles Keepax 328983a40ce9SCharles Keepax /* update read index to account for words read */ 329083a40ce9SCharles Keepax buf->read_index += nwords; 329183a40ce9SCharles Keepax if (buf->read_index == wm_adsp_buffer_size(buf)) 329283a40ce9SCharles Keepax buf->read_index = 0; 329383a40ce9SCharles Keepax 329483a40ce9SCharles Keepax ret = wm_adsp_buffer_write(buf, HOST_BUFFER_FIELD(next_read_index), 329583a40ce9SCharles Keepax buf->read_index); 329683a40ce9SCharles Keepax if (ret < 0) 329783a40ce9SCharles Keepax return ret; 329883a40ce9SCharles Keepax 329983a40ce9SCharles Keepax /* update avail to account for words read */ 330083a40ce9SCharles Keepax buf->avail -= nwords; 330183a40ce9SCharles Keepax 330283a40ce9SCharles Keepax return nwords; 330383a40ce9SCharles Keepax } 330483a40ce9SCharles Keepax 330583a40ce9SCharles Keepax static int wm_adsp_compr_read(struct wm_adsp_compr *compr, 330683a40ce9SCharles Keepax char __user *buf, size_t count) 330783a40ce9SCharles Keepax { 330883a40ce9SCharles Keepax struct wm_adsp *dsp = compr->dsp; 330983a40ce9SCharles Keepax int ntotal = 0; 331083a40ce9SCharles Keepax int nwords, nbytes; 331183a40ce9SCharles Keepax 331283a40ce9SCharles Keepax adsp_dbg(dsp, "Requested read of %zu bytes\n", count); 331383a40ce9SCharles Keepax 331428ee3d73SCharles Keepax if (!compr->buf || compr->buf->error) { 33158d280664SCharles Keepax snd_compr_stop_error(compr->stream, SNDRV_PCM_STATE_XRUN); 331683a40ce9SCharles Keepax return -EIO; 33178d280664SCharles Keepax } 331883a40ce9SCharles Keepax 331983a40ce9SCharles Keepax count /= WM_ADSP_DATA_WORD_SIZE; 332083a40ce9SCharles Keepax 332183a40ce9SCharles Keepax do { 332283a40ce9SCharles Keepax nwords = wm_adsp_buffer_capture_block(compr, count); 332383a40ce9SCharles Keepax if (nwords < 0) { 332483a40ce9SCharles Keepax adsp_err(dsp, "Failed to capture block: %d\n", nwords); 332583a40ce9SCharles Keepax return nwords; 332683a40ce9SCharles Keepax } 332783a40ce9SCharles Keepax 332883a40ce9SCharles Keepax nbytes = nwords * WM_ADSP_DATA_WORD_SIZE; 332983a40ce9SCharles Keepax 333083a40ce9SCharles Keepax adsp_dbg(dsp, "Read %d bytes\n", nbytes); 333183a40ce9SCharles Keepax 333283a40ce9SCharles Keepax if (copy_to_user(buf + ntotal, compr->raw_buf, nbytes)) { 333383a40ce9SCharles Keepax adsp_err(dsp, "Failed to copy data to user: %d, %d\n", 333483a40ce9SCharles Keepax ntotal, nbytes); 333583a40ce9SCharles Keepax return -EFAULT; 333683a40ce9SCharles Keepax } 333783a40ce9SCharles Keepax 333883a40ce9SCharles Keepax count -= nwords; 333983a40ce9SCharles Keepax ntotal += nbytes; 334083a40ce9SCharles Keepax } while (nwords > 0 && count > 0); 334183a40ce9SCharles Keepax 334283a40ce9SCharles Keepax compr->copied_total += ntotal; 334383a40ce9SCharles Keepax 334483a40ce9SCharles Keepax return ntotal; 334583a40ce9SCharles Keepax } 334683a40ce9SCharles Keepax 334783a40ce9SCharles Keepax int wm_adsp_compr_copy(struct snd_compr_stream *stream, char __user *buf, 334883a40ce9SCharles Keepax size_t count) 334983a40ce9SCharles Keepax { 335083a40ce9SCharles Keepax struct wm_adsp_compr *compr = stream->runtime->private_data; 335183a40ce9SCharles Keepax struct wm_adsp *dsp = compr->dsp; 335283a40ce9SCharles Keepax int ret; 335383a40ce9SCharles Keepax 335483a40ce9SCharles Keepax mutex_lock(&dsp->pwr_lock); 335583a40ce9SCharles Keepax 335683a40ce9SCharles Keepax if (stream->direction == SND_COMPRESS_CAPTURE) 335783a40ce9SCharles Keepax ret = wm_adsp_compr_read(compr, buf, count); 335883a40ce9SCharles Keepax else 335983a40ce9SCharles Keepax ret = -ENOTSUPP; 336083a40ce9SCharles Keepax 336183a40ce9SCharles Keepax mutex_unlock(&dsp->pwr_lock); 336283a40ce9SCharles Keepax 336383a40ce9SCharles Keepax return ret; 336483a40ce9SCharles Keepax } 336583a40ce9SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_copy); 336683a40ce9SCharles Keepax 33670a37c6efSPraveen Diwakar MODULE_LICENSE("GPL v2"); 3368