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 165cf17c83cSMark Brown struct wm_adsp_buf { 166cf17c83cSMark Brown struct list_head list; 167cf17c83cSMark Brown void *buf; 168cf17c83cSMark Brown }; 169cf17c83cSMark Brown 170cf17c83cSMark Brown static struct wm_adsp_buf *wm_adsp_buf_alloc(const void *src, size_t len, 171cf17c83cSMark Brown struct list_head *list) 172cf17c83cSMark Brown { 173cf17c83cSMark Brown struct wm_adsp_buf *buf = kzalloc(sizeof(*buf), GFP_KERNEL); 174cf17c83cSMark Brown 175cf17c83cSMark Brown if (buf == NULL) 176cf17c83cSMark Brown return NULL; 177cf17c83cSMark Brown 178cdcd7f72SCharles Keepax buf->buf = vmalloc(len); 179cf17c83cSMark Brown if (!buf->buf) { 180cdcd7f72SCharles Keepax vfree(buf); 181cf17c83cSMark Brown return NULL; 182cf17c83cSMark Brown } 183cdcd7f72SCharles Keepax memcpy(buf->buf, src, len); 184cf17c83cSMark Brown 185cf17c83cSMark Brown if (list) 186cf17c83cSMark Brown list_add_tail(&buf->list, list); 187cf17c83cSMark Brown 188cf17c83cSMark Brown return buf; 189cf17c83cSMark Brown } 190cf17c83cSMark Brown 191cf17c83cSMark Brown static void wm_adsp_buf_free(struct list_head *list) 192cf17c83cSMark Brown { 193cf17c83cSMark Brown while (!list_empty(list)) { 194cf17c83cSMark Brown struct wm_adsp_buf *buf = list_first_entry(list, 195cf17c83cSMark Brown struct wm_adsp_buf, 196cf17c83cSMark Brown list); 197cf17c83cSMark Brown list_del(&buf->list); 198cdcd7f72SCharles Keepax vfree(buf->buf); 199cf17c83cSMark Brown kfree(buf); 200cf17c83cSMark Brown } 201cf17c83cSMark Brown } 202cf17c83cSMark Brown 203dd84f925SMark Brown #define WM_ADSP_FW_MBC_VSS 0 20404d1300fSCharles Keepax #define WM_ADSP_FW_HIFI 1 20504d1300fSCharles Keepax #define WM_ADSP_FW_TX 2 20604d1300fSCharles Keepax #define WM_ADSP_FW_TX_SPK 3 20704d1300fSCharles Keepax #define WM_ADSP_FW_RX 4 20804d1300fSCharles Keepax #define WM_ADSP_FW_RX_ANC 5 20904d1300fSCharles Keepax #define WM_ADSP_FW_CTRL 6 21004d1300fSCharles Keepax #define WM_ADSP_FW_ASR 7 21104d1300fSCharles Keepax #define WM_ADSP_FW_TRACE 8 21204d1300fSCharles Keepax #define WM_ADSP_FW_SPK_PROT 9 21304d1300fSCharles Keepax #define WM_ADSP_FW_MISC 10 21404d1300fSCharles Keepax 21504d1300fSCharles Keepax #define WM_ADSP_NUM_FW 11 216dd84f925SMark Brown 2171023dbd9SMark Brown static const char *wm_adsp_fw_text[WM_ADSP_NUM_FW] = { 218dd84f925SMark Brown [WM_ADSP_FW_MBC_VSS] = "MBC/VSS", 21904d1300fSCharles Keepax [WM_ADSP_FW_HIFI] = "MasterHiFi", 220dd84f925SMark Brown [WM_ADSP_FW_TX] = "Tx", 221dd84f925SMark Brown [WM_ADSP_FW_TX_SPK] = "Tx Speaker", 22204d1300fSCharles Keepax [WM_ADSP_FW_RX] = "Rx", 223dd84f925SMark Brown [WM_ADSP_FW_RX_ANC] = "Rx ANC", 22404d1300fSCharles Keepax [WM_ADSP_FW_CTRL] = "Voice Ctrl", 22504d1300fSCharles Keepax [WM_ADSP_FW_ASR] = "ASR Assist", 22604d1300fSCharles Keepax [WM_ADSP_FW_TRACE] = "Dbg Trace", 22704d1300fSCharles Keepax [WM_ADSP_FW_SPK_PROT] = "Protection", 22804d1300fSCharles Keepax [WM_ADSP_FW_MISC] = "Misc", 2291023dbd9SMark Brown }; 2301023dbd9SMark Brown 2312cd19bdbSCharles Keepax struct wm_adsp_system_config_xm_hdr { 2322cd19bdbSCharles Keepax __be32 sys_enable; 2332cd19bdbSCharles Keepax __be32 fw_id; 2342cd19bdbSCharles Keepax __be32 fw_rev; 2352cd19bdbSCharles Keepax __be32 boot_status; 2362cd19bdbSCharles Keepax __be32 watchdog; 2372cd19bdbSCharles Keepax __be32 dma_buffer_size; 2382cd19bdbSCharles Keepax __be32 rdma[6]; 2392cd19bdbSCharles Keepax __be32 wdma[8]; 2402cd19bdbSCharles Keepax __be32 build_job_name[3]; 2412cd19bdbSCharles Keepax __be32 build_job_number; 2422cd19bdbSCharles Keepax }; 2432cd19bdbSCharles Keepax 2442cd19bdbSCharles Keepax struct wm_adsp_alg_xm_struct { 2452cd19bdbSCharles Keepax __be32 magic; 2462cd19bdbSCharles Keepax __be32 smoothing; 2472cd19bdbSCharles Keepax __be32 threshold; 2482cd19bdbSCharles Keepax __be32 host_buf_ptr; 2492cd19bdbSCharles Keepax __be32 start_seq; 2502cd19bdbSCharles Keepax __be32 high_water_mark; 2512cd19bdbSCharles Keepax __be32 low_water_mark; 2522cd19bdbSCharles Keepax __be64 smoothed_power; 2532cd19bdbSCharles Keepax }; 2542cd19bdbSCharles Keepax 2552cd19bdbSCharles Keepax struct wm_adsp_buffer { 2562cd19bdbSCharles Keepax __be32 X_buf_base; /* XM base addr of first X area */ 2572cd19bdbSCharles Keepax __be32 X_buf_size; /* Size of 1st X area in words */ 2582cd19bdbSCharles Keepax __be32 X_buf_base2; /* XM base addr of 2nd X area */ 2592cd19bdbSCharles Keepax __be32 X_buf_brk; /* Total X size in words */ 2602cd19bdbSCharles Keepax __be32 Y_buf_base; /* YM base addr of Y area */ 2612cd19bdbSCharles Keepax __be32 wrap; /* Total size X and Y in words */ 2622cd19bdbSCharles Keepax __be32 high_water_mark; /* Point at which IRQ is asserted */ 2632cd19bdbSCharles Keepax __be32 irq_count; /* bits 1-31 count IRQ assertions */ 2642cd19bdbSCharles Keepax __be32 irq_ack; /* acked IRQ count, bit 0 enables IRQ */ 2652cd19bdbSCharles Keepax __be32 next_write_index; /* word index of next write */ 2662cd19bdbSCharles Keepax __be32 next_read_index; /* word index of next read */ 2672cd19bdbSCharles Keepax __be32 error; /* error if any */ 2682cd19bdbSCharles Keepax __be32 oldest_block_index; /* word index of oldest surviving */ 2692cd19bdbSCharles Keepax __be32 requested_rewind; /* how many blocks rewind was done */ 2702cd19bdbSCharles Keepax __be32 reserved_space; /* internal */ 2712cd19bdbSCharles Keepax __be32 min_free; /* min free space since stream start */ 2722cd19bdbSCharles Keepax __be32 blocks_written[2]; /* total blocks written (64 bit) */ 2732cd19bdbSCharles Keepax __be32 words_written[2]; /* total words written (64 bit) */ 2742cd19bdbSCharles Keepax }; 2752cd19bdbSCharles Keepax 276721be3beSCharles Keepax struct wm_adsp_compr; 277721be3beSCharles Keepax 2782cd19bdbSCharles Keepax struct wm_adsp_compr_buf { 2792cd19bdbSCharles Keepax struct wm_adsp *dsp; 280721be3beSCharles Keepax struct wm_adsp_compr *compr; 2812cd19bdbSCharles Keepax 2822cd19bdbSCharles Keepax struct wm_adsp_buffer_region *regions; 2832cd19bdbSCharles Keepax u32 host_buf_ptr; 284565ace46SCharles Keepax 285565ace46SCharles Keepax u32 error; 286565ace46SCharles Keepax u32 irq_count; 287565ace46SCharles Keepax int read_index; 288565ace46SCharles Keepax int avail; 2892cd19bdbSCharles Keepax }; 2902cd19bdbSCharles Keepax 291406abc95SCharles Keepax struct wm_adsp_compr { 292406abc95SCharles Keepax struct wm_adsp *dsp; 29395fe9597SCharles Keepax struct wm_adsp_compr_buf *buf; 294406abc95SCharles Keepax 295406abc95SCharles Keepax struct snd_compr_stream *stream; 296406abc95SCharles Keepax struct snd_compressed_buffer size; 297565ace46SCharles Keepax 29883a40ce9SCharles Keepax u32 *raw_buf; 299565ace46SCharles Keepax unsigned int copied_total; 300da2b3358SCharles Keepax 301da2b3358SCharles Keepax unsigned int sample_rate; 302406abc95SCharles Keepax }; 303406abc95SCharles Keepax 304406abc95SCharles Keepax #define WM_ADSP_DATA_WORD_SIZE 3 305406abc95SCharles Keepax 306406abc95SCharles Keepax #define WM_ADSP_MIN_FRAGMENTS 1 307406abc95SCharles Keepax #define WM_ADSP_MAX_FRAGMENTS 256 308406abc95SCharles Keepax #define WM_ADSP_MIN_FRAGMENT_SIZE (64 * WM_ADSP_DATA_WORD_SIZE) 309406abc95SCharles Keepax #define WM_ADSP_MAX_FRAGMENT_SIZE (4096 * WM_ADSP_DATA_WORD_SIZE) 310406abc95SCharles Keepax 3112cd19bdbSCharles Keepax #define WM_ADSP_ALG_XM_STRUCT_MAGIC 0x49aec7 3122cd19bdbSCharles Keepax 3132cd19bdbSCharles Keepax #define HOST_BUFFER_FIELD(field) \ 3142cd19bdbSCharles Keepax (offsetof(struct wm_adsp_buffer, field) / sizeof(__be32)) 3152cd19bdbSCharles Keepax 3162cd19bdbSCharles Keepax #define ALG_XM_FIELD(field) \ 3172cd19bdbSCharles Keepax (offsetof(struct wm_adsp_alg_xm_struct, field) / sizeof(__be32)) 3182cd19bdbSCharles Keepax 3192cd19bdbSCharles Keepax static int wm_adsp_buffer_init(struct wm_adsp *dsp); 3202cd19bdbSCharles Keepax static int wm_adsp_buffer_free(struct wm_adsp *dsp); 3212cd19bdbSCharles Keepax 3222cd19bdbSCharles Keepax struct wm_adsp_buffer_region { 3232cd19bdbSCharles Keepax unsigned int offset; 3242cd19bdbSCharles Keepax unsigned int cumulative_size; 3252cd19bdbSCharles Keepax unsigned int mem_type; 3262cd19bdbSCharles Keepax unsigned int base_addr; 3272cd19bdbSCharles Keepax }; 3282cd19bdbSCharles Keepax 3292cd19bdbSCharles Keepax struct wm_adsp_buffer_region_def { 3302cd19bdbSCharles Keepax unsigned int mem_type; 3312cd19bdbSCharles Keepax unsigned int base_offset; 3322cd19bdbSCharles Keepax unsigned int size_offset; 3332cd19bdbSCharles Keepax }; 3342cd19bdbSCharles Keepax 3353a9686c4SCharles Keepax static const struct wm_adsp_buffer_region_def default_regions[] = { 3362cd19bdbSCharles Keepax { 3372cd19bdbSCharles Keepax .mem_type = WMFW_ADSP2_XM, 3382cd19bdbSCharles Keepax .base_offset = HOST_BUFFER_FIELD(X_buf_base), 3392cd19bdbSCharles Keepax .size_offset = HOST_BUFFER_FIELD(X_buf_size), 3402cd19bdbSCharles Keepax }, 3412cd19bdbSCharles Keepax { 3422cd19bdbSCharles Keepax .mem_type = WMFW_ADSP2_XM, 3432cd19bdbSCharles Keepax .base_offset = HOST_BUFFER_FIELD(X_buf_base2), 3442cd19bdbSCharles Keepax .size_offset = HOST_BUFFER_FIELD(X_buf_brk), 3452cd19bdbSCharles Keepax }, 3462cd19bdbSCharles Keepax { 3472cd19bdbSCharles Keepax .mem_type = WMFW_ADSP2_YM, 3482cd19bdbSCharles Keepax .base_offset = HOST_BUFFER_FIELD(Y_buf_base), 3492cd19bdbSCharles Keepax .size_offset = HOST_BUFFER_FIELD(wrap), 3502cd19bdbSCharles Keepax }, 3512cd19bdbSCharles Keepax }; 3522cd19bdbSCharles Keepax 353406abc95SCharles Keepax struct wm_adsp_fw_caps { 354406abc95SCharles Keepax u32 id; 355406abc95SCharles Keepax struct snd_codec_desc desc; 3562cd19bdbSCharles Keepax int num_regions; 3573a9686c4SCharles Keepax const struct wm_adsp_buffer_region_def *region_defs; 358406abc95SCharles Keepax }; 359406abc95SCharles Keepax 360e6d00f34SCharles Keepax static const struct wm_adsp_fw_caps ctrl_caps[] = { 361406abc95SCharles Keepax { 362406abc95SCharles Keepax .id = SND_AUDIOCODEC_BESPOKE, 363406abc95SCharles Keepax .desc = { 364406abc95SCharles Keepax .max_ch = 1, 365406abc95SCharles Keepax .sample_rates = { 16000 }, 366406abc95SCharles Keepax .num_sample_rates = 1, 367406abc95SCharles Keepax .formats = SNDRV_PCM_FMTBIT_S16_LE, 368406abc95SCharles Keepax }, 369e6d00f34SCharles Keepax .num_regions = ARRAY_SIZE(default_regions), 370e6d00f34SCharles Keepax .region_defs = default_regions, 371406abc95SCharles Keepax }, 372406abc95SCharles Keepax }; 373406abc95SCharles Keepax 3747ce4283cSCharles Keepax static const struct wm_adsp_fw_caps trace_caps[] = { 3757ce4283cSCharles Keepax { 3767ce4283cSCharles Keepax .id = SND_AUDIOCODEC_BESPOKE, 3777ce4283cSCharles Keepax .desc = { 3787ce4283cSCharles Keepax .max_ch = 8, 3797ce4283cSCharles Keepax .sample_rates = { 3807ce4283cSCharles Keepax 4000, 8000, 11025, 12000, 16000, 22050, 3817ce4283cSCharles Keepax 24000, 32000, 44100, 48000, 64000, 88200, 3827ce4283cSCharles Keepax 96000, 176400, 192000 3837ce4283cSCharles Keepax }, 3847ce4283cSCharles Keepax .num_sample_rates = 15, 3857ce4283cSCharles Keepax .formats = SNDRV_PCM_FMTBIT_S16_LE, 3867ce4283cSCharles Keepax }, 3877ce4283cSCharles Keepax .num_regions = ARRAY_SIZE(default_regions), 3887ce4283cSCharles Keepax .region_defs = default_regions, 389406abc95SCharles Keepax }, 390406abc95SCharles Keepax }; 391406abc95SCharles Keepax 392406abc95SCharles Keepax static const struct { 3931023dbd9SMark Brown const char *file; 394406abc95SCharles Keepax int compr_direction; 395406abc95SCharles Keepax int num_caps; 396406abc95SCharles Keepax const struct wm_adsp_fw_caps *caps; 39720b7f7c5SCharles Keepax bool voice_trigger; 3981023dbd9SMark Brown } wm_adsp_fw[WM_ADSP_NUM_FW] = { 399dd84f925SMark Brown [WM_ADSP_FW_MBC_VSS] = { .file = "mbc-vss" }, 40004d1300fSCharles Keepax [WM_ADSP_FW_HIFI] = { .file = "hifi" }, 401dd84f925SMark Brown [WM_ADSP_FW_TX] = { .file = "tx" }, 402dd84f925SMark Brown [WM_ADSP_FW_TX_SPK] = { .file = "tx-spk" }, 40304d1300fSCharles Keepax [WM_ADSP_FW_RX] = { .file = "rx" }, 404dd84f925SMark Brown [WM_ADSP_FW_RX_ANC] = { .file = "rx-anc" }, 405406abc95SCharles Keepax [WM_ADSP_FW_CTRL] = { 406406abc95SCharles Keepax .file = "ctrl", 407406abc95SCharles Keepax .compr_direction = SND_COMPRESS_CAPTURE, 408e6d00f34SCharles Keepax .num_caps = ARRAY_SIZE(ctrl_caps), 409e6d00f34SCharles Keepax .caps = ctrl_caps, 41020b7f7c5SCharles Keepax .voice_trigger = true, 411406abc95SCharles Keepax }, 41204d1300fSCharles Keepax [WM_ADSP_FW_ASR] = { .file = "asr" }, 4137ce4283cSCharles Keepax [WM_ADSP_FW_TRACE] = { 4147ce4283cSCharles Keepax .file = "trace", 4157ce4283cSCharles Keepax .compr_direction = SND_COMPRESS_CAPTURE, 4167ce4283cSCharles Keepax .num_caps = ARRAY_SIZE(trace_caps), 4177ce4283cSCharles Keepax .caps = trace_caps, 4187ce4283cSCharles Keepax }, 41904d1300fSCharles Keepax [WM_ADSP_FW_SPK_PROT] = { .file = "spk-prot" }, 42004d1300fSCharles Keepax [WM_ADSP_FW_MISC] = { .file = "misc" }, 4211023dbd9SMark Brown }; 4221023dbd9SMark Brown 4236ab2b7b4SDimitris Papastamos struct wm_coeff_ctl_ops { 4246ab2b7b4SDimitris Papastamos int (*xget)(struct snd_kcontrol *kcontrol, 4256ab2b7b4SDimitris Papastamos struct snd_ctl_elem_value *ucontrol); 4266ab2b7b4SDimitris Papastamos int (*xput)(struct snd_kcontrol *kcontrol, 4276ab2b7b4SDimitris Papastamos struct snd_ctl_elem_value *ucontrol); 4286ab2b7b4SDimitris Papastamos int (*xinfo)(struct snd_kcontrol *kcontrol, 4296ab2b7b4SDimitris Papastamos struct snd_ctl_elem_info *uinfo); 4306ab2b7b4SDimitris Papastamos }; 4316ab2b7b4SDimitris Papastamos 4326ab2b7b4SDimitris Papastamos struct wm_coeff_ctl { 4336ab2b7b4SDimitris Papastamos const char *name; 4342323736dSCharles Keepax const char *fw_name; 4353809f001SCharles Keepax struct wm_adsp_alg_region alg_region; 4366ab2b7b4SDimitris Papastamos struct wm_coeff_ctl_ops ops; 4373809f001SCharles Keepax struct wm_adsp *dsp; 4386ab2b7b4SDimitris Papastamos unsigned int enabled:1; 4396ab2b7b4SDimitris Papastamos struct list_head list; 4406ab2b7b4SDimitris Papastamos void *cache; 4412323736dSCharles Keepax unsigned int offset; 4426ab2b7b4SDimitris Papastamos size_t len; 4430c2e3f34SDimitris Papastamos unsigned int set:1; 4446ab2b7b4SDimitris Papastamos struct snd_kcontrol *kcontrol; 4459ee78757SCharles Keepax struct soc_bytes_ext bytes_ext; 44626c22a19SCharles Keepax unsigned int flags; 4476ab2b7b4SDimitris Papastamos }; 4486ab2b7b4SDimitris Papastamos 449f9f55e31SRichard Fitzgerald #ifdef CONFIG_DEBUG_FS 450f9f55e31SRichard Fitzgerald static void wm_adsp_debugfs_save_wmfwname(struct wm_adsp *dsp, const char *s) 451f9f55e31SRichard Fitzgerald { 452f9f55e31SRichard Fitzgerald char *tmp = kasprintf(GFP_KERNEL, "%s\n", s); 453f9f55e31SRichard Fitzgerald 454f9f55e31SRichard Fitzgerald kfree(dsp->wmfw_file_name); 455f9f55e31SRichard Fitzgerald dsp->wmfw_file_name = tmp; 456f9f55e31SRichard Fitzgerald } 457f9f55e31SRichard Fitzgerald 458f9f55e31SRichard Fitzgerald static void wm_adsp_debugfs_save_binname(struct wm_adsp *dsp, const char *s) 459f9f55e31SRichard Fitzgerald { 460f9f55e31SRichard Fitzgerald char *tmp = kasprintf(GFP_KERNEL, "%s\n", s); 461f9f55e31SRichard Fitzgerald 462f9f55e31SRichard Fitzgerald kfree(dsp->bin_file_name); 463f9f55e31SRichard Fitzgerald dsp->bin_file_name = tmp; 464f9f55e31SRichard Fitzgerald } 465f9f55e31SRichard Fitzgerald 466f9f55e31SRichard Fitzgerald static void wm_adsp_debugfs_clear(struct wm_adsp *dsp) 467f9f55e31SRichard Fitzgerald { 468f9f55e31SRichard Fitzgerald kfree(dsp->wmfw_file_name); 469f9f55e31SRichard Fitzgerald kfree(dsp->bin_file_name); 470f9f55e31SRichard Fitzgerald dsp->wmfw_file_name = NULL; 471f9f55e31SRichard Fitzgerald dsp->bin_file_name = NULL; 472f9f55e31SRichard Fitzgerald } 473f9f55e31SRichard Fitzgerald 474f9f55e31SRichard Fitzgerald static ssize_t wm_adsp_debugfs_wmfw_read(struct file *file, 475f9f55e31SRichard Fitzgerald char __user *user_buf, 476f9f55e31SRichard Fitzgerald size_t count, loff_t *ppos) 477f9f55e31SRichard Fitzgerald { 478f9f55e31SRichard Fitzgerald struct wm_adsp *dsp = file->private_data; 479f9f55e31SRichard Fitzgerald ssize_t ret; 480f9f55e31SRichard Fitzgerald 481078e7183SCharles Keepax mutex_lock(&dsp->pwr_lock); 482f9f55e31SRichard Fitzgerald 48328823ebaSCharles Keepax if (!dsp->wmfw_file_name || !dsp->booted) 484f9f55e31SRichard Fitzgerald ret = 0; 485f9f55e31SRichard Fitzgerald else 486f9f55e31SRichard Fitzgerald ret = simple_read_from_buffer(user_buf, count, ppos, 487f9f55e31SRichard Fitzgerald dsp->wmfw_file_name, 488f9f55e31SRichard Fitzgerald strlen(dsp->wmfw_file_name)); 489f9f55e31SRichard Fitzgerald 490078e7183SCharles Keepax mutex_unlock(&dsp->pwr_lock); 491f9f55e31SRichard Fitzgerald return ret; 492f9f55e31SRichard Fitzgerald } 493f9f55e31SRichard Fitzgerald 494f9f55e31SRichard Fitzgerald static ssize_t wm_adsp_debugfs_bin_read(struct file *file, 495f9f55e31SRichard Fitzgerald char __user *user_buf, 496f9f55e31SRichard Fitzgerald size_t count, loff_t *ppos) 497f9f55e31SRichard Fitzgerald { 498f9f55e31SRichard Fitzgerald struct wm_adsp *dsp = file->private_data; 499f9f55e31SRichard Fitzgerald ssize_t ret; 500f9f55e31SRichard Fitzgerald 501078e7183SCharles Keepax mutex_lock(&dsp->pwr_lock); 502f9f55e31SRichard Fitzgerald 50328823ebaSCharles Keepax if (!dsp->bin_file_name || !dsp->booted) 504f9f55e31SRichard Fitzgerald ret = 0; 505f9f55e31SRichard Fitzgerald else 506f9f55e31SRichard Fitzgerald ret = simple_read_from_buffer(user_buf, count, ppos, 507f9f55e31SRichard Fitzgerald dsp->bin_file_name, 508f9f55e31SRichard Fitzgerald strlen(dsp->bin_file_name)); 509f9f55e31SRichard Fitzgerald 510078e7183SCharles Keepax mutex_unlock(&dsp->pwr_lock); 511f9f55e31SRichard Fitzgerald return ret; 512f9f55e31SRichard Fitzgerald } 513f9f55e31SRichard Fitzgerald 514f9f55e31SRichard Fitzgerald static const struct { 515f9f55e31SRichard Fitzgerald const char *name; 516f9f55e31SRichard Fitzgerald const struct file_operations fops; 517f9f55e31SRichard Fitzgerald } wm_adsp_debugfs_fops[] = { 518f9f55e31SRichard Fitzgerald { 519f9f55e31SRichard Fitzgerald .name = "wmfw_file_name", 520f9f55e31SRichard Fitzgerald .fops = { 521f9f55e31SRichard Fitzgerald .open = simple_open, 522f9f55e31SRichard Fitzgerald .read = wm_adsp_debugfs_wmfw_read, 523f9f55e31SRichard Fitzgerald }, 524f9f55e31SRichard Fitzgerald }, 525f9f55e31SRichard Fitzgerald { 526f9f55e31SRichard Fitzgerald .name = "bin_file_name", 527f9f55e31SRichard Fitzgerald .fops = { 528f9f55e31SRichard Fitzgerald .open = simple_open, 529f9f55e31SRichard Fitzgerald .read = wm_adsp_debugfs_bin_read, 530f9f55e31SRichard Fitzgerald }, 531f9f55e31SRichard Fitzgerald }, 532f9f55e31SRichard Fitzgerald }; 533f9f55e31SRichard Fitzgerald 534f9f55e31SRichard Fitzgerald static void wm_adsp2_init_debugfs(struct wm_adsp *dsp, 535f9f55e31SRichard Fitzgerald struct snd_soc_codec *codec) 536f9f55e31SRichard Fitzgerald { 537f9f55e31SRichard Fitzgerald struct dentry *root = NULL; 538f9f55e31SRichard Fitzgerald char *root_name; 539f9f55e31SRichard Fitzgerald int i; 540f9f55e31SRichard Fitzgerald 541f9f55e31SRichard Fitzgerald if (!codec->component.debugfs_root) { 542f9f55e31SRichard Fitzgerald adsp_err(dsp, "No codec debugfs root\n"); 543f9f55e31SRichard Fitzgerald goto err; 544f9f55e31SRichard Fitzgerald } 545f9f55e31SRichard Fitzgerald 546f9f55e31SRichard Fitzgerald root_name = kmalloc(PAGE_SIZE, GFP_KERNEL); 547f9f55e31SRichard Fitzgerald if (!root_name) 548f9f55e31SRichard Fitzgerald goto err; 549f9f55e31SRichard Fitzgerald 550f9f55e31SRichard Fitzgerald snprintf(root_name, PAGE_SIZE, "dsp%d", dsp->num); 551f9f55e31SRichard Fitzgerald root = debugfs_create_dir(root_name, codec->component.debugfs_root); 552f9f55e31SRichard Fitzgerald kfree(root_name); 553f9f55e31SRichard Fitzgerald 554f9f55e31SRichard Fitzgerald if (!root) 555f9f55e31SRichard Fitzgerald goto err; 556f9f55e31SRichard Fitzgerald 55728823ebaSCharles Keepax if (!debugfs_create_bool("booted", S_IRUGO, root, &dsp->booted)) 55828823ebaSCharles Keepax goto err; 55928823ebaSCharles Keepax 560f9f55e31SRichard Fitzgerald if (!debugfs_create_bool("running", S_IRUGO, root, &dsp->running)) 561f9f55e31SRichard Fitzgerald goto err; 562f9f55e31SRichard Fitzgerald 563f9f55e31SRichard Fitzgerald if (!debugfs_create_x32("fw_id", S_IRUGO, root, &dsp->fw_id)) 564f9f55e31SRichard Fitzgerald goto err; 565f9f55e31SRichard Fitzgerald 566f9f55e31SRichard Fitzgerald if (!debugfs_create_x32("fw_version", S_IRUGO, root, 567f9f55e31SRichard Fitzgerald &dsp->fw_id_version)) 568f9f55e31SRichard Fitzgerald goto err; 569f9f55e31SRichard Fitzgerald 570f9f55e31SRichard Fitzgerald for (i = 0; i < ARRAY_SIZE(wm_adsp_debugfs_fops); ++i) { 571f9f55e31SRichard Fitzgerald if (!debugfs_create_file(wm_adsp_debugfs_fops[i].name, 572f9f55e31SRichard Fitzgerald S_IRUGO, root, dsp, 573f9f55e31SRichard Fitzgerald &wm_adsp_debugfs_fops[i].fops)) 574f9f55e31SRichard Fitzgerald goto err; 575f9f55e31SRichard Fitzgerald } 576f9f55e31SRichard Fitzgerald 577f9f55e31SRichard Fitzgerald dsp->debugfs_root = root; 578f9f55e31SRichard Fitzgerald return; 579f9f55e31SRichard Fitzgerald 580f9f55e31SRichard Fitzgerald err: 581f9f55e31SRichard Fitzgerald debugfs_remove_recursive(root); 582f9f55e31SRichard Fitzgerald adsp_err(dsp, "Failed to create debugfs\n"); 583f9f55e31SRichard Fitzgerald } 584f9f55e31SRichard Fitzgerald 585f9f55e31SRichard Fitzgerald static void wm_adsp2_cleanup_debugfs(struct wm_adsp *dsp) 586f9f55e31SRichard Fitzgerald { 587f9f55e31SRichard Fitzgerald wm_adsp_debugfs_clear(dsp); 588f9f55e31SRichard Fitzgerald debugfs_remove_recursive(dsp->debugfs_root); 589f9f55e31SRichard Fitzgerald } 590f9f55e31SRichard Fitzgerald #else 591f9f55e31SRichard Fitzgerald static inline void wm_adsp2_init_debugfs(struct wm_adsp *dsp, 592f9f55e31SRichard Fitzgerald struct snd_soc_codec *codec) 593f9f55e31SRichard Fitzgerald { 594f9f55e31SRichard Fitzgerald } 595f9f55e31SRichard Fitzgerald 596f9f55e31SRichard Fitzgerald static inline void wm_adsp2_cleanup_debugfs(struct wm_adsp *dsp) 597f9f55e31SRichard Fitzgerald { 598f9f55e31SRichard Fitzgerald } 599f9f55e31SRichard Fitzgerald 600f9f55e31SRichard Fitzgerald static inline void wm_adsp_debugfs_save_wmfwname(struct wm_adsp *dsp, 601f9f55e31SRichard Fitzgerald const char *s) 602f9f55e31SRichard Fitzgerald { 603f9f55e31SRichard Fitzgerald } 604f9f55e31SRichard Fitzgerald 605f9f55e31SRichard Fitzgerald static inline void wm_adsp_debugfs_save_binname(struct wm_adsp *dsp, 606f9f55e31SRichard Fitzgerald const char *s) 607f9f55e31SRichard Fitzgerald { 608f9f55e31SRichard Fitzgerald } 609f9f55e31SRichard Fitzgerald 610f9f55e31SRichard Fitzgerald static inline void wm_adsp_debugfs_clear(struct wm_adsp *dsp) 611f9f55e31SRichard Fitzgerald { 612f9f55e31SRichard Fitzgerald } 613f9f55e31SRichard Fitzgerald #endif 614f9f55e31SRichard Fitzgerald 6151023dbd9SMark Brown static int wm_adsp_fw_get(struct snd_kcontrol *kcontrol, 6161023dbd9SMark Brown struct snd_ctl_elem_value *ucontrol) 6171023dbd9SMark Brown { 618ea53bf77SLars-Peter Clausen struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); 6191023dbd9SMark Brown struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; 6203809f001SCharles Keepax struct wm_adsp *dsp = snd_soc_codec_get_drvdata(codec); 6211023dbd9SMark Brown 62215c66570STakashi Iwai ucontrol->value.enumerated.item[0] = dsp[e->shift_l].fw; 6231023dbd9SMark Brown 6241023dbd9SMark Brown return 0; 6251023dbd9SMark Brown } 6261023dbd9SMark Brown 6271023dbd9SMark Brown static int wm_adsp_fw_put(struct snd_kcontrol *kcontrol, 6281023dbd9SMark Brown struct snd_ctl_elem_value *ucontrol) 6291023dbd9SMark Brown { 630ea53bf77SLars-Peter Clausen struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); 6311023dbd9SMark Brown struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; 6323809f001SCharles Keepax struct wm_adsp *dsp = snd_soc_codec_get_drvdata(codec); 633d27c5e15SCharles Keepax int ret = 0; 6341023dbd9SMark Brown 63515c66570STakashi Iwai if (ucontrol->value.enumerated.item[0] == dsp[e->shift_l].fw) 6361023dbd9SMark Brown return 0; 6371023dbd9SMark Brown 63815c66570STakashi Iwai if (ucontrol->value.enumerated.item[0] >= WM_ADSP_NUM_FW) 6391023dbd9SMark Brown return -EINVAL; 6401023dbd9SMark Brown 641d27c5e15SCharles Keepax mutex_lock(&dsp[e->shift_l].pwr_lock); 6421023dbd9SMark Brown 64328823ebaSCharles Keepax if (dsp[e->shift_l].booted || dsp[e->shift_l].compr) 644d27c5e15SCharles Keepax ret = -EBUSY; 645d27c5e15SCharles Keepax else 64615c66570STakashi Iwai dsp[e->shift_l].fw = ucontrol->value.enumerated.item[0]; 6471023dbd9SMark Brown 648d27c5e15SCharles Keepax mutex_unlock(&dsp[e->shift_l].pwr_lock); 649d27c5e15SCharles Keepax 650d27c5e15SCharles Keepax return ret; 6511023dbd9SMark Brown } 6521023dbd9SMark Brown 6531023dbd9SMark Brown static const struct soc_enum wm_adsp_fw_enum[] = { 6541023dbd9SMark Brown SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 6551023dbd9SMark Brown SOC_ENUM_SINGLE(0, 1, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 6561023dbd9SMark Brown SOC_ENUM_SINGLE(0, 2, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 6571023dbd9SMark Brown SOC_ENUM_SINGLE(0, 3, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 6581023dbd9SMark Brown }; 6591023dbd9SMark Brown 660336d0442SRichard Fitzgerald const struct snd_kcontrol_new wm_adsp_fw_controls[] = { 6611023dbd9SMark Brown SOC_ENUM_EXT("DSP1 Firmware", wm_adsp_fw_enum[0], 6621023dbd9SMark Brown wm_adsp_fw_get, wm_adsp_fw_put), 6631023dbd9SMark Brown SOC_ENUM_EXT("DSP2 Firmware", wm_adsp_fw_enum[1], 6641023dbd9SMark Brown wm_adsp_fw_get, wm_adsp_fw_put), 6651023dbd9SMark Brown SOC_ENUM_EXT("DSP3 Firmware", wm_adsp_fw_enum[2], 6661023dbd9SMark Brown wm_adsp_fw_get, wm_adsp_fw_put), 6671023dbd9SMark Brown SOC_ENUM_EXT("DSP4 Firmware", wm_adsp_fw_enum[3], 6681023dbd9SMark Brown wm_adsp_fw_get, wm_adsp_fw_put), 6691023dbd9SMark Brown }; 670336d0442SRichard Fitzgerald EXPORT_SYMBOL_GPL(wm_adsp_fw_controls); 6712159ad93SMark Brown 6722159ad93SMark Brown static struct wm_adsp_region const *wm_adsp_find_region(struct wm_adsp *dsp, 6732159ad93SMark Brown int type) 6742159ad93SMark Brown { 6752159ad93SMark Brown int i; 6762159ad93SMark Brown 6772159ad93SMark Brown for (i = 0; i < dsp->num_mems; i++) 6782159ad93SMark Brown if (dsp->mem[i].type == type) 6792159ad93SMark Brown return &dsp->mem[i]; 6802159ad93SMark Brown 6812159ad93SMark Brown return NULL; 6822159ad93SMark Brown } 6832159ad93SMark Brown 6843809f001SCharles Keepax static unsigned int wm_adsp_region_to_reg(struct wm_adsp_region const *mem, 68545b9ee72SMark Brown unsigned int offset) 68645b9ee72SMark Brown { 6873809f001SCharles Keepax if (WARN_ON(!mem)) 6886c452bdaSTakashi Iwai return offset; 6893809f001SCharles Keepax switch (mem->type) { 69045b9ee72SMark Brown case WMFW_ADSP1_PM: 6913809f001SCharles Keepax return mem->base + (offset * 3); 69245b9ee72SMark Brown case WMFW_ADSP1_DM: 6933809f001SCharles Keepax return mem->base + (offset * 2); 69445b9ee72SMark Brown case WMFW_ADSP2_XM: 6953809f001SCharles Keepax return mem->base + (offset * 2); 69645b9ee72SMark Brown case WMFW_ADSP2_YM: 6973809f001SCharles Keepax return mem->base + (offset * 2); 69845b9ee72SMark Brown case WMFW_ADSP1_ZM: 6993809f001SCharles Keepax return mem->base + (offset * 2); 70045b9ee72SMark Brown default: 7016c452bdaSTakashi Iwai WARN(1, "Unknown memory region type"); 70245b9ee72SMark Brown return offset; 70345b9ee72SMark Brown } 70445b9ee72SMark Brown } 70545b9ee72SMark Brown 70610337b07SRichard Fitzgerald static void wm_adsp2_show_fw_status(struct wm_adsp *dsp) 70710337b07SRichard Fitzgerald { 70810337b07SRichard Fitzgerald u16 scratch[4]; 70910337b07SRichard Fitzgerald int ret; 71010337b07SRichard Fitzgerald 71110337b07SRichard Fitzgerald ret = regmap_raw_read(dsp->regmap, dsp->base + ADSP2_SCRATCH0, 71210337b07SRichard Fitzgerald scratch, sizeof(scratch)); 71310337b07SRichard Fitzgerald if (ret) { 71410337b07SRichard Fitzgerald adsp_err(dsp, "Failed to read SCRATCH regs: %d\n", ret); 71510337b07SRichard Fitzgerald return; 71610337b07SRichard Fitzgerald } 71710337b07SRichard Fitzgerald 71810337b07SRichard Fitzgerald adsp_dbg(dsp, "FW SCRATCH 0:0x%x 1:0x%x 2:0x%x 3:0x%x\n", 71910337b07SRichard Fitzgerald be16_to_cpu(scratch[0]), 72010337b07SRichard Fitzgerald be16_to_cpu(scratch[1]), 72110337b07SRichard Fitzgerald be16_to_cpu(scratch[2]), 72210337b07SRichard Fitzgerald be16_to_cpu(scratch[3])); 72310337b07SRichard Fitzgerald } 72410337b07SRichard Fitzgerald 7259ee78757SCharles Keepax static inline struct wm_coeff_ctl *bytes_ext_to_ctl(struct soc_bytes_ext *ext) 7269ee78757SCharles Keepax { 7279ee78757SCharles Keepax return container_of(ext, struct wm_coeff_ctl, bytes_ext); 7289ee78757SCharles Keepax } 7299ee78757SCharles Keepax 7307585a5b0SCharles Keepax static int wm_coeff_info(struct snd_kcontrol *kctl, 7316ab2b7b4SDimitris Papastamos struct snd_ctl_elem_info *uinfo) 7326ab2b7b4SDimitris Papastamos { 7339ee78757SCharles Keepax struct soc_bytes_ext *bytes_ext = 7349ee78757SCharles Keepax (struct soc_bytes_ext *)kctl->private_value; 7359ee78757SCharles Keepax struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); 7366ab2b7b4SDimitris Papastamos 7376ab2b7b4SDimitris Papastamos uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; 7386ab2b7b4SDimitris Papastamos uinfo->count = ctl->len; 7396ab2b7b4SDimitris Papastamos return 0; 7406ab2b7b4SDimitris Papastamos } 7416ab2b7b4SDimitris Papastamos 742c9f8dd71SCharles Keepax static int wm_coeff_write_control(struct wm_coeff_ctl *ctl, 7436ab2b7b4SDimitris Papastamos const void *buf, size_t len) 7446ab2b7b4SDimitris Papastamos { 7453809f001SCharles Keepax struct wm_adsp_alg_region *alg_region = &ctl->alg_region; 7466ab2b7b4SDimitris Papastamos const struct wm_adsp_region *mem; 7473809f001SCharles Keepax struct wm_adsp *dsp = ctl->dsp; 7486ab2b7b4SDimitris Papastamos void *scratch; 7496ab2b7b4SDimitris Papastamos int ret; 7506ab2b7b4SDimitris Papastamos unsigned int reg; 7516ab2b7b4SDimitris Papastamos 7523809f001SCharles Keepax mem = wm_adsp_find_region(dsp, alg_region->type); 7536ab2b7b4SDimitris Papastamos if (!mem) { 7543809f001SCharles Keepax adsp_err(dsp, "No base for region %x\n", 7553809f001SCharles Keepax alg_region->type); 7566ab2b7b4SDimitris Papastamos return -EINVAL; 7576ab2b7b4SDimitris Papastamos } 7586ab2b7b4SDimitris Papastamos 7592323736dSCharles Keepax reg = ctl->alg_region.base + ctl->offset; 7606ab2b7b4SDimitris Papastamos reg = wm_adsp_region_to_reg(mem, reg); 7616ab2b7b4SDimitris Papastamos 7624f8ea6d7SCharles Keepax scratch = kmemdup(buf, len, GFP_KERNEL | GFP_DMA); 7636ab2b7b4SDimitris Papastamos if (!scratch) 7646ab2b7b4SDimitris Papastamos return -ENOMEM; 7656ab2b7b4SDimitris Papastamos 7663809f001SCharles Keepax ret = regmap_raw_write(dsp->regmap, reg, scratch, 7674f8ea6d7SCharles Keepax len); 7686ab2b7b4SDimitris Papastamos if (ret) { 7693809f001SCharles Keepax adsp_err(dsp, "Failed to write %zu bytes to %x: %d\n", 7704f8ea6d7SCharles Keepax len, reg, ret); 7716ab2b7b4SDimitris Papastamos kfree(scratch); 7726ab2b7b4SDimitris Papastamos return ret; 7736ab2b7b4SDimitris Papastamos } 7744f8ea6d7SCharles Keepax adsp_dbg(dsp, "Wrote %zu bytes to %x\n", len, reg); 7756ab2b7b4SDimitris Papastamos 7766ab2b7b4SDimitris Papastamos kfree(scratch); 7776ab2b7b4SDimitris Papastamos 7786ab2b7b4SDimitris Papastamos return 0; 7796ab2b7b4SDimitris Papastamos } 7806ab2b7b4SDimitris Papastamos 7817585a5b0SCharles Keepax static int wm_coeff_put(struct snd_kcontrol *kctl, 7826ab2b7b4SDimitris Papastamos struct snd_ctl_elem_value *ucontrol) 7836ab2b7b4SDimitris Papastamos { 7849ee78757SCharles Keepax struct soc_bytes_ext *bytes_ext = 7859ee78757SCharles Keepax (struct soc_bytes_ext *)kctl->private_value; 7869ee78757SCharles Keepax struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); 7876ab2b7b4SDimitris Papastamos char *p = ucontrol->value.bytes.data; 788168d10e7SCharles Keepax int ret = 0; 789168d10e7SCharles Keepax 790168d10e7SCharles Keepax mutex_lock(&ctl->dsp->pwr_lock); 7916ab2b7b4SDimitris Papastamos 7926ab2b7b4SDimitris Papastamos memcpy(ctl->cache, p, ctl->len); 7936ab2b7b4SDimitris Papastamos 7940c2e3f34SDimitris Papastamos ctl->set = 1; 795cef45771SCharles Keepax if (ctl->enabled && ctl->dsp->running) 796168d10e7SCharles Keepax ret = wm_coeff_write_control(ctl, p, ctl->len); 7976ab2b7b4SDimitris Papastamos 798168d10e7SCharles Keepax mutex_unlock(&ctl->dsp->pwr_lock); 799168d10e7SCharles Keepax 800168d10e7SCharles Keepax return ret; 8016ab2b7b4SDimitris Papastamos } 8026ab2b7b4SDimitris Papastamos 8039ee78757SCharles Keepax static int wm_coeff_tlv_put(struct snd_kcontrol *kctl, 8049ee78757SCharles Keepax const unsigned int __user *bytes, unsigned int size) 8059ee78757SCharles Keepax { 8069ee78757SCharles Keepax struct soc_bytes_ext *bytes_ext = 8079ee78757SCharles Keepax (struct soc_bytes_ext *)kctl->private_value; 8089ee78757SCharles Keepax struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); 8099ee78757SCharles Keepax int ret = 0; 8109ee78757SCharles Keepax 8119ee78757SCharles Keepax mutex_lock(&ctl->dsp->pwr_lock); 8129ee78757SCharles Keepax 8139ee78757SCharles Keepax if (copy_from_user(ctl->cache, bytes, size)) { 8149ee78757SCharles Keepax ret = -EFAULT; 8159ee78757SCharles Keepax } else { 8169ee78757SCharles Keepax ctl->set = 1; 817cef45771SCharles Keepax if (ctl->enabled && ctl->dsp->running) 8189ee78757SCharles Keepax ret = wm_coeff_write_control(ctl, ctl->cache, size); 8199ee78757SCharles Keepax } 8209ee78757SCharles Keepax 8219ee78757SCharles Keepax mutex_unlock(&ctl->dsp->pwr_lock); 8229ee78757SCharles Keepax 8239ee78757SCharles Keepax return ret; 8249ee78757SCharles Keepax } 8259ee78757SCharles Keepax 826c9f8dd71SCharles Keepax static int wm_coeff_read_control(struct wm_coeff_ctl *ctl, 8276ab2b7b4SDimitris Papastamos void *buf, size_t len) 8286ab2b7b4SDimitris Papastamos { 8293809f001SCharles Keepax struct wm_adsp_alg_region *alg_region = &ctl->alg_region; 8306ab2b7b4SDimitris Papastamos const struct wm_adsp_region *mem; 8313809f001SCharles Keepax struct wm_adsp *dsp = ctl->dsp; 8326ab2b7b4SDimitris Papastamos void *scratch; 8336ab2b7b4SDimitris Papastamos int ret; 8346ab2b7b4SDimitris Papastamos unsigned int reg; 8356ab2b7b4SDimitris Papastamos 8363809f001SCharles Keepax mem = wm_adsp_find_region(dsp, alg_region->type); 8376ab2b7b4SDimitris Papastamos if (!mem) { 8383809f001SCharles Keepax adsp_err(dsp, "No base for region %x\n", 8393809f001SCharles Keepax alg_region->type); 8406ab2b7b4SDimitris Papastamos return -EINVAL; 8416ab2b7b4SDimitris Papastamos } 8426ab2b7b4SDimitris Papastamos 8432323736dSCharles Keepax reg = ctl->alg_region.base + ctl->offset; 8446ab2b7b4SDimitris Papastamos reg = wm_adsp_region_to_reg(mem, reg); 8456ab2b7b4SDimitris Papastamos 8464f8ea6d7SCharles Keepax scratch = kmalloc(len, GFP_KERNEL | GFP_DMA); 8476ab2b7b4SDimitris Papastamos if (!scratch) 8486ab2b7b4SDimitris Papastamos return -ENOMEM; 8496ab2b7b4SDimitris Papastamos 8504f8ea6d7SCharles Keepax ret = regmap_raw_read(dsp->regmap, reg, scratch, len); 8516ab2b7b4SDimitris Papastamos if (ret) { 8523809f001SCharles Keepax adsp_err(dsp, "Failed to read %zu bytes from %x: %d\n", 8535602a643SCharles Keepax len, reg, ret); 8546ab2b7b4SDimitris Papastamos kfree(scratch); 8556ab2b7b4SDimitris Papastamos return ret; 8566ab2b7b4SDimitris Papastamos } 8574f8ea6d7SCharles Keepax adsp_dbg(dsp, "Read %zu bytes from %x\n", len, reg); 8586ab2b7b4SDimitris Papastamos 8594f8ea6d7SCharles Keepax memcpy(buf, scratch, len); 8606ab2b7b4SDimitris Papastamos kfree(scratch); 8616ab2b7b4SDimitris Papastamos 8626ab2b7b4SDimitris Papastamos return 0; 8636ab2b7b4SDimitris Papastamos } 8646ab2b7b4SDimitris Papastamos 8657585a5b0SCharles Keepax static int wm_coeff_get(struct snd_kcontrol *kctl, 8666ab2b7b4SDimitris Papastamos struct snd_ctl_elem_value *ucontrol) 8676ab2b7b4SDimitris Papastamos { 8689ee78757SCharles Keepax struct soc_bytes_ext *bytes_ext = 8699ee78757SCharles Keepax (struct soc_bytes_ext *)kctl->private_value; 8709ee78757SCharles Keepax struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); 8716ab2b7b4SDimitris Papastamos char *p = ucontrol->value.bytes.data; 872168d10e7SCharles Keepax int ret = 0; 873168d10e7SCharles Keepax 874168d10e7SCharles Keepax mutex_lock(&ctl->dsp->pwr_lock); 8756ab2b7b4SDimitris Papastamos 87626c22a19SCharles Keepax if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) { 877cef45771SCharles Keepax if (ctl->enabled && ctl->dsp->running) 878168d10e7SCharles Keepax ret = wm_coeff_read_control(ctl, p, ctl->len); 87926c22a19SCharles Keepax else 880168d10e7SCharles Keepax ret = -EPERM; 881168d10e7SCharles Keepax } else { 882cef45771SCharles Keepax if (!ctl->flags && ctl->enabled && ctl->dsp->running) 883bc1765d6SCharles Keepax ret = wm_coeff_read_control(ctl, ctl->cache, ctl->len); 884bc1765d6SCharles Keepax 885168d10e7SCharles Keepax memcpy(p, ctl->cache, ctl->len); 88626c22a19SCharles Keepax } 88726c22a19SCharles Keepax 888168d10e7SCharles Keepax mutex_unlock(&ctl->dsp->pwr_lock); 88926c22a19SCharles Keepax 890168d10e7SCharles Keepax return ret; 8916ab2b7b4SDimitris Papastamos } 8926ab2b7b4SDimitris Papastamos 8939ee78757SCharles Keepax static int wm_coeff_tlv_get(struct snd_kcontrol *kctl, 8949ee78757SCharles Keepax unsigned int __user *bytes, unsigned int size) 8959ee78757SCharles Keepax { 8969ee78757SCharles Keepax struct soc_bytes_ext *bytes_ext = 8979ee78757SCharles Keepax (struct soc_bytes_ext *)kctl->private_value; 8989ee78757SCharles Keepax struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); 8999ee78757SCharles Keepax int ret = 0; 9009ee78757SCharles Keepax 9019ee78757SCharles Keepax mutex_lock(&ctl->dsp->pwr_lock); 9029ee78757SCharles Keepax 9039ee78757SCharles Keepax if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) { 904cef45771SCharles Keepax if (ctl->enabled && ctl->dsp->running) 9059ee78757SCharles Keepax ret = wm_coeff_read_control(ctl, ctl->cache, size); 9069ee78757SCharles Keepax else 9079ee78757SCharles Keepax ret = -EPERM; 9089ee78757SCharles Keepax } else { 909cef45771SCharles Keepax if (!ctl->flags && ctl->enabled && ctl->dsp->running) 9109ee78757SCharles Keepax ret = wm_coeff_read_control(ctl, ctl->cache, size); 9119ee78757SCharles Keepax } 9129ee78757SCharles Keepax 9139ee78757SCharles Keepax if (!ret && copy_to_user(bytes, ctl->cache, size)) 9149ee78757SCharles Keepax ret = -EFAULT; 9159ee78757SCharles Keepax 9169ee78757SCharles Keepax mutex_unlock(&ctl->dsp->pwr_lock); 9179ee78757SCharles Keepax 9189ee78757SCharles Keepax return ret; 9199ee78757SCharles Keepax } 9209ee78757SCharles Keepax 9216ab2b7b4SDimitris Papastamos struct wmfw_ctl_work { 9223809f001SCharles Keepax struct wm_adsp *dsp; 9236ab2b7b4SDimitris Papastamos struct wm_coeff_ctl *ctl; 9246ab2b7b4SDimitris Papastamos struct work_struct work; 9256ab2b7b4SDimitris Papastamos }; 9266ab2b7b4SDimitris Papastamos 9279ee78757SCharles Keepax static unsigned int wmfw_convert_flags(unsigned int in, unsigned int len) 9289ee78757SCharles Keepax { 9299ee78757SCharles Keepax unsigned int out, rd, wr, vol; 9309ee78757SCharles Keepax 9319ee78757SCharles Keepax if (len > ADSP_MAX_STD_CTRL_SIZE) { 9329ee78757SCharles Keepax rd = SNDRV_CTL_ELEM_ACCESS_TLV_READ; 9339ee78757SCharles Keepax wr = SNDRV_CTL_ELEM_ACCESS_TLV_WRITE; 9349ee78757SCharles Keepax vol = SNDRV_CTL_ELEM_ACCESS_VOLATILE; 9359ee78757SCharles Keepax 9369ee78757SCharles Keepax out = SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK; 9379ee78757SCharles Keepax } else { 9389ee78757SCharles Keepax rd = SNDRV_CTL_ELEM_ACCESS_READ; 9399ee78757SCharles Keepax wr = SNDRV_CTL_ELEM_ACCESS_WRITE; 9409ee78757SCharles Keepax vol = SNDRV_CTL_ELEM_ACCESS_VOLATILE; 9419ee78757SCharles Keepax 9429ee78757SCharles Keepax out = 0; 9439ee78757SCharles Keepax } 9449ee78757SCharles Keepax 9459ee78757SCharles Keepax if (in) { 9469ee78757SCharles Keepax if (in & WMFW_CTL_FLAG_READABLE) 9479ee78757SCharles Keepax out |= rd; 9489ee78757SCharles Keepax if (in & WMFW_CTL_FLAG_WRITEABLE) 9499ee78757SCharles Keepax out |= wr; 9509ee78757SCharles Keepax if (in & WMFW_CTL_FLAG_VOLATILE) 9519ee78757SCharles Keepax out |= vol; 9529ee78757SCharles Keepax } else { 9539ee78757SCharles Keepax out |= rd | wr | vol; 9549ee78757SCharles Keepax } 9559ee78757SCharles Keepax 9569ee78757SCharles Keepax return out; 9579ee78757SCharles Keepax } 9589ee78757SCharles Keepax 9593809f001SCharles Keepax static int wmfw_add_ctl(struct wm_adsp *dsp, struct wm_coeff_ctl *ctl) 9606ab2b7b4SDimitris Papastamos { 9616ab2b7b4SDimitris Papastamos struct snd_kcontrol_new *kcontrol; 9626ab2b7b4SDimitris Papastamos int ret; 9636ab2b7b4SDimitris Papastamos 96492bb4c32SDimitris Papastamos if (!ctl || !ctl->name) 9656ab2b7b4SDimitris Papastamos return -EINVAL; 9666ab2b7b4SDimitris Papastamos 9676ab2b7b4SDimitris Papastamos kcontrol = kzalloc(sizeof(*kcontrol), GFP_KERNEL); 9686ab2b7b4SDimitris Papastamos if (!kcontrol) 9696ab2b7b4SDimitris Papastamos return -ENOMEM; 9706ab2b7b4SDimitris Papastamos kcontrol->iface = SNDRV_CTL_ELEM_IFACE_MIXER; 9716ab2b7b4SDimitris Papastamos 9726ab2b7b4SDimitris Papastamos kcontrol->name = ctl->name; 9736ab2b7b4SDimitris Papastamos kcontrol->info = wm_coeff_info; 9746ab2b7b4SDimitris Papastamos kcontrol->get = wm_coeff_get; 9756ab2b7b4SDimitris Papastamos kcontrol->put = wm_coeff_put; 9769ee78757SCharles Keepax kcontrol->iface = SNDRV_CTL_ELEM_IFACE_MIXER; 9779ee78757SCharles Keepax kcontrol->tlv.c = snd_soc_bytes_tlv_callback; 9789ee78757SCharles Keepax kcontrol->private_value = (unsigned long)&ctl->bytes_ext; 9796ab2b7b4SDimitris Papastamos 9809ee78757SCharles Keepax ctl->bytes_ext.max = ctl->len; 9819ee78757SCharles Keepax ctl->bytes_ext.get = wm_coeff_tlv_get; 9829ee78757SCharles Keepax ctl->bytes_ext.put = wm_coeff_tlv_put; 9839ee78757SCharles Keepax 9849ee78757SCharles Keepax kcontrol->access = wmfw_convert_flags(ctl->flags, ctl->len); 98526c22a19SCharles Keepax 9867d00cd97SCharles Keepax ret = snd_soc_add_card_controls(dsp->card, kcontrol, 1); 9876ab2b7b4SDimitris Papastamos if (ret < 0) 9886ab2b7b4SDimitris Papastamos goto err_kcontrol; 9896ab2b7b4SDimitris Papastamos 9906ab2b7b4SDimitris Papastamos kfree(kcontrol); 9916ab2b7b4SDimitris Papastamos 9927d00cd97SCharles Keepax ctl->kcontrol = snd_soc_card_get_kcontrol(dsp->card, ctl->name); 99381ad93ecSDimitris Papastamos 9946ab2b7b4SDimitris Papastamos return 0; 9956ab2b7b4SDimitris Papastamos 9966ab2b7b4SDimitris Papastamos err_kcontrol: 9976ab2b7b4SDimitris Papastamos kfree(kcontrol); 9986ab2b7b4SDimitris Papastamos return ret; 9996ab2b7b4SDimitris Papastamos } 10006ab2b7b4SDimitris Papastamos 1001b21acc1cSCharles Keepax static int wm_coeff_init_control_caches(struct wm_adsp *dsp) 1002b21acc1cSCharles Keepax { 1003b21acc1cSCharles Keepax struct wm_coeff_ctl *ctl; 1004b21acc1cSCharles Keepax int ret; 1005b21acc1cSCharles Keepax 1006b21acc1cSCharles Keepax list_for_each_entry(ctl, &dsp->ctl_list, list) { 1007b21acc1cSCharles Keepax if (!ctl->enabled || ctl->set) 1008b21acc1cSCharles Keepax continue; 100926c22a19SCharles Keepax if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) 101026c22a19SCharles Keepax continue; 101126c22a19SCharles Keepax 10127d00cd97SCharles Keepax ret = wm_coeff_read_control(ctl, ctl->cache, ctl->len); 1013b21acc1cSCharles Keepax if (ret < 0) 1014b21acc1cSCharles Keepax return ret; 1015b21acc1cSCharles Keepax } 1016b21acc1cSCharles Keepax 1017b21acc1cSCharles Keepax return 0; 1018b21acc1cSCharles Keepax } 1019b21acc1cSCharles Keepax 1020b21acc1cSCharles Keepax static int wm_coeff_sync_controls(struct wm_adsp *dsp) 1021b21acc1cSCharles Keepax { 1022b21acc1cSCharles Keepax struct wm_coeff_ctl *ctl; 1023b21acc1cSCharles Keepax int ret; 1024b21acc1cSCharles Keepax 1025b21acc1cSCharles Keepax list_for_each_entry(ctl, &dsp->ctl_list, list) { 1026b21acc1cSCharles Keepax if (!ctl->enabled) 1027b21acc1cSCharles Keepax continue; 102826c22a19SCharles Keepax if (ctl->set && !(ctl->flags & WMFW_CTL_FLAG_VOLATILE)) { 10297d00cd97SCharles Keepax ret = wm_coeff_write_control(ctl, ctl->cache, ctl->len); 1030b21acc1cSCharles Keepax if (ret < 0) 1031b21acc1cSCharles Keepax return ret; 1032b21acc1cSCharles Keepax } 1033b21acc1cSCharles Keepax } 1034b21acc1cSCharles Keepax 1035b21acc1cSCharles Keepax return 0; 1036b21acc1cSCharles Keepax } 1037b21acc1cSCharles Keepax 1038b21acc1cSCharles Keepax static void wm_adsp_ctl_work(struct work_struct *work) 1039b21acc1cSCharles Keepax { 1040b21acc1cSCharles Keepax struct wmfw_ctl_work *ctl_work = container_of(work, 1041b21acc1cSCharles Keepax struct wmfw_ctl_work, 1042b21acc1cSCharles Keepax work); 1043b21acc1cSCharles Keepax 1044b21acc1cSCharles Keepax wmfw_add_ctl(ctl_work->dsp, ctl_work->ctl); 1045b21acc1cSCharles Keepax kfree(ctl_work); 1046b21acc1cSCharles Keepax } 1047b21acc1cSCharles Keepax 104866225e98SRichard Fitzgerald static void wm_adsp_free_ctl_blk(struct wm_coeff_ctl *ctl) 104966225e98SRichard Fitzgerald { 105066225e98SRichard Fitzgerald kfree(ctl->cache); 105166225e98SRichard Fitzgerald kfree(ctl->name); 105266225e98SRichard Fitzgerald kfree(ctl); 105366225e98SRichard Fitzgerald } 105466225e98SRichard Fitzgerald 1055b21acc1cSCharles Keepax static int wm_adsp_create_control(struct wm_adsp *dsp, 1056b21acc1cSCharles Keepax const struct wm_adsp_alg_region *alg_region, 10572323736dSCharles Keepax unsigned int offset, unsigned int len, 105826c22a19SCharles Keepax const char *subname, unsigned int subname_len, 105926c22a19SCharles Keepax unsigned int flags) 1060b21acc1cSCharles Keepax { 1061b21acc1cSCharles Keepax struct wm_coeff_ctl *ctl; 1062b21acc1cSCharles Keepax struct wmfw_ctl_work *ctl_work; 1063b21acc1cSCharles Keepax char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; 1064b21acc1cSCharles Keepax char *region_name; 1065b21acc1cSCharles Keepax int ret; 1066b21acc1cSCharles Keepax 106726c22a19SCharles Keepax if (flags & WMFW_CTL_FLAG_SYS) 106826c22a19SCharles Keepax return 0; 106926c22a19SCharles Keepax 1070b21acc1cSCharles Keepax switch (alg_region->type) { 1071b21acc1cSCharles Keepax case WMFW_ADSP1_PM: 1072b21acc1cSCharles Keepax region_name = "PM"; 1073b21acc1cSCharles Keepax break; 1074b21acc1cSCharles Keepax case WMFW_ADSP1_DM: 1075b21acc1cSCharles Keepax region_name = "DM"; 1076b21acc1cSCharles Keepax break; 1077b21acc1cSCharles Keepax case WMFW_ADSP2_XM: 1078b21acc1cSCharles Keepax region_name = "XM"; 1079b21acc1cSCharles Keepax break; 1080b21acc1cSCharles Keepax case WMFW_ADSP2_YM: 1081b21acc1cSCharles Keepax region_name = "YM"; 1082b21acc1cSCharles Keepax break; 1083b21acc1cSCharles Keepax case WMFW_ADSP1_ZM: 1084b21acc1cSCharles Keepax region_name = "ZM"; 1085b21acc1cSCharles Keepax break; 1086b21acc1cSCharles Keepax default: 10872323736dSCharles Keepax adsp_err(dsp, "Unknown region type: %d\n", alg_region->type); 1088b21acc1cSCharles Keepax return -EINVAL; 1089b21acc1cSCharles Keepax } 1090b21acc1cSCharles Keepax 1091cb5b57a9SCharles Keepax switch (dsp->fw_ver) { 1092cb5b57a9SCharles Keepax case 0: 1093cb5b57a9SCharles Keepax case 1: 1094b21acc1cSCharles Keepax snprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "DSP%d %s %x", 1095b21acc1cSCharles Keepax dsp->num, region_name, alg_region->alg); 1096cb5b57a9SCharles Keepax break; 1097cb5b57a9SCharles Keepax default: 1098cb5b57a9SCharles Keepax ret = snprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, 1099cb5b57a9SCharles Keepax "DSP%d%c %.12s %x", dsp->num, *region_name, 1100cb5b57a9SCharles Keepax wm_adsp_fw_text[dsp->fw], alg_region->alg); 1101cb5b57a9SCharles Keepax 1102cb5b57a9SCharles Keepax /* Truncate the subname from the start if it is too long */ 1103cb5b57a9SCharles Keepax if (subname) { 1104cb5b57a9SCharles Keepax int avail = SNDRV_CTL_ELEM_ID_NAME_MAXLEN - ret - 2; 1105cb5b57a9SCharles Keepax int skip = 0; 1106cb5b57a9SCharles Keepax 1107cb5b57a9SCharles Keepax if (subname_len > avail) 1108cb5b57a9SCharles Keepax skip = subname_len - avail; 1109cb5b57a9SCharles Keepax 1110cb5b57a9SCharles Keepax snprintf(name + ret, 1111cb5b57a9SCharles Keepax SNDRV_CTL_ELEM_ID_NAME_MAXLEN - ret, " %.*s", 1112cb5b57a9SCharles Keepax subname_len - skip, subname + skip); 1113cb5b57a9SCharles Keepax } 1114cb5b57a9SCharles Keepax break; 1115cb5b57a9SCharles Keepax } 1116b21acc1cSCharles Keepax 11177585a5b0SCharles Keepax list_for_each_entry(ctl, &dsp->ctl_list, list) { 1118b21acc1cSCharles Keepax if (!strcmp(ctl->name, name)) { 1119b21acc1cSCharles Keepax if (!ctl->enabled) 1120b21acc1cSCharles Keepax ctl->enabled = 1; 1121b21acc1cSCharles Keepax return 0; 1122b21acc1cSCharles Keepax } 1123b21acc1cSCharles Keepax } 1124b21acc1cSCharles Keepax 1125b21acc1cSCharles Keepax ctl = kzalloc(sizeof(*ctl), GFP_KERNEL); 1126b21acc1cSCharles Keepax if (!ctl) 1127b21acc1cSCharles Keepax return -ENOMEM; 11282323736dSCharles Keepax ctl->fw_name = wm_adsp_fw_text[dsp->fw]; 1129b21acc1cSCharles Keepax ctl->alg_region = *alg_region; 1130b21acc1cSCharles Keepax ctl->name = kmemdup(name, strlen(name) + 1, GFP_KERNEL); 1131b21acc1cSCharles Keepax if (!ctl->name) { 1132b21acc1cSCharles Keepax ret = -ENOMEM; 1133b21acc1cSCharles Keepax goto err_ctl; 1134b21acc1cSCharles Keepax } 1135b21acc1cSCharles Keepax ctl->enabled = 1; 1136b21acc1cSCharles Keepax ctl->set = 0; 1137b21acc1cSCharles Keepax ctl->ops.xget = wm_coeff_get; 1138b21acc1cSCharles Keepax ctl->ops.xput = wm_coeff_put; 1139b21acc1cSCharles Keepax ctl->dsp = dsp; 1140b21acc1cSCharles Keepax 114126c22a19SCharles Keepax ctl->flags = flags; 11422323736dSCharles Keepax ctl->offset = offset; 1143b21acc1cSCharles Keepax ctl->len = len; 1144b21acc1cSCharles Keepax ctl->cache = kzalloc(ctl->len, GFP_KERNEL); 1145b21acc1cSCharles Keepax if (!ctl->cache) { 1146b21acc1cSCharles Keepax ret = -ENOMEM; 1147b21acc1cSCharles Keepax goto err_ctl_name; 1148b21acc1cSCharles Keepax } 1149b21acc1cSCharles Keepax 11502323736dSCharles Keepax list_add(&ctl->list, &dsp->ctl_list); 11512323736dSCharles Keepax 1152b21acc1cSCharles Keepax ctl_work = kzalloc(sizeof(*ctl_work), GFP_KERNEL); 1153b21acc1cSCharles Keepax if (!ctl_work) { 1154b21acc1cSCharles Keepax ret = -ENOMEM; 1155b21acc1cSCharles Keepax goto err_ctl_cache; 1156b21acc1cSCharles Keepax } 1157b21acc1cSCharles Keepax 1158b21acc1cSCharles Keepax ctl_work->dsp = dsp; 1159b21acc1cSCharles Keepax ctl_work->ctl = ctl; 1160b21acc1cSCharles Keepax INIT_WORK(&ctl_work->work, wm_adsp_ctl_work); 1161b21acc1cSCharles Keepax schedule_work(&ctl_work->work); 1162b21acc1cSCharles Keepax 1163b21acc1cSCharles Keepax return 0; 1164b21acc1cSCharles Keepax 1165b21acc1cSCharles Keepax err_ctl_cache: 1166b21acc1cSCharles Keepax kfree(ctl->cache); 1167b21acc1cSCharles Keepax err_ctl_name: 1168b21acc1cSCharles Keepax kfree(ctl->name); 1169b21acc1cSCharles Keepax err_ctl: 1170b21acc1cSCharles Keepax kfree(ctl); 1171b21acc1cSCharles Keepax 1172b21acc1cSCharles Keepax return ret; 1173b21acc1cSCharles Keepax } 1174b21acc1cSCharles Keepax 11752323736dSCharles Keepax struct wm_coeff_parsed_alg { 11762323736dSCharles Keepax int id; 11772323736dSCharles Keepax const u8 *name; 11782323736dSCharles Keepax int name_len; 11792323736dSCharles Keepax int ncoeff; 11802323736dSCharles Keepax }; 11812323736dSCharles Keepax 11822323736dSCharles Keepax struct wm_coeff_parsed_coeff { 11832323736dSCharles Keepax int offset; 11842323736dSCharles Keepax int mem_type; 11852323736dSCharles Keepax const u8 *name; 11862323736dSCharles Keepax int name_len; 11872323736dSCharles Keepax int ctl_type; 11882323736dSCharles Keepax int flags; 11892323736dSCharles Keepax int len; 11902323736dSCharles Keepax }; 11912323736dSCharles Keepax 1192cb5b57a9SCharles Keepax static int wm_coeff_parse_string(int bytes, const u8 **pos, const u8 **str) 1193cb5b57a9SCharles Keepax { 1194cb5b57a9SCharles Keepax int length; 1195cb5b57a9SCharles Keepax 1196cb5b57a9SCharles Keepax switch (bytes) { 1197cb5b57a9SCharles Keepax case 1: 1198cb5b57a9SCharles Keepax length = **pos; 1199cb5b57a9SCharles Keepax break; 1200cb5b57a9SCharles Keepax case 2: 12018299ee81SCharles Keepax length = le16_to_cpu(*((__le16 *)*pos)); 1202cb5b57a9SCharles Keepax break; 1203cb5b57a9SCharles Keepax default: 1204cb5b57a9SCharles Keepax return 0; 1205cb5b57a9SCharles Keepax } 1206cb5b57a9SCharles Keepax 1207cb5b57a9SCharles Keepax if (str) 1208cb5b57a9SCharles Keepax *str = *pos + bytes; 1209cb5b57a9SCharles Keepax 1210cb5b57a9SCharles Keepax *pos += ((length + bytes) + 3) & ~0x03; 1211cb5b57a9SCharles Keepax 1212cb5b57a9SCharles Keepax return length; 1213cb5b57a9SCharles Keepax } 1214cb5b57a9SCharles Keepax 1215cb5b57a9SCharles Keepax static int wm_coeff_parse_int(int bytes, const u8 **pos) 1216cb5b57a9SCharles Keepax { 1217cb5b57a9SCharles Keepax int val = 0; 1218cb5b57a9SCharles Keepax 1219cb5b57a9SCharles Keepax switch (bytes) { 1220cb5b57a9SCharles Keepax case 2: 12218299ee81SCharles Keepax val = le16_to_cpu(*((__le16 *)*pos)); 1222cb5b57a9SCharles Keepax break; 1223cb5b57a9SCharles Keepax case 4: 12248299ee81SCharles Keepax val = le32_to_cpu(*((__le32 *)*pos)); 1225cb5b57a9SCharles Keepax break; 1226cb5b57a9SCharles Keepax default: 1227cb5b57a9SCharles Keepax break; 1228cb5b57a9SCharles Keepax } 1229cb5b57a9SCharles Keepax 1230cb5b57a9SCharles Keepax *pos += bytes; 1231cb5b57a9SCharles Keepax 1232cb5b57a9SCharles Keepax return val; 1233cb5b57a9SCharles Keepax } 1234cb5b57a9SCharles Keepax 12352323736dSCharles Keepax static inline void wm_coeff_parse_alg(struct wm_adsp *dsp, const u8 **data, 12362323736dSCharles Keepax struct wm_coeff_parsed_alg *blk) 12372323736dSCharles Keepax { 12382323736dSCharles Keepax const struct wmfw_adsp_alg_data *raw; 12392323736dSCharles Keepax 1240cb5b57a9SCharles Keepax switch (dsp->fw_ver) { 1241cb5b57a9SCharles Keepax case 0: 1242cb5b57a9SCharles Keepax case 1: 12432323736dSCharles Keepax raw = (const struct wmfw_adsp_alg_data *)*data; 12442323736dSCharles Keepax *data = raw->data; 12452323736dSCharles Keepax 12462323736dSCharles Keepax blk->id = le32_to_cpu(raw->id); 12472323736dSCharles Keepax blk->name = raw->name; 12482323736dSCharles Keepax blk->name_len = strlen(raw->name); 12492323736dSCharles Keepax blk->ncoeff = le32_to_cpu(raw->ncoeff); 1250cb5b57a9SCharles Keepax break; 1251cb5b57a9SCharles Keepax default: 1252cb5b57a9SCharles Keepax blk->id = wm_coeff_parse_int(sizeof(raw->id), data); 1253cb5b57a9SCharles Keepax blk->name_len = wm_coeff_parse_string(sizeof(u8), data, 1254cb5b57a9SCharles Keepax &blk->name); 1255cb5b57a9SCharles Keepax wm_coeff_parse_string(sizeof(u16), data, NULL); 1256cb5b57a9SCharles Keepax blk->ncoeff = wm_coeff_parse_int(sizeof(raw->ncoeff), data); 1257cb5b57a9SCharles Keepax break; 1258cb5b57a9SCharles Keepax } 12592323736dSCharles Keepax 12602323736dSCharles Keepax adsp_dbg(dsp, "Algorithm ID: %#x\n", blk->id); 12612323736dSCharles Keepax adsp_dbg(dsp, "Algorithm name: %.*s\n", blk->name_len, blk->name); 12622323736dSCharles Keepax adsp_dbg(dsp, "# of coefficient descriptors: %#x\n", blk->ncoeff); 12632323736dSCharles Keepax } 12642323736dSCharles Keepax 12652323736dSCharles Keepax static inline void wm_coeff_parse_coeff(struct wm_adsp *dsp, const u8 **data, 12662323736dSCharles Keepax struct wm_coeff_parsed_coeff *blk) 12672323736dSCharles Keepax { 12682323736dSCharles Keepax const struct wmfw_adsp_coeff_data *raw; 1269cb5b57a9SCharles Keepax const u8 *tmp; 1270cb5b57a9SCharles Keepax int length; 12712323736dSCharles Keepax 1272cb5b57a9SCharles Keepax switch (dsp->fw_ver) { 1273cb5b57a9SCharles Keepax case 0: 1274cb5b57a9SCharles Keepax case 1: 12752323736dSCharles Keepax raw = (const struct wmfw_adsp_coeff_data *)*data; 12762323736dSCharles Keepax *data = *data + sizeof(raw->hdr) + le32_to_cpu(raw->hdr.size); 12772323736dSCharles Keepax 12782323736dSCharles Keepax blk->offset = le16_to_cpu(raw->hdr.offset); 12792323736dSCharles Keepax blk->mem_type = le16_to_cpu(raw->hdr.type); 12802323736dSCharles Keepax blk->name = raw->name; 12812323736dSCharles Keepax blk->name_len = strlen(raw->name); 12822323736dSCharles Keepax blk->ctl_type = le16_to_cpu(raw->ctl_type); 12832323736dSCharles Keepax blk->flags = le16_to_cpu(raw->flags); 12842323736dSCharles Keepax blk->len = le32_to_cpu(raw->len); 1285cb5b57a9SCharles Keepax break; 1286cb5b57a9SCharles Keepax default: 1287cb5b57a9SCharles Keepax tmp = *data; 1288cb5b57a9SCharles Keepax blk->offset = wm_coeff_parse_int(sizeof(raw->hdr.offset), &tmp); 1289cb5b57a9SCharles Keepax blk->mem_type = wm_coeff_parse_int(sizeof(raw->hdr.type), &tmp); 1290cb5b57a9SCharles Keepax length = wm_coeff_parse_int(sizeof(raw->hdr.size), &tmp); 1291cb5b57a9SCharles Keepax blk->name_len = wm_coeff_parse_string(sizeof(u8), &tmp, 1292cb5b57a9SCharles Keepax &blk->name); 1293cb5b57a9SCharles Keepax wm_coeff_parse_string(sizeof(u8), &tmp, NULL); 1294cb5b57a9SCharles Keepax wm_coeff_parse_string(sizeof(u16), &tmp, NULL); 1295cb5b57a9SCharles Keepax blk->ctl_type = wm_coeff_parse_int(sizeof(raw->ctl_type), &tmp); 1296cb5b57a9SCharles Keepax blk->flags = wm_coeff_parse_int(sizeof(raw->flags), &tmp); 1297cb5b57a9SCharles Keepax blk->len = wm_coeff_parse_int(sizeof(raw->len), &tmp); 1298cb5b57a9SCharles Keepax 1299cb5b57a9SCharles Keepax *data = *data + sizeof(raw->hdr) + length; 1300cb5b57a9SCharles Keepax break; 1301cb5b57a9SCharles Keepax } 13022323736dSCharles Keepax 13032323736dSCharles Keepax adsp_dbg(dsp, "\tCoefficient type: %#x\n", blk->mem_type); 13042323736dSCharles Keepax adsp_dbg(dsp, "\tCoefficient offset: %#x\n", blk->offset); 13052323736dSCharles Keepax adsp_dbg(dsp, "\tCoefficient name: %.*s\n", blk->name_len, blk->name); 13062323736dSCharles Keepax adsp_dbg(dsp, "\tCoefficient flags: %#x\n", blk->flags); 13072323736dSCharles Keepax adsp_dbg(dsp, "\tALSA control type: %#x\n", blk->ctl_type); 13082323736dSCharles Keepax adsp_dbg(dsp, "\tALSA control len: %#x\n", blk->len); 13092323736dSCharles Keepax } 13102323736dSCharles Keepax 13112323736dSCharles Keepax static int wm_adsp_parse_coeff(struct wm_adsp *dsp, 13122323736dSCharles Keepax const struct wmfw_region *region) 13132323736dSCharles Keepax { 13142323736dSCharles Keepax struct wm_adsp_alg_region alg_region = {}; 13152323736dSCharles Keepax struct wm_coeff_parsed_alg alg_blk; 13162323736dSCharles Keepax struct wm_coeff_parsed_coeff coeff_blk; 13172323736dSCharles Keepax const u8 *data = region->data; 13182323736dSCharles Keepax int i, ret; 13192323736dSCharles Keepax 13202323736dSCharles Keepax wm_coeff_parse_alg(dsp, &data, &alg_blk); 13212323736dSCharles Keepax for (i = 0; i < alg_blk.ncoeff; i++) { 13222323736dSCharles Keepax wm_coeff_parse_coeff(dsp, &data, &coeff_blk); 13232323736dSCharles Keepax 13242323736dSCharles Keepax switch (coeff_blk.ctl_type) { 13252323736dSCharles Keepax case SNDRV_CTL_ELEM_TYPE_BYTES: 13262323736dSCharles Keepax break; 13272323736dSCharles Keepax default: 13282323736dSCharles Keepax adsp_err(dsp, "Unknown control type: %d\n", 13292323736dSCharles Keepax coeff_blk.ctl_type); 13302323736dSCharles Keepax return -EINVAL; 13312323736dSCharles Keepax } 13322323736dSCharles Keepax 13332323736dSCharles Keepax alg_region.type = coeff_blk.mem_type; 13342323736dSCharles Keepax alg_region.alg = alg_blk.id; 13352323736dSCharles Keepax 13362323736dSCharles Keepax ret = wm_adsp_create_control(dsp, &alg_region, 13372323736dSCharles Keepax coeff_blk.offset, 13382323736dSCharles Keepax coeff_blk.len, 13392323736dSCharles Keepax coeff_blk.name, 134026c22a19SCharles Keepax coeff_blk.name_len, 134126c22a19SCharles Keepax coeff_blk.flags); 13422323736dSCharles Keepax if (ret < 0) 13432323736dSCharles Keepax adsp_err(dsp, "Failed to create control: %.*s, %d\n", 13442323736dSCharles Keepax coeff_blk.name_len, coeff_blk.name, ret); 13452323736dSCharles Keepax } 13462323736dSCharles Keepax 13472323736dSCharles Keepax return 0; 13482323736dSCharles Keepax } 13492323736dSCharles Keepax 13502159ad93SMark Brown static int wm_adsp_load(struct wm_adsp *dsp) 13512159ad93SMark Brown { 1352cf17c83cSMark Brown LIST_HEAD(buf_list); 13532159ad93SMark Brown const struct firmware *firmware; 13542159ad93SMark Brown struct regmap *regmap = dsp->regmap; 13552159ad93SMark Brown unsigned int pos = 0; 13562159ad93SMark Brown const struct wmfw_header *header; 13572159ad93SMark Brown const struct wmfw_adsp1_sizes *adsp1_sizes; 13582159ad93SMark Brown const struct wmfw_adsp2_sizes *adsp2_sizes; 13592159ad93SMark Brown const struct wmfw_footer *footer; 13602159ad93SMark Brown const struct wmfw_region *region; 13612159ad93SMark Brown const struct wm_adsp_region *mem; 13622159ad93SMark Brown const char *region_name; 13632159ad93SMark Brown char *file, *text; 1364cf17c83cSMark Brown struct wm_adsp_buf *buf; 13652159ad93SMark Brown unsigned int reg; 13662159ad93SMark Brown int regions = 0; 13672159ad93SMark Brown int ret, offset, type, sizes; 13682159ad93SMark Brown 13692159ad93SMark Brown file = kzalloc(PAGE_SIZE, GFP_KERNEL); 13702159ad93SMark Brown if (file == NULL) 13712159ad93SMark Brown return -ENOMEM; 13722159ad93SMark Brown 13731023dbd9SMark Brown snprintf(file, PAGE_SIZE, "%s-dsp%d-%s.wmfw", dsp->part, dsp->num, 13741023dbd9SMark Brown wm_adsp_fw[dsp->fw].file); 13752159ad93SMark Brown file[PAGE_SIZE - 1] = '\0'; 13762159ad93SMark Brown 13772159ad93SMark Brown ret = request_firmware(&firmware, file, dsp->dev); 13782159ad93SMark Brown if (ret != 0) { 13792159ad93SMark Brown adsp_err(dsp, "Failed to request '%s'\n", file); 13802159ad93SMark Brown goto out; 13812159ad93SMark Brown } 13822159ad93SMark Brown ret = -EINVAL; 13832159ad93SMark Brown 13842159ad93SMark Brown pos = sizeof(*header) + sizeof(*adsp1_sizes) + sizeof(*footer); 13852159ad93SMark Brown if (pos >= firmware->size) { 13862159ad93SMark Brown adsp_err(dsp, "%s: file too short, %zu bytes\n", 13872159ad93SMark Brown file, firmware->size); 13882159ad93SMark Brown goto out_fw; 13892159ad93SMark Brown } 13902159ad93SMark Brown 13912159ad93SMark Brown header = (void *)&firmware->data[0]; 13922159ad93SMark Brown 13932159ad93SMark Brown if (memcmp(&header->magic[0], "WMFW", 4) != 0) { 13942159ad93SMark Brown adsp_err(dsp, "%s: invalid magic\n", file); 13952159ad93SMark Brown goto out_fw; 13962159ad93SMark Brown } 13972159ad93SMark Brown 13982323736dSCharles Keepax switch (header->ver) { 13992323736dSCharles Keepax case 0: 1400c61e59feSCharles Keepax adsp_warn(dsp, "%s: Depreciated file format %d\n", 1401c61e59feSCharles Keepax file, header->ver); 1402c61e59feSCharles Keepax break; 14032323736dSCharles Keepax case 1: 1404cb5b57a9SCharles Keepax case 2: 14052323736dSCharles Keepax break; 14062323736dSCharles Keepax default: 14072159ad93SMark Brown adsp_err(dsp, "%s: unknown file format %d\n", 14082159ad93SMark Brown file, header->ver); 14092159ad93SMark Brown goto out_fw; 14102159ad93SMark Brown } 14112323736dSCharles Keepax 14123626992aSDimitris Papastamos adsp_info(dsp, "Firmware version: %d\n", header->ver); 14132323736dSCharles Keepax dsp->fw_ver = header->ver; 14142159ad93SMark Brown 14152159ad93SMark Brown if (header->core != dsp->type) { 14162159ad93SMark Brown adsp_err(dsp, "%s: invalid core %d != %d\n", 14172159ad93SMark Brown file, header->core, dsp->type); 14182159ad93SMark Brown goto out_fw; 14192159ad93SMark Brown } 14202159ad93SMark Brown 14212159ad93SMark Brown switch (dsp->type) { 14222159ad93SMark Brown case WMFW_ADSP1: 14232159ad93SMark Brown pos = sizeof(*header) + sizeof(*adsp1_sizes) + sizeof(*footer); 14242159ad93SMark Brown adsp1_sizes = (void *)&(header[1]); 14252159ad93SMark Brown footer = (void *)&(adsp1_sizes[1]); 14262159ad93SMark Brown sizes = sizeof(*adsp1_sizes); 14272159ad93SMark Brown 14282159ad93SMark Brown adsp_dbg(dsp, "%s: %d DM, %d PM, %d ZM\n", 14292159ad93SMark Brown file, le32_to_cpu(adsp1_sizes->dm), 14302159ad93SMark Brown le32_to_cpu(adsp1_sizes->pm), 14312159ad93SMark Brown le32_to_cpu(adsp1_sizes->zm)); 14322159ad93SMark Brown break; 14332159ad93SMark Brown 14342159ad93SMark Brown case WMFW_ADSP2: 14352159ad93SMark Brown pos = sizeof(*header) + sizeof(*adsp2_sizes) + sizeof(*footer); 14362159ad93SMark Brown adsp2_sizes = (void *)&(header[1]); 14372159ad93SMark Brown footer = (void *)&(adsp2_sizes[1]); 14382159ad93SMark Brown sizes = sizeof(*adsp2_sizes); 14392159ad93SMark Brown 14402159ad93SMark Brown adsp_dbg(dsp, "%s: %d XM, %d YM %d PM, %d ZM\n", 14412159ad93SMark Brown file, le32_to_cpu(adsp2_sizes->xm), 14422159ad93SMark Brown le32_to_cpu(adsp2_sizes->ym), 14432159ad93SMark Brown le32_to_cpu(adsp2_sizes->pm), 14442159ad93SMark Brown le32_to_cpu(adsp2_sizes->zm)); 14452159ad93SMark Brown break; 14462159ad93SMark Brown 14472159ad93SMark Brown default: 14486c452bdaSTakashi Iwai WARN(1, "Unknown DSP type"); 14492159ad93SMark Brown goto out_fw; 14502159ad93SMark Brown } 14512159ad93SMark Brown 14522159ad93SMark Brown if (le32_to_cpu(header->len) != sizeof(*header) + 14532159ad93SMark Brown sizes + sizeof(*footer)) { 14542159ad93SMark Brown adsp_err(dsp, "%s: unexpected header length %d\n", 14552159ad93SMark Brown file, le32_to_cpu(header->len)); 14562159ad93SMark Brown goto out_fw; 14572159ad93SMark Brown } 14582159ad93SMark Brown 14592159ad93SMark Brown adsp_dbg(dsp, "%s: timestamp %llu\n", file, 14602159ad93SMark Brown le64_to_cpu(footer->timestamp)); 14612159ad93SMark Brown 14622159ad93SMark Brown while (pos < firmware->size && 14632159ad93SMark Brown pos - firmware->size > sizeof(*region)) { 14642159ad93SMark Brown region = (void *)&(firmware->data[pos]); 14652159ad93SMark Brown region_name = "Unknown"; 14662159ad93SMark Brown reg = 0; 14672159ad93SMark Brown text = NULL; 14682159ad93SMark Brown offset = le32_to_cpu(region->offset) & 0xffffff; 14692159ad93SMark Brown type = be32_to_cpu(region->type) & 0xff; 14702159ad93SMark Brown mem = wm_adsp_find_region(dsp, type); 14712159ad93SMark Brown 14722159ad93SMark Brown switch (type) { 14732159ad93SMark Brown case WMFW_NAME_TEXT: 14742159ad93SMark Brown region_name = "Firmware name"; 14752159ad93SMark Brown text = kzalloc(le32_to_cpu(region->len) + 1, 14762159ad93SMark Brown GFP_KERNEL); 14772159ad93SMark Brown break; 14782323736dSCharles Keepax case WMFW_ALGORITHM_DATA: 14792323736dSCharles Keepax region_name = "Algorithm"; 14802323736dSCharles Keepax ret = wm_adsp_parse_coeff(dsp, region); 14812323736dSCharles Keepax if (ret != 0) 14822323736dSCharles Keepax goto out_fw; 14832323736dSCharles Keepax break; 14842159ad93SMark Brown case WMFW_INFO_TEXT: 14852159ad93SMark Brown region_name = "Information"; 14862159ad93SMark Brown text = kzalloc(le32_to_cpu(region->len) + 1, 14872159ad93SMark Brown GFP_KERNEL); 14882159ad93SMark Brown break; 14892159ad93SMark Brown case WMFW_ABSOLUTE: 14902159ad93SMark Brown region_name = "Absolute"; 14912159ad93SMark Brown reg = offset; 14922159ad93SMark Brown break; 14932159ad93SMark Brown case WMFW_ADSP1_PM: 14942159ad93SMark Brown region_name = "PM"; 149545b9ee72SMark Brown reg = wm_adsp_region_to_reg(mem, offset); 14962159ad93SMark Brown break; 14972159ad93SMark Brown case WMFW_ADSP1_DM: 14982159ad93SMark Brown region_name = "DM"; 149945b9ee72SMark Brown reg = wm_adsp_region_to_reg(mem, offset); 15002159ad93SMark Brown break; 15012159ad93SMark Brown case WMFW_ADSP2_XM: 15022159ad93SMark Brown region_name = "XM"; 150345b9ee72SMark Brown reg = wm_adsp_region_to_reg(mem, offset); 15042159ad93SMark Brown break; 15052159ad93SMark Brown case WMFW_ADSP2_YM: 15062159ad93SMark Brown region_name = "YM"; 150745b9ee72SMark Brown reg = wm_adsp_region_to_reg(mem, offset); 15082159ad93SMark Brown break; 15092159ad93SMark Brown case WMFW_ADSP1_ZM: 15102159ad93SMark Brown region_name = "ZM"; 151145b9ee72SMark Brown reg = wm_adsp_region_to_reg(mem, offset); 15122159ad93SMark Brown break; 15132159ad93SMark Brown default: 15142159ad93SMark Brown adsp_warn(dsp, 15152159ad93SMark Brown "%s.%d: Unknown region type %x at %d(%x)\n", 15162159ad93SMark Brown file, regions, type, pos, pos); 15172159ad93SMark Brown break; 15182159ad93SMark Brown } 15192159ad93SMark Brown 15202159ad93SMark Brown adsp_dbg(dsp, "%s.%d: %d bytes at %d in %s\n", file, 15212159ad93SMark Brown regions, le32_to_cpu(region->len), offset, 15222159ad93SMark Brown region_name); 15232159ad93SMark Brown 15242159ad93SMark Brown if (text) { 15252159ad93SMark Brown memcpy(text, region->data, le32_to_cpu(region->len)); 15262159ad93SMark Brown adsp_info(dsp, "%s: %s\n", file, text); 15272159ad93SMark Brown kfree(text); 15282159ad93SMark Brown } 15292159ad93SMark Brown 15302159ad93SMark Brown if (reg) { 1531cdcd7f72SCharles Keepax buf = wm_adsp_buf_alloc(region->data, 1532cdcd7f72SCharles Keepax le32_to_cpu(region->len), 1533cf17c83cSMark Brown &buf_list); 1534a76fefabSMark Brown if (!buf) { 1535a76fefabSMark Brown adsp_err(dsp, "Out of memory\n"); 15367328823dSDimitris Papastamos ret = -ENOMEM; 15377328823dSDimitris Papastamos goto out_fw; 1538a76fefabSMark Brown } 1539a76fefabSMark Brown 1540cdcd7f72SCharles Keepax ret = regmap_raw_write_async(regmap, reg, buf->buf, 1541cdcd7f72SCharles Keepax le32_to_cpu(region->len)); 15422159ad93SMark Brown if (ret != 0) { 15432159ad93SMark Brown adsp_err(dsp, 1544cdcd7f72SCharles Keepax "%s.%d: Failed to write %d bytes at %d in %s: %d\n", 15452159ad93SMark Brown file, regions, 1546cdcd7f72SCharles Keepax le32_to_cpu(region->len), offset, 15472159ad93SMark Brown region_name, ret); 15482159ad93SMark Brown goto out_fw; 15492159ad93SMark Brown } 15502159ad93SMark Brown } 15512159ad93SMark Brown 15522159ad93SMark Brown pos += le32_to_cpu(region->len) + sizeof(*region); 15532159ad93SMark Brown regions++; 15542159ad93SMark Brown } 15552159ad93SMark Brown 1556cf17c83cSMark Brown ret = regmap_async_complete(regmap); 1557cf17c83cSMark Brown if (ret != 0) { 1558cf17c83cSMark Brown adsp_err(dsp, "Failed to complete async write: %d\n", ret); 1559cf17c83cSMark Brown goto out_fw; 1560cf17c83cSMark Brown } 1561cf17c83cSMark Brown 15622159ad93SMark Brown if (pos > firmware->size) 15632159ad93SMark Brown adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n", 15642159ad93SMark Brown file, regions, pos - firmware->size); 15652159ad93SMark Brown 1566f9f55e31SRichard Fitzgerald wm_adsp_debugfs_save_wmfwname(dsp, file); 1567f9f55e31SRichard Fitzgerald 15682159ad93SMark Brown out_fw: 1569cf17c83cSMark Brown regmap_async_complete(regmap); 1570cf17c83cSMark Brown wm_adsp_buf_free(&buf_list); 15712159ad93SMark Brown release_firmware(firmware); 15722159ad93SMark Brown out: 15732159ad93SMark Brown kfree(file); 15742159ad93SMark Brown 15752159ad93SMark Brown return ret; 15762159ad93SMark Brown } 15772159ad93SMark Brown 15782323736dSCharles Keepax static void wm_adsp_ctl_fixup_base(struct wm_adsp *dsp, 15792323736dSCharles Keepax const struct wm_adsp_alg_region *alg_region) 15802323736dSCharles Keepax { 15812323736dSCharles Keepax struct wm_coeff_ctl *ctl; 15822323736dSCharles Keepax 15832323736dSCharles Keepax list_for_each_entry(ctl, &dsp->ctl_list, list) { 15842323736dSCharles Keepax if (ctl->fw_name == wm_adsp_fw_text[dsp->fw] && 15852323736dSCharles Keepax alg_region->alg == ctl->alg_region.alg && 15862323736dSCharles Keepax alg_region->type == ctl->alg_region.type) { 15872323736dSCharles Keepax ctl->alg_region.base = alg_region->base; 15882323736dSCharles Keepax } 15892323736dSCharles Keepax } 15902323736dSCharles Keepax } 15912323736dSCharles Keepax 15923809f001SCharles Keepax static void *wm_adsp_read_algs(struct wm_adsp *dsp, size_t n_algs, 1593b618a185SCharles Keepax unsigned int pos, unsigned int len) 1594db40517cSMark Brown { 1595b618a185SCharles Keepax void *alg; 1596b618a185SCharles Keepax int ret; 1597db40517cSMark Brown __be32 val; 1598db40517cSMark Brown 15993809f001SCharles Keepax if (n_algs == 0) { 1600b618a185SCharles Keepax adsp_err(dsp, "No algorithms\n"); 1601b618a185SCharles Keepax return ERR_PTR(-EINVAL); 1602db40517cSMark Brown } 1603db40517cSMark Brown 16043809f001SCharles Keepax if (n_algs > 1024) { 16053809f001SCharles Keepax adsp_err(dsp, "Algorithm count %zx excessive\n", n_algs); 1606b618a185SCharles Keepax return ERR_PTR(-EINVAL); 1607b618a185SCharles Keepax } 1608b618a185SCharles Keepax 1609b618a185SCharles Keepax /* Read the terminator first to validate the length */ 1610b618a185SCharles Keepax ret = regmap_raw_read(dsp->regmap, pos + len, &val, sizeof(val)); 1611b618a185SCharles Keepax if (ret != 0) { 1612b618a185SCharles Keepax adsp_err(dsp, "Failed to read algorithm list end: %d\n", 1613b618a185SCharles Keepax ret); 1614b618a185SCharles Keepax return ERR_PTR(ret); 1615b618a185SCharles Keepax } 1616b618a185SCharles Keepax 1617b618a185SCharles Keepax if (be32_to_cpu(val) != 0xbedead) 1618b618a185SCharles Keepax adsp_warn(dsp, "Algorithm list end %x 0x%x != 0xbeadead\n", 1619b618a185SCharles Keepax pos + len, be32_to_cpu(val)); 1620b618a185SCharles Keepax 1621b618a185SCharles Keepax alg = kzalloc(len * 2, GFP_KERNEL | GFP_DMA); 1622b618a185SCharles Keepax if (!alg) 1623b618a185SCharles Keepax return ERR_PTR(-ENOMEM); 1624b618a185SCharles Keepax 1625b618a185SCharles Keepax ret = regmap_raw_read(dsp->regmap, pos, alg, len * 2); 1626b618a185SCharles Keepax if (ret != 0) { 16277d00cd97SCharles Keepax adsp_err(dsp, "Failed to read algorithm list: %d\n", ret); 1628b618a185SCharles Keepax kfree(alg); 1629b618a185SCharles Keepax return ERR_PTR(ret); 1630b618a185SCharles Keepax } 1631b618a185SCharles Keepax 1632b618a185SCharles Keepax return alg; 1633b618a185SCharles Keepax } 1634b618a185SCharles Keepax 163514197095SCharles Keepax static struct wm_adsp_alg_region * 163614197095SCharles Keepax wm_adsp_find_alg_region(struct wm_adsp *dsp, int type, unsigned int id) 163714197095SCharles Keepax { 163814197095SCharles Keepax struct wm_adsp_alg_region *alg_region; 163914197095SCharles Keepax 164014197095SCharles Keepax list_for_each_entry(alg_region, &dsp->alg_regions, list) { 164114197095SCharles Keepax if (id == alg_region->alg && type == alg_region->type) 164214197095SCharles Keepax return alg_region; 164314197095SCharles Keepax } 164414197095SCharles Keepax 164514197095SCharles Keepax return NULL; 164614197095SCharles Keepax } 164714197095SCharles Keepax 1648d9d20e17SCharles Keepax static struct wm_adsp_alg_region *wm_adsp_create_region(struct wm_adsp *dsp, 1649d9d20e17SCharles Keepax int type, __be32 id, 1650d9d20e17SCharles Keepax __be32 base) 1651d9d20e17SCharles Keepax { 1652d9d20e17SCharles Keepax struct wm_adsp_alg_region *alg_region; 1653d9d20e17SCharles Keepax 1654d9d20e17SCharles Keepax alg_region = kzalloc(sizeof(*alg_region), GFP_KERNEL); 1655d9d20e17SCharles Keepax if (!alg_region) 1656d9d20e17SCharles Keepax return ERR_PTR(-ENOMEM); 1657d9d20e17SCharles Keepax 1658d9d20e17SCharles Keepax alg_region->type = type; 1659d9d20e17SCharles Keepax alg_region->alg = be32_to_cpu(id); 1660d9d20e17SCharles Keepax alg_region->base = be32_to_cpu(base); 1661d9d20e17SCharles Keepax 1662d9d20e17SCharles Keepax list_add_tail(&alg_region->list, &dsp->alg_regions); 1663d9d20e17SCharles Keepax 16642323736dSCharles Keepax if (dsp->fw_ver > 0) 16652323736dSCharles Keepax wm_adsp_ctl_fixup_base(dsp, alg_region); 16662323736dSCharles Keepax 1667d9d20e17SCharles Keepax return alg_region; 1668d9d20e17SCharles Keepax } 1669d9d20e17SCharles Keepax 167056574d54SRichard Fitzgerald static void wm_adsp_free_alg_regions(struct wm_adsp *dsp) 167156574d54SRichard Fitzgerald { 167256574d54SRichard Fitzgerald struct wm_adsp_alg_region *alg_region; 167356574d54SRichard Fitzgerald 167456574d54SRichard Fitzgerald while (!list_empty(&dsp->alg_regions)) { 167556574d54SRichard Fitzgerald alg_region = list_first_entry(&dsp->alg_regions, 167656574d54SRichard Fitzgerald struct wm_adsp_alg_region, 167756574d54SRichard Fitzgerald list); 167856574d54SRichard Fitzgerald list_del(&alg_region->list); 167956574d54SRichard Fitzgerald kfree(alg_region); 168056574d54SRichard Fitzgerald } 168156574d54SRichard Fitzgerald } 168256574d54SRichard Fitzgerald 1683b618a185SCharles Keepax static int wm_adsp1_setup_algs(struct wm_adsp *dsp) 1684b618a185SCharles Keepax { 1685b618a185SCharles Keepax struct wmfw_adsp1_id_hdr adsp1_id; 1686b618a185SCharles Keepax struct wmfw_adsp1_alg_hdr *adsp1_alg; 16873809f001SCharles Keepax struct wm_adsp_alg_region *alg_region; 1688b618a185SCharles Keepax const struct wm_adsp_region *mem; 1689b618a185SCharles Keepax unsigned int pos, len; 16903809f001SCharles Keepax size_t n_algs; 1691b618a185SCharles Keepax int i, ret; 1692b618a185SCharles Keepax 1693b618a185SCharles Keepax mem = wm_adsp_find_region(dsp, WMFW_ADSP1_DM); 16946c452bdaSTakashi Iwai if (WARN_ON(!mem)) 1695db40517cSMark Brown return -EINVAL; 1696db40517cSMark Brown 1697b618a185SCharles Keepax ret = regmap_raw_read(dsp->regmap, mem->base, &adsp1_id, 1698db40517cSMark Brown sizeof(adsp1_id)); 1699db40517cSMark Brown if (ret != 0) { 1700db40517cSMark Brown adsp_err(dsp, "Failed to read algorithm info: %d\n", 1701db40517cSMark Brown ret); 1702db40517cSMark Brown return ret; 1703db40517cSMark Brown } 1704db40517cSMark Brown 17053809f001SCharles Keepax n_algs = be32_to_cpu(adsp1_id.n_algs); 1706f395a218SMark Brown dsp->fw_id = be32_to_cpu(adsp1_id.fw.id); 1707db40517cSMark Brown adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n", 1708f395a218SMark Brown dsp->fw_id, 1709db40517cSMark Brown (be32_to_cpu(adsp1_id.fw.ver) & 0xff0000) >> 16, 1710db40517cSMark Brown (be32_to_cpu(adsp1_id.fw.ver) & 0xff00) >> 8, 1711db40517cSMark Brown be32_to_cpu(adsp1_id.fw.ver) & 0xff, 17123809f001SCharles Keepax n_algs); 1713db40517cSMark Brown 1714d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_ZM, 1715d9d20e17SCharles Keepax adsp1_id.fw.id, adsp1_id.zm); 1716d9d20e17SCharles Keepax if (IS_ERR(alg_region)) 1717d9d20e17SCharles Keepax return PTR_ERR(alg_region); 1718ac50009fSMark Brown 1719d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_DM, 1720d9d20e17SCharles Keepax adsp1_id.fw.id, adsp1_id.dm); 1721d9d20e17SCharles Keepax if (IS_ERR(alg_region)) 1722d9d20e17SCharles Keepax return PTR_ERR(alg_region); 1723ac50009fSMark Brown 1724db40517cSMark Brown pos = sizeof(adsp1_id) / 2; 17253809f001SCharles Keepax len = (sizeof(*adsp1_alg) * n_algs) / 2; 1726db40517cSMark Brown 17273809f001SCharles Keepax adsp1_alg = wm_adsp_read_algs(dsp, n_algs, mem->base + pos, len); 1728b618a185SCharles Keepax if (IS_ERR(adsp1_alg)) 1729b618a185SCharles Keepax return PTR_ERR(adsp1_alg); 1730db40517cSMark Brown 17313809f001SCharles Keepax for (i = 0; i < n_algs; i++) { 1732471f4885SMark Brown adsp_info(dsp, "%d: ID %x v%d.%d.%d DM@%x ZM@%x\n", 1733db40517cSMark Brown i, be32_to_cpu(adsp1_alg[i].alg.id), 1734db40517cSMark Brown (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff0000) >> 16, 1735db40517cSMark Brown (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff00) >> 8, 1736471f4885SMark Brown be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff, 1737471f4885SMark Brown be32_to_cpu(adsp1_alg[i].dm), 1738471f4885SMark Brown be32_to_cpu(adsp1_alg[i].zm)); 1739471f4885SMark Brown 1740d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_DM, 1741d9d20e17SCharles Keepax adsp1_alg[i].alg.id, 1742d9d20e17SCharles Keepax adsp1_alg[i].dm); 1743d9d20e17SCharles Keepax if (IS_ERR(alg_region)) { 1744d9d20e17SCharles Keepax ret = PTR_ERR(alg_region); 1745d6d52179SJS Park goto out; 1746d6d52179SJS Park } 17472323736dSCharles Keepax if (dsp->fw_ver == 0) { 17483809f001SCharles Keepax if (i + 1 < n_algs) { 17496958eb2aSCharles Keepax len = be32_to_cpu(adsp1_alg[i + 1].dm); 17506958eb2aSCharles Keepax len -= be32_to_cpu(adsp1_alg[i].dm); 17516958eb2aSCharles Keepax len *= 4; 17522323736dSCharles Keepax wm_adsp_create_control(dsp, alg_region, 0, 175326c22a19SCharles Keepax len, NULL, 0, 0); 17546ab2b7b4SDimitris Papastamos } else { 17556ab2b7b4SDimitris Papastamos adsp_warn(dsp, "Missing length info for region DM with ID %x\n", 17566ab2b7b4SDimitris Papastamos be32_to_cpu(adsp1_alg[i].alg.id)); 17576ab2b7b4SDimitris Papastamos } 17582323736dSCharles Keepax } 1759471f4885SMark Brown 1760d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_ZM, 1761d9d20e17SCharles Keepax adsp1_alg[i].alg.id, 1762d9d20e17SCharles Keepax adsp1_alg[i].zm); 1763d9d20e17SCharles Keepax if (IS_ERR(alg_region)) { 1764d9d20e17SCharles Keepax ret = PTR_ERR(alg_region); 1765d6d52179SJS Park goto out; 1766d6d52179SJS Park } 17672323736dSCharles Keepax if (dsp->fw_ver == 0) { 17683809f001SCharles Keepax if (i + 1 < n_algs) { 17696958eb2aSCharles Keepax len = be32_to_cpu(adsp1_alg[i + 1].zm); 17706958eb2aSCharles Keepax len -= be32_to_cpu(adsp1_alg[i].zm); 17716958eb2aSCharles Keepax len *= 4; 17722323736dSCharles Keepax wm_adsp_create_control(dsp, alg_region, 0, 177326c22a19SCharles Keepax len, NULL, 0, 0); 17746ab2b7b4SDimitris Papastamos } else { 17756ab2b7b4SDimitris Papastamos adsp_warn(dsp, "Missing length info for region ZM with ID %x\n", 17766ab2b7b4SDimitris Papastamos be32_to_cpu(adsp1_alg[i].alg.id)); 17776ab2b7b4SDimitris Papastamos } 1778b618a185SCharles Keepax } 17792323736dSCharles Keepax } 1780db40517cSMark Brown 1781b618a185SCharles Keepax out: 1782b618a185SCharles Keepax kfree(adsp1_alg); 1783b618a185SCharles Keepax return ret; 1784b618a185SCharles Keepax } 1785b618a185SCharles Keepax 1786b618a185SCharles Keepax static int wm_adsp2_setup_algs(struct wm_adsp *dsp) 1787b618a185SCharles Keepax { 1788b618a185SCharles Keepax struct wmfw_adsp2_id_hdr adsp2_id; 1789b618a185SCharles Keepax struct wmfw_adsp2_alg_hdr *adsp2_alg; 17903809f001SCharles Keepax struct wm_adsp_alg_region *alg_region; 1791b618a185SCharles Keepax const struct wm_adsp_region *mem; 1792b618a185SCharles Keepax unsigned int pos, len; 17933809f001SCharles Keepax size_t n_algs; 1794b618a185SCharles Keepax int i, ret; 1795b618a185SCharles Keepax 1796b618a185SCharles Keepax mem = wm_adsp_find_region(dsp, WMFW_ADSP2_XM); 1797b618a185SCharles Keepax if (WARN_ON(!mem)) 1798b618a185SCharles Keepax return -EINVAL; 1799b618a185SCharles Keepax 1800b618a185SCharles Keepax ret = regmap_raw_read(dsp->regmap, mem->base, &adsp2_id, 1801b618a185SCharles Keepax sizeof(adsp2_id)); 1802b618a185SCharles Keepax if (ret != 0) { 1803b618a185SCharles Keepax adsp_err(dsp, "Failed to read algorithm info: %d\n", 1804b618a185SCharles Keepax ret); 1805b618a185SCharles Keepax return ret; 1806b618a185SCharles Keepax } 1807b618a185SCharles Keepax 18083809f001SCharles Keepax n_algs = be32_to_cpu(adsp2_id.n_algs); 1809b618a185SCharles Keepax dsp->fw_id = be32_to_cpu(adsp2_id.fw.id); 1810f9f55e31SRichard Fitzgerald dsp->fw_id_version = be32_to_cpu(adsp2_id.fw.ver); 1811b618a185SCharles Keepax adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n", 1812b618a185SCharles Keepax dsp->fw_id, 1813f9f55e31SRichard Fitzgerald (dsp->fw_id_version & 0xff0000) >> 16, 1814f9f55e31SRichard Fitzgerald (dsp->fw_id_version & 0xff00) >> 8, 1815f9f55e31SRichard Fitzgerald dsp->fw_id_version & 0xff, 18163809f001SCharles Keepax n_algs); 1817b618a185SCharles Keepax 1818d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_XM, 1819d9d20e17SCharles Keepax adsp2_id.fw.id, adsp2_id.xm); 1820d9d20e17SCharles Keepax if (IS_ERR(alg_region)) 1821d9d20e17SCharles Keepax return PTR_ERR(alg_region); 1822b618a185SCharles Keepax 1823d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_YM, 1824d9d20e17SCharles Keepax adsp2_id.fw.id, adsp2_id.ym); 1825d9d20e17SCharles Keepax if (IS_ERR(alg_region)) 1826d9d20e17SCharles Keepax return PTR_ERR(alg_region); 1827b618a185SCharles Keepax 1828d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_ZM, 1829d9d20e17SCharles Keepax adsp2_id.fw.id, adsp2_id.zm); 1830d9d20e17SCharles Keepax if (IS_ERR(alg_region)) 1831d9d20e17SCharles Keepax return PTR_ERR(alg_region); 1832b618a185SCharles Keepax 1833b618a185SCharles Keepax pos = sizeof(adsp2_id) / 2; 18343809f001SCharles Keepax len = (sizeof(*adsp2_alg) * n_algs) / 2; 1835b618a185SCharles Keepax 18363809f001SCharles Keepax adsp2_alg = wm_adsp_read_algs(dsp, n_algs, mem->base + pos, len); 1837b618a185SCharles Keepax if (IS_ERR(adsp2_alg)) 1838b618a185SCharles Keepax return PTR_ERR(adsp2_alg); 1839b618a185SCharles Keepax 18403809f001SCharles Keepax for (i = 0; i < n_algs; i++) { 1841471f4885SMark Brown adsp_info(dsp, 1842471f4885SMark Brown "%d: ID %x v%d.%d.%d XM@%x YM@%x ZM@%x\n", 1843db40517cSMark Brown i, be32_to_cpu(adsp2_alg[i].alg.id), 1844db40517cSMark Brown (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff0000) >> 16, 1845db40517cSMark Brown (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff00) >> 8, 1846471f4885SMark Brown be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff, 1847471f4885SMark Brown be32_to_cpu(adsp2_alg[i].xm), 1848471f4885SMark Brown be32_to_cpu(adsp2_alg[i].ym), 1849471f4885SMark Brown be32_to_cpu(adsp2_alg[i].zm)); 1850471f4885SMark Brown 1851d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_XM, 1852d9d20e17SCharles Keepax adsp2_alg[i].alg.id, 1853d9d20e17SCharles Keepax adsp2_alg[i].xm); 1854d9d20e17SCharles Keepax if (IS_ERR(alg_region)) { 1855d9d20e17SCharles Keepax ret = PTR_ERR(alg_region); 1856d6d52179SJS Park goto out; 1857d6d52179SJS Park } 18582323736dSCharles Keepax if (dsp->fw_ver == 0) { 18593809f001SCharles Keepax if (i + 1 < n_algs) { 18606958eb2aSCharles Keepax len = be32_to_cpu(adsp2_alg[i + 1].xm); 18616958eb2aSCharles Keepax len -= be32_to_cpu(adsp2_alg[i].xm); 18626958eb2aSCharles Keepax len *= 4; 18632323736dSCharles Keepax wm_adsp_create_control(dsp, alg_region, 0, 186426c22a19SCharles Keepax len, NULL, 0, 0); 18656ab2b7b4SDimitris Papastamos } else { 18666ab2b7b4SDimitris Papastamos adsp_warn(dsp, "Missing length info for region XM with ID %x\n", 18676ab2b7b4SDimitris Papastamos be32_to_cpu(adsp2_alg[i].alg.id)); 18686ab2b7b4SDimitris Papastamos } 18692323736dSCharles Keepax } 1870471f4885SMark Brown 1871d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_YM, 1872d9d20e17SCharles Keepax adsp2_alg[i].alg.id, 1873d9d20e17SCharles Keepax adsp2_alg[i].ym); 1874d9d20e17SCharles Keepax if (IS_ERR(alg_region)) { 1875d9d20e17SCharles Keepax ret = PTR_ERR(alg_region); 1876d6d52179SJS Park goto out; 1877d6d52179SJS Park } 18782323736dSCharles Keepax if (dsp->fw_ver == 0) { 18793809f001SCharles Keepax if (i + 1 < n_algs) { 18806958eb2aSCharles Keepax len = be32_to_cpu(adsp2_alg[i + 1].ym); 18816958eb2aSCharles Keepax len -= be32_to_cpu(adsp2_alg[i].ym); 18826958eb2aSCharles Keepax len *= 4; 18832323736dSCharles Keepax wm_adsp_create_control(dsp, alg_region, 0, 188426c22a19SCharles Keepax len, NULL, 0, 0); 18856ab2b7b4SDimitris Papastamos } else { 18866ab2b7b4SDimitris Papastamos adsp_warn(dsp, "Missing length info for region YM with ID %x\n", 18876ab2b7b4SDimitris Papastamos be32_to_cpu(adsp2_alg[i].alg.id)); 18886ab2b7b4SDimitris Papastamos } 18892323736dSCharles Keepax } 1890471f4885SMark Brown 1891d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_ZM, 1892d9d20e17SCharles Keepax adsp2_alg[i].alg.id, 1893d9d20e17SCharles Keepax adsp2_alg[i].zm); 1894d9d20e17SCharles Keepax if (IS_ERR(alg_region)) { 1895d9d20e17SCharles Keepax ret = PTR_ERR(alg_region); 1896d6d52179SJS Park goto out; 1897d6d52179SJS Park } 18982323736dSCharles Keepax if (dsp->fw_ver == 0) { 18993809f001SCharles Keepax if (i + 1 < n_algs) { 19006958eb2aSCharles Keepax len = be32_to_cpu(adsp2_alg[i + 1].zm); 19016958eb2aSCharles Keepax len -= be32_to_cpu(adsp2_alg[i].zm); 19026958eb2aSCharles Keepax len *= 4; 19032323736dSCharles Keepax wm_adsp_create_control(dsp, alg_region, 0, 190426c22a19SCharles Keepax len, NULL, 0, 0); 19056ab2b7b4SDimitris Papastamos } else { 19066ab2b7b4SDimitris Papastamos adsp_warn(dsp, "Missing length info for region ZM with ID %x\n", 19076ab2b7b4SDimitris Papastamos be32_to_cpu(adsp2_alg[i].alg.id)); 19086ab2b7b4SDimitris Papastamos } 1909db40517cSMark Brown } 19102323736dSCharles Keepax } 1911db40517cSMark Brown 1912db40517cSMark Brown out: 1913b618a185SCharles Keepax kfree(adsp2_alg); 1914db40517cSMark Brown return ret; 1915db40517cSMark Brown } 1916db40517cSMark Brown 19172159ad93SMark Brown static int wm_adsp_load_coeff(struct wm_adsp *dsp) 19182159ad93SMark Brown { 1919cf17c83cSMark Brown LIST_HEAD(buf_list); 19202159ad93SMark Brown struct regmap *regmap = dsp->regmap; 19212159ad93SMark Brown struct wmfw_coeff_hdr *hdr; 19222159ad93SMark Brown struct wmfw_coeff_item *blk; 19232159ad93SMark Brown const struct firmware *firmware; 1924471f4885SMark Brown const struct wm_adsp_region *mem; 1925471f4885SMark Brown struct wm_adsp_alg_region *alg_region; 19262159ad93SMark Brown const char *region_name; 19272159ad93SMark Brown int ret, pos, blocks, type, offset, reg; 19282159ad93SMark Brown char *file; 1929cf17c83cSMark Brown struct wm_adsp_buf *buf; 19302159ad93SMark Brown 19312159ad93SMark Brown file = kzalloc(PAGE_SIZE, GFP_KERNEL); 19322159ad93SMark Brown if (file == NULL) 19332159ad93SMark Brown return -ENOMEM; 19342159ad93SMark Brown 19351023dbd9SMark Brown snprintf(file, PAGE_SIZE, "%s-dsp%d-%s.bin", dsp->part, dsp->num, 19361023dbd9SMark Brown wm_adsp_fw[dsp->fw].file); 19372159ad93SMark Brown file[PAGE_SIZE - 1] = '\0'; 19382159ad93SMark Brown 19392159ad93SMark Brown ret = request_firmware(&firmware, file, dsp->dev); 19402159ad93SMark Brown if (ret != 0) { 19412159ad93SMark Brown adsp_warn(dsp, "Failed to request '%s'\n", file); 19422159ad93SMark Brown ret = 0; 19432159ad93SMark Brown goto out; 19442159ad93SMark Brown } 19452159ad93SMark Brown ret = -EINVAL; 19462159ad93SMark Brown 19472159ad93SMark Brown if (sizeof(*hdr) >= firmware->size) { 19482159ad93SMark Brown adsp_err(dsp, "%s: file too short, %zu bytes\n", 19492159ad93SMark Brown file, firmware->size); 19502159ad93SMark Brown goto out_fw; 19512159ad93SMark Brown } 19522159ad93SMark Brown 19532159ad93SMark Brown hdr = (void *)&firmware->data[0]; 19542159ad93SMark Brown if (memcmp(hdr->magic, "WMDR", 4) != 0) { 19552159ad93SMark Brown adsp_err(dsp, "%s: invalid magic\n", file); 1956a4cdbec7SCharles Keepax goto out_fw; 19572159ad93SMark Brown } 19582159ad93SMark Brown 1959c712326dSMark Brown switch (be32_to_cpu(hdr->rev) & 0xff) { 1960c712326dSMark Brown case 1: 1961c712326dSMark Brown break; 1962c712326dSMark Brown default: 1963c712326dSMark Brown adsp_err(dsp, "%s: Unsupported coefficient file format %d\n", 1964c712326dSMark Brown file, be32_to_cpu(hdr->rev) & 0xff); 1965c712326dSMark Brown ret = -EINVAL; 1966c712326dSMark Brown goto out_fw; 1967c712326dSMark Brown } 1968c712326dSMark Brown 19692159ad93SMark Brown adsp_dbg(dsp, "%s: v%d.%d.%d\n", file, 19702159ad93SMark Brown (le32_to_cpu(hdr->ver) >> 16) & 0xff, 19712159ad93SMark Brown (le32_to_cpu(hdr->ver) >> 8) & 0xff, 19722159ad93SMark Brown le32_to_cpu(hdr->ver) & 0xff); 19732159ad93SMark Brown 19742159ad93SMark Brown pos = le32_to_cpu(hdr->len); 19752159ad93SMark Brown 19762159ad93SMark Brown blocks = 0; 19772159ad93SMark Brown while (pos < firmware->size && 19782159ad93SMark Brown pos - firmware->size > sizeof(*blk)) { 19792159ad93SMark Brown blk = (void *)(&firmware->data[pos]); 19802159ad93SMark Brown 1981c712326dSMark Brown type = le16_to_cpu(blk->type); 1982c712326dSMark Brown offset = le16_to_cpu(blk->offset); 19832159ad93SMark Brown 19842159ad93SMark Brown adsp_dbg(dsp, "%s.%d: %x v%d.%d.%d\n", 19852159ad93SMark Brown file, blocks, le32_to_cpu(blk->id), 19862159ad93SMark Brown (le32_to_cpu(blk->ver) >> 16) & 0xff, 19872159ad93SMark Brown (le32_to_cpu(blk->ver) >> 8) & 0xff, 19882159ad93SMark Brown le32_to_cpu(blk->ver) & 0xff); 19892159ad93SMark Brown adsp_dbg(dsp, "%s.%d: %d bytes at 0x%x in %x\n", 19902159ad93SMark Brown file, blocks, le32_to_cpu(blk->len), offset, type); 19912159ad93SMark Brown 19922159ad93SMark Brown reg = 0; 19932159ad93SMark Brown region_name = "Unknown"; 19942159ad93SMark Brown switch (type) { 1995c712326dSMark Brown case (WMFW_NAME_TEXT << 8): 1996c712326dSMark Brown case (WMFW_INFO_TEXT << 8): 19972159ad93SMark Brown break; 1998c712326dSMark Brown case (WMFW_ABSOLUTE << 8): 1999f395a218SMark Brown /* 2000f395a218SMark Brown * Old files may use this for global 2001f395a218SMark Brown * coefficients. 2002f395a218SMark Brown */ 2003f395a218SMark Brown if (le32_to_cpu(blk->id) == dsp->fw_id && 2004f395a218SMark Brown offset == 0) { 2005f395a218SMark Brown region_name = "global coefficients"; 2006f395a218SMark Brown mem = wm_adsp_find_region(dsp, type); 2007f395a218SMark Brown if (!mem) { 2008f395a218SMark Brown adsp_err(dsp, "No ZM\n"); 2009f395a218SMark Brown break; 2010f395a218SMark Brown } 2011f395a218SMark Brown reg = wm_adsp_region_to_reg(mem, 0); 2012f395a218SMark Brown 2013f395a218SMark Brown } else { 20142159ad93SMark Brown region_name = "register"; 20152159ad93SMark Brown reg = offset; 2016f395a218SMark Brown } 20172159ad93SMark Brown break; 2018471f4885SMark Brown 2019471f4885SMark Brown case WMFW_ADSP1_DM: 2020471f4885SMark Brown case WMFW_ADSP1_ZM: 2021471f4885SMark Brown case WMFW_ADSP2_XM: 2022471f4885SMark Brown case WMFW_ADSP2_YM: 2023471f4885SMark Brown adsp_dbg(dsp, "%s.%d: %d bytes in %x for %x\n", 2024471f4885SMark Brown file, blocks, le32_to_cpu(blk->len), 2025471f4885SMark Brown type, le32_to_cpu(blk->id)); 2026471f4885SMark Brown 2027471f4885SMark Brown mem = wm_adsp_find_region(dsp, type); 2028471f4885SMark Brown if (!mem) { 2029471f4885SMark Brown adsp_err(dsp, "No base for region %x\n", type); 2030471f4885SMark Brown break; 2031471f4885SMark Brown } 2032471f4885SMark Brown 203314197095SCharles Keepax alg_region = wm_adsp_find_alg_region(dsp, type, 203414197095SCharles Keepax le32_to_cpu(blk->id)); 203514197095SCharles Keepax if (alg_region) { 2036338c5188SMark Brown reg = alg_region->base; 203714197095SCharles Keepax reg = wm_adsp_region_to_reg(mem, reg); 2038338c5188SMark Brown reg += offset; 203914197095SCharles Keepax } else { 2040471f4885SMark Brown adsp_err(dsp, "No %x for algorithm %x\n", 2041471f4885SMark Brown type, le32_to_cpu(blk->id)); 204214197095SCharles Keepax } 2043471f4885SMark Brown break; 2044471f4885SMark Brown 20452159ad93SMark Brown default: 204625c62f7eSMark Brown adsp_err(dsp, "%s.%d: Unknown region type %x at %d\n", 204725c62f7eSMark Brown file, blocks, type, pos); 20482159ad93SMark Brown break; 20492159ad93SMark Brown } 20502159ad93SMark Brown 20512159ad93SMark Brown if (reg) { 2052cf17c83cSMark Brown buf = wm_adsp_buf_alloc(blk->data, 2053cf17c83cSMark Brown le32_to_cpu(blk->len), 2054cf17c83cSMark Brown &buf_list); 2055a76fefabSMark Brown if (!buf) { 2056a76fefabSMark Brown adsp_err(dsp, "Out of memory\n"); 2057f4b82812SWei Yongjun ret = -ENOMEM; 2058f4b82812SWei Yongjun goto out_fw; 2059a76fefabSMark Brown } 2060a76fefabSMark Brown 206120da6d5aSMark Brown adsp_dbg(dsp, "%s.%d: Writing %d bytes at %x\n", 206220da6d5aSMark Brown file, blocks, le32_to_cpu(blk->len), 206320da6d5aSMark Brown reg); 2064cf17c83cSMark Brown ret = regmap_raw_write_async(regmap, reg, buf->buf, 20652159ad93SMark Brown le32_to_cpu(blk->len)); 20662159ad93SMark Brown if (ret != 0) { 20672159ad93SMark Brown adsp_err(dsp, 206843bc3bf6SDimitris Papastamos "%s.%d: Failed to write to %x in %s: %d\n", 206943bc3bf6SDimitris Papastamos file, blocks, reg, region_name, ret); 20702159ad93SMark Brown } 20712159ad93SMark Brown } 20722159ad93SMark Brown 2073be951017SCharles Keepax pos += (le32_to_cpu(blk->len) + sizeof(*blk) + 3) & ~0x03; 20742159ad93SMark Brown blocks++; 20752159ad93SMark Brown } 20762159ad93SMark Brown 2077cf17c83cSMark Brown ret = regmap_async_complete(regmap); 2078cf17c83cSMark Brown if (ret != 0) 2079cf17c83cSMark Brown adsp_err(dsp, "Failed to complete async write: %d\n", ret); 2080cf17c83cSMark Brown 20812159ad93SMark Brown if (pos > firmware->size) 20822159ad93SMark Brown adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n", 20832159ad93SMark Brown file, blocks, pos - firmware->size); 20842159ad93SMark Brown 2085f9f55e31SRichard Fitzgerald wm_adsp_debugfs_save_binname(dsp, file); 2086f9f55e31SRichard Fitzgerald 20872159ad93SMark Brown out_fw: 20889da7a5a9SCharles Keepax regmap_async_complete(regmap); 20892159ad93SMark Brown release_firmware(firmware); 2090cf17c83cSMark Brown wm_adsp_buf_free(&buf_list); 20912159ad93SMark Brown out: 20922159ad93SMark Brown kfree(file); 2093f4b82812SWei Yongjun return ret; 20942159ad93SMark Brown } 20952159ad93SMark Brown 20963809f001SCharles Keepax int wm_adsp1_init(struct wm_adsp *dsp) 20975e7a7a22SMark Brown { 20983809f001SCharles Keepax INIT_LIST_HEAD(&dsp->alg_regions); 20995e7a7a22SMark Brown 2100078e7183SCharles Keepax mutex_init(&dsp->pwr_lock); 2101078e7183SCharles Keepax 21025e7a7a22SMark Brown return 0; 21035e7a7a22SMark Brown } 21045e7a7a22SMark Brown EXPORT_SYMBOL_GPL(wm_adsp1_init); 21055e7a7a22SMark Brown 21062159ad93SMark Brown int wm_adsp1_event(struct snd_soc_dapm_widget *w, 21072159ad93SMark Brown struct snd_kcontrol *kcontrol, 21082159ad93SMark Brown int event) 21092159ad93SMark Brown { 211072718517SLars-Peter Clausen struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); 21112159ad93SMark Brown struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec); 21122159ad93SMark Brown struct wm_adsp *dsp = &dsps[w->shift]; 21136ab2b7b4SDimitris Papastamos struct wm_coeff_ctl *ctl; 21142159ad93SMark Brown int ret; 21157585a5b0SCharles Keepax unsigned int val; 21162159ad93SMark Brown 211700200107SLars-Peter Clausen dsp->card = codec->component.card; 211892bb4c32SDimitris Papastamos 2119078e7183SCharles Keepax mutex_lock(&dsp->pwr_lock); 2120078e7183SCharles Keepax 21212159ad93SMark Brown switch (event) { 21222159ad93SMark Brown case SND_SOC_DAPM_POST_PMU: 21232159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 21242159ad93SMark Brown ADSP1_SYS_ENA, ADSP1_SYS_ENA); 21252159ad93SMark Brown 212694e205bfSChris Rattray /* 212794e205bfSChris Rattray * For simplicity set the DSP clock rate to be the 212894e205bfSChris Rattray * SYSCLK rate rather than making it configurable. 212994e205bfSChris Rattray */ 213094e205bfSChris Rattray if (dsp->sysclk_reg) { 213194e205bfSChris Rattray ret = regmap_read(dsp->regmap, dsp->sysclk_reg, &val); 213294e205bfSChris Rattray if (ret != 0) { 213394e205bfSChris Rattray adsp_err(dsp, "Failed to read SYSCLK state: %d\n", 213494e205bfSChris Rattray ret); 2135078e7183SCharles Keepax goto err_mutex; 213694e205bfSChris Rattray } 213794e205bfSChris Rattray 21387d00cd97SCharles Keepax val = (val & dsp->sysclk_mask) >> dsp->sysclk_shift; 213994e205bfSChris Rattray 214094e205bfSChris Rattray ret = regmap_update_bits(dsp->regmap, 214194e205bfSChris Rattray dsp->base + ADSP1_CONTROL_31, 214294e205bfSChris Rattray ADSP1_CLK_SEL_MASK, val); 214394e205bfSChris Rattray if (ret != 0) { 214494e205bfSChris Rattray adsp_err(dsp, "Failed to set clock rate: %d\n", 214594e205bfSChris Rattray ret); 2146078e7183SCharles Keepax goto err_mutex; 214794e205bfSChris Rattray } 214894e205bfSChris Rattray } 214994e205bfSChris Rattray 21502159ad93SMark Brown ret = wm_adsp_load(dsp); 21512159ad93SMark Brown if (ret != 0) 2152078e7183SCharles Keepax goto err_ena; 21532159ad93SMark Brown 2154b618a185SCharles Keepax ret = wm_adsp1_setup_algs(dsp); 2155db40517cSMark Brown if (ret != 0) 2156078e7183SCharles Keepax goto err_ena; 2157db40517cSMark Brown 21582159ad93SMark Brown ret = wm_adsp_load_coeff(dsp); 21592159ad93SMark Brown if (ret != 0) 2160078e7183SCharles Keepax goto err_ena; 21612159ad93SMark Brown 21620c2e3f34SDimitris Papastamos /* Initialize caches for enabled and unset controls */ 216381ad93ecSDimitris Papastamos ret = wm_coeff_init_control_caches(dsp); 21646ab2b7b4SDimitris Papastamos if (ret != 0) 2165078e7183SCharles Keepax goto err_ena; 21666ab2b7b4SDimitris Papastamos 21670c2e3f34SDimitris Papastamos /* Sync set controls */ 216881ad93ecSDimitris Papastamos ret = wm_coeff_sync_controls(dsp); 21696ab2b7b4SDimitris Papastamos if (ret != 0) 2170078e7183SCharles Keepax goto err_ena; 21716ab2b7b4SDimitris Papastamos 217228823ebaSCharles Keepax dsp->booted = true; 217328823ebaSCharles Keepax 21742159ad93SMark Brown /* Start the core running */ 21752159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 21762159ad93SMark Brown ADSP1_CORE_ENA | ADSP1_START, 21772159ad93SMark Brown ADSP1_CORE_ENA | ADSP1_START); 217828823ebaSCharles Keepax 217928823ebaSCharles Keepax dsp->running = true; 21802159ad93SMark Brown break; 21812159ad93SMark Brown 21822159ad93SMark Brown case SND_SOC_DAPM_PRE_PMD: 218328823ebaSCharles Keepax dsp->running = false; 218428823ebaSCharles Keepax dsp->booted = false; 218528823ebaSCharles Keepax 21862159ad93SMark Brown /* Halt the core */ 21872159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 21882159ad93SMark Brown ADSP1_CORE_ENA | ADSP1_START, 0); 21892159ad93SMark Brown 21902159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_19, 21912159ad93SMark Brown ADSP1_WDMA_BUFFER_LENGTH_MASK, 0); 21922159ad93SMark Brown 21932159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 21942159ad93SMark Brown ADSP1_SYS_ENA, 0); 21956ab2b7b4SDimitris Papastamos 219681ad93ecSDimitris Papastamos list_for_each_entry(ctl, &dsp->ctl_list, list) 21976ab2b7b4SDimitris Papastamos ctl->enabled = 0; 2198b0101b4fSDimitris Papastamos 219956574d54SRichard Fitzgerald 220056574d54SRichard Fitzgerald wm_adsp_free_alg_regions(dsp); 22012159ad93SMark Brown break; 22022159ad93SMark Brown 22032159ad93SMark Brown default: 22042159ad93SMark Brown break; 22052159ad93SMark Brown } 22062159ad93SMark Brown 2207078e7183SCharles Keepax mutex_unlock(&dsp->pwr_lock); 2208078e7183SCharles Keepax 22092159ad93SMark Brown return 0; 22102159ad93SMark Brown 2211078e7183SCharles Keepax err_ena: 22122159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 22132159ad93SMark Brown ADSP1_SYS_ENA, 0); 2214078e7183SCharles Keepax err_mutex: 2215078e7183SCharles Keepax mutex_unlock(&dsp->pwr_lock); 2216078e7183SCharles Keepax 22172159ad93SMark Brown return ret; 22182159ad93SMark Brown } 22192159ad93SMark Brown EXPORT_SYMBOL_GPL(wm_adsp1_event); 22202159ad93SMark Brown 22212159ad93SMark Brown static int wm_adsp2_ena(struct wm_adsp *dsp) 22222159ad93SMark Brown { 22232159ad93SMark Brown unsigned int val; 22242159ad93SMark Brown int ret, count; 22252159ad93SMark Brown 22261552c325SMark Brown ret = regmap_update_bits_async(dsp->regmap, dsp->base + ADSP2_CONTROL, 22272159ad93SMark Brown ADSP2_SYS_ENA, ADSP2_SYS_ENA); 22282159ad93SMark Brown if (ret != 0) 22292159ad93SMark Brown return ret; 22302159ad93SMark Brown 22312159ad93SMark Brown /* Wait for the RAM to start, should be near instantaneous */ 2232939fd1e8SCharles Keepax for (count = 0; count < 10; ++count) { 22337d00cd97SCharles Keepax ret = regmap_read(dsp->regmap, dsp->base + ADSP2_STATUS1, &val); 22342159ad93SMark Brown if (ret != 0) 22352159ad93SMark Brown return ret; 2236939fd1e8SCharles Keepax 2237939fd1e8SCharles Keepax if (val & ADSP2_RAM_RDY) 2238939fd1e8SCharles Keepax break; 2239939fd1e8SCharles Keepax 2240939fd1e8SCharles Keepax msleep(1); 2241939fd1e8SCharles Keepax } 22422159ad93SMark Brown 22432159ad93SMark Brown if (!(val & ADSP2_RAM_RDY)) { 22442159ad93SMark Brown adsp_err(dsp, "Failed to start DSP RAM\n"); 22452159ad93SMark Brown return -EBUSY; 22462159ad93SMark Brown } 22472159ad93SMark Brown 22482159ad93SMark Brown adsp_dbg(dsp, "RAM ready after %d polls\n", count); 22492159ad93SMark Brown 22502159ad93SMark Brown return 0; 22512159ad93SMark Brown } 22522159ad93SMark Brown 225318b1a902SCharles Keepax static void wm_adsp2_boot_work(struct work_struct *work) 22542159ad93SMark Brown { 2255d8a64d6aSCharles Keepax struct wm_adsp *dsp = container_of(work, 2256d8a64d6aSCharles Keepax struct wm_adsp, 2257d8a64d6aSCharles Keepax boot_work); 22582159ad93SMark Brown int ret; 22592159ad93SMark Brown 2260078e7183SCharles Keepax mutex_lock(&dsp->pwr_lock); 2261078e7183SCharles Keepax 22622159ad93SMark Brown ret = wm_adsp2_ena(dsp); 22632159ad93SMark Brown if (ret != 0) 2264078e7183SCharles Keepax goto err_mutex; 22652159ad93SMark Brown 22662159ad93SMark Brown ret = wm_adsp_load(dsp); 22672159ad93SMark Brown if (ret != 0) 2268078e7183SCharles Keepax goto err_ena; 22692159ad93SMark Brown 2270b618a185SCharles Keepax ret = wm_adsp2_setup_algs(dsp); 2271db40517cSMark Brown if (ret != 0) 2272078e7183SCharles Keepax goto err_ena; 2273db40517cSMark Brown 22742159ad93SMark Brown ret = wm_adsp_load_coeff(dsp); 22752159ad93SMark Brown if (ret != 0) 2276078e7183SCharles Keepax goto err_ena; 22772159ad93SMark Brown 22780c2e3f34SDimitris Papastamos /* Initialize caches for enabled and unset controls */ 227981ad93ecSDimitris Papastamos ret = wm_coeff_init_control_caches(dsp); 22806ab2b7b4SDimitris Papastamos if (ret != 0) 2281078e7183SCharles Keepax goto err_ena; 22826ab2b7b4SDimitris Papastamos 228328823ebaSCharles Keepax dsp->booted = true; 2284d8a64d6aSCharles Keepax 2285078e7183SCharles Keepax mutex_unlock(&dsp->pwr_lock); 2286078e7183SCharles Keepax 2287d8a64d6aSCharles Keepax return; 2288d8a64d6aSCharles Keepax 2289078e7183SCharles Keepax err_ena: 2290d8a64d6aSCharles Keepax regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 2291d8a64d6aSCharles Keepax ADSP2_SYS_ENA | ADSP2_CORE_ENA | ADSP2_START, 0); 2292078e7183SCharles Keepax err_mutex: 2293078e7183SCharles Keepax mutex_unlock(&dsp->pwr_lock); 2294d8a64d6aSCharles Keepax } 2295d8a64d6aSCharles Keepax 2296d82d767fSCharles Keepax static void wm_adsp2_set_dspclk(struct wm_adsp *dsp, unsigned int freq) 2297d82d767fSCharles Keepax { 2298d82d767fSCharles Keepax int ret; 2299d82d767fSCharles Keepax 2300d82d767fSCharles Keepax ret = regmap_update_bits_async(dsp->regmap, 2301d82d767fSCharles Keepax dsp->base + ADSP2_CLOCKING, 2302d82d767fSCharles Keepax ADSP2_CLK_SEL_MASK, 2303d82d767fSCharles Keepax freq << ADSP2_CLK_SEL_SHIFT); 2304d82d767fSCharles Keepax if (ret != 0) 2305d82d767fSCharles Keepax adsp_err(dsp, "Failed to set clock rate: %d\n", ret); 2306d82d767fSCharles Keepax } 2307d82d767fSCharles Keepax 230812db5eddSCharles Keepax int wm_adsp2_early_event(struct snd_soc_dapm_widget *w, 2309d82d767fSCharles Keepax struct snd_kcontrol *kcontrol, int event, 2310d82d767fSCharles Keepax unsigned int freq) 231112db5eddSCharles Keepax { 231272718517SLars-Peter Clausen struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); 231312db5eddSCharles Keepax struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec); 231412db5eddSCharles Keepax struct wm_adsp *dsp = &dsps[w->shift]; 231512db5eddSCharles Keepax 231600200107SLars-Peter Clausen dsp->card = codec->component.card; 231712db5eddSCharles Keepax 231812db5eddSCharles Keepax switch (event) { 231912db5eddSCharles Keepax case SND_SOC_DAPM_PRE_PMU: 2320d82d767fSCharles Keepax wm_adsp2_set_dspclk(dsp, freq); 232112db5eddSCharles Keepax queue_work(system_unbound_wq, &dsp->boot_work); 232212db5eddSCharles Keepax break; 232312db5eddSCharles Keepax default: 232412db5eddSCharles Keepax break; 2325cab27258SCharles Keepax } 232612db5eddSCharles Keepax 232712db5eddSCharles Keepax return 0; 232812db5eddSCharles Keepax } 232912db5eddSCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp2_early_event); 233012db5eddSCharles Keepax 2331d8a64d6aSCharles Keepax int wm_adsp2_event(struct snd_soc_dapm_widget *w, 2332d8a64d6aSCharles Keepax struct snd_kcontrol *kcontrol, int event) 2333d8a64d6aSCharles Keepax { 233472718517SLars-Peter Clausen struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); 2335d8a64d6aSCharles Keepax struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec); 2336d8a64d6aSCharles Keepax struct wm_adsp *dsp = &dsps[w->shift]; 2337d8a64d6aSCharles Keepax struct wm_coeff_ctl *ctl; 2338d8a64d6aSCharles Keepax int ret; 2339d8a64d6aSCharles Keepax 2340d8a64d6aSCharles Keepax switch (event) { 2341d8a64d6aSCharles Keepax case SND_SOC_DAPM_POST_PMU: 2342d8a64d6aSCharles Keepax flush_work(&dsp->boot_work); 2343d8a64d6aSCharles Keepax 234428823ebaSCharles Keepax if (!dsp->booted) 2345d8a64d6aSCharles Keepax return -EIO; 2346d8a64d6aSCharles Keepax 2347cef45771SCharles Keepax /* Sync set controls */ 2348cef45771SCharles Keepax ret = wm_coeff_sync_controls(dsp); 2349cef45771SCharles Keepax if (ret != 0) 2350cef45771SCharles Keepax goto err; 2351cef45771SCharles Keepax 2352d8a64d6aSCharles Keepax ret = regmap_update_bits(dsp->regmap, 2353d8a64d6aSCharles Keepax dsp->base + ADSP2_CONTROL, 235400e4c3b6SCharles Keepax ADSP2_CORE_ENA | ADSP2_START, 235500e4c3b6SCharles Keepax ADSP2_CORE_ENA | ADSP2_START); 2356d8a64d6aSCharles Keepax if (ret != 0) 2357d8a64d6aSCharles Keepax goto err; 23582cd19bdbSCharles Keepax 235928823ebaSCharles Keepax dsp->running = true; 236028823ebaSCharles Keepax 2361612047f0SCharles Keepax mutex_lock(&dsp->pwr_lock); 2362612047f0SCharles Keepax 23632cd19bdbSCharles Keepax if (wm_adsp_fw[dsp->fw].num_caps != 0) 23642cd19bdbSCharles Keepax ret = wm_adsp_buffer_init(dsp); 23652cd19bdbSCharles Keepax 2366612047f0SCharles Keepax mutex_unlock(&dsp->pwr_lock); 2367612047f0SCharles Keepax 23682159ad93SMark Brown break; 23692159ad93SMark Brown 23702159ad93SMark Brown case SND_SOC_DAPM_PRE_PMD: 237110337b07SRichard Fitzgerald /* Log firmware state, it can be useful for analysis */ 237210337b07SRichard Fitzgerald wm_adsp2_show_fw_status(dsp); 237310337b07SRichard Fitzgerald 2374078e7183SCharles Keepax mutex_lock(&dsp->pwr_lock); 2375078e7183SCharles Keepax 2376f9f55e31SRichard Fitzgerald wm_adsp_debugfs_clear(dsp); 2377f9f55e31SRichard Fitzgerald 2378f9f55e31SRichard Fitzgerald dsp->fw_id = 0; 2379f9f55e31SRichard Fitzgerald dsp->fw_id_version = 0; 238028823ebaSCharles Keepax 23811023dbd9SMark Brown dsp->running = false; 238228823ebaSCharles Keepax dsp->booted = false; 23831023dbd9SMark Brown 23842159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 23856facd2d1SSimon Trimmer ADSP2_CORE_ENA | ADSP2_START, 0); 2386973838a0SMark Brown 23872d30b575SMark Brown /* Make sure DMAs are quiesced */ 23886facd2d1SSimon Trimmer regmap_write(dsp->regmap, dsp->base + ADSP2_RDMA_CONFIG_1, 0); 23892d30b575SMark Brown regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_1, 0); 23902d30b575SMark Brown regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_2, 0); 23916facd2d1SSimon Trimmer 23926facd2d1SSimon Trimmer regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 23936facd2d1SSimon Trimmer ADSP2_SYS_ENA, 0); 23942d30b575SMark Brown 239581ad93ecSDimitris Papastamos list_for_each_entry(ctl, &dsp->ctl_list, list) 23966ab2b7b4SDimitris Papastamos ctl->enabled = 0; 23976ab2b7b4SDimitris Papastamos 239856574d54SRichard Fitzgerald wm_adsp_free_alg_regions(dsp); 2399ddbc5efeSCharles Keepax 24002cd19bdbSCharles Keepax if (wm_adsp_fw[dsp->fw].num_caps != 0) 24012cd19bdbSCharles Keepax wm_adsp_buffer_free(dsp); 24022cd19bdbSCharles Keepax 2403078e7183SCharles Keepax mutex_unlock(&dsp->pwr_lock); 2404078e7183SCharles Keepax 2405ddbc5efeSCharles Keepax adsp_dbg(dsp, "Shutdown complete\n"); 24062159ad93SMark Brown break; 24072159ad93SMark Brown 24082159ad93SMark Brown default: 24092159ad93SMark Brown break; 24102159ad93SMark Brown } 24112159ad93SMark Brown 24122159ad93SMark Brown return 0; 24132159ad93SMark Brown err: 24142159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 2415a7f9be7eSMark Brown ADSP2_SYS_ENA | ADSP2_CORE_ENA | ADSP2_START, 0); 24162159ad93SMark Brown return ret; 24172159ad93SMark Brown } 24182159ad93SMark Brown EXPORT_SYMBOL_GPL(wm_adsp2_event); 2419973838a0SMark Brown 2420f5e2ce92SRichard Fitzgerald int wm_adsp2_codec_probe(struct wm_adsp *dsp, struct snd_soc_codec *codec) 2421f5e2ce92SRichard Fitzgerald { 2422f9f55e31SRichard Fitzgerald wm_adsp2_init_debugfs(dsp, codec); 2423f9f55e31SRichard Fitzgerald 2424218e5087SRichard Fitzgerald return snd_soc_add_codec_controls(codec, 2425336d0442SRichard Fitzgerald &wm_adsp_fw_controls[dsp->num - 1], 2426336d0442SRichard Fitzgerald 1); 2427f5e2ce92SRichard Fitzgerald } 2428f5e2ce92SRichard Fitzgerald EXPORT_SYMBOL_GPL(wm_adsp2_codec_probe); 2429f5e2ce92SRichard Fitzgerald 2430f5e2ce92SRichard Fitzgerald int wm_adsp2_codec_remove(struct wm_adsp *dsp, struct snd_soc_codec *codec) 2431f5e2ce92SRichard Fitzgerald { 2432f9f55e31SRichard Fitzgerald wm_adsp2_cleanup_debugfs(dsp); 2433f9f55e31SRichard Fitzgerald 2434f5e2ce92SRichard Fitzgerald return 0; 2435f5e2ce92SRichard Fitzgerald } 2436f5e2ce92SRichard Fitzgerald EXPORT_SYMBOL_GPL(wm_adsp2_codec_remove); 2437f5e2ce92SRichard Fitzgerald 243881ac58b1SRichard Fitzgerald int wm_adsp2_init(struct wm_adsp *dsp) 2439973838a0SMark Brown { 2440973838a0SMark Brown int ret; 2441973838a0SMark Brown 244210a2b662SMark Brown /* 244310a2b662SMark Brown * Disable the DSP memory by default when in reset for a small 244410a2b662SMark Brown * power saving. 244510a2b662SMark Brown */ 24463809f001SCharles Keepax ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 244710a2b662SMark Brown ADSP2_MEM_ENA, 0); 244810a2b662SMark Brown if (ret != 0) { 24493809f001SCharles Keepax adsp_err(dsp, "Failed to clear memory retention: %d\n", ret); 245010a2b662SMark Brown return ret; 245110a2b662SMark Brown } 245210a2b662SMark Brown 24533809f001SCharles Keepax INIT_LIST_HEAD(&dsp->alg_regions); 24543809f001SCharles Keepax INIT_LIST_HEAD(&dsp->ctl_list); 24553809f001SCharles Keepax INIT_WORK(&dsp->boot_work, wm_adsp2_boot_work); 24566ab2b7b4SDimitris Papastamos 2457078e7183SCharles Keepax mutex_init(&dsp->pwr_lock); 2458078e7183SCharles Keepax 2459973838a0SMark Brown return 0; 2460973838a0SMark Brown } 2461973838a0SMark Brown EXPORT_SYMBOL_GPL(wm_adsp2_init); 24620a37c6efSPraveen Diwakar 246366225e98SRichard Fitzgerald void wm_adsp2_remove(struct wm_adsp *dsp) 246466225e98SRichard Fitzgerald { 246566225e98SRichard Fitzgerald struct wm_coeff_ctl *ctl; 246666225e98SRichard Fitzgerald 246766225e98SRichard Fitzgerald while (!list_empty(&dsp->ctl_list)) { 246866225e98SRichard Fitzgerald ctl = list_first_entry(&dsp->ctl_list, struct wm_coeff_ctl, 246966225e98SRichard Fitzgerald list); 247066225e98SRichard Fitzgerald list_del(&ctl->list); 247166225e98SRichard Fitzgerald wm_adsp_free_ctl_blk(ctl); 247266225e98SRichard Fitzgerald } 247366225e98SRichard Fitzgerald } 247466225e98SRichard Fitzgerald EXPORT_SYMBOL_GPL(wm_adsp2_remove); 247566225e98SRichard Fitzgerald 2476edd71350SCharles Keepax static inline int wm_adsp_compr_attached(struct wm_adsp_compr *compr) 2477edd71350SCharles Keepax { 2478edd71350SCharles Keepax return compr->buf != NULL; 2479edd71350SCharles Keepax } 2480edd71350SCharles Keepax 2481edd71350SCharles Keepax static int wm_adsp_compr_attach(struct wm_adsp_compr *compr) 2482edd71350SCharles Keepax { 2483edd71350SCharles Keepax /* 2484edd71350SCharles Keepax * Note this will be more complex once each DSP can support multiple 2485edd71350SCharles Keepax * streams 2486edd71350SCharles Keepax */ 2487edd71350SCharles Keepax if (!compr->dsp->buffer) 2488edd71350SCharles Keepax return -EINVAL; 2489edd71350SCharles Keepax 2490edd71350SCharles Keepax compr->buf = compr->dsp->buffer; 2491721be3beSCharles Keepax compr->buf->compr = compr; 2492edd71350SCharles Keepax 2493edd71350SCharles Keepax return 0; 2494edd71350SCharles Keepax } 2495edd71350SCharles Keepax 2496721be3beSCharles Keepax static void wm_adsp_compr_detach(struct wm_adsp_compr *compr) 2497721be3beSCharles Keepax { 2498721be3beSCharles Keepax if (!compr) 2499721be3beSCharles Keepax return; 2500721be3beSCharles Keepax 2501721be3beSCharles Keepax /* Wake the poll so it can see buffer is no longer attached */ 2502721be3beSCharles Keepax if (compr->stream) 2503721be3beSCharles Keepax snd_compr_fragment_elapsed(compr->stream); 2504721be3beSCharles Keepax 2505721be3beSCharles Keepax if (wm_adsp_compr_attached(compr)) { 2506721be3beSCharles Keepax compr->buf->compr = NULL; 2507721be3beSCharles Keepax compr->buf = NULL; 2508721be3beSCharles Keepax } 2509721be3beSCharles Keepax } 2510721be3beSCharles Keepax 2511406abc95SCharles Keepax int wm_adsp_compr_open(struct wm_adsp *dsp, struct snd_compr_stream *stream) 2512406abc95SCharles Keepax { 2513406abc95SCharles Keepax struct wm_adsp_compr *compr; 2514406abc95SCharles Keepax int ret = 0; 2515406abc95SCharles Keepax 2516406abc95SCharles Keepax mutex_lock(&dsp->pwr_lock); 2517406abc95SCharles Keepax 2518406abc95SCharles Keepax if (wm_adsp_fw[dsp->fw].num_caps == 0) { 2519406abc95SCharles Keepax adsp_err(dsp, "Firmware does not support compressed API\n"); 2520406abc95SCharles Keepax ret = -ENXIO; 2521406abc95SCharles Keepax goto out; 2522406abc95SCharles Keepax } 2523406abc95SCharles Keepax 2524406abc95SCharles Keepax if (wm_adsp_fw[dsp->fw].compr_direction != stream->direction) { 2525406abc95SCharles Keepax adsp_err(dsp, "Firmware does not support stream direction\n"); 2526406abc95SCharles Keepax ret = -EINVAL; 2527406abc95SCharles Keepax goto out; 2528406abc95SCharles Keepax } 2529406abc95SCharles Keepax 253095fe9597SCharles Keepax if (dsp->compr) { 253195fe9597SCharles Keepax /* It is expect this limitation will be removed in future */ 253295fe9597SCharles Keepax adsp_err(dsp, "Only a single stream supported per DSP\n"); 253395fe9597SCharles Keepax ret = -EBUSY; 253495fe9597SCharles Keepax goto out; 253595fe9597SCharles Keepax } 253695fe9597SCharles Keepax 2537406abc95SCharles Keepax compr = kzalloc(sizeof(*compr), GFP_KERNEL); 2538406abc95SCharles Keepax if (!compr) { 2539406abc95SCharles Keepax ret = -ENOMEM; 2540406abc95SCharles Keepax goto out; 2541406abc95SCharles Keepax } 2542406abc95SCharles Keepax 2543406abc95SCharles Keepax compr->dsp = dsp; 2544406abc95SCharles Keepax compr->stream = stream; 2545406abc95SCharles Keepax 2546406abc95SCharles Keepax dsp->compr = compr; 2547406abc95SCharles Keepax 2548406abc95SCharles Keepax stream->runtime->private_data = compr; 2549406abc95SCharles Keepax 2550406abc95SCharles Keepax out: 2551406abc95SCharles Keepax mutex_unlock(&dsp->pwr_lock); 2552406abc95SCharles Keepax 2553406abc95SCharles Keepax return ret; 2554406abc95SCharles Keepax } 2555406abc95SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_open); 2556406abc95SCharles Keepax 2557406abc95SCharles Keepax int wm_adsp_compr_free(struct snd_compr_stream *stream) 2558406abc95SCharles Keepax { 2559406abc95SCharles Keepax struct wm_adsp_compr *compr = stream->runtime->private_data; 2560406abc95SCharles Keepax struct wm_adsp *dsp = compr->dsp; 2561406abc95SCharles Keepax 2562406abc95SCharles Keepax mutex_lock(&dsp->pwr_lock); 2563406abc95SCharles Keepax 2564721be3beSCharles Keepax wm_adsp_compr_detach(compr); 2565406abc95SCharles Keepax dsp->compr = NULL; 2566406abc95SCharles Keepax 256783a40ce9SCharles Keepax kfree(compr->raw_buf); 2568406abc95SCharles Keepax kfree(compr); 2569406abc95SCharles Keepax 2570406abc95SCharles Keepax mutex_unlock(&dsp->pwr_lock); 2571406abc95SCharles Keepax 2572406abc95SCharles Keepax return 0; 2573406abc95SCharles Keepax } 2574406abc95SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_free); 2575406abc95SCharles Keepax 2576406abc95SCharles Keepax static int wm_adsp_compr_check_params(struct snd_compr_stream *stream, 2577406abc95SCharles Keepax struct snd_compr_params *params) 2578406abc95SCharles Keepax { 2579406abc95SCharles Keepax struct wm_adsp_compr *compr = stream->runtime->private_data; 2580406abc95SCharles Keepax struct wm_adsp *dsp = compr->dsp; 2581406abc95SCharles Keepax const struct wm_adsp_fw_caps *caps; 2582406abc95SCharles Keepax const struct snd_codec_desc *desc; 2583406abc95SCharles Keepax int i, j; 2584406abc95SCharles Keepax 2585406abc95SCharles Keepax if (params->buffer.fragment_size < WM_ADSP_MIN_FRAGMENT_SIZE || 2586406abc95SCharles Keepax params->buffer.fragment_size > WM_ADSP_MAX_FRAGMENT_SIZE || 2587406abc95SCharles Keepax params->buffer.fragments < WM_ADSP_MIN_FRAGMENTS || 2588406abc95SCharles Keepax params->buffer.fragments > WM_ADSP_MAX_FRAGMENTS || 2589406abc95SCharles Keepax params->buffer.fragment_size % WM_ADSP_DATA_WORD_SIZE) { 2590406abc95SCharles Keepax adsp_err(dsp, "Invalid buffer fragsize=%d fragments=%d\n", 2591406abc95SCharles Keepax params->buffer.fragment_size, 2592406abc95SCharles Keepax params->buffer.fragments); 2593406abc95SCharles Keepax 2594406abc95SCharles Keepax return -EINVAL; 2595406abc95SCharles Keepax } 2596406abc95SCharles Keepax 2597406abc95SCharles Keepax for (i = 0; i < wm_adsp_fw[dsp->fw].num_caps; i++) { 2598406abc95SCharles Keepax caps = &wm_adsp_fw[dsp->fw].caps[i]; 2599406abc95SCharles Keepax desc = &caps->desc; 2600406abc95SCharles Keepax 2601406abc95SCharles Keepax if (caps->id != params->codec.id) 2602406abc95SCharles Keepax continue; 2603406abc95SCharles Keepax 2604406abc95SCharles Keepax if (stream->direction == SND_COMPRESS_PLAYBACK) { 2605406abc95SCharles Keepax if (desc->max_ch < params->codec.ch_out) 2606406abc95SCharles Keepax continue; 2607406abc95SCharles Keepax } else { 2608406abc95SCharles Keepax if (desc->max_ch < params->codec.ch_in) 2609406abc95SCharles Keepax continue; 2610406abc95SCharles Keepax } 2611406abc95SCharles Keepax 2612406abc95SCharles Keepax if (!(desc->formats & (1 << params->codec.format))) 2613406abc95SCharles Keepax continue; 2614406abc95SCharles Keepax 2615406abc95SCharles Keepax for (j = 0; j < desc->num_sample_rates; ++j) 2616406abc95SCharles Keepax if (desc->sample_rates[j] == params->codec.sample_rate) 2617406abc95SCharles Keepax return 0; 2618406abc95SCharles Keepax } 2619406abc95SCharles Keepax 2620406abc95SCharles Keepax adsp_err(dsp, "Invalid params id=%u ch=%u,%u rate=%u fmt=%u\n", 2621406abc95SCharles Keepax params->codec.id, params->codec.ch_in, params->codec.ch_out, 2622406abc95SCharles Keepax params->codec.sample_rate, params->codec.format); 2623406abc95SCharles Keepax return -EINVAL; 2624406abc95SCharles Keepax } 2625406abc95SCharles Keepax 2626565ace46SCharles Keepax static inline unsigned int wm_adsp_compr_frag_words(struct wm_adsp_compr *compr) 2627565ace46SCharles Keepax { 2628565ace46SCharles Keepax return compr->size.fragment_size / WM_ADSP_DATA_WORD_SIZE; 2629565ace46SCharles Keepax } 2630565ace46SCharles Keepax 2631406abc95SCharles Keepax int wm_adsp_compr_set_params(struct snd_compr_stream *stream, 2632406abc95SCharles Keepax struct snd_compr_params *params) 2633406abc95SCharles Keepax { 2634406abc95SCharles Keepax struct wm_adsp_compr *compr = stream->runtime->private_data; 263583a40ce9SCharles Keepax unsigned int size; 2636406abc95SCharles Keepax int ret; 2637406abc95SCharles Keepax 2638406abc95SCharles Keepax ret = wm_adsp_compr_check_params(stream, params); 2639406abc95SCharles Keepax if (ret) 2640406abc95SCharles Keepax return ret; 2641406abc95SCharles Keepax 2642406abc95SCharles Keepax compr->size = params->buffer; 2643406abc95SCharles Keepax 2644406abc95SCharles Keepax adsp_dbg(compr->dsp, "fragment_size=%d fragments=%d\n", 2645406abc95SCharles Keepax compr->size.fragment_size, compr->size.fragments); 2646406abc95SCharles Keepax 264783a40ce9SCharles Keepax size = wm_adsp_compr_frag_words(compr) * sizeof(*compr->raw_buf); 264883a40ce9SCharles Keepax compr->raw_buf = kmalloc(size, GFP_DMA | GFP_KERNEL); 264983a40ce9SCharles Keepax if (!compr->raw_buf) 265083a40ce9SCharles Keepax return -ENOMEM; 265183a40ce9SCharles Keepax 2652da2b3358SCharles Keepax compr->sample_rate = params->codec.sample_rate; 2653da2b3358SCharles Keepax 2654406abc95SCharles Keepax return 0; 2655406abc95SCharles Keepax } 2656406abc95SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_set_params); 2657406abc95SCharles Keepax 2658406abc95SCharles Keepax int wm_adsp_compr_get_caps(struct snd_compr_stream *stream, 2659406abc95SCharles Keepax struct snd_compr_caps *caps) 2660406abc95SCharles Keepax { 2661406abc95SCharles Keepax struct wm_adsp_compr *compr = stream->runtime->private_data; 2662406abc95SCharles Keepax int fw = compr->dsp->fw; 2663406abc95SCharles Keepax int i; 2664406abc95SCharles Keepax 2665406abc95SCharles Keepax if (wm_adsp_fw[fw].caps) { 2666406abc95SCharles Keepax for (i = 0; i < wm_adsp_fw[fw].num_caps; i++) 2667406abc95SCharles Keepax caps->codecs[i] = wm_adsp_fw[fw].caps[i].id; 2668406abc95SCharles Keepax 2669406abc95SCharles Keepax caps->num_codecs = i; 2670406abc95SCharles Keepax caps->direction = wm_adsp_fw[fw].compr_direction; 2671406abc95SCharles Keepax 2672406abc95SCharles Keepax caps->min_fragment_size = WM_ADSP_MIN_FRAGMENT_SIZE; 2673406abc95SCharles Keepax caps->max_fragment_size = WM_ADSP_MAX_FRAGMENT_SIZE; 2674406abc95SCharles Keepax caps->min_fragments = WM_ADSP_MIN_FRAGMENTS; 2675406abc95SCharles Keepax caps->max_fragments = WM_ADSP_MAX_FRAGMENTS; 2676406abc95SCharles Keepax } 2677406abc95SCharles Keepax 2678406abc95SCharles Keepax return 0; 2679406abc95SCharles Keepax } 2680406abc95SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_get_caps); 2681406abc95SCharles Keepax 26822cd19bdbSCharles Keepax static int wm_adsp_read_data_block(struct wm_adsp *dsp, int mem_type, 26832cd19bdbSCharles Keepax unsigned int mem_addr, 26842cd19bdbSCharles Keepax unsigned int num_words, u32 *data) 26852cd19bdbSCharles Keepax { 26862cd19bdbSCharles Keepax struct wm_adsp_region const *mem = wm_adsp_find_region(dsp, mem_type); 26872cd19bdbSCharles Keepax unsigned int i, reg; 26882cd19bdbSCharles Keepax int ret; 26892cd19bdbSCharles Keepax 26902cd19bdbSCharles Keepax if (!mem) 26912cd19bdbSCharles Keepax return -EINVAL; 26922cd19bdbSCharles Keepax 26932cd19bdbSCharles Keepax reg = wm_adsp_region_to_reg(mem, mem_addr); 26942cd19bdbSCharles Keepax 26952cd19bdbSCharles Keepax ret = regmap_raw_read(dsp->regmap, reg, data, 26962cd19bdbSCharles Keepax sizeof(*data) * num_words); 26972cd19bdbSCharles Keepax if (ret < 0) 26982cd19bdbSCharles Keepax return ret; 26992cd19bdbSCharles Keepax 27002cd19bdbSCharles Keepax for (i = 0; i < num_words; ++i) 27012cd19bdbSCharles Keepax data[i] = be32_to_cpu(data[i]) & 0x00ffffffu; 27022cd19bdbSCharles Keepax 27032cd19bdbSCharles Keepax return 0; 27042cd19bdbSCharles Keepax } 27052cd19bdbSCharles Keepax 27062cd19bdbSCharles Keepax static inline int wm_adsp_read_data_word(struct wm_adsp *dsp, int mem_type, 27072cd19bdbSCharles Keepax unsigned int mem_addr, u32 *data) 27082cd19bdbSCharles Keepax { 27092cd19bdbSCharles Keepax return wm_adsp_read_data_block(dsp, mem_type, mem_addr, 1, data); 27102cd19bdbSCharles Keepax } 27112cd19bdbSCharles Keepax 27122cd19bdbSCharles Keepax static int wm_adsp_write_data_word(struct wm_adsp *dsp, int mem_type, 27132cd19bdbSCharles Keepax unsigned int mem_addr, u32 data) 27142cd19bdbSCharles Keepax { 27152cd19bdbSCharles Keepax struct wm_adsp_region const *mem = wm_adsp_find_region(dsp, mem_type); 27162cd19bdbSCharles Keepax unsigned int reg; 27172cd19bdbSCharles Keepax 27182cd19bdbSCharles Keepax if (!mem) 27192cd19bdbSCharles Keepax return -EINVAL; 27202cd19bdbSCharles Keepax 27212cd19bdbSCharles Keepax reg = wm_adsp_region_to_reg(mem, mem_addr); 27222cd19bdbSCharles Keepax 27232cd19bdbSCharles Keepax data = cpu_to_be32(data & 0x00ffffffu); 27242cd19bdbSCharles Keepax 27252cd19bdbSCharles Keepax return regmap_raw_write(dsp->regmap, reg, &data, sizeof(data)); 27262cd19bdbSCharles Keepax } 27272cd19bdbSCharles Keepax 27282cd19bdbSCharles Keepax static inline int wm_adsp_buffer_read(struct wm_adsp_compr_buf *buf, 27292cd19bdbSCharles Keepax unsigned int field_offset, u32 *data) 27302cd19bdbSCharles Keepax { 27312cd19bdbSCharles Keepax return wm_adsp_read_data_word(buf->dsp, WMFW_ADSP2_XM, 27322cd19bdbSCharles Keepax buf->host_buf_ptr + field_offset, data); 27332cd19bdbSCharles Keepax } 27342cd19bdbSCharles Keepax 27352cd19bdbSCharles Keepax static inline int wm_adsp_buffer_write(struct wm_adsp_compr_buf *buf, 27362cd19bdbSCharles Keepax unsigned int field_offset, u32 data) 27372cd19bdbSCharles Keepax { 27382cd19bdbSCharles Keepax return wm_adsp_write_data_word(buf->dsp, WMFW_ADSP2_XM, 27392cd19bdbSCharles Keepax buf->host_buf_ptr + field_offset, data); 27402cd19bdbSCharles Keepax } 27412cd19bdbSCharles Keepax 27422cd19bdbSCharles Keepax static int wm_adsp_buffer_locate(struct wm_adsp_compr_buf *buf) 27432cd19bdbSCharles Keepax { 27442cd19bdbSCharles Keepax struct wm_adsp_alg_region *alg_region; 27452cd19bdbSCharles Keepax struct wm_adsp *dsp = buf->dsp; 27462cd19bdbSCharles Keepax u32 xmalg, addr, magic; 27472cd19bdbSCharles Keepax int i, ret; 27482cd19bdbSCharles Keepax 27492cd19bdbSCharles Keepax alg_region = wm_adsp_find_alg_region(dsp, WMFW_ADSP2_XM, dsp->fw_id); 27502cd19bdbSCharles Keepax xmalg = sizeof(struct wm_adsp_system_config_xm_hdr) / sizeof(__be32); 27512cd19bdbSCharles Keepax 27522cd19bdbSCharles Keepax addr = alg_region->base + xmalg + ALG_XM_FIELD(magic); 27532cd19bdbSCharles Keepax ret = wm_adsp_read_data_word(dsp, WMFW_ADSP2_XM, addr, &magic); 27542cd19bdbSCharles Keepax if (ret < 0) 27552cd19bdbSCharles Keepax return ret; 27562cd19bdbSCharles Keepax 27572cd19bdbSCharles Keepax if (magic != WM_ADSP_ALG_XM_STRUCT_MAGIC) 27582cd19bdbSCharles Keepax return -EINVAL; 27592cd19bdbSCharles Keepax 27602cd19bdbSCharles Keepax addr = alg_region->base + xmalg + ALG_XM_FIELD(host_buf_ptr); 27612cd19bdbSCharles Keepax for (i = 0; i < 5; ++i) { 27622cd19bdbSCharles Keepax ret = wm_adsp_read_data_word(dsp, WMFW_ADSP2_XM, addr, 27632cd19bdbSCharles Keepax &buf->host_buf_ptr); 27642cd19bdbSCharles Keepax if (ret < 0) 27652cd19bdbSCharles Keepax return ret; 27662cd19bdbSCharles Keepax 27672cd19bdbSCharles Keepax if (buf->host_buf_ptr) 27682cd19bdbSCharles Keepax break; 27692cd19bdbSCharles Keepax 27702cd19bdbSCharles Keepax usleep_range(1000, 2000); 27712cd19bdbSCharles Keepax } 27722cd19bdbSCharles Keepax 27732cd19bdbSCharles Keepax if (!buf->host_buf_ptr) 27742cd19bdbSCharles Keepax return -EIO; 27752cd19bdbSCharles Keepax 27762cd19bdbSCharles Keepax adsp_dbg(dsp, "host_buf_ptr=%x\n", buf->host_buf_ptr); 27772cd19bdbSCharles Keepax 27782cd19bdbSCharles Keepax return 0; 27792cd19bdbSCharles Keepax } 27802cd19bdbSCharles Keepax 27812cd19bdbSCharles Keepax static int wm_adsp_buffer_populate(struct wm_adsp_compr_buf *buf) 27822cd19bdbSCharles Keepax { 27832cd19bdbSCharles Keepax const struct wm_adsp_fw_caps *caps = wm_adsp_fw[buf->dsp->fw].caps; 27842cd19bdbSCharles Keepax struct wm_adsp_buffer_region *region; 27852cd19bdbSCharles Keepax u32 offset = 0; 27862cd19bdbSCharles Keepax int i, ret; 27872cd19bdbSCharles Keepax 27882cd19bdbSCharles Keepax for (i = 0; i < caps->num_regions; ++i) { 27892cd19bdbSCharles Keepax region = &buf->regions[i]; 27902cd19bdbSCharles Keepax 27912cd19bdbSCharles Keepax region->offset = offset; 27922cd19bdbSCharles Keepax region->mem_type = caps->region_defs[i].mem_type; 27932cd19bdbSCharles Keepax 27942cd19bdbSCharles Keepax ret = wm_adsp_buffer_read(buf, caps->region_defs[i].base_offset, 27952cd19bdbSCharles Keepax ®ion->base_addr); 27962cd19bdbSCharles Keepax if (ret < 0) 27972cd19bdbSCharles Keepax return ret; 27982cd19bdbSCharles Keepax 27992cd19bdbSCharles Keepax ret = wm_adsp_buffer_read(buf, caps->region_defs[i].size_offset, 28002cd19bdbSCharles Keepax &offset); 28012cd19bdbSCharles Keepax if (ret < 0) 28022cd19bdbSCharles Keepax return ret; 28032cd19bdbSCharles Keepax 28042cd19bdbSCharles Keepax region->cumulative_size = offset; 28052cd19bdbSCharles Keepax 28062cd19bdbSCharles Keepax adsp_dbg(buf->dsp, 28072cd19bdbSCharles Keepax "region=%d type=%d base=%04x off=%04x size=%04x\n", 28082cd19bdbSCharles Keepax i, region->mem_type, region->base_addr, 28092cd19bdbSCharles Keepax region->offset, region->cumulative_size); 28102cd19bdbSCharles Keepax } 28112cd19bdbSCharles Keepax 28122cd19bdbSCharles Keepax return 0; 28132cd19bdbSCharles Keepax } 28142cd19bdbSCharles Keepax 28152cd19bdbSCharles Keepax static int wm_adsp_buffer_init(struct wm_adsp *dsp) 28162cd19bdbSCharles Keepax { 28172cd19bdbSCharles Keepax struct wm_adsp_compr_buf *buf; 28182cd19bdbSCharles Keepax int ret; 28192cd19bdbSCharles Keepax 28202cd19bdbSCharles Keepax buf = kzalloc(sizeof(*buf), GFP_KERNEL); 28212cd19bdbSCharles Keepax if (!buf) 28222cd19bdbSCharles Keepax return -ENOMEM; 28232cd19bdbSCharles Keepax 28242cd19bdbSCharles Keepax buf->dsp = dsp; 2825565ace46SCharles Keepax buf->read_index = -1; 2826565ace46SCharles Keepax buf->irq_count = 0xFFFFFFFF; 28272cd19bdbSCharles Keepax 28282cd19bdbSCharles Keepax ret = wm_adsp_buffer_locate(buf); 28292cd19bdbSCharles Keepax if (ret < 0) { 28302cd19bdbSCharles Keepax adsp_err(dsp, "Failed to acquire host buffer: %d\n", ret); 28312cd19bdbSCharles Keepax goto err_buffer; 28322cd19bdbSCharles Keepax } 28332cd19bdbSCharles Keepax 28342cd19bdbSCharles Keepax buf->regions = kcalloc(wm_adsp_fw[dsp->fw].caps->num_regions, 28352cd19bdbSCharles Keepax sizeof(*buf->regions), GFP_KERNEL); 28362cd19bdbSCharles Keepax if (!buf->regions) { 28372cd19bdbSCharles Keepax ret = -ENOMEM; 28382cd19bdbSCharles Keepax goto err_buffer; 28392cd19bdbSCharles Keepax } 28402cd19bdbSCharles Keepax 28412cd19bdbSCharles Keepax ret = wm_adsp_buffer_populate(buf); 28422cd19bdbSCharles Keepax if (ret < 0) { 28432cd19bdbSCharles Keepax adsp_err(dsp, "Failed to populate host buffer: %d\n", ret); 28442cd19bdbSCharles Keepax goto err_regions; 28452cd19bdbSCharles Keepax } 28462cd19bdbSCharles Keepax 28472cd19bdbSCharles Keepax dsp->buffer = buf; 28482cd19bdbSCharles Keepax 28492cd19bdbSCharles Keepax return 0; 28502cd19bdbSCharles Keepax 28512cd19bdbSCharles Keepax err_regions: 28522cd19bdbSCharles Keepax kfree(buf->regions); 28532cd19bdbSCharles Keepax err_buffer: 28542cd19bdbSCharles Keepax kfree(buf); 28552cd19bdbSCharles Keepax return ret; 28562cd19bdbSCharles Keepax } 28572cd19bdbSCharles Keepax 28582cd19bdbSCharles Keepax static int wm_adsp_buffer_free(struct wm_adsp *dsp) 28592cd19bdbSCharles Keepax { 28602cd19bdbSCharles Keepax if (dsp->buffer) { 2861721be3beSCharles Keepax wm_adsp_compr_detach(dsp->buffer->compr); 2862721be3beSCharles Keepax 28632cd19bdbSCharles Keepax kfree(dsp->buffer->regions); 28642cd19bdbSCharles Keepax kfree(dsp->buffer); 28652cd19bdbSCharles Keepax 28662cd19bdbSCharles Keepax dsp->buffer = NULL; 28672cd19bdbSCharles Keepax } 28682cd19bdbSCharles Keepax 28692cd19bdbSCharles Keepax return 0; 28702cd19bdbSCharles Keepax } 28712cd19bdbSCharles Keepax 287295fe9597SCharles Keepax int wm_adsp_compr_trigger(struct snd_compr_stream *stream, int cmd) 287395fe9597SCharles Keepax { 287495fe9597SCharles Keepax struct wm_adsp_compr *compr = stream->runtime->private_data; 287595fe9597SCharles Keepax struct wm_adsp *dsp = compr->dsp; 287695fe9597SCharles Keepax int ret = 0; 287795fe9597SCharles Keepax 287895fe9597SCharles Keepax adsp_dbg(dsp, "Trigger: %d\n", cmd); 287995fe9597SCharles Keepax 288095fe9597SCharles Keepax mutex_lock(&dsp->pwr_lock); 288195fe9597SCharles Keepax 288295fe9597SCharles Keepax switch (cmd) { 288395fe9597SCharles Keepax case SNDRV_PCM_TRIGGER_START: 288495fe9597SCharles Keepax if (wm_adsp_compr_attached(compr)) 288595fe9597SCharles Keepax break; 288695fe9597SCharles Keepax 288795fe9597SCharles Keepax ret = wm_adsp_compr_attach(compr); 288895fe9597SCharles Keepax if (ret < 0) { 288995fe9597SCharles Keepax adsp_err(dsp, "Failed to link buffer and stream: %d\n", 289095fe9597SCharles Keepax ret); 289195fe9597SCharles Keepax break; 289295fe9597SCharles Keepax } 2893565ace46SCharles Keepax 2894565ace46SCharles Keepax /* Trigger the IRQ at one fragment of data */ 2895565ace46SCharles Keepax ret = wm_adsp_buffer_write(compr->buf, 2896565ace46SCharles Keepax HOST_BUFFER_FIELD(high_water_mark), 2897565ace46SCharles Keepax wm_adsp_compr_frag_words(compr)); 2898565ace46SCharles Keepax if (ret < 0) { 2899565ace46SCharles Keepax adsp_err(dsp, "Failed to set high water mark: %d\n", 2900565ace46SCharles Keepax ret); 2901565ace46SCharles Keepax break; 2902565ace46SCharles Keepax } 290395fe9597SCharles Keepax break; 290495fe9597SCharles Keepax case SNDRV_PCM_TRIGGER_STOP: 290595fe9597SCharles Keepax break; 290695fe9597SCharles Keepax default: 290795fe9597SCharles Keepax ret = -EINVAL; 290895fe9597SCharles Keepax break; 290995fe9597SCharles Keepax } 291095fe9597SCharles Keepax 291195fe9597SCharles Keepax mutex_unlock(&dsp->pwr_lock); 291295fe9597SCharles Keepax 291395fe9597SCharles Keepax return ret; 291495fe9597SCharles Keepax } 291595fe9597SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_trigger); 291695fe9597SCharles Keepax 2917565ace46SCharles Keepax static inline int wm_adsp_buffer_size(struct wm_adsp_compr_buf *buf) 2918565ace46SCharles Keepax { 2919565ace46SCharles Keepax int last_region = wm_adsp_fw[buf->dsp->fw].caps->num_regions - 1; 2920565ace46SCharles Keepax 2921565ace46SCharles Keepax return buf->regions[last_region].cumulative_size; 2922565ace46SCharles Keepax } 2923565ace46SCharles Keepax 2924565ace46SCharles Keepax static int wm_adsp_buffer_update_avail(struct wm_adsp_compr_buf *buf) 2925565ace46SCharles Keepax { 2926565ace46SCharles Keepax u32 next_read_index, next_write_index; 2927565ace46SCharles Keepax int write_index, read_index, avail; 2928565ace46SCharles Keepax int ret; 2929565ace46SCharles Keepax 2930565ace46SCharles Keepax /* Only sync read index if we haven't already read a valid index */ 2931565ace46SCharles Keepax if (buf->read_index < 0) { 2932565ace46SCharles Keepax ret = wm_adsp_buffer_read(buf, 2933565ace46SCharles Keepax HOST_BUFFER_FIELD(next_read_index), 2934565ace46SCharles Keepax &next_read_index); 2935565ace46SCharles Keepax if (ret < 0) 2936565ace46SCharles Keepax return ret; 2937565ace46SCharles Keepax 2938565ace46SCharles Keepax read_index = sign_extend32(next_read_index, 23); 2939565ace46SCharles Keepax 2940565ace46SCharles Keepax if (read_index < 0) { 2941565ace46SCharles Keepax adsp_dbg(buf->dsp, "Avail check on unstarted stream\n"); 2942565ace46SCharles Keepax return 0; 2943565ace46SCharles Keepax } 2944565ace46SCharles Keepax 2945565ace46SCharles Keepax buf->read_index = read_index; 2946565ace46SCharles Keepax } 2947565ace46SCharles Keepax 2948565ace46SCharles Keepax ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(next_write_index), 2949565ace46SCharles Keepax &next_write_index); 2950565ace46SCharles Keepax if (ret < 0) 2951565ace46SCharles Keepax return ret; 2952565ace46SCharles Keepax 2953565ace46SCharles Keepax write_index = sign_extend32(next_write_index, 23); 2954565ace46SCharles Keepax 2955565ace46SCharles Keepax avail = write_index - buf->read_index; 2956565ace46SCharles Keepax if (avail < 0) 2957565ace46SCharles Keepax avail += wm_adsp_buffer_size(buf); 2958565ace46SCharles Keepax 2959565ace46SCharles Keepax adsp_dbg(buf->dsp, "readindex=0x%x, writeindex=0x%x, avail=%d\n", 296033d740e0SCharles Keepax buf->read_index, write_index, avail * WM_ADSP_DATA_WORD_SIZE); 2961565ace46SCharles Keepax 2962565ace46SCharles Keepax buf->avail = avail; 2963565ace46SCharles Keepax 2964565ace46SCharles Keepax return 0; 2965565ace46SCharles Keepax } 2966565ace46SCharles Keepax 29679771b18aSCharles Keepax static int wm_adsp_buffer_get_error(struct wm_adsp_compr_buf *buf) 29689771b18aSCharles Keepax { 29699771b18aSCharles Keepax int ret; 29709771b18aSCharles Keepax 29719771b18aSCharles Keepax ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(error), &buf->error); 29729771b18aSCharles Keepax if (ret < 0) { 29739771b18aSCharles Keepax adsp_err(buf->dsp, "Failed to check buffer error: %d\n", ret); 29749771b18aSCharles Keepax return ret; 29759771b18aSCharles Keepax } 29769771b18aSCharles Keepax if (buf->error != 0) { 29779771b18aSCharles Keepax adsp_err(buf->dsp, "Buffer error occurred: %d\n", buf->error); 29789771b18aSCharles Keepax return -EIO; 29799771b18aSCharles Keepax } 29809771b18aSCharles Keepax 29819771b18aSCharles Keepax return 0; 29829771b18aSCharles Keepax } 29839771b18aSCharles Keepax 2984565ace46SCharles Keepax int wm_adsp_compr_handle_irq(struct wm_adsp *dsp) 2985565ace46SCharles Keepax { 2986612047f0SCharles Keepax struct wm_adsp_compr_buf *buf; 2987612047f0SCharles Keepax struct wm_adsp_compr *compr; 2988565ace46SCharles Keepax int ret = 0; 2989565ace46SCharles Keepax 2990565ace46SCharles Keepax mutex_lock(&dsp->pwr_lock); 2991565ace46SCharles Keepax 2992612047f0SCharles Keepax buf = dsp->buffer; 2993612047f0SCharles Keepax compr = dsp->compr; 2994612047f0SCharles Keepax 2995565ace46SCharles Keepax if (!buf) { 2996565ace46SCharles Keepax ret = -ENODEV; 2997565ace46SCharles Keepax goto out; 2998565ace46SCharles Keepax } 2999565ace46SCharles Keepax 3000565ace46SCharles Keepax adsp_dbg(dsp, "Handling buffer IRQ\n"); 3001565ace46SCharles Keepax 30029771b18aSCharles Keepax ret = wm_adsp_buffer_get_error(buf); 30039771b18aSCharles Keepax if (ret < 0) 30045847609eSCharles Keepax goto out_notify; /* Wake poll to report error */ 3005565ace46SCharles Keepax 3006565ace46SCharles Keepax ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(irq_count), 3007565ace46SCharles Keepax &buf->irq_count); 3008565ace46SCharles Keepax if (ret < 0) { 3009565ace46SCharles Keepax adsp_err(dsp, "Failed to get irq_count: %d\n", ret); 3010565ace46SCharles Keepax goto out; 3011565ace46SCharles Keepax } 3012565ace46SCharles Keepax 3013565ace46SCharles Keepax ret = wm_adsp_buffer_update_avail(buf); 3014565ace46SCharles Keepax if (ret < 0) { 3015565ace46SCharles Keepax adsp_err(dsp, "Error reading avail: %d\n", ret); 3016565ace46SCharles Keepax goto out; 3017565ace46SCharles Keepax } 3018565ace46SCharles Keepax 301920b7f7c5SCharles Keepax if (wm_adsp_fw[dsp->fw].voice_trigger && buf->irq_count == 2) 302020b7f7c5SCharles Keepax ret = WM_ADSP_COMPR_VOICE_TRIGGER; 302120b7f7c5SCharles Keepax 30225847609eSCharles Keepax out_notify: 3023c7dae7c4SCharles Keepax if (compr && compr->stream) 302483a40ce9SCharles Keepax snd_compr_fragment_elapsed(compr->stream); 302583a40ce9SCharles Keepax 3026565ace46SCharles Keepax out: 3027565ace46SCharles Keepax mutex_unlock(&dsp->pwr_lock); 3028565ace46SCharles Keepax 3029565ace46SCharles Keepax return ret; 3030565ace46SCharles Keepax } 3031565ace46SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_handle_irq); 3032565ace46SCharles Keepax 3033565ace46SCharles Keepax static int wm_adsp_buffer_reenable_irq(struct wm_adsp_compr_buf *buf) 3034565ace46SCharles Keepax { 3035565ace46SCharles Keepax if (buf->irq_count & 0x01) 3036565ace46SCharles Keepax return 0; 3037565ace46SCharles Keepax 3038565ace46SCharles Keepax adsp_dbg(buf->dsp, "Enable IRQ(0x%x) for next fragment\n", 3039565ace46SCharles Keepax buf->irq_count); 3040565ace46SCharles Keepax 3041565ace46SCharles Keepax buf->irq_count |= 0x01; 3042565ace46SCharles Keepax 3043565ace46SCharles Keepax return wm_adsp_buffer_write(buf, HOST_BUFFER_FIELD(irq_ack), 3044565ace46SCharles Keepax buf->irq_count); 3045565ace46SCharles Keepax } 3046565ace46SCharles Keepax 3047565ace46SCharles Keepax int wm_adsp_compr_pointer(struct snd_compr_stream *stream, 3048565ace46SCharles Keepax struct snd_compr_tstamp *tstamp) 3049565ace46SCharles Keepax { 3050565ace46SCharles Keepax struct wm_adsp_compr *compr = stream->runtime->private_data; 3051565ace46SCharles Keepax struct wm_adsp *dsp = compr->dsp; 3052612047f0SCharles Keepax struct wm_adsp_compr_buf *buf; 3053565ace46SCharles Keepax int ret = 0; 3054565ace46SCharles Keepax 3055565ace46SCharles Keepax adsp_dbg(dsp, "Pointer request\n"); 3056565ace46SCharles Keepax 3057565ace46SCharles Keepax mutex_lock(&dsp->pwr_lock); 3058565ace46SCharles Keepax 3059612047f0SCharles Keepax buf = compr->buf; 3060612047f0SCharles Keepax 306128ee3d73SCharles Keepax if (!compr->buf || compr->buf->error) { 30628d280664SCharles Keepax snd_compr_stop_error(stream, SNDRV_PCM_STATE_XRUN); 3063565ace46SCharles Keepax ret = -EIO; 3064565ace46SCharles Keepax goto out; 3065565ace46SCharles Keepax } 3066565ace46SCharles Keepax 3067565ace46SCharles Keepax if (buf->avail < wm_adsp_compr_frag_words(compr)) { 3068565ace46SCharles Keepax ret = wm_adsp_buffer_update_avail(buf); 3069565ace46SCharles Keepax if (ret < 0) { 3070565ace46SCharles Keepax adsp_err(dsp, "Error reading avail: %d\n", ret); 3071565ace46SCharles Keepax goto out; 3072565ace46SCharles Keepax } 3073565ace46SCharles Keepax 3074565ace46SCharles Keepax /* 3075565ace46SCharles Keepax * If we really have less than 1 fragment available tell the 3076565ace46SCharles Keepax * DSP to inform us once a whole fragment is available. 3077565ace46SCharles Keepax */ 3078565ace46SCharles Keepax if (buf->avail < wm_adsp_compr_frag_words(compr)) { 30795847609eSCharles Keepax ret = wm_adsp_buffer_get_error(buf); 30808d280664SCharles Keepax if (ret < 0) { 30818d280664SCharles Keepax if (compr->buf->error) 30828d280664SCharles Keepax snd_compr_stop_error(stream, 30838d280664SCharles Keepax SNDRV_PCM_STATE_XRUN); 30845847609eSCharles Keepax goto out; 30858d280664SCharles Keepax } 30865847609eSCharles Keepax 3087565ace46SCharles Keepax ret = wm_adsp_buffer_reenable_irq(buf); 3088565ace46SCharles Keepax if (ret < 0) { 3089565ace46SCharles Keepax adsp_err(dsp, 3090565ace46SCharles Keepax "Failed to re-enable buffer IRQ: %d\n", 3091565ace46SCharles Keepax ret); 3092565ace46SCharles Keepax goto out; 3093565ace46SCharles Keepax } 3094565ace46SCharles Keepax } 3095565ace46SCharles Keepax } 3096565ace46SCharles Keepax 3097565ace46SCharles Keepax tstamp->copied_total = compr->copied_total; 3098565ace46SCharles Keepax tstamp->copied_total += buf->avail * WM_ADSP_DATA_WORD_SIZE; 3099da2b3358SCharles Keepax tstamp->sampling_rate = compr->sample_rate; 3100565ace46SCharles Keepax 3101565ace46SCharles Keepax out: 3102565ace46SCharles Keepax mutex_unlock(&dsp->pwr_lock); 3103565ace46SCharles Keepax 3104565ace46SCharles Keepax return ret; 3105565ace46SCharles Keepax } 3106565ace46SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_pointer); 3107565ace46SCharles Keepax 310883a40ce9SCharles Keepax static int wm_adsp_buffer_capture_block(struct wm_adsp_compr *compr, int target) 310983a40ce9SCharles Keepax { 311083a40ce9SCharles Keepax struct wm_adsp_compr_buf *buf = compr->buf; 311183a40ce9SCharles Keepax u8 *pack_in = (u8 *)compr->raw_buf; 311283a40ce9SCharles Keepax u8 *pack_out = (u8 *)compr->raw_buf; 311383a40ce9SCharles Keepax unsigned int adsp_addr; 311483a40ce9SCharles Keepax int mem_type, nwords, max_read; 311583a40ce9SCharles Keepax int i, j, ret; 311683a40ce9SCharles Keepax 311783a40ce9SCharles Keepax /* Calculate read parameters */ 311883a40ce9SCharles Keepax for (i = 0; i < wm_adsp_fw[buf->dsp->fw].caps->num_regions; ++i) 311983a40ce9SCharles Keepax if (buf->read_index < buf->regions[i].cumulative_size) 312083a40ce9SCharles Keepax break; 312183a40ce9SCharles Keepax 312283a40ce9SCharles Keepax if (i == wm_adsp_fw[buf->dsp->fw].caps->num_regions) 312383a40ce9SCharles Keepax return -EINVAL; 312483a40ce9SCharles Keepax 312583a40ce9SCharles Keepax mem_type = buf->regions[i].mem_type; 312683a40ce9SCharles Keepax adsp_addr = buf->regions[i].base_addr + 312783a40ce9SCharles Keepax (buf->read_index - buf->regions[i].offset); 312883a40ce9SCharles Keepax 312983a40ce9SCharles Keepax max_read = wm_adsp_compr_frag_words(compr); 313083a40ce9SCharles Keepax nwords = buf->regions[i].cumulative_size - buf->read_index; 313183a40ce9SCharles Keepax 313283a40ce9SCharles Keepax if (nwords > target) 313383a40ce9SCharles Keepax nwords = target; 313483a40ce9SCharles Keepax if (nwords > buf->avail) 313583a40ce9SCharles Keepax nwords = buf->avail; 313683a40ce9SCharles Keepax if (nwords > max_read) 313783a40ce9SCharles Keepax nwords = max_read; 313883a40ce9SCharles Keepax if (!nwords) 313983a40ce9SCharles Keepax return 0; 314083a40ce9SCharles Keepax 314183a40ce9SCharles Keepax /* Read data from DSP */ 314283a40ce9SCharles Keepax ret = wm_adsp_read_data_block(buf->dsp, mem_type, adsp_addr, 314383a40ce9SCharles Keepax nwords, compr->raw_buf); 314483a40ce9SCharles Keepax if (ret < 0) 314583a40ce9SCharles Keepax return ret; 314683a40ce9SCharles Keepax 314783a40ce9SCharles Keepax /* Remove the padding bytes from the data read from the DSP */ 314883a40ce9SCharles Keepax for (i = 0; i < nwords; i++) { 314983a40ce9SCharles Keepax for (j = 0; j < WM_ADSP_DATA_WORD_SIZE; j++) 315083a40ce9SCharles Keepax *pack_out++ = *pack_in++; 315183a40ce9SCharles Keepax 315283a40ce9SCharles Keepax pack_in += sizeof(*(compr->raw_buf)) - WM_ADSP_DATA_WORD_SIZE; 315383a40ce9SCharles Keepax } 315483a40ce9SCharles Keepax 315583a40ce9SCharles Keepax /* update read index to account for words read */ 315683a40ce9SCharles Keepax buf->read_index += nwords; 315783a40ce9SCharles Keepax if (buf->read_index == wm_adsp_buffer_size(buf)) 315883a40ce9SCharles Keepax buf->read_index = 0; 315983a40ce9SCharles Keepax 316083a40ce9SCharles Keepax ret = wm_adsp_buffer_write(buf, HOST_BUFFER_FIELD(next_read_index), 316183a40ce9SCharles Keepax buf->read_index); 316283a40ce9SCharles Keepax if (ret < 0) 316383a40ce9SCharles Keepax return ret; 316483a40ce9SCharles Keepax 316583a40ce9SCharles Keepax /* update avail to account for words read */ 316683a40ce9SCharles Keepax buf->avail -= nwords; 316783a40ce9SCharles Keepax 316883a40ce9SCharles Keepax return nwords; 316983a40ce9SCharles Keepax } 317083a40ce9SCharles Keepax 317183a40ce9SCharles Keepax static int wm_adsp_compr_read(struct wm_adsp_compr *compr, 317283a40ce9SCharles Keepax char __user *buf, size_t count) 317383a40ce9SCharles Keepax { 317483a40ce9SCharles Keepax struct wm_adsp *dsp = compr->dsp; 317583a40ce9SCharles Keepax int ntotal = 0; 317683a40ce9SCharles Keepax int nwords, nbytes; 317783a40ce9SCharles Keepax 317883a40ce9SCharles Keepax adsp_dbg(dsp, "Requested read of %zu bytes\n", count); 317983a40ce9SCharles Keepax 318028ee3d73SCharles Keepax if (!compr->buf || compr->buf->error) { 31818d280664SCharles Keepax snd_compr_stop_error(compr->stream, SNDRV_PCM_STATE_XRUN); 318283a40ce9SCharles Keepax return -EIO; 31838d280664SCharles Keepax } 318483a40ce9SCharles Keepax 318583a40ce9SCharles Keepax count /= WM_ADSP_DATA_WORD_SIZE; 318683a40ce9SCharles Keepax 318783a40ce9SCharles Keepax do { 318883a40ce9SCharles Keepax nwords = wm_adsp_buffer_capture_block(compr, count); 318983a40ce9SCharles Keepax if (nwords < 0) { 319083a40ce9SCharles Keepax adsp_err(dsp, "Failed to capture block: %d\n", nwords); 319183a40ce9SCharles Keepax return nwords; 319283a40ce9SCharles Keepax } 319383a40ce9SCharles Keepax 319483a40ce9SCharles Keepax nbytes = nwords * WM_ADSP_DATA_WORD_SIZE; 319583a40ce9SCharles Keepax 319683a40ce9SCharles Keepax adsp_dbg(dsp, "Read %d bytes\n", nbytes); 319783a40ce9SCharles Keepax 319883a40ce9SCharles Keepax if (copy_to_user(buf + ntotal, compr->raw_buf, nbytes)) { 319983a40ce9SCharles Keepax adsp_err(dsp, "Failed to copy data to user: %d, %d\n", 320083a40ce9SCharles Keepax ntotal, nbytes); 320183a40ce9SCharles Keepax return -EFAULT; 320283a40ce9SCharles Keepax } 320383a40ce9SCharles Keepax 320483a40ce9SCharles Keepax count -= nwords; 320583a40ce9SCharles Keepax ntotal += nbytes; 320683a40ce9SCharles Keepax } while (nwords > 0 && count > 0); 320783a40ce9SCharles Keepax 320883a40ce9SCharles Keepax compr->copied_total += ntotal; 320983a40ce9SCharles Keepax 321083a40ce9SCharles Keepax return ntotal; 321183a40ce9SCharles Keepax } 321283a40ce9SCharles Keepax 321383a40ce9SCharles Keepax int wm_adsp_compr_copy(struct snd_compr_stream *stream, char __user *buf, 321483a40ce9SCharles Keepax size_t count) 321583a40ce9SCharles Keepax { 321683a40ce9SCharles Keepax struct wm_adsp_compr *compr = stream->runtime->private_data; 321783a40ce9SCharles Keepax struct wm_adsp *dsp = compr->dsp; 321883a40ce9SCharles Keepax int ret; 321983a40ce9SCharles Keepax 322083a40ce9SCharles Keepax mutex_lock(&dsp->pwr_lock); 322183a40ce9SCharles Keepax 322283a40ce9SCharles Keepax if (stream->direction == SND_COMPRESS_CAPTURE) 322383a40ce9SCharles Keepax ret = wm_adsp_compr_read(compr, buf, count); 322483a40ce9SCharles Keepax else 322583a40ce9SCharles Keepax ret = -ENOTSUPP; 322683a40ce9SCharles Keepax 322783a40ce9SCharles Keepax mutex_unlock(&dsp->pwr_lock); 322883a40ce9SCharles Keepax 322983a40ce9SCharles Keepax return ret; 323083a40ce9SCharles Keepax } 323183a40ce9SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_copy); 323283a40ce9SCharles Keepax 32330a37c6efSPraveen Diwakar MODULE_LICENSE("GPL v2"); 3234