12159ad93SMark Brown /* 22159ad93SMark Brown * wm_adsp.c -- Wolfson ADSP support 32159ad93SMark Brown * 42159ad93SMark Brown * Copyright 2012 Wolfson Microelectronics plc 52159ad93SMark Brown * 62159ad93SMark Brown * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> 72159ad93SMark Brown * 82159ad93SMark Brown * This program is free software; you can redistribute it and/or modify 92159ad93SMark Brown * it under the terms of the GNU General Public License version 2 as 102159ad93SMark Brown * published by the Free Software Foundation. 112159ad93SMark Brown */ 122159ad93SMark Brown 132159ad93SMark Brown #include <linux/module.h> 142159ad93SMark Brown #include <linux/moduleparam.h> 152159ad93SMark Brown #include <linux/init.h> 162159ad93SMark Brown #include <linux/delay.h> 172159ad93SMark Brown #include <linux/firmware.h> 18cf17c83cSMark Brown #include <linux/list.h> 192159ad93SMark Brown #include <linux/pm.h> 202159ad93SMark Brown #include <linux/pm_runtime.h> 212159ad93SMark Brown #include <linux/regmap.h> 22973838a0SMark Brown #include <linux/regulator/consumer.h> 232159ad93SMark Brown #include <linux/slab.h> 24cdcd7f72SCharles Keepax #include <linux/vmalloc.h> 256ab2b7b4SDimitris Papastamos #include <linux/workqueue.h> 26f9f55e31SRichard Fitzgerald #include <linux/debugfs.h> 272159ad93SMark Brown #include <sound/core.h> 282159ad93SMark Brown #include <sound/pcm.h> 292159ad93SMark Brown #include <sound/pcm_params.h> 302159ad93SMark Brown #include <sound/soc.h> 312159ad93SMark Brown #include <sound/jack.h> 322159ad93SMark Brown #include <sound/initval.h> 332159ad93SMark Brown #include <sound/tlv.h> 342159ad93SMark Brown 352159ad93SMark Brown #include <linux/mfd/arizona/registers.h> 362159ad93SMark Brown 37dc91428aSMark Brown #include "arizona.h" 382159ad93SMark Brown #include "wm_adsp.h" 392159ad93SMark Brown 402159ad93SMark Brown #define adsp_crit(_dsp, fmt, ...) \ 412159ad93SMark Brown dev_crit(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__) 422159ad93SMark Brown #define adsp_err(_dsp, fmt, ...) \ 432159ad93SMark Brown dev_err(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__) 442159ad93SMark Brown #define adsp_warn(_dsp, fmt, ...) \ 452159ad93SMark Brown dev_warn(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__) 462159ad93SMark Brown #define adsp_info(_dsp, fmt, ...) \ 472159ad93SMark Brown dev_info(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__) 482159ad93SMark Brown #define adsp_dbg(_dsp, fmt, ...) \ 492159ad93SMark Brown dev_dbg(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__) 502159ad93SMark Brown 512159ad93SMark Brown #define ADSP1_CONTROL_1 0x00 522159ad93SMark Brown #define ADSP1_CONTROL_2 0x02 532159ad93SMark Brown #define ADSP1_CONTROL_3 0x03 542159ad93SMark Brown #define ADSP1_CONTROL_4 0x04 552159ad93SMark Brown #define ADSP1_CONTROL_5 0x06 562159ad93SMark Brown #define ADSP1_CONTROL_6 0x07 572159ad93SMark Brown #define ADSP1_CONTROL_7 0x08 582159ad93SMark Brown #define ADSP1_CONTROL_8 0x09 592159ad93SMark Brown #define ADSP1_CONTROL_9 0x0A 602159ad93SMark Brown #define ADSP1_CONTROL_10 0x0B 612159ad93SMark Brown #define ADSP1_CONTROL_11 0x0C 622159ad93SMark Brown #define ADSP1_CONTROL_12 0x0D 632159ad93SMark Brown #define ADSP1_CONTROL_13 0x0F 642159ad93SMark Brown #define ADSP1_CONTROL_14 0x10 652159ad93SMark Brown #define ADSP1_CONTROL_15 0x11 662159ad93SMark Brown #define ADSP1_CONTROL_16 0x12 672159ad93SMark Brown #define ADSP1_CONTROL_17 0x13 682159ad93SMark Brown #define ADSP1_CONTROL_18 0x14 692159ad93SMark Brown #define ADSP1_CONTROL_19 0x16 702159ad93SMark Brown #define ADSP1_CONTROL_20 0x17 712159ad93SMark Brown #define ADSP1_CONTROL_21 0x18 722159ad93SMark Brown #define ADSP1_CONTROL_22 0x1A 732159ad93SMark Brown #define ADSP1_CONTROL_23 0x1B 742159ad93SMark Brown #define ADSP1_CONTROL_24 0x1C 752159ad93SMark Brown #define ADSP1_CONTROL_25 0x1E 762159ad93SMark Brown #define ADSP1_CONTROL_26 0x20 772159ad93SMark Brown #define ADSP1_CONTROL_27 0x21 782159ad93SMark Brown #define ADSP1_CONTROL_28 0x22 792159ad93SMark Brown #define ADSP1_CONTROL_29 0x23 802159ad93SMark Brown #define ADSP1_CONTROL_30 0x24 812159ad93SMark Brown #define ADSP1_CONTROL_31 0x26 822159ad93SMark Brown 832159ad93SMark Brown /* 842159ad93SMark Brown * ADSP1 Control 19 852159ad93SMark Brown */ 862159ad93SMark Brown #define ADSP1_WDMA_BUFFER_LENGTH_MASK 0x00FF /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */ 872159ad93SMark Brown #define ADSP1_WDMA_BUFFER_LENGTH_SHIFT 0 /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */ 882159ad93SMark Brown #define ADSP1_WDMA_BUFFER_LENGTH_WIDTH 8 /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */ 892159ad93SMark Brown 902159ad93SMark Brown 912159ad93SMark Brown /* 922159ad93SMark Brown * ADSP1 Control 30 932159ad93SMark Brown */ 942159ad93SMark Brown #define ADSP1_DBG_CLK_ENA 0x0008 /* DSP1_DBG_CLK_ENA */ 952159ad93SMark Brown #define ADSP1_DBG_CLK_ENA_MASK 0x0008 /* DSP1_DBG_CLK_ENA */ 962159ad93SMark Brown #define ADSP1_DBG_CLK_ENA_SHIFT 3 /* DSP1_DBG_CLK_ENA */ 972159ad93SMark Brown #define ADSP1_DBG_CLK_ENA_WIDTH 1 /* DSP1_DBG_CLK_ENA */ 982159ad93SMark Brown #define ADSP1_SYS_ENA 0x0004 /* DSP1_SYS_ENA */ 992159ad93SMark Brown #define ADSP1_SYS_ENA_MASK 0x0004 /* DSP1_SYS_ENA */ 1002159ad93SMark Brown #define ADSP1_SYS_ENA_SHIFT 2 /* DSP1_SYS_ENA */ 1012159ad93SMark Brown #define ADSP1_SYS_ENA_WIDTH 1 /* DSP1_SYS_ENA */ 1022159ad93SMark Brown #define ADSP1_CORE_ENA 0x0002 /* DSP1_CORE_ENA */ 1032159ad93SMark Brown #define ADSP1_CORE_ENA_MASK 0x0002 /* DSP1_CORE_ENA */ 1042159ad93SMark Brown #define ADSP1_CORE_ENA_SHIFT 1 /* DSP1_CORE_ENA */ 1052159ad93SMark Brown #define ADSP1_CORE_ENA_WIDTH 1 /* DSP1_CORE_ENA */ 1062159ad93SMark Brown #define ADSP1_START 0x0001 /* DSP1_START */ 1072159ad93SMark Brown #define ADSP1_START_MASK 0x0001 /* DSP1_START */ 1082159ad93SMark Brown #define ADSP1_START_SHIFT 0 /* DSP1_START */ 1092159ad93SMark Brown #define ADSP1_START_WIDTH 1 /* DSP1_START */ 1102159ad93SMark Brown 11194e205bfSChris Rattray /* 11294e205bfSChris Rattray * ADSP1 Control 31 11394e205bfSChris Rattray */ 11494e205bfSChris Rattray #define ADSP1_CLK_SEL_MASK 0x0007 /* CLK_SEL_ENA */ 11594e205bfSChris Rattray #define ADSP1_CLK_SEL_SHIFT 0 /* CLK_SEL_ENA */ 11694e205bfSChris Rattray #define ADSP1_CLK_SEL_WIDTH 3 /* CLK_SEL_ENA */ 11794e205bfSChris Rattray 1182d30b575SMark Brown #define ADSP2_CONTROL 0x0 1192d30b575SMark Brown #define ADSP2_CLOCKING 0x1 1202d30b575SMark Brown #define ADSP2_STATUS1 0x4 1212d30b575SMark Brown #define ADSP2_WDMA_CONFIG_1 0x30 1222d30b575SMark Brown #define ADSP2_WDMA_CONFIG_2 0x31 1232d30b575SMark Brown #define ADSP2_RDMA_CONFIG_1 0x34 1242159ad93SMark Brown 12510337b07SRichard Fitzgerald #define ADSP2_SCRATCH0 0x40 12610337b07SRichard Fitzgerald #define ADSP2_SCRATCH1 0x41 12710337b07SRichard Fitzgerald #define ADSP2_SCRATCH2 0x42 12810337b07SRichard Fitzgerald #define ADSP2_SCRATCH3 0x43 12910337b07SRichard Fitzgerald 1302159ad93SMark Brown /* 1312159ad93SMark Brown * ADSP2 Control 1322159ad93SMark Brown */ 1332159ad93SMark Brown 1342159ad93SMark Brown #define ADSP2_MEM_ENA 0x0010 /* DSP1_MEM_ENA */ 1352159ad93SMark Brown #define ADSP2_MEM_ENA_MASK 0x0010 /* DSP1_MEM_ENA */ 1362159ad93SMark Brown #define ADSP2_MEM_ENA_SHIFT 4 /* DSP1_MEM_ENA */ 1372159ad93SMark Brown #define ADSP2_MEM_ENA_WIDTH 1 /* DSP1_MEM_ENA */ 1382159ad93SMark Brown #define ADSP2_SYS_ENA 0x0004 /* DSP1_SYS_ENA */ 1392159ad93SMark Brown #define ADSP2_SYS_ENA_MASK 0x0004 /* DSP1_SYS_ENA */ 1402159ad93SMark Brown #define ADSP2_SYS_ENA_SHIFT 2 /* DSP1_SYS_ENA */ 1412159ad93SMark Brown #define ADSP2_SYS_ENA_WIDTH 1 /* DSP1_SYS_ENA */ 1422159ad93SMark Brown #define ADSP2_CORE_ENA 0x0002 /* DSP1_CORE_ENA */ 1432159ad93SMark Brown #define ADSP2_CORE_ENA_MASK 0x0002 /* DSP1_CORE_ENA */ 1442159ad93SMark Brown #define ADSP2_CORE_ENA_SHIFT 1 /* DSP1_CORE_ENA */ 1452159ad93SMark Brown #define ADSP2_CORE_ENA_WIDTH 1 /* DSP1_CORE_ENA */ 1462159ad93SMark Brown #define ADSP2_START 0x0001 /* DSP1_START */ 1472159ad93SMark Brown #define ADSP2_START_MASK 0x0001 /* DSP1_START */ 1482159ad93SMark Brown #define ADSP2_START_SHIFT 0 /* DSP1_START */ 1492159ad93SMark Brown #define ADSP2_START_WIDTH 1 /* DSP1_START */ 1502159ad93SMark Brown 1512159ad93SMark Brown /* 152973838a0SMark Brown * ADSP2 clocking 153973838a0SMark Brown */ 154973838a0SMark Brown #define ADSP2_CLK_SEL_MASK 0x0007 /* CLK_SEL_ENA */ 155973838a0SMark Brown #define ADSP2_CLK_SEL_SHIFT 0 /* CLK_SEL_ENA */ 156973838a0SMark Brown #define ADSP2_CLK_SEL_WIDTH 3 /* CLK_SEL_ENA */ 157973838a0SMark Brown 158973838a0SMark Brown /* 1592159ad93SMark Brown * ADSP2 Status 1 1602159ad93SMark Brown */ 1612159ad93SMark Brown #define ADSP2_RAM_RDY 0x0001 1622159ad93SMark Brown #define ADSP2_RAM_RDY_MASK 0x0001 1632159ad93SMark Brown #define ADSP2_RAM_RDY_SHIFT 0 1642159ad93SMark Brown #define ADSP2_RAM_RDY_WIDTH 1 1652159ad93SMark Brown 166cf17c83cSMark Brown struct wm_adsp_buf { 167cf17c83cSMark Brown struct list_head list; 168cf17c83cSMark Brown void *buf; 169cf17c83cSMark Brown }; 170cf17c83cSMark Brown 171cf17c83cSMark Brown static struct wm_adsp_buf *wm_adsp_buf_alloc(const void *src, size_t len, 172cf17c83cSMark Brown struct list_head *list) 173cf17c83cSMark Brown { 174cf17c83cSMark Brown struct wm_adsp_buf *buf = kzalloc(sizeof(*buf), GFP_KERNEL); 175cf17c83cSMark Brown 176cf17c83cSMark Brown if (buf == NULL) 177cf17c83cSMark Brown return NULL; 178cf17c83cSMark Brown 179cdcd7f72SCharles Keepax buf->buf = vmalloc(len); 180cf17c83cSMark Brown if (!buf->buf) { 181cdcd7f72SCharles Keepax vfree(buf); 182cf17c83cSMark Brown return NULL; 183cf17c83cSMark Brown } 184cdcd7f72SCharles Keepax memcpy(buf->buf, src, len); 185cf17c83cSMark Brown 186cf17c83cSMark Brown if (list) 187cf17c83cSMark Brown list_add_tail(&buf->list, list); 188cf17c83cSMark Brown 189cf17c83cSMark Brown return buf; 190cf17c83cSMark Brown } 191cf17c83cSMark Brown 192cf17c83cSMark Brown static void wm_adsp_buf_free(struct list_head *list) 193cf17c83cSMark Brown { 194cf17c83cSMark Brown while (!list_empty(list)) { 195cf17c83cSMark Brown struct wm_adsp_buf *buf = list_first_entry(list, 196cf17c83cSMark Brown struct wm_adsp_buf, 197cf17c83cSMark Brown list); 198cf17c83cSMark Brown list_del(&buf->list); 199cdcd7f72SCharles Keepax vfree(buf->buf); 200cf17c83cSMark Brown kfree(buf); 201cf17c83cSMark Brown } 202cf17c83cSMark Brown } 203cf17c83cSMark Brown 204dd84f925SMark Brown #define WM_ADSP_FW_MBC_VSS 0 20504d1300fSCharles Keepax #define WM_ADSP_FW_HIFI 1 20604d1300fSCharles Keepax #define WM_ADSP_FW_TX 2 20704d1300fSCharles Keepax #define WM_ADSP_FW_TX_SPK 3 20804d1300fSCharles Keepax #define WM_ADSP_FW_RX 4 20904d1300fSCharles Keepax #define WM_ADSP_FW_RX_ANC 5 21004d1300fSCharles Keepax #define WM_ADSP_FW_CTRL 6 21104d1300fSCharles Keepax #define WM_ADSP_FW_ASR 7 21204d1300fSCharles Keepax #define WM_ADSP_FW_TRACE 8 21304d1300fSCharles Keepax #define WM_ADSP_FW_SPK_PROT 9 21404d1300fSCharles Keepax #define WM_ADSP_FW_MISC 10 21504d1300fSCharles Keepax 21604d1300fSCharles Keepax #define WM_ADSP_NUM_FW 11 217dd84f925SMark Brown 2181023dbd9SMark Brown static const char *wm_adsp_fw_text[WM_ADSP_NUM_FW] = { 219dd84f925SMark Brown [WM_ADSP_FW_MBC_VSS] = "MBC/VSS", 22004d1300fSCharles Keepax [WM_ADSP_FW_HIFI] = "MasterHiFi", 221dd84f925SMark Brown [WM_ADSP_FW_TX] = "Tx", 222dd84f925SMark Brown [WM_ADSP_FW_TX_SPK] = "Tx Speaker", 22304d1300fSCharles Keepax [WM_ADSP_FW_RX] = "Rx", 224dd84f925SMark Brown [WM_ADSP_FW_RX_ANC] = "Rx ANC", 22504d1300fSCharles Keepax [WM_ADSP_FW_CTRL] = "Voice Ctrl", 22604d1300fSCharles Keepax [WM_ADSP_FW_ASR] = "ASR Assist", 22704d1300fSCharles Keepax [WM_ADSP_FW_TRACE] = "Dbg Trace", 22804d1300fSCharles Keepax [WM_ADSP_FW_SPK_PROT] = "Protection", 22904d1300fSCharles Keepax [WM_ADSP_FW_MISC] = "Misc", 2301023dbd9SMark Brown }; 2311023dbd9SMark Brown 2322cd19bdbSCharles Keepax struct wm_adsp_system_config_xm_hdr { 2332cd19bdbSCharles Keepax __be32 sys_enable; 2342cd19bdbSCharles Keepax __be32 fw_id; 2352cd19bdbSCharles Keepax __be32 fw_rev; 2362cd19bdbSCharles Keepax __be32 boot_status; 2372cd19bdbSCharles Keepax __be32 watchdog; 2382cd19bdbSCharles Keepax __be32 dma_buffer_size; 2392cd19bdbSCharles Keepax __be32 rdma[6]; 2402cd19bdbSCharles Keepax __be32 wdma[8]; 2412cd19bdbSCharles Keepax __be32 build_job_name[3]; 2422cd19bdbSCharles Keepax __be32 build_job_number; 2432cd19bdbSCharles Keepax }; 2442cd19bdbSCharles Keepax 2452cd19bdbSCharles Keepax struct wm_adsp_alg_xm_struct { 2462cd19bdbSCharles Keepax __be32 magic; 2472cd19bdbSCharles Keepax __be32 smoothing; 2482cd19bdbSCharles Keepax __be32 threshold; 2492cd19bdbSCharles Keepax __be32 host_buf_ptr; 2502cd19bdbSCharles Keepax __be32 start_seq; 2512cd19bdbSCharles Keepax __be32 high_water_mark; 2522cd19bdbSCharles Keepax __be32 low_water_mark; 2532cd19bdbSCharles Keepax __be64 smoothed_power; 2542cd19bdbSCharles Keepax }; 2552cd19bdbSCharles Keepax 2562cd19bdbSCharles Keepax struct wm_adsp_buffer { 2572cd19bdbSCharles Keepax __be32 X_buf_base; /* XM base addr of first X area */ 2582cd19bdbSCharles Keepax __be32 X_buf_size; /* Size of 1st X area in words */ 2592cd19bdbSCharles Keepax __be32 X_buf_base2; /* XM base addr of 2nd X area */ 2602cd19bdbSCharles Keepax __be32 X_buf_brk; /* Total X size in words */ 2612cd19bdbSCharles Keepax __be32 Y_buf_base; /* YM base addr of Y area */ 2622cd19bdbSCharles Keepax __be32 wrap; /* Total size X and Y in words */ 2632cd19bdbSCharles Keepax __be32 high_water_mark; /* Point at which IRQ is asserted */ 2642cd19bdbSCharles Keepax __be32 irq_count; /* bits 1-31 count IRQ assertions */ 2652cd19bdbSCharles Keepax __be32 irq_ack; /* acked IRQ count, bit 0 enables IRQ */ 2662cd19bdbSCharles Keepax __be32 next_write_index; /* word index of next write */ 2672cd19bdbSCharles Keepax __be32 next_read_index; /* word index of next read */ 2682cd19bdbSCharles Keepax __be32 error; /* error if any */ 2692cd19bdbSCharles Keepax __be32 oldest_block_index; /* word index of oldest surviving */ 2702cd19bdbSCharles Keepax __be32 requested_rewind; /* how many blocks rewind was done */ 2712cd19bdbSCharles Keepax __be32 reserved_space; /* internal */ 2722cd19bdbSCharles Keepax __be32 min_free; /* min free space since stream start */ 2732cd19bdbSCharles Keepax __be32 blocks_written[2]; /* total blocks written (64 bit) */ 2742cd19bdbSCharles Keepax __be32 words_written[2]; /* total words written (64 bit) */ 2752cd19bdbSCharles Keepax }; 2762cd19bdbSCharles Keepax 2772cd19bdbSCharles Keepax struct wm_adsp_compr_buf { 2782cd19bdbSCharles Keepax struct wm_adsp *dsp; 2792cd19bdbSCharles Keepax 2802cd19bdbSCharles Keepax struct wm_adsp_buffer_region *regions; 2812cd19bdbSCharles Keepax u32 host_buf_ptr; 282565ace46SCharles Keepax 283565ace46SCharles Keepax u32 error; 284565ace46SCharles Keepax u32 irq_count; 285565ace46SCharles Keepax int read_index; 286565ace46SCharles Keepax int avail; 2872cd19bdbSCharles Keepax }; 2882cd19bdbSCharles Keepax 289406abc95SCharles Keepax struct wm_adsp_compr { 290406abc95SCharles Keepax struct wm_adsp *dsp; 29195fe9597SCharles Keepax struct wm_adsp_compr_buf *buf; 292406abc95SCharles Keepax 293406abc95SCharles Keepax struct snd_compr_stream *stream; 294406abc95SCharles Keepax struct snd_compressed_buffer size; 295565ace46SCharles Keepax 29683a40ce9SCharles Keepax u32 *raw_buf; 297565ace46SCharles Keepax unsigned int copied_total; 298406abc95SCharles Keepax }; 299406abc95SCharles Keepax 300406abc95SCharles Keepax #define WM_ADSP_DATA_WORD_SIZE 3 301406abc95SCharles Keepax 302406abc95SCharles Keepax #define WM_ADSP_MIN_FRAGMENTS 1 303406abc95SCharles Keepax #define WM_ADSP_MAX_FRAGMENTS 256 304406abc95SCharles Keepax #define WM_ADSP_MIN_FRAGMENT_SIZE (64 * WM_ADSP_DATA_WORD_SIZE) 305406abc95SCharles Keepax #define WM_ADSP_MAX_FRAGMENT_SIZE (4096 * WM_ADSP_DATA_WORD_SIZE) 306406abc95SCharles Keepax 3072cd19bdbSCharles Keepax #define WM_ADSP_ALG_XM_STRUCT_MAGIC 0x49aec7 3082cd19bdbSCharles Keepax 3092cd19bdbSCharles Keepax #define HOST_BUFFER_FIELD(field) \ 3102cd19bdbSCharles Keepax (offsetof(struct wm_adsp_buffer, field) / sizeof(__be32)) 3112cd19bdbSCharles Keepax 3122cd19bdbSCharles Keepax #define ALG_XM_FIELD(field) \ 3132cd19bdbSCharles Keepax (offsetof(struct wm_adsp_alg_xm_struct, field) / sizeof(__be32)) 3142cd19bdbSCharles Keepax 3152cd19bdbSCharles Keepax static int wm_adsp_buffer_init(struct wm_adsp *dsp); 3162cd19bdbSCharles Keepax static int wm_adsp_buffer_free(struct wm_adsp *dsp); 3172cd19bdbSCharles Keepax 3182cd19bdbSCharles Keepax struct wm_adsp_buffer_region { 3192cd19bdbSCharles Keepax unsigned int offset; 3202cd19bdbSCharles Keepax unsigned int cumulative_size; 3212cd19bdbSCharles Keepax unsigned int mem_type; 3222cd19bdbSCharles Keepax unsigned int base_addr; 3232cd19bdbSCharles Keepax }; 3242cd19bdbSCharles Keepax 3252cd19bdbSCharles Keepax struct wm_adsp_buffer_region_def { 3262cd19bdbSCharles Keepax unsigned int mem_type; 3272cd19bdbSCharles Keepax unsigned int base_offset; 3282cd19bdbSCharles Keepax unsigned int size_offset; 3292cd19bdbSCharles Keepax }; 3302cd19bdbSCharles Keepax 331e6d00f34SCharles Keepax static struct wm_adsp_buffer_region_def default_regions[] = { 3322cd19bdbSCharles Keepax { 3332cd19bdbSCharles Keepax .mem_type = WMFW_ADSP2_XM, 3342cd19bdbSCharles Keepax .base_offset = HOST_BUFFER_FIELD(X_buf_base), 3352cd19bdbSCharles Keepax .size_offset = HOST_BUFFER_FIELD(X_buf_size), 3362cd19bdbSCharles Keepax }, 3372cd19bdbSCharles Keepax { 3382cd19bdbSCharles Keepax .mem_type = WMFW_ADSP2_XM, 3392cd19bdbSCharles Keepax .base_offset = HOST_BUFFER_FIELD(X_buf_base2), 3402cd19bdbSCharles Keepax .size_offset = HOST_BUFFER_FIELD(X_buf_brk), 3412cd19bdbSCharles Keepax }, 3422cd19bdbSCharles Keepax { 3432cd19bdbSCharles Keepax .mem_type = WMFW_ADSP2_YM, 3442cd19bdbSCharles Keepax .base_offset = HOST_BUFFER_FIELD(Y_buf_base), 3452cd19bdbSCharles Keepax .size_offset = HOST_BUFFER_FIELD(wrap), 3462cd19bdbSCharles Keepax }, 3472cd19bdbSCharles Keepax }; 3482cd19bdbSCharles Keepax 349406abc95SCharles Keepax struct wm_adsp_fw_caps { 350406abc95SCharles Keepax u32 id; 351406abc95SCharles Keepax struct snd_codec_desc desc; 3522cd19bdbSCharles Keepax int num_regions; 3532cd19bdbSCharles Keepax struct wm_adsp_buffer_region_def *region_defs; 354406abc95SCharles Keepax }; 355406abc95SCharles Keepax 356e6d00f34SCharles Keepax static const struct wm_adsp_fw_caps ctrl_caps[] = { 357406abc95SCharles Keepax { 358406abc95SCharles Keepax .id = SND_AUDIOCODEC_BESPOKE, 359406abc95SCharles Keepax .desc = { 360406abc95SCharles Keepax .max_ch = 1, 361406abc95SCharles Keepax .sample_rates = { 16000 }, 362406abc95SCharles Keepax .num_sample_rates = 1, 363406abc95SCharles Keepax .formats = SNDRV_PCM_FMTBIT_S16_LE, 364406abc95SCharles Keepax }, 365e6d00f34SCharles Keepax .num_regions = ARRAY_SIZE(default_regions), 366e6d00f34SCharles Keepax .region_defs = default_regions, 367406abc95SCharles Keepax }, 368406abc95SCharles Keepax }; 369406abc95SCharles Keepax 3707ce4283cSCharles Keepax static const struct wm_adsp_fw_caps trace_caps[] = { 3717ce4283cSCharles Keepax { 3727ce4283cSCharles Keepax .id = SND_AUDIOCODEC_BESPOKE, 3737ce4283cSCharles Keepax .desc = { 3747ce4283cSCharles Keepax .max_ch = 8, 3757ce4283cSCharles Keepax .sample_rates = { 3767ce4283cSCharles Keepax 4000, 8000, 11025, 12000, 16000, 22050, 3777ce4283cSCharles Keepax 24000, 32000, 44100, 48000, 64000, 88200, 3787ce4283cSCharles Keepax 96000, 176400, 192000 3797ce4283cSCharles Keepax }, 3807ce4283cSCharles Keepax .num_sample_rates = 15, 3817ce4283cSCharles Keepax .formats = SNDRV_PCM_FMTBIT_S16_LE, 3827ce4283cSCharles Keepax }, 3837ce4283cSCharles Keepax .num_regions = ARRAY_SIZE(default_regions), 3847ce4283cSCharles Keepax .region_defs = default_regions, 3857ce4283cSCharles Keepax }, 3867ce4283cSCharles Keepax }; 3877ce4283cSCharles Keepax 388406abc95SCharles Keepax static const struct { 3891023dbd9SMark Brown const char *file; 390406abc95SCharles Keepax int compr_direction; 391406abc95SCharles Keepax int num_caps; 392406abc95SCharles Keepax const struct wm_adsp_fw_caps *caps; 3931023dbd9SMark Brown } wm_adsp_fw[WM_ADSP_NUM_FW] = { 394dd84f925SMark Brown [WM_ADSP_FW_MBC_VSS] = { .file = "mbc-vss" }, 39504d1300fSCharles Keepax [WM_ADSP_FW_HIFI] = { .file = "hifi" }, 396dd84f925SMark Brown [WM_ADSP_FW_TX] = { .file = "tx" }, 397dd84f925SMark Brown [WM_ADSP_FW_TX_SPK] = { .file = "tx-spk" }, 39804d1300fSCharles Keepax [WM_ADSP_FW_RX] = { .file = "rx" }, 399dd84f925SMark Brown [WM_ADSP_FW_RX_ANC] = { .file = "rx-anc" }, 400406abc95SCharles Keepax [WM_ADSP_FW_CTRL] = { 401406abc95SCharles Keepax .file = "ctrl", 402406abc95SCharles Keepax .compr_direction = SND_COMPRESS_CAPTURE, 403e6d00f34SCharles Keepax .num_caps = ARRAY_SIZE(ctrl_caps), 404e6d00f34SCharles Keepax .caps = ctrl_caps, 405406abc95SCharles Keepax }, 40604d1300fSCharles Keepax [WM_ADSP_FW_ASR] = { .file = "asr" }, 4077ce4283cSCharles Keepax [WM_ADSP_FW_TRACE] = { 4087ce4283cSCharles Keepax .file = "trace", 4097ce4283cSCharles Keepax .compr_direction = SND_COMPRESS_CAPTURE, 4107ce4283cSCharles Keepax .num_caps = ARRAY_SIZE(trace_caps), 4117ce4283cSCharles Keepax .caps = trace_caps, 4127ce4283cSCharles Keepax }, 41304d1300fSCharles Keepax [WM_ADSP_FW_SPK_PROT] = { .file = "spk-prot" }, 41404d1300fSCharles Keepax [WM_ADSP_FW_MISC] = { .file = "misc" }, 4151023dbd9SMark Brown }; 4161023dbd9SMark Brown 4176ab2b7b4SDimitris Papastamos struct wm_coeff_ctl_ops { 4186ab2b7b4SDimitris Papastamos int (*xget)(struct snd_kcontrol *kcontrol, 4196ab2b7b4SDimitris Papastamos struct snd_ctl_elem_value *ucontrol); 4206ab2b7b4SDimitris Papastamos int (*xput)(struct snd_kcontrol *kcontrol, 4216ab2b7b4SDimitris Papastamos struct snd_ctl_elem_value *ucontrol); 4226ab2b7b4SDimitris Papastamos int (*xinfo)(struct snd_kcontrol *kcontrol, 4236ab2b7b4SDimitris Papastamos struct snd_ctl_elem_info *uinfo); 4246ab2b7b4SDimitris Papastamos }; 4256ab2b7b4SDimitris Papastamos 4266ab2b7b4SDimitris Papastamos struct wm_coeff_ctl { 4276ab2b7b4SDimitris Papastamos const char *name; 4282323736dSCharles Keepax const char *fw_name; 4293809f001SCharles Keepax struct wm_adsp_alg_region alg_region; 4306ab2b7b4SDimitris Papastamos struct wm_coeff_ctl_ops ops; 4313809f001SCharles Keepax struct wm_adsp *dsp; 4326ab2b7b4SDimitris Papastamos unsigned int enabled:1; 4336ab2b7b4SDimitris Papastamos struct list_head list; 4346ab2b7b4SDimitris Papastamos void *cache; 4352323736dSCharles Keepax unsigned int offset; 4366ab2b7b4SDimitris Papastamos size_t len; 4370c2e3f34SDimitris Papastamos unsigned int set:1; 4386ab2b7b4SDimitris Papastamos struct snd_kcontrol *kcontrol; 43926c22a19SCharles Keepax unsigned int flags; 4406ab2b7b4SDimitris Papastamos }; 4416ab2b7b4SDimitris Papastamos 442f9f55e31SRichard Fitzgerald #ifdef CONFIG_DEBUG_FS 443f9f55e31SRichard Fitzgerald static void wm_adsp_debugfs_save_wmfwname(struct wm_adsp *dsp, const char *s) 444f9f55e31SRichard Fitzgerald { 445f9f55e31SRichard Fitzgerald char *tmp = kasprintf(GFP_KERNEL, "%s\n", s); 446f9f55e31SRichard Fitzgerald 447f9f55e31SRichard Fitzgerald kfree(dsp->wmfw_file_name); 448f9f55e31SRichard Fitzgerald dsp->wmfw_file_name = tmp; 449f9f55e31SRichard Fitzgerald } 450f9f55e31SRichard Fitzgerald 451f9f55e31SRichard Fitzgerald static void wm_adsp_debugfs_save_binname(struct wm_adsp *dsp, const char *s) 452f9f55e31SRichard Fitzgerald { 453f9f55e31SRichard Fitzgerald char *tmp = kasprintf(GFP_KERNEL, "%s\n", s); 454f9f55e31SRichard Fitzgerald 455f9f55e31SRichard Fitzgerald kfree(dsp->bin_file_name); 456f9f55e31SRichard Fitzgerald dsp->bin_file_name = tmp; 457f9f55e31SRichard Fitzgerald } 458f9f55e31SRichard Fitzgerald 459f9f55e31SRichard Fitzgerald static void wm_adsp_debugfs_clear(struct wm_adsp *dsp) 460f9f55e31SRichard Fitzgerald { 461f9f55e31SRichard Fitzgerald kfree(dsp->wmfw_file_name); 462f9f55e31SRichard Fitzgerald kfree(dsp->bin_file_name); 463f9f55e31SRichard Fitzgerald dsp->wmfw_file_name = NULL; 464f9f55e31SRichard Fitzgerald dsp->bin_file_name = NULL; 465f9f55e31SRichard Fitzgerald } 466f9f55e31SRichard Fitzgerald 467f9f55e31SRichard Fitzgerald static ssize_t wm_adsp_debugfs_wmfw_read(struct file *file, 468f9f55e31SRichard Fitzgerald char __user *user_buf, 469f9f55e31SRichard Fitzgerald size_t count, loff_t *ppos) 470f9f55e31SRichard Fitzgerald { 471f9f55e31SRichard Fitzgerald struct wm_adsp *dsp = file->private_data; 472f9f55e31SRichard Fitzgerald ssize_t ret; 473f9f55e31SRichard Fitzgerald 474078e7183SCharles Keepax mutex_lock(&dsp->pwr_lock); 475f9f55e31SRichard Fitzgerald 476f9f55e31SRichard Fitzgerald if (!dsp->wmfw_file_name || !dsp->running) 477f9f55e31SRichard Fitzgerald ret = 0; 478f9f55e31SRichard Fitzgerald else 479f9f55e31SRichard Fitzgerald ret = simple_read_from_buffer(user_buf, count, ppos, 480f9f55e31SRichard Fitzgerald dsp->wmfw_file_name, 481f9f55e31SRichard Fitzgerald strlen(dsp->wmfw_file_name)); 482f9f55e31SRichard Fitzgerald 483078e7183SCharles Keepax mutex_unlock(&dsp->pwr_lock); 484f9f55e31SRichard Fitzgerald return ret; 485f9f55e31SRichard Fitzgerald } 486f9f55e31SRichard Fitzgerald 487f9f55e31SRichard Fitzgerald static ssize_t wm_adsp_debugfs_bin_read(struct file *file, 488f9f55e31SRichard Fitzgerald char __user *user_buf, 489f9f55e31SRichard Fitzgerald size_t count, loff_t *ppos) 490f9f55e31SRichard Fitzgerald { 491f9f55e31SRichard Fitzgerald struct wm_adsp *dsp = file->private_data; 492f9f55e31SRichard Fitzgerald ssize_t ret; 493f9f55e31SRichard Fitzgerald 494078e7183SCharles Keepax mutex_lock(&dsp->pwr_lock); 495f9f55e31SRichard Fitzgerald 496f9f55e31SRichard Fitzgerald if (!dsp->bin_file_name || !dsp->running) 497f9f55e31SRichard Fitzgerald ret = 0; 498f9f55e31SRichard Fitzgerald else 499f9f55e31SRichard Fitzgerald ret = simple_read_from_buffer(user_buf, count, ppos, 500f9f55e31SRichard Fitzgerald dsp->bin_file_name, 501f9f55e31SRichard Fitzgerald strlen(dsp->bin_file_name)); 502f9f55e31SRichard Fitzgerald 503078e7183SCharles Keepax mutex_unlock(&dsp->pwr_lock); 504f9f55e31SRichard Fitzgerald return ret; 505f9f55e31SRichard Fitzgerald } 506f9f55e31SRichard Fitzgerald 507f9f55e31SRichard Fitzgerald static const struct { 508f9f55e31SRichard Fitzgerald const char *name; 509f9f55e31SRichard Fitzgerald const struct file_operations fops; 510f9f55e31SRichard Fitzgerald } wm_adsp_debugfs_fops[] = { 511f9f55e31SRichard Fitzgerald { 512f9f55e31SRichard Fitzgerald .name = "wmfw_file_name", 513f9f55e31SRichard Fitzgerald .fops = { 514f9f55e31SRichard Fitzgerald .open = simple_open, 515f9f55e31SRichard Fitzgerald .read = wm_adsp_debugfs_wmfw_read, 516f9f55e31SRichard Fitzgerald }, 517f9f55e31SRichard Fitzgerald }, 518f9f55e31SRichard Fitzgerald { 519f9f55e31SRichard Fitzgerald .name = "bin_file_name", 520f9f55e31SRichard Fitzgerald .fops = { 521f9f55e31SRichard Fitzgerald .open = simple_open, 522f9f55e31SRichard Fitzgerald .read = wm_adsp_debugfs_bin_read, 523f9f55e31SRichard Fitzgerald }, 524f9f55e31SRichard Fitzgerald }, 525f9f55e31SRichard Fitzgerald }; 526f9f55e31SRichard Fitzgerald 527f9f55e31SRichard Fitzgerald static void wm_adsp2_init_debugfs(struct wm_adsp *dsp, 528f9f55e31SRichard Fitzgerald struct snd_soc_codec *codec) 529f9f55e31SRichard Fitzgerald { 530f9f55e31SRichard Fitzgerald struct dentry *root = NULL; 531f9f55e31SRichard Fitzgerald char *root_name; 532f9f55e31SRichard Fitzgerald int i; 533f9f55e31SRichard Fitzgerald 534f9f55e31SRichard Fitzgerald if (!codec->component.debugfs_root) { 535f9f55e31SRichard Fitzgerald adsp_err(dsp, "No codec debugfs root\n"); 536f9f55e31SRichard Fitzgerald goto err; 537f9f55e31SRichard Fitzgerald } 538f9f55e31SRichard Fitzgerald 539f9f55e31SRichard Fitzgerald root_name = kmalloc(PAGE_SIZE, GFP_KERNEL); 540f9f55e31SRichard Fitzgerald if (!root_name) 541f9f55e31SRichard Fitzgerald goto err; 542f9f55e31SRichard Fitzgerald 543f9f55e31SRichard Fitzgerald snprintf(root_name, PAGE_SIZE, "dsp%d", dsp->num); 544f9f55e31SRichard Fitzgerald root = debugfs_create_dir(root_name, codec->component.debugfs_root); 545f9f55e31SRichard Fitzgerald kfree(root_name); 546f9f55e31SRichard Fitzgerald 547f9f55e31SRichard Fitzgerald if (!root) 548f9f55e31SRichard Fitzgerald goto err; 549f9f55e31SRichard Fitzgerald 550f9f55e31SRichard Fitzgerald if (!debugfs_create_bool("running", S_IRUGO, root, &dsp->running)) 551f9f55e31SRichard Fitzgerald goto err; 552f9f55e31SRichard Fitzgerald 553f9f55e31SRichard Fitzgerald if (!debugfs_create_x32("fw_id", S_IRUGO, root, &dsp->fw_id)) 554f9f55e31SRichard Fitzgerald goto err; 555f9f55e31SRichard Fitzgerald 556f9f55e31SRichard Fitzgerald if (!debugfs_create_x32("fw_version", S_IRUGO, root, 557f9f55e31SRichard Fitzgerald &dsp->fw_id_version)) 558f9f55e31SRichard Fitzgerald goto err; 559f9f55e31SRichard Fitzgerald 560f9f55e31SRichard Fitzgerald for (i = 0; i < ARRAY_SIZE(wm_adsp_debugfs_fops); ++i) { 561f9f55e31SRichard Fitzgerald if (!debugfs_create_file(wm_adsp_debugfs_fops[i].name, 562f9f55e31SRichard Fitzgerald S_IRUGO, root, dsp, 563f9f55e31SRichard Fitzgerald &wm_adsp_debugfs_fops[i].fops)) 564f9f55e31SRichard Fitzgerald goto err; 565f9f55e31SRichard Fitzgerald } 566f9f55e31SRichard Fitzgerald 567f9f55e31SRichard Fitzgerald dsp->debugfs_root = root; 568f9f55e31SRichard Fitzgerald return; 569f9f55e31SRichard Fitzgerald 570f9f55e31SRichard Fitzgerald err: 571f9f55e31SRichard Fitzgerald debugfs_remove_recursive(root); 572f9f55e31SRichard Fitzgerald adsp_err(dsp, "Failed to create debugfs\n"); 573f9f55e31SRichard Fitzgerald } 574f9f55e31SRichard Fitzgerald 575f9f55e31SRichard Fitzgerald static void wm_adsp2_cleanup_debugfs(struct wm_adsp *dsp) 576f9f55e31SRichard Fitzgerald { 577f9f55e31SRichard Fitzgerald wm_adsp_debugfs_clear(dsp); 578f9f55e31SRichard Fitzgerald debugfs_remove_recursive(dsp->debugfs_root); 579f9f55e31SRichard Fitzgerald } 580f9f55e31SRichard Fitzgerald #else 581f9f55e31SRichard Fitzgerald static inline void wm_adsp2_init_debugfs(struct wm_adsp *dsp, 582f9f55e31SRichard Fitzgerald struct snd_soc_codec *codec) 583f9f55e31SRichard Fitzgerald { 584f9f55e31SRichard Fitzgerald } 585f9f55e31SRichard Fitzgerald 586f9f55e31SRichard Fitzgerald static inline void wm_adsp2_cleanup_debugfs(struct wm_adsp *dsp) 587f9f55e31SRichard Fitzgerald { 588f9f55e31SRichard Fitzgerald } 589f9f55e31SRichard Fitzgerald 590f9f55e31SRichard Fitzgerald static inline void wm_adsp_debugfs_save_wmfwname(struct wm_adsp *dsp, 591f9f55e31SRichard Fitzgerald const char *s) 592f9f55e31SRichard Fitzgerald { 593f9f55e31SRichard Fitzgerald } 594f9f55e31SRichard Fitzgerald 595f9f55e31SRichard Fitzgerald static inline void wm_adsp_debugfs_save_binname(struct wm_adsp *dsp, 596f9f55e31SRichard Fitzgerald const char *s) 597f9f55e31SRichard Fitzgerald { 598f9f55e31SRichard Fitzgerald } 599f9f55e31SRichard Fitzgerald 600f9f55e31SRichard Fitzgerald static inline void wm_adsp_debugfs_clear(struct wm_adsp *dsp) 601f9f55e31SRichard Fitzgerald { 602f9f55e31SRichard Fitzgerald } 603f9f55e31SRichard Fitzgerald #endif 604f9f55e31SRichard Fitzgerald 6051023dbd9SMark Brown static int wm_adsp_fw_get(struct snd_kcontrol *kcontrol, 6061023dbd9SMark Brown struct snd_ctl_elem_value *ucontrol) 6071023dbd9SMark Brown { 608ea53bf77SLars-Peter Clausen struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); 6091023dbd9SMark Brown struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; 6103809f001SCharles Keepax struct wm_adsp *dsp = snd_soc_codec_get_drvdata(codec); 6111023dbd9SMark Brown 6123809f001SCharles Keepax ucontrol->value.integer.value[0] = dsp[e->shift_l].fw; 6131023dbd9SMark Brown 6141023dbd9SMark Brown return 0; 6151023dbd9SMark Brown } 6161023dbd9SMark Brown 6171023dbd9SMark Brown static int wm_adsp_fw_put(struct snd_kcontrol *kcontrol, 6181023dbd9SMark Brown struct snd_ctl_elem_value *ucontrol) 6191023dbd9SMark Brown { 620ea53bf77SLars-Peter Clausen struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); 6211023dbd9SMark Brown struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; 6223809f001SCharles Keepax struct wm_adsp *dsp = snd_soc_codec_get_drvdata(codec); 623d27c5e15SCharles Keepax int ret = 0; 6241023dbd9SMark Brown 6253809f001SCharles Keepax if (ucontrol->value.integer.value[0] == dsp[e->shift_l].fw) 6261023dbd9SMark Brown return 0; 6271023dbd9SMark Brown 6281023dbd9SMark Brown if (ucontrol->value.integer.value[0] >= WM_ADSP_NUM_FW) 6291023dbd9SMark Brown return -EINVAL; 6301023dbd9SMark Brown 631d27c5e15SCharles Keepax mutex_lock(&dsp[e->shift_l].pwr_lock); 6321023dbd9SMark Brown 633406abc95SCharles Keepax if (dsp[e->shift_l].running || dsp[e->shift_l].compr) 634d27c5e15SCharles Keepax ret = -EBUSY; 635d27c5e15SCharles Keepax else 6363809f001SCharles Keepax dsp[e->shift_l].fw = ucontrol->value.integer.value[0]; 6371023dbd9SMark Brown 638d27c5e15SCharles Keepax mutex_unlock(&dsp[e->shift_l].pwr_lock); 639d27c5e15SCharles Keepax 640d27c5e15SCharles Keepax return ret; 6411023dbd9SMark Brown } 6421023dbd9SMark Brown 6431023dbd9SMark Brown static const struct soc_enum wm_adsp_fw_enum[] = { 6441023dbd9SMark Brown SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 6451023dbd9SMark Brown SOC_ENUM_SINGLE(0, 1, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 6461023dbd9SMark Brown SOC_ENUM_SINGLE(0, 2, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 6471023dbd9SMark Brown SOC_ENUM_SINGLE(0, 3, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 6481023dbd9SMark Brown }; 6491023dbd9SMark Brown 650336d0442SRichard Fitzgerald const struct snd_kcontrol_new wm_adsp_fw_controls[] = { 6511023dbd9SMark Brown SOC_ENUM_EXT("DSP1 Firmware", wm_adsp_fw_enum[0], 6521023dbd9SMark Brown wm_adsp_fw_get, wm_adsp_fw_put), 6531023dbd9SMark Brown SOC_ENUM_EXT("DSP2 Firmware", wm_adsp_fw_enum[1], 6541023dbd9SMark Brown wm_adsp_fw_get, wm_adsp_fw_put), 6551023dbd9SMark Brown SOC_ENUM_EXT("DSP3 Firmware", wm_adsp_fw_enum[2], 6561023dbd9SMark Brown wm_adsp_fw_get, wm_adsp_fw_put), 6571023dbd9SMark Brown SOC_ENUM_EXT("DSP4 Firmware", wm_adsp_fw_enum[3], 6581023dbd9SMark Brown wm_adsp_fw_get, wm_adsp_fw_put), 6591023dbd9SMark Brown }; 660336d0442SRichard Fitzgerald EXPORT_SYMBOL_GPL(wm_adsp_fw_controls); 6612159ad93SMark Brown 6622159ad93SMark Brown static struct wm_adsp_region const *wm_adsp_find_region(struct wm_adsp *dsp, 6632159ad93SMark Brown int type) 6642159ad93SMark Brown { 6652159ad93SMark Brown int i; 6662159ad93SMark Brown 6672159ad93SMark Brown for (i = 0; i < dsp->num_mems; i++) 6682159ad93SMark Brown if (dsp->mem[i].type == type) 6692159ad93SMark Brown return &dsp->mem[i]; 6702159ad93SMark Brown 6712159ad93SMark Brown return NULL; 6722159ad93SMark Brown } 6732159ad93SMark Brown 6743809f001SCharles Keepax static unsigned int wm_adsp_region_to_reg(struct wm_adsp_region const *mem, 67545b9ee72SMark Brown unsigned int offset) 67645b9ee72SMark Brown { 6773809f001SCharles Keepax if (WARN_ON(!mem)) 6786c452bdaSTakashi Iwai return offset; 6793809f001SCharles Keepax switch (mem->type) { 68045b9ee72SMark Brown case WMFW_ADSP1_PM: 6813809f001SCharles Keepax return mem->base + (offset * 3); 68245b9ee72SMark Brown case WMFW_ADSP1_DM: 6833809f001SCharles Keepax return mem->base + (offset * 2); 68445b9ee72SMark Brown case WMFW_ADSP2_XM: 6853809f001SCharles Keepax return mem->base + (offset * 2); 68645b9ee72SMark Brown case WMFW_ADSP2_YM: 6873809f001SCharles Keepax return mem->base + (offset * 2); 68845b9ee72SMark Brown case WMFW_ADSP1_ZM: 6893809f001SCharles Keepax return mem->base + (offset * 2); 69045b9ee72SMark Brown default: 6916c452bdaSTakashi Iwai WARN(1, "Unknown memory region type"); 69245b9ee72SMark Brown return offset; 69345b9ee72SMark Brown } 69445b9ee72SMark Brown } 69545b9ee72SMark Brown 69610337b07SRichard Fitzgerald static void wm_adsp2_show_fw_status(struct wm_adsp *dsp) 69710337b07SRichard Fitzgerald { 69810337b07SRichard Fitzgerald u16 scratch[4]; 69910337b07SRichard Fitzgerald int ret; 70010337b07SRichard Fitzgerald 70110337b07SRichard Fitzgerald ret = regmap_raw_read(dsp->regmap, dsp->base + ADSP2_SCRATCH0, 70210337b07SRichard Fitzgerald scratch, sizeof(scratch)); 70310337b07SRichard Fitzgerald if (ret) { 70410337b07SRichard Fitzgerald adsp_err(dsp, "Failed to read SCRATCH regs: %d\n", ret); 70510337b07SRichard Fitzgerald return; 70610337b07SRichard Fitzgerald } 70710337b07SRichard Fitzgerald 70810337b07SRichard Fitzgerald adsp_dbg(dsp, "FW SCRATCH 0:0x%x 1:0x%x 2:0x%x 3:0x%x\n", 70910337b07SRichard Fitzgerald be16_to_cpu(scratch[0]), 71010337b07SRichard Fitzgerald be16_to_cpu(scratch[1]), 71110337b07SRichard Fitzgerald be16_to_cpu(scratch[2]), 71210337b07SRichard Fitzgerald be16_to_cpu(scratch[3])); 71310337b07SRichard Fitzgerald } 71410337b07SRichard Fitzgerald 7157585a5b0SCharles Keepax static int wm_coeff_info(struct snd_kcontrol *kctl, 7166ab2b7b4SDimitris Papastamos struct snd_ctl_elem_info *uinfo) 7176ab2b7b4SDimitris Papastamos { 7187585a5b0SCharles Keepax struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kctl->private_value; 7196ab2b7b4SDimitris Papastamos 7206ab2b7b4SDimitris Papastamos uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; 7216ab2b7b4SDimitris Papastamos uinfo->count = ctl->len; 7226ab2b7b4SDimitris Papastamos return 0; 7236ab2b7b4SDimitris Papastamos } 7246ab2b7b4SDimitris Papastamos 725c9f8dd71SCharles Keepax static int wm_coeff_write_control(struct wm_coeff_ctl *ctl, 7266ab2b7b4SDimitris Papastamos const void *buf, size_t len) 7276ab2b7b4SDimitris Papastamos { 7283809f001SCharles Keepax struct wm_adsp_alg_region *alg_region = &ctl->alg_region; 7296ab2b7b4SDimitris Papastamos const struct wm_adsp_region *mem; 7303809f001SCharles Keepax struct wm_adsp *dsp = ctl->dsp; 7316ab2b7b4SDimitris Papastamos void *scratch; 7326ab2b7b4SDimitris Papastamos int ret; 7336ab2b7b4SDimitris Papastamos unsigned int reg; 7346ab2b7b4SDimitris Papastamos 7353809f001SCharles Keepax mem = wm_adsp_find_region(dsp, alg_region->type); 7366ab2b7b4SDimitris Papastamos if (!mem) { 7373809f001SCharles Keepax adsp_err(dsp, "No base for region %x\n", 7383809f001SCharles Keepax alg_region->type); 7396ab2b7b4SDimitris Papastamos return -EINVAL; 7406ab2b7b4SDimitris Papastamos } 7416ab2b7b4SDimitris Papastamos 7422323736dSCharles Keepax reg = ctl->alg_region.base + ctl->offset; 7436ab2b7b4SDimitris Papastamos reg = wm_adsp_region_to_reg(mem, reg); 7446ab2b7b4SDimitris Papastamos 7456ab2b7b4SDimitris Papastamos scratch = kmemdup(buf, ctl->len, GFP_KERNEL | GFP_DMA); 7466ab2b7b4SDimitris Papastamos if (!scratch) 7476ab2b7b4SDimitris Papastamos return -ENOMEM; 7486ab2b7b4SDimitris Papastamos 7493809f001SCharles Keepax ret = regmap_raw_write(dsp->regmap, reg, scratch, 7506ab2b7b4SDimitris Papastamos ctl->len); 7516ab2b7b4SDimitris Papastamos if (ret) { 7523809f001SCharles Keepax adsp_err(dsp, "Failed to write %zu bytes to %x: %d\n", 75343bc3bf6SDimitris Papastamos ctl->len, reg, ret); 7546ab2b7b4SDimitris Papastamos kfree(scratch); 7556ab2b7b4SDimitris Papastamos return ret; 7566ab2b7b4SDimitris Papastamos } 7573809f001SCharles Keepax adsp_dbg(dsp, "Wrote %zu bytes to %x\n", ctl->len, reg); 7586ab2b7b4SDimitris Papastamos 7596ab2b7b4SDimitris Papastamos kfree(scratch); 7606ab2b7b4SDimitris Papastamos 7616ab2b7b4SDimitris Papastamos return 0; 7626ab2b7b4SDimitris Papastamos } 7636ab2b7b4SDimitris Papastamos 7647585a5b0SCharles Keepax static int wm_coeff_put(struct snd_kcontrol *kctl, 7656ab2b7b4SDimitris Papastamos struct snd_ctl_elem_value *ucontrol) 7666ab2b7b4SDimitris Papastamos { 7677585a5b0SCharles Keepax struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kctl->private_value; 7686ab2b7b4SDimitris Papastamos char *p = ucontrol->value.bytes.data; 769168d10e7SCharles Keepax int ret = 0; 770168d10e7SCharles Keepax 771168d10e7SCharles Keepax mutex_lock(&ctl->dsp->pwr_lock); 7726ab2b7b4SDimitris Papastamos 7736ab2b7b4SDimitris Papastamos memcpy(ctl->cache, p, ctl->len); 7746ab2b7b4SDimitris Papastamos 7750c2e3f34SDimitris Papastamos ctl->set = 1; 776168d10e7SCharles Keepax if (ctl->enabled) 777168d10e7SCharles Keepax ret = wm_coeff_write_control(ctl, p, ctl->len); 7786ab2b7b4SDimitris Papastamos 779168d10e7SCharles Keepax mutex_unlock(&ctl->dsp->pwr_lock); 780168d10e7SCharles Keepax 781168d10e7SCharles Keepax return ret; 7826ab2b7b4SDimitris Papastamos } 7836ab2b7b4SDimitris Papastamos 784c9f8dd71SCharles Keepax static int wm_coeff_read_control(struct wm_coeff_ctl *ctl, 7856ab2b7b4SDimitris Papastamos void *buf, size_t len) 7866ab2b7b4SDimitris Papastamos { 7873809f001SCharles Keepax struct wm_adsp_alg_region *alg_region = &ctl->alg_region; 7886ab2b7b4SDimitris Papastamos const struct wm_adsp_region *mem; 7893809f001SCharles Keepax struct wm_adsp *dsp = ctl->dsp; 7906ab2b7b4SDimitris Papastamos void *scratch; 7916ab2b7b4SDimitris Papastamos int ret; 7926ab2b7b4SDimitris Papastamos unsigned int reg; 7936ab2b7b4SDimitris Papastamos 7943809f001SCharles Keepax mem = wm_adsp_find_region(dsp, alg_region->type); 7956ab2b7b4SDimitris Papastamos if (!mem) { 7963809f001SCharles Keepax adsp_err(dsp, "No base for region %x\n", 7973809f001SCharles Keepax alg_region->type); 7986ab2b7b4SDimitris Papastamos return -EINVAL; 7996ab2b7b4SDimitris Papastamos } 8006ab2b7b4SDimitris Papastamos 8012323736dSCharles Keepax reg = ctl->alg_region.base + ctl->offset; 8026ab2b7b4SDimitris Papastamos reg = wm_adsp_region_to_reg(mem, reg); 8036ab2b7b4SDimitris Papastamos 8046ab2b7b4SDimitris Papastamos scratch = kmalloc(ctl->len, GFP_KERNEL | GFP_DMA); 8056ab2b7b4SDimitris Papastamos if (!scratch) 8066ab2b7b4SDimitris Papastamos return -ENOMEM; 8076ab2b7b4SDimitris Papastamos 8083809f001SCharles Keepax ret = regmap_raw_read(dsp->regmap, reg, scratch, ctl->len); 8096ab2b7b4SDimitris Papastamos if (ret) { 8103809f001SCharles Keepax adsp_err(dsp, "Failed to read %zu bytes from %x: %d\n", 81143bc3bf6SDimitris Papastamos ctl->len, reg, ret); 8126ab2b7b4SDimitris Papastamos kfree(scratch); 8136ab2b7b4SDimitris Papastamos return ret; 8146ab2b7b4SDimitris Papastamos } 8153809f001SCharles Keepax adsp_dbg(dsp, "Read %zu bytes from %x\n", ctl->len, reg); 8166ab2b7b4SDimitris Papastamos 8176ab2b7b4SDimitris Papastamos memcpy(buf, scratch, ctl->len); 8186ab2b7b4SDimitris Papastamos kfree(scratch); 8196ab2b7b4SDimitris Papastamos 8206ab2b7b4SDimitris Papastamos return 0; 8216ab2b7b4SDimitris Papastamos } 8226ab2b7b4SDimitris Papastamos 8237585a5b0SCharles Keepax static int wm_coeff_get(struct snd_kcontrol *kctl, 8246ab2b7b4SDimitris Papastamos struct snd_ctl_elem_value *ucontrol) 8256ab2b7b4SDimitris Papastamos { 8267585a5b0SCharles Keepax struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kctl->private_value; 8276ab2b7b4SDimitris Papastamos char *p = ucontrol->value.bytes.data; 828168d10e7SCharles Keepax int ret = 0; 829168d10e7SCharles Keepax 830168d10e7SCharles Keepax mutex_lock(&ctl->dsp->pwr_lock); 8316ab2b7b4SDimitris Papastamos 83226c22a19SCharles Keepax if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) { 83326c22a19SCharles Keepax if (ctl->enabled) 834168d10e7SCharles Keepax ret = wm_coeff_read_control(ctl, p, ctl->len); 83526c22a19SCharles Keepax else 836168d10e7SCharles Keepax ret = -EPERM; 837168d10e7SCharles Keepax } else { 838bc1765d6SCharles Keepax if (!ctl->flags && ctl->enabled) 839bc1765d6SCharles Keepax ret = wm_coeff_read_control(ctl, ctl->cache, ctl->len); 840bc1765d6SCharles Keepax 841168d10e7SCharles Keepax memcpy(p, ctl->cache, ctl->len); 84226c22a19SCharles Keepax } 84326c22a19SCharles Keepax 844168d10e7SCharles Keepax mutex_unlock(&ctl->dsp->pwr_lock); 84526c22a19SCharles Keepax 846168d10e7SCharles Keepax return ret; 8476ab2b7b4SDimitris Papastamos } 8486ab2b7b4SDimitris Papastamos 8496ab2b7b4SDimitris Papastamos struct wmfw_ctl_work { 8503809f001SCharles Keepax struct wm_adsp *dsp; 8516ab2b7b4SDimitris Papastamos struct wm_coeff_ctl *ctl; 8526ab2b7b4SDimitris Papastamos struct work_struct work; 8536ab2b7b4SDimitris Papastamos }; 8546ab2b7b4SDimitris Papastamos 8553809f001SCharles Keepax static int wmfw_add_ctl(struct wm_adsp *dsp, struct wm_coeff_ctl *ctl) 8566ab2b7b4SDimitris Papastamos { 8576ab2b7b4SDimitris Papastamos struct snd_kcontrol_new *kcontrol; 8586ab2b7b4SDimitris Papastamos int ret; 8596ab2b7b4SDimitris Papastamos 86092bb4c32SDimitris Papastamos if (!ctl || !ctl->name) 8616ab2b7b4SDimitris Papastamos return -EINVAL; 8626ab2b7b4SDimitris Papastamos 8636ab2b7b4SDimitris Papastamos kcontrol = kzalloc(sizeof(*kcontrol), GFP_KERNEL); 8646ab2b7b4SDimitris Papastamos if (!kcontrol) 8656ab2b7b4SDimitris Papastamos return -ENOMEM; 8666ab2b7b4SDimitris Papastamos kcontrol->iface = SNDRV_CTL_ELEM_IFACE_MIXER; 8676ab2b7b4SDimitris Papastamos 8686ab2b7b4SDimitris Papastamos kcontrol->name = ctl->name; 8696ab2b7b4SDimitris Papastamos kcontrol->info = wm_coeff_info; 8706ab2b7b4SDimitris Papastamos kcontrol->get = wm_coeff_get; 8716ab2b7b4SDimitris Papastamos kcontrol->put = wm_coeff_put; 8726ab2b7b4SDimitris Papastamos kcontrol->private_value = (unsigned long)ctl; 8736ab2b7b4SDimitris Papastamos 87426c22a19SCharles Keepax if (ctl->flags) { 87526c22a19SCharles Keepax if (ctl->flags & WMFW_CTL_FLAG_WRITEABLE) 87626c22a19SCharles Keepax kcontrol->access |= SNDRV_CTL_ELEM_ACCESS_WRITE; 87726c22a19SCharles Keepax if (ctl->flags & WMFW_CTL_FLAG_READABLE) 87826c22a19SCharles Keepax kcontrol->access |= SNDRV_CTL_ELEM_ACCESS_READ; 87926c22a19SCharles Keepax if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) 88026c22a19SCharles Keepax kcontrol->access |= SNDRV_CTL_ELEM_ACCESS_VOLATILE; 88126c22a19SCharles Keepax } 88226c22a19SCharles Keepax 8833809f001SCharles Keepax ret = snd_soc_add_card_controls(dsp->card, 88481ad93ecSDimitris Papastamos kcontrol, 1); 8856ab2b7b4SDimitris Papastamos if (ret < 0) 8866ab2b7b4SDimitris Papastamos goto err_kcontrol; 8876ab2b7b4SDimitris Papastamos 8886ab2b7b4SDimitris Papastamos kfree(kcontrol); 8896ab2b7b4SDimitris Papastamos 8903809f001SCharles Keepax ctl->kcontrol = snd_soc_card_get_kcontrol(dsp->card, 89181ad93ecSDimitris Papastamos ctl->name); 89281ad93ecSDimitris Papastamos 8936ab2b7b4SDimitris Papastamos return 0; 8946ab2b7b4SDimitris Papastamos 8956ab2b7b4SDimitris Papastamos err_kcontrol: 8966ab2b7b4SDimitris Papastamos kfree(kcontrol); 8976ab2b7b4SDimitris Papastamos return ret; 8986ab2b7b4SDimitris Papastamos } 8996ab2b7b4SDimitris Papastamos 900b21acc1cSCharles Keepax static int wm_coeff_init_control_caches(struct wm_adsp *dsp) 901b21acc1cSCharles Keepax { 902b21acc1cSCharles Keepax struct wm_coeff_ctl *ctl; 903b21acc1cSCharles Keepax int ret; 904b21acc1cSCharles Keepax 905b21acc1cSCharles Keepax list_for_each_entry(ctl, &dsp->ctl_list, list) { 906b21acc1cSCharles Keepax if (!ctl->enabled || ctl->set) 907b21acc1cSCharles Keepax continue; 90826c22a19SCharles Keepax if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) 90926c22a19SCharles Keepax continue; 91026c22a19SCharles Keepax 911b21acc1cSCharles Keepax ret = wm_coeff_read_control(ctl, 912b21acc1cSCharles Keepax ctl->cache, 913b21acc1cSCharles Keepax ctl->len); 914b21acc1cSCharles Keepax if (ret < 0) 915b21acc1cSCharles Keepax return ret; 916b21acc1cSCharles Keepax } 917b21acc1cSCharles Keepax 918b21acc1cSCharles Keepax return 0; 919b21acc1cSCharles Keepax } 920b21acc1cSCharles Keepax 921b21acc1cSCharles Keepax static int wm_coeff_sync_controls(struct wm_adsp *dsp) 922b21acc1cSCharles Keepax { 923b21acc1cSCharles Keepax struct wm_coeff_ctl *ctl; 924b21acc1cSCharles Keepax int ret; 925b21acc1cSCharles Keepax 926b21acc1cSCharles Keepax list_for_each_entry(ctl, &dsp->ctl_list, list) { 927b21acc1cSCharles Keepax if (!ctl->enabled) 928b21acc1cSCharles Keepax continue; 92926c22a19SCharles Keepax if (ctl->set && !(ctl->flags & WMFW_CTL_FLAG_VOLATILE)) { 930b21acc1cSCharles Keepax ret = wm_coeff_write_control(ctl, 931b21acc1cSCharles Keepax ctl->cache, 932b21acc1cSCharles Keepax ctl->len); 933b21acc1cSCharles Keepax if (ret < 0) 934b21acc1cSCharles Keepax return ret; 935b21acc1cSCharles Keepax } 936b21acc1cSCharles Keepax } 937b21acc1cSCharles Keepax 938b21acc1cSCharles Keepax return 0; 939b21acc1cSCharles Keepax } 940b21acc1cSCharles Keepax 941b21acc1cSCharles Keepax static void wm_adsp_ctl_work(struct work_struct *work) 942b21acc1cSCharles Keepax { 943b21acc1cSCharles Keepax struct wmfw_ctl_work *ctl_work = container_of(work, 944b21acc1cSCharles Keepax struct wmfw_ctl_work, 945b21acc1cSCharles Keepax work); 946b21acc1cSCharles Keepax 947b21acc1cSCharles Keepax wmfw_add_ctl(ctl_work->dsp, ctl_work->ctl); 948b21acc1cSCharles Keepax kfree(ctl_work); 949b21acc1cSCharles Keepax } 950b21acc1cSCharles Keepax 951b21acc1cSCharles Keepax static int wm_adsp_create_control(struct wm_adsp *dsp, 952b21acc1cSCharles Keepax const struct wm_adsp_alg_region *alg_region, 9532323736dSCharles Keepax unsigned int offset, unsigned int len, 95426c22a19SCharles Keepax const char *subname, unsigned int subname_len, 95526c22a19SCharles Keepax unsigned int flags) 956b21acc1cSCharles Keepax { 957b21acc1cSCharles Keepax struct wm_coeff_ctl *ctl; 958b21acc1cSCharles Keepax struct wmfw_ctl_work *ctl_work; 959b21acc1cSCharles Keepax char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; 960b21acc1cSCharles Keepax char *region_name; 961b21acc1cSCharles Keepax int ret; 962b21acc1cSCharles Keepax 96326c22a19SCharles Keepax if (flags & WMFW_CTL_FLAG_SYS) 96426c22a19SCharles Keepax return 0; 96526c22a19SCharles Keepax 966b21acc1cSCharles Keepax switch (alg_region->type) { 967b21acc1cSCharles Keepax case WMFW_ADSP1_PM: 968b21acc1cSCharles Keepax region_name = "PM"; 969b21acc1cSCharles Keepax break; 970b21acc1cSCharles Keepax case WMFW_ADSP1_DM: 971b21acc1cSCharles Keepax region_name = "DM"; 972b21acc1cSCharles Keepax break; 973b21acc1cSCharles Keepax case WMFW_ADSP2_XM: 974b21acc1cSCharles Keepax region_name = "XM"; 975b21acc1cSCharles Keepax break; 976b21acc1cSCharles Keepax case WMFW_ADSP2_YM: 977b21acc1cSCharles Keepax region_name = "YM"; 978b21acc1cSCharles Keepax break; 979b21acc1cSCharles Keepax case WMFW_ADSP1_ZM: 980b21acc1cSCharles Keepax region_name = "ZM"; 981b21acc1cSCharles Keepax break; 982b21acc1cSCharles Keepax default: 9832323736dSCharles Keepax adsp_err(dsp, "Unknown region type: %d\n", alg_region->type); 984b21acc1cSCharles Keepax return -EINVAL; 985b21acc1cSCharles Keepax } 986b21acc1cSCharles Keepax 987cb5b57a9SCharles Keepax switch (dsp->fw_ver) { 988cb5b57a9SCharles Keepax case 0: 989cb5b57a9SCharles Keepax case 1: 990b21acc1cSCharles Keepax snprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "DSP%d %s %x", 991b21acc1cSCharles Keepax dsp->num, region_name, alg_region->alg); 992cb5b57a9SCharles Keepax break; 993cb5b57a9SCharles Keepax default: 994cb5b57a9SCharles Keepax ret = snprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, 995cb5b57a9SCharles Keepax "DSP%d%c %.12s %x", dsp->num, *region_name, 996cb5b57a9SCharles Keepax wm_adsp_fw_text[dsp->fw], alg_region->alg); 997cb5b57a9SCharles Keepax 998cb5b57a9SCharles Keepax /* Truncate the subname from the start if it is too long */ 999cb5b57a9SCharles Keepax if (subname) { 1000cb5b57a9SCharles Keepax int avail = SNDRV_CTL_ELEM_ID_NAME_MAXLEN - ret - 2; 1001cb5b57a9SCharles Keepax int skip = 0; 1002cb5b57a9SCharles Keepax 1003cb5b57a9SCharles Keepax if (subname_len > avail) 1004cb5b57a9SCharles Keepax skip = subname_len - avail; 1005cb5b57a9SCharles Keepax 1006cb5b57a9SCharles Keepax snprintf(name + ret, 1007cb5b57a9SCharles Keepax SNDRV_CTL_ELEM_ID_NAME_MAXLEN - ret, " %.*s", 1008cb5b57a9SCharles Keepax subname_len - skip, subname + skip); 1009cb5b57a9SCharles Keepax } 1010cb5b57a9SCharles Keepax break; 1011cb5b57a9SCharles Keepax } 1012b21acc1cSCharles Keepax 10137585a5b0SCharles Keepax list_for_each_entry(ctl, &dsp->ctl_list, list) { 1014b21acc1cSCharles Keepax if (!strcmp(ctl->name, name)) { 1015b21acc1cSCharles Keepax if (!ctl->enabled) 1016b21acc1cSCharles Keepax ctl->enabled = 1; 1017b21acc1cSCharles Keepax return 0; 1018b21acc1cSCharles Keepax } 1019b21acc1cSCharles Keepax } 1020b21acc1cSCharles Keepax 1021b21acc1cSCharles Keepax ctl = kzalloc(sizeof(*ctl), GFP_KERNEL); 1022b21acc1cSCharles Keepax if (!ctl) 1023b21acc1cSCharles Keepax return -ENOMEM; 10242323736dSCharles Keepax ctl->fw_name = wm_adsp_fw_text[dsp->fw]; 1025b21acc1cSCharles Keepax ctl->alg_region = *alg_region; 1026b21acc1cSCharles Keepax ctl->name = kmemdup(name, strlen(name) + 1, GFP_KERNEL); 1027b21acc1cSCharles Keepax if (!ctl->name) { 1028b21acc1cSCharles Keepax ret = -ENOMEM; 1029b21acc1cSCharles Keepax goto err_ctl; 1030b21acc1cSCharles Keepax } 1031b21acc1cSCharles Keepax ctl->enabled = 1; 1032b21acc1cSCharles Keepax ctl->set = 0; 1033b21acc1cSCharles Keepax ctl->ops.xget = wm_coeff_get; 1034b21acc1cSCharles Keepax ctl->ops.xput = wm_coeff_put; 1035b21acc1cSCharles Keepax ctl->dsp = dsp; 1036b21acc1cSCharles Keepax 103726c22a19SCharles Keepax ctl->flags = flags; 10382323736dSCharles Keepax ctl->offset = offset; 1039b21acc1cSCharles Keepax if (len > 512) { 1040b21acc1cSCharles Keepax adsp_warn(dsp, "Truncating control %s from %d\n", 1041b21acc1cSCharles Keepax ctl->name, len); 1042b21acc1cSCharles Keepax len = 512; 1043b21acc1cSCharles Keepax } 1044b21acc1cSCharles Keepax ctl->len = len; 1045b21acc1cSCharles Keepax ctl->cache = kzalloc(ctl->len, GFP_KERNEL); 1046b21acc1cSCharles Keepax if (!ctl->cache) { 1047b21acc1cSCharles Keepax ret = -ENOMEM; 1048b21acc1cSCharles Keepax goto err_ctl_name; 1049b21acc1cSCharles Keepax } 1050b21acc1cSCharles Keepax 10512323736dSCharles Keepax list_add(&ctl->list, &dsp->ctl_list); 10522323736dSCharles Keepax 1053b21acc1cSCharles Keepax ctl_work = kzalloc(sizeof(*ctl_work), GFP_KERNEL); 1054b21acc1cSCharles Keepax if (!ctl_work) { 1055b21acc1cSCharles Keepax ret = -ENOMEM; 1056b21acc1cSCharles Keepax goto err_ctl_cache; 1057b21acc1cSCharles Keepax } 1058b21acc1cSCharles Keepax 1059b21acc1cSCharles Keepax ctl_work->dsp = dsp; 1060b21acc1cSCharles Keepax ctl_work->ctl = ctl; 1061b21acc1cSCharles Keepax INIT_WORK(&ctl_work->work, wm_adsp_ctl_work); 1062b21acc1cSCharles Keepax schedule_work(&ctl_work->work); 1063b21acc1cSCharles Keepax 1064b21acc1cSCharles Keepax return 0; 1065b21acc1cSCharles Keepax 1066b21acc1cSCharles Keepax err_ctl_cache: 1067b21acc1cSCharles Keepax kfree(ctl->cache); 1068b21acc1cSCharles Keepax err_ctl_name: 1069b21acc1cSCharles Keepax kfree(ctl->name); 1070b21acc1cSCharles Keepax err_ctl: 1071b21acc1cSCharles Keepax kfree(ctl); 1072b21acc1cSCharles Keepax 1073b21acc1cSCharles Keepax return ret; 1074b21acc1cSCharles Keepax } 1075b21acc1cSCharles Keepax 10762323736dSCharles Keepax struct wm_coeff_parsed_alg { 10772323736dSCharles Keepax int id; 10782323736dSCharles Keepax const u8 *name; 10792323736dSCharles Keepax int name_len; 10802323736dSCharles Keepax int ncoeff; 10812323736dSCharles Keepax }; 10822323736dSCharles Keepax 10832323736dSCharles Keepax struct wm_coeff_parsed_coeff { 10842323736dSCharles Keepax int offset; 10852323736dSCharles Keepax int mem_type; 10862323736dSCharles Keepax const u8 *name; 10872323736dSCharles Keepax int name_len; 10882323736dSCharles Keepax int ctl_type; 10892323736dSCharles Keepax int flags; 10902323736dSCharles Keepax int len; 10912323736dSCharles Keepax }; 10922323736dSCharles Keepax 1093cb5b57a9SCharles Keepax static int wm_coeff_parse_string(int bytes, const u8 **pos, const u8 **str) 1094cb5b57a9SCharles Keepax { 1095cb5b57a9SCharles Keepax int length; 1096cb5b57a9SCharles Keepax 1097cb5b57a9SCharles Keepax switch (bytes) { 1098cb5b57a9SCharles Keepax case 1: 1099cb5b57a9SCharles Keepax length = **pos; 1100cb5b57a9SCharles Keepax break; 1101cb5b57a9SCharles Keepax case 2: 11028299ee81SCharles Keepax length = le16_to_cpu(*((__le16 *)*pos)); 1103cb5b57a9SCharles Keepax break; 1104cb5b57a9SCharles Keepax default: 1105cb5b57a9SCharles Keepax return 0; 1106cb5b57a9SCharles Keepax } 1107cb5b57a9SCharles Keepax 1108cb5b57a9SCharles Keepax if (str) 1109cb5b57a9SCharles Keepax *str = *pos + bytes; 1110cb5b57a9SCharles Keepax 1111cb5b57a9SCharles Keepax *pos += ((length + bytes) + 3) & ~0x03; 1112cb5b57a9SCharles Keepax 1113cb5b57a9SCharles Keepax return length; 1114cb5b57a9SCharles Keepax } 1115cb5b57a9SCharles Keepax 1116cb5b57a9SCharles Keepax static int wm_coeff_parse_int(int bytes, const u8 **pos) 1117cb5b57a9SCharles Keepax { 1118cb5b57a9SCharles Keepax int val = 0; 1119cb5b57a9SCharles Keepax 1120cb5b57a9SCharles Keepax switch (bytes) { 1121cb5b57a9SCharles Keepax case 2: 11228299ee81SCharles Keepax val = le16_to_cpu(*((__le16 *)*pos)); 1123cb5b57a9SCharles Keepax break; 1124cb5b57a9SCharles Keepax case 4: 11258299ee81SCharles Keepax val = le32_to_cpu(*((__le32 *)*pos)); 1126cb5b57a9SCharles Keepax break; 1127cb5b57a9SCharles Keepax default: 1128cb5b57a9SCharles Keepax break; 1129cb5b57a9SCharles Keepax } 1130cb5b57a9SCharles Keepax 1131cb5b57a9SCharles Keepax *pos += bytes; 1132cb5b57a9SCharles Keepax 1133cb5b57a9SCharles Keepax return val; 1134cb5b57a9SCharles Keepax } 1135cb5b57a9SCharles Keepax 11362323736dSCharles Keepax static inline void wm_coeff_parse_alg(struct wm_adsp *dsp, const u8 **data, 11372323736dSCharles Keepax struct wm_coeff_parsed_alg *blk) 11382323736dSCharles Keepax { 11392323736dSCharles Keepax const struct wmfw_adsp_alg_data *raw; 11402323736dSCharles Keepax 1141cb5b57a9SCharles Keepax switch (dsp->fw_ver) { 1142cb5b57a9SCharles Keepax case 0: 1143cb5b57a9SCharles Keepax case 1: 11442323736dSCharles Keepax raw = (const struct wmfw_adsp_alg_data *)*data; 11452323736dSCharles Keepax *data = raw->data; 11462323736dSCharles Keepax 11472323736dSCharles Keepax blk->id = le32_to_cpu(raw->id); 11482323736dSCharles Keepax blk->name = raw->name; 11492323736dSCharles Keepax blk->name_len = strlen(raw->name); 11502323736dSCharles Keepax blk->ncoeff = le32_to_cpu(raw->ncoeff); 1151cb5b57a9SCharles Keepax break; 1152cb5b57a9SCharles Keepax default: 1153cb5b57a9SCharles Keepax blk->id = wm_coeff_parse_int(sizeof(raw->id), data); 1154cb5b57a9SCharles Keepax blk->name_len = wm_coeff_parse_string(sizeof(u8), data, 1155cb5b57a9SCharles Keepax &blk->name); 1156cb5b57a9SCharles Keepax wm_coeff_parse_string(sizeof(u16), data, NULL); 1157cb5b57a9SCharles Keepax blk->ncoeff = wm_coeff_parse_int(sizeof(raw->ncoeff), data); 1158cb5b57a9SCharles Keepax break; 1159cb5b57a9SCharles Keepax } 11602323736dSCharles Keepax 11612323736dSCharles Keepax adsp_dbg(dsp, "Algorithm ID: %#x\n", blk->id); 11622323736dSCharles Keepax adsp_dbg(dsp, "Algorithm name: %.*s\n", blk->name_len, blk->name); 11632323736dSCharles Keepax adsp_dbg(dsp, "# of coefficient descriptors: %#x\n", blk->ncoeff); 11642323736dSCharles Keepax } 11652323736dSCharles Keepax 11662323736dSCharles Keepax static inline void wm_coeff_parse_coeff(struct wm_adsp *dsp, const u8 **data, 11672323736dSCharles Keepax struct wm_coeff_parsed_coeff *blk) 11682323736dSCharles Keepax { 11692323736dSCharles Keepax const struct wmfw_adsp_coeff_data *raw; 1170cb5b57a9SCharles Keepax const u8 *tmp; 1171cb5b57a9SCharles Keepax int length; 11722323736dSCharles Keepax 1173cb5b57a9SCharles Keepax switch (dsp->fw_ver) { 1174cb5b57a9SCharles Keepax case 0: 1175cb5b57a9SCharles Keepax case 1: 11762323736dSCharles Keepax raw = (const struct wmfw_adsp_coeff_data *)*data; 11772323736dSCharles Keepax *data = *data + sizeof(raw->hdr) + le32_to_cpu(raw->hdr.size); 11782323736dSCharles Keepax 11792323736dSCharles Keepax blk->offset = le16_to_cpu(raw->hdr.offset); 11802323736dSCharles Keepax blk->mem_type = le16_to_cpu(raw->hdr.type); 11812323736dSCharles Keepax blk->name = raw->name; 11822323736dSCharles Keepax blk->name_len = strlen(raw->name); 11832323736dSCharles Keepax blk->ctl_type = le16_to_cpu(raw->ctl_type); 11842323736dSCharles Keepax blk->flags = le16_to_cpu(raw->flags); 11852323736dSCharles Keepax blk->len = le32_to_cpu(raw->len); 1186cb5b57a9SCharles Keepax break; 1187cb5b57a9SCharles Keepax default: 1188cb5b57a9SCharles Keepax tmp = *data; 1189cb5b57a9SCharles Keepax blk->offset = wm_coeff_parse_int(sizeof(raw->hdr.offset), &tmp); 1190cb5b57a9SCharles Keepax blk->mem_type = wm_coeff_parse_int(sizeof(raw->hdr.type), &tmp); 1191cb5b57a9SCharles Keepax length = wm_coeff_parse_int(sizeof(raw->hdr.size), &tmp); 1192cb5b57a9SCharles Keepax blk->name_len = wm_coeff_parse_string(sizeof(u8), &tmp, 1193cb5b57a9SCharles Keepax &blk->name); 1194cb5b57a9SCharles Keepax wm_coeff_parse_string(sizeof(u8), &tmp, NULL); 1195cb5b57a9SCharles Keepax wm_coeff_parse_string(sizeof(u16), &tmp, NULL); 1196cb5b57a9SCharles Keepax blk->ctl_type = wm_coeff_parse_int(sizeof(raw->ctl_type), &tmp); 1197cb5b57a9SCharles Keepax blk->flags = wm_coeff_parse_int(sizeof(raw->flags), &tmp); 1198cb5b57a9SCharles Keepax blk->len = wm_coeff_parse_int(sizeof(raw->len), &tmp); 1199cb5b57a9SCharles Keepax 1200cb5b57a9SCharles Keepax *data = *data + sizeof(raw->hdr) + length; 1201cb5b57a9SCharles Keepax break; 1202cb5b57a9SCharles Keepax } 12032323736dSCharles Keepax 12042323736dSCharles Keepax adsp_dbg(dsp, "\tCoefficient type: %#x\n", blk->mem_type); 12052323736dSCharles Keepax adsp_dbg(dsp, "\tCoefficient offset: %#x\n", blk->offset); 12062323736dSCharles Keepax adsp_dbg(dsp, "\tCoefficient name: %.*s\n", blk->name_len, blk->name); 12072323736dSCharles Keepax adsp_dbg(dsp, "\tCoefficient flags: %#x\n", blk->flags); 12082323736dSCharles Keepax adsp_dbg(dsp, "\tALSA control type: %#x\n", blk->ctl_type); 12092323736dSCharles Keepax adsp_dbg(dsp, "\tALSA control len: %#x\n", blk->len); 12102323736dSCharles Keepax } 12112323736dSCharles Keepax 12122323736dSCharles Keepax static int wm_adsp_parse_coeff(struct wm_adsp *dsp, 12132323736dSCharles Keepax const struct wmfw_region *region) 12142323736dSCharles Keepax { 12152323736dSCharles Keepax struct wm_adsp_alg_region alg_region = {}; 12162323736dSCharles Keepax struct wm_coeff_parsed_alg alg_blk; 12172323736dSCharles Keepax struct wm_coeff_parsed_coeff coeff_blk; 12182323736dSCharles Keepax const u8 *data = region->data; 12192323736dSCharles Keepax int i, ret; 12202323736dSCharles Keepax 12212323736dSCharles Keepax wm_coeff_parse_alg(dsp, &data, &alg_blk); 12222323736dSCharles Keepax for (i = 0; i < alg_blk.ncoeff; i++) { 12232323736dSCharles Keepax wm_coeff_parse_coeff(dsp, &data, &coeff_blk); 12242323736dSCharles Keepax 12252323736dSCharles Keepax switch (coeff_blk.ctl_type) { 12262323736dSCharles Keepax case SNDRV_CTL_ELEM_TYPE_BYTES: 12272323736dSCharles Keepax break; 12282323736dSCharles Keepax default: 12292323736dSCharles Keepax adsp_err(dsp, "Unknown control type: %d\n", 12302323736dSCharles Keepax coeff_blk.ctl_type); 12312323736dSCharles Keepax return -EINVAL; 12322323736dSCharles Keepax } 12332323736dSCharles Keepax 12342323736dSCharles Keepax alg_region.type = coeff_blk.mem_type; 12352323736dSCharles Keepax alg_region.alg = alg_blk.id; 12362323736dSCharles Keepax 12372323736dSCharles Keepax ret = wm_adsp_create_control(dsp, &alg_region, 12382323736dSCharles Keepax coeff_blk.offset, 12392323736dSCharles Keepax coeff_blk.len, 12402323736dSCharles Keepax coeff_blk.name, 124126c22a19SCharles Keepax coeff_blk.name_len, 124226c22a19SCharles Keepax coeff_blk.flags); 12432323736dSCharles Keepax if (ret < 0) 12442323736dSCharles Keepax adsp_err(dsp, "Failed to create control: %.*s, %d\n", 12452323736dSCharles Keepax coeff_blk.name_len, coeff_blk.name, ret); 12462323736dSCharles Keepax } 12472323736dSCharles Keepax 12482323736dSCharles Keepax return 0; 12492323736dSCharles Keepax } 12502323736dSCharles Keepax 12512159ad93SMark Brown static int wm_adsp_load(struct wm_adsp *dsp) 12522159ad93SMark Brown { 1253cf17c83cSMark Brown LIST_HEAD(buf_list); 12542159ad93SMark Brown const struct firmware *firmware; 12552159ad93SMark Brown struct regmap *regmap = dsp->regmap; 12562159ad93SMark Brown unsigned int pos = 0; 12572159ad93SMark Brown const struct wmfw_header *header; 12582159ad93SMark Brown const struct wmfw_adsp1_sizes *adsp1_sizes; 12592159ad93SMark Brown const struct wmfw_adsp2_sizes *adsp2_sizes; 12602159ad93SMark Brown const struct wmfw_footer *footer; 12612159ad93SMark Brown const struct wmfw_region *region; 12622159ad93SMark Brown const struct wm_adsp_region *mem; 12632159ad93SMark Brown const char *region_name; 12642159ad93SMark Brown char *file, *text; 1265cf17c83cSMark Brown struct wm_adsp_buf *buf; 12662159ad93SMark Brown unsigned int reg; 12672159ad93SMark Brown int regions = 0; 12682159ad93SMark Brown int ret, offset, type, sizes; 12692159ad93SMark Brown 12702159ad93SMark Brown file = kzalloc(PAGE_SIZE, GFP_KERNEL); 12712159ad93SMark Brown if (file == NULL) 12722159ad93SMark Brown return -ENOMEM; 12732159ad93SMark Brown 12741023dbd9SMark Brown snprintf(file, PAGE_SIZE, "%s-dsp%d-%s.wmfw", dsp->part, dsp->num, 12751023dbd9SMark Brown wm_adsp_fw[dsp->fw].file); 12762159ad93SMark Brown file[PAGE_SIZE - 1] = '\0'; 12772159ad93SMark Brown 12782159ad93SMark Brown ret = request_firmware(&firmware, file, dsp->dev); 12792159ad93SMark Brown if (ret != 0) { 12802159ad93SMark Brown adsp_err(dsp, "Failed to request '%s'\n", file); 12812159ad93SMark Brown goto out; 12822159ad93SMark Brown } 12832159ad93SMark Brown ret = -EINVAL; 12842159ad93SMark Brown 12852159ad93SMark Brown pos = sizeof(*header) + sizeof(*adsp1_sizes) + sizeof(*footer); 12862159ad93SMark Brown if (pos >= firmware->size) { 12872159ad93SMark Brown adsp_err(dsp, "%s: file too short, %zu bytes\n", 12882159ad93SMark Brown file, firmware->size); 12892159ad93SMark Brown goto out_fw; 12902159ad93SMark Brown } 12912159ad93SMark Brown 12922159ad93SMark Brown header = (void *)&firmware->data[0]; 12932159ad93SMark Brown 12942159ad93SMark Brown if (memcmp(&header->magic[0], "WMFW", 4) != 0) { 12952159ad93SMark Brown adsp_err(dsp, "%s: invalid magic\n", file); 12962159ad93SMark Brown goto out_fw; 12972159ad93SMark Brown } 12982159ad93SMark Brown 12992323736dSCharles Keepax switch (header->ver) { 13002323736dSCharles Keepax case 0: 1301c61e59feSCharles Keepax adsp_warn(dsp, "%s: Depreciated file format %d\n", 1302c61e59feSCharles Keepax file, header->ver); 1303c61e59feSCharles Keepax break; 13042323736dSCharles Keepax case 1: 1305cb5b57a9SCharles Keepax case 2: 13062323736dSCharles Keepax break; 13072323736dSCharles Keepax default: 13082159ad93SMark Brown adsp_err(dsp, "%s: unknown file format %d\n", 13092159ad93SMark Brown file, header->ver); 13102159ad93SMark Brown goto out_fw; 13112159ad93SMark Brown } 13122323736dSCharles Keepax 13133626992aSDimitris Papastamos adsp_info(dsp, "Firmware version: %d\n", header->ver); 13142323736dSCharles Keepax dsp->fw_ver = header->ver; 13152159ad93SMark Brown 13162159ad93SMark Brown if (header->core != dsp->type) { 13172159ad93SMark Brown adsp_err(dsp, "%s: invalid core %d != %d\n", 13182159ad93SMark Brown file, header->core, dsp->type); 13192159ad93SMark Brown goto out_fw; 13202159ad93SMark Brown } 13212159ad93SMark Brown 13222159ad93SMark Brown switch (dsp->type) { 13232159ad93SMark Brown case WMFW_ADSP1: 13242159ad93SMark Brown pos = sizeof(*header) + sizeof(*adsp1_sizes) + sizeof(*footer); 13252159ad93SMark Brown adsp1_sizes = (void *)&(header[1]); 13262159ad93SMark Brown footer = (void *)&(adsp1_sizes[1]); 13272159ad93SMark Brown sizes = sizeof(*adsp1_sizes); 13282159ad93SMark Brown 13292159ad93SMark Brown adsp_dbg(dsp, "%s: %d DM, %d PM, %d ZM\n", 13302159ad93SMark Brown file, le32_to_cpu(adsp1_sizes->dm), 13312159ad93SMark Brown le32_to_cpu(adsp1_sizes->pm), 13322159ad93SMark Brown le32_to_cpu(adsp1_sizes->zm)); 13332159ad93SMark Brown break; 13342159ad93SMark Brown 13352159ad93SMark Brown case WMFW_ADSP2: 13362159ad93SMark Brown pos = sizeof(*header) + sizeof(*adsp2_sizes) + sizeof(*footer); 13372159ad93SMark Brown adsp2_sizes = (void *)&(header[1]); 13382159ad93SMark Brown footer = (void *)&(adsp2_sizes[1]); 13392159ad93SMark Brown sizes = sizeof(*adsp2_sizes); 13402159ad93SMark Brown 13412159ad93SMark Brown adsp_dbg(dsp, "%s: %d XM, %d YM %d PM, %d ZM\n", 13422159ad93SMark Brown file, le32_to_cpu(adsp2_sizes->xm), 13432159ad93SMark Brown le32_to_cpu(adsp2_sizes->ym), 13442159ad93SMark Brown le32_to_cpu(adsp2_sizes->pm), 13452159ad93SMark Brown le32_to_cpu(adsp2_sizes->zm)); 13462159ad93SMark Brown break; 13472159ad93SMark Brown 13482159ad93SMark Brown default: 13496c452bdaSTakashi Iwai WARN(1, "Unknown DSP type"); 13502159ad93SMark Brown goto out_fw; 13512159ad93SMark Brown } 13522159ad93SMark Brown 13532159ad93SMark Brown if (le32_to_cpu(header->len) != sizeof(*header) + 13542159ad93SMark Brown sizes + sizeof(*footer)) { 13552159ad93SMark Brown adsp_err(dsp, "%s: unexpected header length %d\n", 13562159ad93SMark Brown file, le32_to_cpu(header->len)); 13572159ad93SMark Brown goto out_fw; 13582159ad93SMark Brown } 13592159ad93SMark Brown 13602159ad93SMark Brown adsp_dbg(dsp, "%s: timestamp %llu\n", file, 13612159ad93SMark Brown le64_to_cpu(footer->timestamp)); 13622159ad93SMark Brown 13632159ad93SMark Brown while (pos < firmware->size && 13642159ad93SMark Brown pos - firmware->size > sizeof(*region)) { 13652159ad93SMark Brown region = (void *)&(firmware->data[pos]); 13662159ad93SMark Brown region_name = "Unknown"; 13672159ad93SMark Brown reg = 0; 13682159ad93SMark Brown text = NULL; 13692159ad93SMark Brown offset = le32_to_cpu(region->offset) & 0xffffff; 13702159ad93SMark Brown type = be32_to_cpu(region->type) & 0xff; 13712159ad93SMark Brown mem = wm_adsp_find_region(dsp, type); 13722159ad93SMark Brown 13732159ad93SMark Brown switch (type) { 13742159ad93SMark Brown case WMFW_NAME_TEXT: 13752159ad93SMark Brown region_name = "Firmware name"; 13762159ad93SMark Brown text = kzalloc(le32_to_cpu(region->len) + 1, 13772159ad93SMark Brown GFP_KERNEL); 13782159ad93SMark Brown break; 13792323736dSCharles Keepax case WMFW_ALGORITHM_DATA: 13802323736dSCharles Keepax region_name = "Algorithm"; 13812323736dSCharles Keepax ret = wm_adsp_parse_coeff(dsp, region); 13822323736dSCharles Keepax if (ret != 0) 13832323736dSCharles Keepax goto out_fw; 13842323736dSCharles Keepax break; 13852159ad93SMark Brown case WMFW_INFO_TEXT: 13862159ad93SMark Brown region_name = "Information"; 13872159ad93SMark Brown text = kzalloc(le32_to_cpu(region->len) + 1, 13882159ad93SMark Brown GFP_KERNEL); 13892159ad93SMark Brown break; 13902159ad93SMark Brown case WMFW_ABSOLUTE: 13912159ad93SMark Brown region_name = "Absolute"; 13922159ad93SMark Brown reg = offset; 13932159ad93SMark Brown break; 13942159ad93SMark Brown case WMFW_ADSP1_PM: 13952159ad93SMark Brown region_name = "PM"; 139645b9ee72SMark Brown reg = wm_adsp_region_to_reg(mem, offset); 13972159ad93SMark Brown break; 13982159ad93SMark Brown case WMFW_ADSP1_DM: 13992159ad93SMark Brown region_name = "DM"; 140045b9ee72SMark Brown reg = wm_adsp_region_to_reg(mem, offset); 14012159ad93SMark Brown break; 14022159ad93SMark Brown case WMFW_ADSP2_XM: 14032159ad93SMark Brown region_name = "XM"; 140445b9ee72SMark Brown reg = wm_adsp_region_to_reg(mem, offset); 14052159ad93SMark Brown break; 14062159ad93SMark Brown case WMFW_ADSP2_YM: 14072159ad93SMark Brown region_name = "YM"; 140845b9ee72SMark Brown reg = wm_adsp_region_to_reg(mem, offset); 14092159ad93SMark Brown break; 14102159ad93SMark Brown case WMFW_ADSP1_ZM: 14112159ad93SMark Brown region_name = "ZM"; 141245b9ee72SMark Brown reg = wm_adsp_region_to_reg(mem, offset); 14132159ad93SMark Brown break; 14142159ad93SMark Brown default: 14152159ad93SMark Brown adsp_warn(dsp, 14162159ad93SMark Brown "%s.%d: Unknown region type %x at %d(%x)\n", 14172159ad93SMark Brown file, regions, type, pos, pos); 14182159ad93SMark Brown break; 14192159ad93SMark Brown } 14202159ad93SMark Brown 14212159ad93SMark Brown adsp_dbg(dsp, "%s.%d: %d bytes at %d in %s\n", file, 14222159ad93SMark Brown regions, le32_to_cpu(region->len), offset, 14232159ad93SMark Brown region_name); 14242159ad93SMark Brown 14252159ad93SMark Brown if (text) { 14262159ad93SMark Brown memcpy(text, region->data, le32_to_cpu(region->len)); 14272159ad93SMark Brown adsp_info(dsp, "%s: %s\n", file, text); 14282159ad93SMark Brown kfree(text); 14292159ad93SMark Brown } 14302159ad93SMark Brown 14312159ad93SMark Brown if (reg) { 1432cdcd7f72SCharles Keepax buf = wm_adsp_buf_alloc(region->data, 1433cdcd7f72SCharles Keepax le32_to_cpu(region->len), 1434cf17c83cSMark Brown &buf_list); 1435a76fefabSMark Brown if (!buf) { 1436a76fefabSMark Brown adsp_err(dsp, "Out of memory\n"); 14377328823dSDimitris Papastamos ret = -ENOMEM; 14387328823dSDimitris Papastamos goto out_fw; 1439a76fefabSMark Brown } 1440a76fefabSMark Brown 1441cdcd7f72SCharles Keepax ret = regmap_raw_write_async(regmap, reg, buf->buf, 1442cdcd7f72SCharles Keepax le32_to_cpu(region->len)); 14432159ad93SMark Brown if (ret != 0) { 14442159ad93SMark Brown adsp_err(dsp, 1445cdcd7f72SCharles Keepax "%s.%d: Failed to write %d bytes at %d in %s: %d\n", 14462159ad93SMark Brown file, regions, 1447cdcd7f72SCharles Keepax le32_to_cpu(region->len), offset, 14482159ad93SMark Brown region_name, ret); 14492159ad93SMark Brown goto out_fw; 14502159ad93SMark Brown } 14512159ad93SMark Brown } 14522159ad93SMark Brown 14532159ad93SMark Brown pos += le32_to_cpu(region->len) + sizeof(*region); 14542159ad93SMark Brown regions++; 14552159ad93SMark Brown } 14562159ad93SMark Brown 1457cf17c83cSMark Brown ret = regmap_async_complete(regmap); 1458cf17c83cSMark Brown if (ret != 0) { 1459cf17c83cSMark Brown adsp_err(dsp, "Failed to complete async write: %d\n", ret); 1460cf17c83cSMark Brown goto out_fw; 1461cf17c83cSMark Brown } 1462cf17c83cSMark Brown 14632159ad93SMark Brown if (pos > firmware->size) 14642159ad93SMark Brown adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n", 14652159ad93SMark Brown file, regions, pos - firmware->size); 14662159ad93SMark Brown 1467f9f55e31SRichard Fitzgerald wm_adsp_debugfs_save_wmfwname(dsp, file); 1468f9f55e31SRichard Fitzgerald 14692159ad93SMark Brown out_fw: 1470cf17c83cSMark Brown regmap_async_complete(regmap); 1471cf17c83cSMark Brown wm_adsp_buf_free(&buf_list); 14722159ad93SMark Brown release_firmware(firmware); 14732159ad93SMark Brown out: 14742159ad93SMark Brown kfree(file); 14752159ad93SMark Brown 14762159ad93SMark Brown return ret; 14772159ad93SMark Brown } 14782159ad93SMark Brown 14792323736dSCharles Keepax static void wm_adsp_ctl_fixup_base(struct wm_adsp *dsp, 14802323736dSCharles Keepax const struct wm_adsp_alg_region *alg_region) 14812323736dSCharles Keepax { 14822323736dSCharles Keepax struct wm_coeff_ctl *ctl; 14832323736dSCharles Keepax 14842323736dSCharles Keepax list_for_each_entry(ctl, &dsp->ctl_list, list) { 14852323736dSCharles Keepax if (ctl->fw_name == wm_adsp_fw_text[dsp->fw] && 14862323736dSCharles Keepax alg_region->alg == ctl->alg_region.alg && 14872323736dSCharles Keepax alg_region->type == ctl->alg_region.type) { 14882323736dSCharles Keepax ctl->alg_region.base = alg_region->base; 14892323736dSCharles Keepax } 14902323736dSCharles Keepax } 14912323736dSCharles Keepax } 14922323736dSCharles Keepax 14933809f001SCharles Keepax static void *wm_adsp_read_algs(struct wm_adsp *dsp, size_t n_algs, 1494b618a185SCharles Keepax unsigned int pos, unsigned int len) 1495db40517cSMark Brown { 1496b618a185SCharles Keepax void *alg; 1497b618a185SCharles Keepax int ret; 1498db40517cSMark Brown __be32 val; 1499db40517cSMark Brown 15003809f001SCharles Keepax if (n_algs == 0) { 1501b618a185SCharles Keepax adsp_err(dsp, "No algorithms\n"); 1502b618a185SCharles Keepax return ERR_PTR(-EINVAL); 1503db40517cSMark Brown } 1504db40517cSMark Brown 15053809f001SCharles Keepax if (n_algs > 1024) { 15063809f001SCharles Keepax adsp_err(dsp, "Algorithm count %zx excessive\n", n_algs); 1507b618a185SCharles Keepax return ERR_PTR(-EINVAL); 1508b618a185SCharles Keepax } 1509b618a185SCharles Keepax 1510b618a185SCharles Keepax /* Read the terminator first to validate the length */ 1511b618a185SCharles Keepax ret = regmap_raw_read(dsp->regmap, pos + len, &val, sizeof(val)); 1512b618a185SCharles Keepax if (ret != 0) { 1513b618a185SCharles Keepax adsp_err(dsp, "Failed to read algorithm list end: %d\n", 1514b618a185SCharles Keepax ret); 1515b618a185SCharles Keepax return ERR_PTR(ret); 1516b618a185SCharles Keepax } 1517b618a185SCharles Keepax 1518b618a185SCharles Keepax if (be32_to_cpu(val) != 0xbedead) 1519b618a185SCharles Keepax adsp_warn(dsp, "Algorithm list end %x 0x%x != 0xbeadead\n", 1520b618a185SCharles Keepax pos + len, be32_to_cpu(val)); 1521b618a185SCharles Keepax 1522b618a185SCharles Keepax alg = kzalloc(len * 2, GFP_KERNEL | GFP_DMA); 1523b618a185SCharles Keepax if (!alg) 1524b618a185SCharles Keepax return ERR_PTR(-ENOMEM); 1525b618a185SCharles Keepax 1526b618a185SCharles Keepax ret = regmap_raw_read(dsp->regmap, pos, alg, len * 2); 1527b618a185SCharles Keepax if (ret != 0) { 1528b618a185SCharles Keepax adsp_err(dsp, "Failed to read algorithm list: %d\n", 1529b618a185SCharles Keepax ret); 1530b618a185SCharles Keepax kfree(alg); 1531b618a185SCharles Keepax return ERR_PTR(ret); 1532b618a185SCharles Keepax } 1533b618a185SCharles Keepax 1534b618a185SCharles Keepax return alg; 1535b618a185SCharles Keepax } 1536b618a185SCharles Keepax 153714197095SCharles Keepax static struct wm_adsp_alg_region * 153814197095SCharles Keepax wm_adsp_find_alg_region(struct wm_adsp *dsp, int type, unsigned int id) 153914197095SCharles Keepax { 154014197095SCharles Keepax struct wm_adsp_alg_region *alg_region; 154114197095SCharles Keepax 154214197095SCharles Keepax list_for_each_entry(alg_region, &dsp->alg_regions, list) { 154314197095SCharles Keepax if (id == alg_region->alg && type == alg_region->type) 154414197095SCharles Keepax return alg_region; 154514197095SCharles Keepax } 154614197095SCharles Keepax 154714197095SCharles Keepax return NULL; 154814197095SCharles Keepax } 154914197095SCharles Keepax 1550d9d20e17SCharles Keepax static struct wm_adsp_alg_region *wm_adsp_create_region(struct wm_adsp *dsp, 1551d9d20e17SCharles Keepax int type, __be32 id, 1552d9d20e17SCharles Keepax __be32 base) 1553d9d20e17SCharles Keepax { 1554d9d20e17SCharles Keepax struct wm_adsp_alg_region *alg_region; 1555d9d20e17SCharles Keepax 1556d9d20e17SCharles Keepax alg_region = kzalloc(sizeof(*alg_region), GFP_KERNEL); 1557d9d20e17SCharles Keepax if (!alg_region) 1558d9d20e17SCharles Keepax return ERR_PTR(-ENOMEM); 1559d9d20e17SCharles Keepax 1560d9d20e17SCharles Keepax alg_region->type = type; 1561d9d20e17SCharles Keepax alg_region->alg = be32_to_cpu(id); 1562d9d20e17SCharles Keepax alg_region->base = be32_to_cpu(base); 1563d9d20e17SCharles Keepax 1564d9d20e17SCharles Keepax list_add_tail(&alg_region->list, &dsp->alg_regions); 1565d9d20e17SCharles Keepax 15662323736dSCharles Keepax if (dsp->fw_ver > 0) 15672323736dSCharles Keepax wm_adsp_ctl_fixup_base(dsp, alg_region); 15682323736dSCharles Keepax 1569d9d20e17SCharles Keepax return alg_region; 1570d9d20e17SCharles Keepax } 1571d9d20e17SCharles Keepax 1572b618a185SCharles Keepax static int wm_adsp1_setup_algs(struct wm_adsp *dsp) 1573b618a185SCharles Keepax { 1574b618a185SCharles Keepax struct wmfw_adsp1_id_hdr adsp1_id; 1575b618a185SCharles Keepax struct wmfw_adsp1_alg_hdr *adsp1_alg; 15763809f001SCharles Keepax struct wm_adsp_alg_region *alg_region; 1577b618a185SCharles Keepax const struct wm_adsp_region *mem; 1578b618a185SCharles Keepax unsigned int pos, len; 15793809f001SCharles Keepax size_t n_algs; 1580b618a185SCharles Keepax int i, ret; 1581b618a185SCharles Keepax 1582b618a185SCharles Keepax mem = wm_adsp_find_region(dsp, WMFW_ADSP1_DM); 15836c452bdaSTakashi Iwai if (WARN_ON(!mem)) 1584db40517cSMark Brown return -EINVAL; 1585db40517cSMark Brown 1586b618a185SCharles Keepax ret = regmap_raw_read(dsp->regmap, mem->base, &adsp1_id, 1587db40517cSMark Brown sizeof(adsp1_id)); 1588db40517cSMark Brown if (ret != 0) { 1589db40517cSMark Brown adsp_err(dsp, "Failed to read algorithm info: %d\n", 1590db40517cSMark Brown ret); 1591db40517cSMark Brown return ret; 1592db40517cSMark Brown } 1593db40517cSMark Brown 15943809f001SCharles Keepax n_algs = be32_to_cpu(adsp1_id.n_algs); 1595f395a218SMark Brown dsp->fw_id = be32_to_cpu(adsp1_id.fw.id); 1596db40517cSMark Brown adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n", 1597f395a218SMark Brown dsp->fw_id, 1598db40517cSMark Brown (be32_to_cpu(adsp1_id.fw.ver) & 0xff0000) >> 16, 1599db40517cSMark Brown (be32_to_cpu(adsp1_id.fw.ver) & 0xff00) >> 8, 1600db40517cSMark Brown be32_to_cpu(adsp1_id.fw.ver) & 0xff, 16013809f001SCharles Keepax n_algs); 1602db40517cSMark Brown 1603d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_ZM, 1604d9d20e17SCharles Keepax adsp1_id.fw.id, adsp1_id.zm); 1605d9d20e17SCharles Keepax if (IS_ERR(alg_region)) 1606d9d20e17SCharles Keepax return PTR_ERR(alg_region); 1607ac50009fSMark Brown 1608d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_DM, 1609d9d20e17SCharles Keepax adsp1_id.fw.id, adsp1_id.dm); 1610d9d20e17SCharles Keepax if (IS_ERR(alg_region)) 1611d9d20e17SCharles Keepax return PTR_ERR(alg_region); 1612ac50009fSMark Brown 1613db40517cSMark Brown pos = sizeof(adsp1_id) / 2; 16143809f001SCharles Keepax len = (sizeof(*adsp1_alg) * n_algs) / 2; 1615db40517cSMark Brown 16163809f001SCharles Keepax adsp1_alg = wm_adsp_read_algs(dsp, n_algs, mem->base + pos, len); 1617b618a185SCharles Keepax if (IS_ERR(adsp1_alg)) 1618b618a185SCharles Keepax return PTR_ERR(adsp1_alg); 1619db40517cSMark Brown 16203809f001SCharles Keepax for (i = 0; i < n_algs; i++) { 1621471f4885SMark Brown adsp_info(dsp, "%d: ID %x v%d.%d.%d DM@%x ZM@%x\n", 1622db40517cSMark Brown i, be32_to_cpu(adsp1_alg[i].alg.id), 1623db40517cSMark Brown (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff0000) >> 16, 1624db40517cSMark Brown (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff00) >> 8, 1625471f4885SMark Brown be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff, 1626471f4885SMark Brown be32_to_cpu(adsp1_alg[i].dm), 1627471f4885SMark Brown be32_to_cpu(adsp1_alg[i].zm)); 1628471f4885SMark Brown 1629d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_DM, 1630d9d20e17SCharles Keepax adsp1_alg[i].alg.id, 1631d9d20e17SCharles Keepax adsp1_alg[i].dm); 1632d9d20e17SCharles Keepax if (IS_ERR(alg_region)) { 1633d9d20e17SCharles Keepax ret = PTR_ERR(alg_region); 1634d6d52179SJS Park goto out; 1635d6d52179SJS Park } 16362323736dSCharles Keepax if (dsp->fw_ver == 0) { 16373809f001SCharles Keepax if (i + 1 < n_algs) { 16386958eb2aSCharles Keepax len = be32_to_cpu(adsp1_alg[i + 1].dm); 16396958eb2aSCharles Keepax len -= be32_to_cpu(adsp1_alg[i].dm); 16406958eb2aSCharles Keepax len *= 4; 16412323736dSCharles Keepax wm_adsp_create_control(dsp, alg_region, 0, 164226c22a19SCharles Keepax len, NULL, 0, 0); 16436ab2b7b4SDimitris Papastamos } else { 16446ab2b7b4SDimitris Papastamos adsp_warn(dsp, "Missing length info for region DM with ID %x\n", 16456ab2b7b4SDimitris Papastamos be32_to_cpu(adsp1_alg[i].alg.id)); 16466ab2b7b4SDimitris Papastamos } 16472323736dSCharles Keepax } 1648471f4885SMark Brown 1649d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_ZM, 1650d9d20e17SCharles Keepax adsp1_alg[i].alg.id, 1651d9d20e17SCharles Keepax adsp1_alg[i].zm); 1652d9d20e17SCharles Keepax if (IS_ERR(alg_region)) { 1653d9d20e17SCharles Keepax ret = PTR_ERR(alg_region); 1654d6d52179SJS Park goto out; 1655d6d52179SJS Park } 16562323736dSCharles Keepax if (dsp->fw_ver == 0) { 16573809f001SCharles Keepax if (i + 1 < n_algs) { 16586958eb2aSCharles Keepax len = be32_to_cpu(adsp1_alg[i + 1].zm); 16596958eb2aSCharles Keepax len -= be32_to_cpu(adsp1_alg[i].zm); 16606958eb2aSCharles Keepax len *= 4; 16612323736dSCharles Keepax wm_adsp_create_control(dsp, alg_region, 0, 166226c22a19SCharles Keepax len, NULL, 0, 0); 16636ab2b7b4SDimitris Papastamos } else { 16646ab2b7b4SDimitris Papastamos adsp_warn(dsp, "Missing length info for region ZM with ID %x\n", 16656ab2b7b4SDimitris Papastamos be32_to_cpu(adsp1_alg[i].alg.id)); 16666ab2b7b4SDimitris Papastamos } 1667b618a185SCharles Keepax } 16682323736dSCharles Keepax } 1669db40517cSMark Brown 1670b618a185SCharles Keepax out: 1671b618a185SCharles Keepax kfree(adsp1_alg); 1672b618a185SCharles Keepax return ret; 1673b618a185SCharles Keepax } 1674b618a185SCharles Keepax 1675b618a185SCharles Keepax static int wm_adsp2_setup_algs(struct wm_adsp *dsp) 1676b618a185SCharles Keepax { 1677b618a185SCharles Keepax struct wmfw_adsp2_id_hdr adsp2_id; 1678b618a185SCharles Keepax struct wmfw_adsp2_alg_hdr *adsp2_alg; 16793809f001SCharles Keepax struct wm_adsp_alg_region *alg_region; 1680b618a185SCharles Keepax const struct wm_adsp_region *mem; 1681b618a185SCharles Keepax unsigned int pos, len; 16823809f001SCharles Keepax size_t n_algs; 1683b618a185SCharles Keepax int i, ret; 1684b618a185SCharles Keepax 1685b618a185SCharles Keepax mem = wm_adsp_find_region(dsp, WMFW_ADSP2_XM); 1686b618a185SCharles Keepax if (WARN_ON(!mem)) 1687b618a185SCharles Keepax return -EINVAL; 1688b618a185SCharles Keepax 1689b618a185SCharles Keepax ret = regmap_raw_read(dsp->regmap, mem->base, &adsp2_id, 1690b618a185SCharles Keepax sizeof(adsp2_id)); 1691b618a185SCharles Keepax if (ret != 0) { 1692b618a185SCharles Keepax adsp_err(dsp, "Failed to read algorithm info: %d\n", 1693b618a185SCharles Keepax ret); 1694b618a185SCharles Keepax return ret; 1695b618a185SCharles Keepax } 1696b618a185SCharles Keepax 16973809f001SCharles Keepax n_algs = be32_to_cpu(adsp2_id.n_algs); 1698b618a185SCharles Keepax dsp->fw_id = be32_to_cpu(adsp2_id.fw.id); 1699f9f55e31SRichard Fitzgerald dsp->fw_id_version = be32_to_cpu(adsp2_id.fw.ver); 1700b618a185SCharles Keepax adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n", 1701b618a185SCharles Keepax dsp->fw_id, 1702f9f55e31SRichard Fitzgerald (dsp->fw_id_version & 0xff0000) >> 16, 1703f9f55e31SRichard Fitzgerald (dsp->fw_id_version & 0xff00) >> 8, 1704f9f55e31SRichard Fitzgerald dsp->fw_id_version & 0xff, 17053809f001SCharles Keepax n_algs); 1706b618a185SCharles Keepax 1707d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_XM, 1708d9d20e17SCharles Keepax adsp2_id.fw.id, adsp2_id.xm); 1709d9d20e17SCharles Keepax if (IS_ERR(alg_region)) 1710d9d20e17SCharles Keepax return PTR_ERR(alg_region); 1711b618a185SCharles Keepax 1712d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_YM, 1713d9d20e17SCharles Keepax adsp2_id.fw.id, adsp2_id.ym); 1714d9d20e17SCharles Keepax if (IS_ERR(alg_region)) 1715d9d20e17SCharles Keepax return PTR_ERR(alg_region); 1716b618a185SCharles Keepax 1717d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_ZM, 1718d9d20e17SCharles Keepax adsp2_id.fw.id, adsp2_id.zm); 1719d9d20e17SCharles Keepax if (IS_ERR(alg_region)) 1720d9d20e17SCharles Keepax return PTR_ERR(alg_region); 1721b618a185SCharles Keepax 1722b618a185SCharles Keepax pos = sizeof(adsp2_id) / 2; 17233809f001SCharles Keepax len = (sizeof(*adsp2_alg) * n_algs) / 2; 1724b618a185SCharles Keepax 17253809f001SCharles Keepax adsp2_alg = wm_adsp_read_algs(dsp, n_algs, mem->base + pos, len); 1726b618a185SCharles Keepax if (IS_ERR(adsp2_alg)) 1727b618a185SCharles Keepax return PTR_ERR(adsp2_alg); 1728b618a185SCharles Keepax 17293809f001SCharles Keepax for (i = 0; i < n_algs; i++) { 1730471f4885SMark Brown adsp_info(dsp, 1731471f4885SMark Brown "%d: ID %x v%d.%d.%d XM@%x YM@%x ZM@%x\n", 1732db40517cSMark Brown i, be32_to_cpu(adsp2_alg[i].alg.id), 1733db40517cSMark Brown (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff0000) >> 16, 1734db40517cSMark Brown (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff00) >> 8, 1735471f4885SMark Brown be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff, 1736471f4885SMark Brown be32_to_cpu(adsp2_alg[i].xm), 1737471f4885SMark Brown be32_to_cpu(adsp2_alg[i].ym), 1738471f4885SMark Brown be32_to_cpu(adsp2_alg[i].zm)); 1739471f4885SMark Brown 1740d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_XM, 1741d9d20e17SCharles Keepax adsp2_alg[i].alg.id, 1742d9d20e17SCharles Keepax adsp2_alg[i].xm); 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(adsp2_alg[i + 1].xm); 17506958eb2aSCharles Keepax len -= be32_to_cpu(adsp2_alg[i].xm); 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 XM with ID %x\n", 17566ab2b7b4SDimitris Papastamos be32_to_cpu(adsp2_alg[i].alg.id)); 17576ab2b7b4SDimitris Papastamos } 17582323736dSCharles Keepax } 1759471f4885SMark Brown 1760d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_YM, 1761d9d20e17SCharles Keepax adsp2_alg[i].alg.id, 1762d9d20e17SCharles Keepax adsp2_alg[i].ym); 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(adsp2_alg[i + 1].ym); 17706958eb2aSCharles Keepax len -= be32_to_cpu(adsp2_alg[i].ym); 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 YM with ID %x\n", 17766ab2b7b4SDimitris Papastamos be32_to_cpu(adsp2_alg[i].alg.id)); 17776ab2b7b4SDimitris Papastamos } 17782323736dSCharles Keepax } 1779471f4885SMark Brown 1780d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_ZM, 1781d9d20e17SCharles Keepax adsp2_alg[i].alg.id, 1782d9d20e17SCharles Keepax adsp2_alg[i].zm); 1783d9d20e17SCharles Keepax if (IS_ERR(alg_region)) { 1784d9d20e17SCharles Keepax ret = PTR_ERR(alg_region); 1785d6d52179SJS Park goto out; 1786d6d52179SJS Park } 17872323736dSCharles Keepax if (dsp->fw_ver == 0) { 17883809f001SCharles Keepax if (i + 1 < n_algs) { 17896958eb2aSCharles Keepax len = be32_to_cpu(adsp2_alg[i + 1].zm); 17906958eb2aSCharles Keepax len -= be32_to_cpu(adsp2_alg[i].zm); 17916958eb2aSCharles Keepax len *= 4; 17922323736dSCharles Keepax wm_adsp_create_control(dsp, alg_region, 0, 179326c22a19SCharles Keepax len, NULL, 0, 0); 17946ab2b7b4SDimitris Papastamos } else { 17956ab2b7b4SDimitris Papastamos adsp_warn(dsp, "Missing length info for region ZM with ID %x\n", 17966ab2b7b4SDimitris Papastamos be32_to_cpu(adsp2_alg[i].alg.id)); 17976ab2b7b4SDimitris Papastamos } 1798db40517cSMark Brown } 17992323736dSCharles Keepax } 1800db40517cSMark Brown 1801db40517cSMark Brown out: 1802b618a185SCharles Keepax kfree(adsp2_alg); 1803db40517cSMark Brown return ret; 1804db40517cSMark Brown } 1805db40517cSMark Brown 18062159ad93SMark Brown static int wm_adsp_load_coeff(struct wm_adsp *dsp) 18072159ad93SMark Brown { 1808cf17c83cSMark Brown LIST_HEAD(buf_list); 18092159ad93SMark Brown struct regmap *regmap = dsp->regmap; 18102159ad93SMark Brown struct wmfw_coeff_hdr *hdr; 18112159ad93SMark Brown struct wmfw_coeff_item *blk; 18122159ad93SMark Brown const struct firmware *firmware; 1813471f4885SMark Brown const struct wm_adsp_region *mem; 1814471f4885SMark Brown struct wm_adsp_alg_region *alg_region; 18152159ad93SMark Brown const char *region_name; 18162159ad93SMark Brown int ret, pos, blocks, type, offset, reg; 18172159ad93SMark Brown char *file; 1818cf17c83cSMark Brown struct wm_adsp_buf *buf; 18192159ad93SMark Brown 18202159ad93SMark Brown file = kzalloc(PAGE_SIZE, GFP_KERNEL); 18212159ad93SMark Brown if (file == NULL) 18222159ad93SMark Brown return -ENOMEM; 18232159ad93SMark Brown 18241023dbd9SMark Brown snprintf(file, PAGE_SIZE, "%s-dsp%d-%s.bin", dsp->part, dsp->num, 18251023dbd9SMark Brown wm_adsp_fw[dsp->fw].file); 18262159ad93SMark Brown file[PAGE_SIZE - 1] = '\0'; 18272159ad93SMark Brown 18282159ad93SMark Brown ret = request_firmware(&firmware, file, dsp->dev); 18292159ad93SMark Brown if (ret != 0) { 18302159ad93SMark Brown adsp_warn(dsp, "Failed to request '%s'\n", file); 18312159ad93SMark Brown ret = 0; 18322159ad93SMark Brown goto out; 18332159ad93SMark Brown } 18342159ad93SMark Brown ret = -EINVAL; 18352159ad93SMark Brown 18362159ad93SMark Brown if (sizeof(*hdr) >= firmware->size) { 18372159ad93SMark Brown adsp_err(dsp, "%s: file too short, %zu bytes\n", 18382159ad93SMark Brown file, firmware->size); 18392159ad93SMark Brown goto out_fw; 18402159ad93SMark Brown } 18412159ad93SMark Brown 18422159ad93SMark Brown hdr = (void *)&firmware->data[0]; 18432159ad93SMark Brown if (memcmp(hdr->magic, "WMDR", 4) != 0) { 18442159ad93SMark Brown adsp_err(dsp, "%s: invalid magic\n", file); 1845a4cdbec7SCharles Keepax goto out_fw; 18462159ad93SMark Brown } 18472159ad93SMark Brown 1848c712326dSMark Brown switch (be32_to_cpu(hdr->rev) & 0xff) { 1849c712326dSMark Brown case 1: 1850c712326dSMark Brown break; 1851c712326dSMark Brown default: 1852c712326dSMark Brown adsp_err(dsp, "%s: Unsupported coefficient file format %d\n", 1853c712326dSMark Brown file, be32_to_cpu(hdr->rev) & 0xff); 1854c712326dSMark Brown ret = -EINVAL; 1855c712326dSMark Brown goto out_fw; 1856c712326dSMark Brown } 1857c712326dSMark Brown 18582159ad93SMark Brown adsp_dbg(dsp, "%s: v%d.%d.%d\n", file, 18592159ad93SMark Brown (le32_to_cpu(hdr->ver) >> 16) & 0xff, 18602159ad93SMark Brown (le32_to_cpu(hdr->ver) >> 8) & 0xff, 18612159ad93SMark Brown le32_to_cpu(hdr->ver) & 0xff); 18622159ad93SMark Brown 18632159ad93SMark Brown pos = le32_to_cpu(hdr->len); 18642159ad93SMark Brown 18652159ad93SMark Brown blocks = 0; 18662159ad93SMark Brown while (pos < firmware->size && 18672159ad93SMark Brown pos - firmware->size > sizeof(*blk)) { 18682159ad93SMark Brown blk = (void *)(&firmware->data[pos]); 18692159ad93SMark Brown 1870c712326dSMark Brown type = le16_to_cpu(blk->type); 1871c712326dSMark Brown offset = le16_to_cpu(blk->offset); 18722159ad93SMark Brown 18732159ad93SMark Brown adsp_dbg(dsp, "%s.%d: %x v%d.%d.%d\n", 18742159ad93SMark Brown file, blocks, le32_to_cpu(blk->id), 18752159ad93SMark Brown (le32_to_cpu(blk->ver) >> 16) & 0xff, 18762159ad93SMark Brown (le32_to_cpu(blk->ver) >> 8) & 0xff, 18772159ad93SMark Brown le32_to_cpu(blk->ver) & 0xff); 18782159ad93SMark Brown adsp_dbg(dsp, "%s.%d: %d bytes at 0x%x in %x\n", 18792159ad93SMark Brown file, blocks, le32_to_cpu(blk->len), offset, type); 18802159ad93SMark Brown 18812159ad93SMark Brown reg = 0; 18822159ad93SMark Brown region_name = "Unknown"; 18832159ad93SMark Brown switch (type) { 1884c712326dSMark Brown case (WMFW_NAME_TEXT << 8): 1885c712326dSMark Brown case (WMFW_INFO_TEXT << 8): 18862159ad93SMark Brown break; 1887c712326dSMark Brown case (WMFW_ABSOLUTE << 8): 1888f395a218SMark Brown /* 1889f395a218SMark Brown * Old files may use this for global 1890f395a218SMark Brown * coefficients. 1891f395a218SMark Brown */ 1892f395a218SMark Brown if (le32_to_cpu(blk->id) == dsp->fw_id && 1893f395a218SMark Brown offset == 0) { 1894f395a218SMark Brown region_name = "global coefficients"; 1895f395a218SMark Brown mem = wm_adsp_find_region(dsp, type); 1896f395a218SMark Brown if (!mem) { 1897f395a218SMark Brown adsp_err(dsp, "No ZM\n"); 1898f395a218SMark Brown break; 1899f395a218SMark Brown } 1900f395a218SMark Brown reg = wm_adsp_region_to_reg(mem, 0); 1901f395a218SMark Brown 1902f395a218SMark Brown } else { 19032159ad93SMark Brown region_name = "register"; 19042159ad93SMark Brown reg = offset; 1905f395a218SMark Brown } 19062159ad93SMark Brown break; 1907471f4885SMark Brown 1908471f4885SMark Brown case WMFW_ADSP1_DM: 1909471f4885SMark Brown case WMFW_ADSP1_ZM: 1910471f4885SMark Brown case WMFW_ADSP2_XM: 1911471f4885SMark Brown case WMFW_ADSP2_YM: 1912471f4885SMark Brown adsp_dbg(dsp, "%s.%d: %d bytes in %x for %x\n", 1913471f4885SMark Brown file, blocks, le32_to_cpu(blk->len), 1914471f4885SMark Brown type, le32_to_cpu(blk->id)); 1915471f4885SMark Brown 1916471f4885SMark Brown mem = wm_adsp_find_region(dsp, type); 1917471f4885SMark Brown if (!mem) { 1918471f4885SMark Brown adsp_err(dsp, "No base for region %x\n", type); 1919471f4885SMark Brown break; 1920471f4885SMark Brown } 1921471f4885SMark Brown 192214197095SCharles Keepax alg_region = wm_adsp_find_alg_region(dsp, type, 192314197095SCharles Keepax le32_to_cpu(blk->id)); 192414197095SCharles Keepax if (alg_region) { 1925338c5188SMark Brown reg = alg_region->base; 192614197095SCharles Keepax reg = wm_adsp_region_to_reg(mem, reg); 1927338c5188SMark Brown reg += offset; 192814197095SCharles Keepax } else { 1929471f4885SMark Brown adsp_err(dsp, "No %x for algorithm %x\n", 1930471f4885SMark Brown type, le32_to_cpu(blk->id)); 193114197095SCharles Keepax } 1932471f4885SMark Brown break; 1933471f4885SMark Brown 19342159ad93SMark Brown default: 193525c62f7eSMark Brown adsp_err(dsp, "%s.%d: Unknown region type %x at %d\n", 193625c62f7eSMark Brown file, blocks, type, pos); 19372159ad93SMark Brown break; 19382159ad93SMark Brown } 19392159ad93SMark Brown 19402159ad93SMark Brown if (reg) { 1941cf17c83cSMark Brown buf = wm_adsp_buf_alloc(blk->data, 1942cf17c83cSMark Brown le32_to_cpu(blk->len), 1943cf17c83cSMark Brown &buf_list); 1944a76fefabSMark Brown if (!buf) { 1945a76fefabSMark Brown adsp_err(dsp, "Out of memory\n"); 1946f4b82812SWei Yongjun ret = -ENOMEM; 1947f4b82812SWei Yongjun goto out_fw; 1948a76fefabSMark Brown } 1949a76fefabSMark Brown 195020da6d5aSMark Brown adsp_dbg(dsp, "%s.%d: Writing %d bytes at %x\n", 195120da6d5aSMark Brown file, blocks, le32_to_cpu(blk->len), 195220da6d5aSMark Brown reg); 1953cf17c83cSMark Brown ret = regmap_raw_write_async(regmap, reg, buf->buf, 19542159ad93SMark Brown le32_to_cpu(blk->len)); 19552159ad93SMark Brown if (ret != 0) { 19562159ad93SMark Brown adsp_err(dsp, 195743bc3bf6SDimitris Papastamos "%s.%d: Failed to write to %x in %s: %d\n", 195843bc3bf6SDimitris Papastamos file, blocks, reg, region_name, ret); 19592159ad93SMark Brown } 19602159ad93SMark Brown } 19612159ad93SMark Brown 1962be951017SCharles Keepax pos += (le32_to_cpu(blk->len) + sizeof(*blk) + 3) & ~0x03; 19632159ad93SMark Brown blocks++; 19642159ad93SMark Brown } 19652159ad93SMark Brown 1966cf17c83cSMark Brown ret = regmap_async_complete(regmap); 1967cf17c83cSMark Brown if (ret != 0) 1968cf17c83cSMark Brown adsp_err(dsp, "Failed to complete async write: %d\n", ret); 1969cf17c83cSMark Brown 19702159ad93SMark Brown if (pos > firmware->size) 19712159ad93SMark Brown adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n", 19722159ad93SMark Brown file, blocks, pos - firmware->size); 19732159ad93SMark Brown 1974f9f55e31SRichard Fitzgerald wm_adsp_debugfs_save_binname(dsp, file); 1975f9f55e31SRichard Fitzgerald 19762159ad93SMark Brown out_fw: 19779da7a5a9SCharles Keepax regmap_async_complete(regmap); 19782159ad93SMark Brown release_firmware(firmware); 1979cf17c83cSMark Brown wm_adsp_buf_free(&buf_list); 19802159ad93SMark Brown out: 19812159ad93SMark Brown kfree(file); 1982f4b82812SWei Yongjun return ret; 19832159ad93SMark Brown } 19842159ad93SMark Brown 19853809f001SCharles Keepax int wm_adsp1_init(struct wm_adsp *dsp) 19865e7a7a22SMark Brown { 19873809f001SCharles Keepax INIT_LIST_HEAD(&dsp->alg_regions); 19885e7a7a22SMark Brown 1989078e7183SCharles Keepax mutex_init(&dsp->pwr_lock); 1990078e7183SCharles Keepax 19915e7a7a22SMark Brown return 0; 19925e7a7a22SMark Brown } 19935e7a7a22SMark Brown EXPORT_SYMBOL_GPL(wm_adsp1_init); 19945e7a7a22SMark Brown 19952159ad93SMark Brown int wm_adsp1_event(struct snd_soc_dapm_widget *w, 19962159ad93SMark Brown struct snd_kcontrol *kcontrol, 19972159ad93SMark Brown int event) 19982159ad93SMark Brown { 199972718517SLars-Peter Clausen struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); 20002159ad93SMark Brown struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec); 20012159ad93SMark Brown struct wm_adsp *dsp = &dsps[w->shift]; 2002b0101b4fSDimitris Papastamos struct wm_adsp_alg_region *alg_region; 20036ab2b7b4SDimitris Papastamos struct wm_coeff_ctl *ctl; 20042159ad93SMark Brown int ret; 20057585a5b0SCharles Keepax unsigned int val; 20062159ad93SMark Brown 200700200107SLars-Peter Clausen dsp->card = codec->component.card; 200892bb4c32SDimitris Papastamos 2009078e7183SCharles Keepax mutex_lock(&dsp->pwr_lock); 2010078e7183SCharles Keepax 20112159ad93SMark Brown switch (event) { 20122159ad93SMark Brown case SND_SOC_DAPM_POST_PMU: 20132159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 20142159ad93SMark Brown ADSP1_SYS_ENA, ADSP1_SYS_ENA); 20152159ad93SMark Brown 201694e205bfSChris Rattray /* 201794e205bfSChris Rattray * For simplicity set the DSP clock rate to be the 201894e205bfSChris Rattray * SYSCLK rate rather than making it configurable. 201994e205bfSChris Rattray */ 202094e205bfSChris Rattray if (dsp->sysclk_reg) { 202194e205bfSChris Rattray ret = regmap_read(dsp->regmap, dsp->sysclk_reg, &val); 202294e205bfSChris Rattray if (ret != 0) { 202394e205bfSChris Rattray adsp_err(dsp, "Failed to read SYSCLK state: %d\n", 202494e205bfSChris Rattray ret); 2025078e7183SCharles Keepax goto err_mutex; 202694e205bfSChris Rattray } 202794e205bfSChris Rattray 202894e205bfSChris Rattray val = (val & dsp->sysclk_mask) 202994e205bfSChris Rattray >> dsp->sysclk_shift; 203094e205bfSChris Rattray 203194e205bfSChris Rattray ret = regmap_update_bits(dsp->regmap, 203294e205bfSChris Rattray dsp->base + ADSP1_CONTROL_31, 203394e205bfSChris Rattray ADSP1_CLK_SEL_MASK, val); 203494e205bfSChris Rattray if (ret != 0) { 203594e205bfSChris Rattray adsp_err(dsp, "Failed to set clock rate: %d\n", 203694e205bfSChris Rattray ret); 2037078e7183SCharles Keepax goto err_mutex; 203894e205bfSChris Rattray } 203994e205bfSChris Rattray } 204094e205bfSChris Rattray 20412159ad93SMark Brown ret = wm_adsp_load(dsp); 20422159ad93SMark Brown if (ret != 0) 2043078e7183SCharles Keepax goto err_ena; 20442159ad93SMark Brown 2045b618a185SCharles Keepax ret = wm_adsp1_setup_algs(dsp); 2046db40517cSMark Brown if (ret != 0) 2047078e7183SCharles Keepax goto err_ena; 2048db40517cSMark Brown 20492159ad93SMark Brown ret = wm_adsp_load_coeff(dsp); 20502159ad93SMark Brown if (ret != 0) 2051078e7183SCharles Keepax goto err_ena; 20522159ad93SMark Brown 20530c2e3f34SDimitris Papastamos /* Initialize caches for enabled and unset controls */ 205481ad93ecSDimitris Papastamos ret = wm_coeff_init_control_caches(dsp); 20556ab2b7b4SDimitris Papastamos if (ret != 0) 2056078e7183SCharles Keepax goto err_ena; 20576ab2b7b4SDimitris Papastamos 20580c2e3f34SDimitris Papastamos /* Sync set controls */ 205981ad93ecSDimitris Papastamos ret = wm_coeff_sync_controls(dsp); 20606ab2b7b4SDimitris Papastamos if (ret != 0) 2061078e7183SCharles Keepax goto err_ena; 20626ab2b7b4SDimitris Papastamos 20632159ad93SMark Brown /* Start the core running */ 20642159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 20652159ad93SMark Brown ADSP1_CORE_ENA | ADSP1_START, 20662159ad93SMark Brown ADSP1_CORE_ENA | ADSP1_START); 20672159ad93SMark Brown break; 20682159ad93SMark Brown 20692159ad93SMark Brown case SND_SOC_DAPM_PRE_PMD: 20702159ad93SMark Brown /* Halt the core */ 20712159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 20722159ad93SMark Brown ADSP1_CORE_ENA | ADSP1_START, 0); 20732159ad93SMark Brown 20742159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_19, 20752159ad93SMark Brown ADSP1_WDMA_BUFFER_LENGTH_MASK, 0); 20762159ad93SMark Brown 20772159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 20782159ad93SMark Brown ADSP1_SYS_ENA, 0); 20796ab2b7b4SDimitris Papastamos 208081ad93ecSDimitris Papastamos list_for_each_entry(ctl, &dsp->ctl_list, list) 20816ab2b7b4SDimitris Papastamos ctl->enabled = 0; 2082b0101b4fSDimitris Papastamos 2083b0101b4fSDimitris Papastamos while (!list_empty(&dsp->alg_regions)) { 2084b0101b4fSDimitris Papastamos alg_region = list_first_entry(&dsp->alg_regions, 2085b0101b4fSDimitris Papastamos struct wm_adsp_alg_region, 2086b0101b4fSDimitris Papastamos list); 2087b0101b4fSDimitris Papastamos list_del(&alg_region->list); 2088b0101b4fSDimitris Papastamos kfree(alg_region); 2089b0101b4fSDimitris Papastamos } 20902159ad93SMark Brown break; 20912159ad93SMark Brown 20922159ad93SMark Brown default: 20932159ad93SMark Brown break; 20942159ad93SMark Brown } 20952159ad93SMark Brown 2096078e7183SCharles Keepax mutex_unlock(&dsp->pwr_lock); 2097078e7183SCharles Keepax 20982159ad93SMark Brown return 0; 20992159ad93SMark Brown 2100078e7183SCharles Keepax err_ena: 21012159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 21022159ad93SMark Brown ADSP1_SYS_ENA, 0); 2103078e7183SCharles Keepax err_mutex: 2104078e7183SCharles Keepax mutex_unlock(&dsp->pwr_lock); 2105078e7183SCharles Keepax 21062159ad93SMark Brown return ret; 21072159ad93SMark Brown } 21082159ad93SMark Brown EXPORT_SYMBOL_GPL(wm_adsp1_event); 21092159ad93SMark Brown 21102159ad93SMark Brown static int wm_adsp2_ena(struct wm_adsp *dsp) 21112159ad93SMark Brown { 21122159ad93SMark Brown unsigned int val; 21132159ad93SMark Brown int ret, count; 21142159ad93SMark Brown 21151552c325SMark Brown ret = regmap_update_bits_async(dsp->regmap, dsp->base + ADSP2_CONTROL, 21162159ad93SMark Brown ADSP2_SYS_ENA, ADSP2_SYS_ENA); 21172159ad93SMark Brown if (ret != 0) 21182159ad93SMark Brown return ret; 21192159ad93SMark Brown 21202159ad93SMark Brown /* Wait for the RAM to start, should be near instantaneous */ 2121939fd1e8SCharles Keepax for (count = 0; count < 10; ++count) { 21222159ad93SMark Brown ret = regmap_read(dsp->regmap, dsp->base + ADSP2_STATUS1, 21232159ad93SMark Brown &val); 21242159ad93SMark Brown if (ret != 0) 21252159ad93SMark Brown return ret; 2126939fd1e8SCharles Keepax 2127939fd1e8SCharles Keepax if (val & ADSP2_RAM_RDY) 2128939fd1e8SCharles Keepax break; 2129939fd1e8SCharles Keepax 2130939fd1e8SCharles Keepax msleep(1); 2131939fd1e8SCharles Keepax } 21322159ad93SMark Brown 21332159ad93SMark Brown if (!(val & ADSP2_RAM_RDY)) { 21342159ad93SMark Brown adsp_err(dsp, "Failed to start DSP RAM\n"); 21352159ad93SMark Brown return -EBUSY; 21362159ad93SMark Brown } 21372159ad93SMark Brown 21382159ad93SMark Brown adsp_dbg(dsp, "RAM ready after %d polls\n", count); 21392159ad93SMark Brown 21402159ad93SMark Brown return 0; 21412159ad93SMark Brown } 21422159ad93SMark Brown 214318b1a902SCharles Keepax static void wm_adsp2_boot_work(struct work_struct *work) 21442159ad93SMark Brown { 2145d8a64d6aSCharles Keepax struct wm_adsp *dsp = container_of(work, 2146d8a64d6aSCharles Keepax struct wm_adsp, 2147d8a64d6aSCharles Keepax boot_work); 21482159ad93SMark Brown int ret; 2149d8a64d6aSCharles Keepax unsigned int val; 21502159ad93SMark Brown 2151078e7183SCharles Keepax mutex_lock(&dsp->pwr_lock); 2152078e7183SCharles Keepax 2153dd49e2c8SMark Brown /* 2154dd49e2c8SMark Brown * For simplicity set the DSP clock rate to be the 2155dd49e2c8SMark Brown * SYSCLK rate rather than making it configurable. 2156dd49e2c8SMark Brown */ 2157dd49e2c8SMark Brown ret = regmap_read(dsp->regmap, ARIZONA_SYSTEM_CLOCK_1, &val); 2158dd49e2c8SMark Brown if (ret != 0) { 2159d8a64d6aSCharles Keepax adsp_err(dsp, "Failed to read SYSCLK state: %d\n", ret); 2160078e7183SCharles Keepax goto err_mutex; 2161dd49e2c8SMark Brown } 2162dd49e2c8SMark Brown val = (val & ARIZONA_SYSCLK_FREQ_MASK) 2163dd49e2c8SMark Brown >> ARIZONA_SYSCLK_FREQ_SHIFT; 2164dd49e2c8SMark Brown 21651552c325SMark Brown ret = regmap_update_bits_async(dsp->regmap, 2166dd49e2c8SMark Brown dsp->base + ADSP2_CLOCKING, 2167dd49e2c8SMark Brown ADSP2_CLK_SEL_MASK, val); 2168dd49e2c8SMark Brown if (ret != 0) { 2169d8a64d6aSCharles Keepax adsp_err(dsp, "Failed to set clock rate: %d\n", ret); 2170078e7183SCharles Keepax goto err_mutex; 2171dd49e2c8SMark Brown } 2172dd49e2c8SMark Brown 21732159ad93SMark Brown ret = wm_adsp2_ena(dsp); 21742159ad93SMark Brown if (ret != 0) 2175078e7183SCharles Keepax goto err_mutex; 21762159ad93SMark Brown 21772159ad93SMark Brown ret = wm_adsp_load(dsp); 21782159ad93SMark Brown if (ret != 0) 2179078e7183SCharles Keepax goto err_ena; 21802159ad93SMark Brown 2181b618a185SCharles Keepax ret = wm_adsp2_setup_algs(dsp); 2182db40517cSMark Brown if (ret != 0) 2183078e7183SCharles Keepax goto err_ena; 2184db40517cSMark Brown 21852159ad93SMark Brown ret = wm_adsp_load_coeff(dsp); 21862159ad93SMark Brown if (ret != 0) 2187078e7183SCharles Keepax goto err_ena; 21882159ad93SMark Brown 21890c2e3f34SDimitris Papastamos /* Initialize caches for enabled and unset controls */ 219081ad93ecSDimitris Papastamos ret = wm_coeff_init_control_caches(dsp); 21916ab2b7b4SDimitris Papastamos if (ret != 0) 2192078e7183SCharles Keepax goto err_ena; 21936ab2b7b4SDimitris Papastamos 21940c2e3f34SDimitris Papastamos /* Sync set controls */ 219581ad93ecSDimitris Papastamos ret = wm_coeff_sync_controls(dsp); 21966ab2b7b4SDimitris Papastamos if (ret != 0) 2197078e7183SCharles Keepax goto err_ena; 21986ab2b7b4SDimitris Papastamos 21991023dbd9SMark Brown dsp->running = true; 2200d8a64d6aSCharles Keepax 2201078e7183SCharles Keepax mutex_unlock(&dsp->pwr_lock); 2202078e7183SCharles Keepax 2203d8a64d6aSCharles Keepax return; 2204d8a64d6aSCharles Keepax 2205078e7183SCharles Keepax err_ena: 2206d8a64d6aSCharles Keepax regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 2207d8a64d6aSCharles Keepax ADSP2_SYS_ENA | ADSP2_CORE_ENA | ADSP2_START, 0); 2208078e7183SCharles Keepax err_mutex: 2209078e7183SCharles Keepax mutex_unlock(&dsp->pwr_lock); 2210d8a64d6aSCharles Keepax } 2211d8a64d6aSCharles Keepax 221212db5eddSCharles Keepax int wm_adsp2_early_event(struct snd_soc_dapm_widget *w, 221312db5eddSCharles Keepax struct snd_kcontrol *kcontrol, int event) 221412db5eddSCharles Keepax { 221572718517SLars-Peter Clausen struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); 221612db5eddSCharles Keepax struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec); 221712db5eddSCharles Keepax struct wm_adsp *dsp = &dsps[w->shift]; 221812db5eddSCharles Keepax 221900200107SLars-Peter Clausen dsp->card = codec->component.card; 222012db5eddSCharles Keepax 222112db5eddSCharles Keepax switch (event) { 222212db5eddSCharles Keepax case SND_SOC_DAPM_PRE_PMU: 222312db5eddSCharles Keepax queue_work(system_unbound_wq, &dsp->boot_work); 222412db5eddSCharles Keepax break; 222512db5eddSCharles Keepax default: 222612db5eddSCharles Keepax break; 2227cab27258SCharles Keepax } 222812db5eddSCharles Keepax 222912db5eddSCharles Keepax return 0; 223012db5eddSCharles Keepax } 223112db5eddSCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp2_early_event); 223212db5eddSCharles Keepax 2233d8a64d6aSCharles Keepax int wm_adsp2_event(struct snd_soc_dapm_widget *w, 2234d8a64d6aSCharles Keepax struct snd_kcontrol *kcontrol, int event) 2235d8a64d6aSCharles Keepax { 223672718517SLars-Peter Clausen struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); 2237d8a64d6aSCharles Keepax struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec); 2238d8a64d6aSCharles Keepax struct wm_adsp *dsp = &dsps[w->shift]; 2239d8a64d6aSCharles Keepax struct wm_adsp_alg_region *alg_region; 2240d8a64d6aSCharles Keepax struct wm_coeff_ctl *ctl; 2241d8a64d6aSCharles Keepax int ret; 2242d8a64d6aSCharles Keepax 2243d8a64d6aSCharles Keepax switch (event) { 2244d8a64d6aSCharles Keepax case SND_SOC_DAPM_POST_PMU: 2245d8a64d6aSCharles Keepax flush_work(&dsp->boot_work); 2246d8a64d6aSCharles Keepax 2247d8a64d6aSCharles Keepax if (!dsp->running) 2248d8a64d6aSCharles Keepax return -EIO; 2249d8a64d6aSCharles Keepax 2250d8a64d6aSCharles Keepax ret = regmap_update_bits(dsp->regmap, 2251d8a64d6aSCharles Keepax dsp->base + ADSP2_CONTROL, 225200e4c3b6SCharles Keepax ADSP2_CORE_ENA | ADSP2_START, 225300e4c3b6SCharles Keepax ADSP2_CORE_ENA | ADSP2_START); 2254d8a64d6aSCharles Keepax if (ret != 0) 2255d8a64d6aSCharles Keepax goto err; 22562cd19bdbSCharles Keepax 22572cd19bdbSCharles Keepax if (wm_adsp_fw[dsp->fw].num_caps != 0) 22582cd19bdbSCharles Keepax ret = wm_adsp_buffer_init(dsp); 22592cd19bdbSCharles Keepax 22602159ad93SMark Brown break; 22612159ad93SMark Brown 22622159ad93SMark Brown case SND_SOC_DAPM_PRE_PMD: 226310337b07SRichard Fitzgerald /* Log firmware state, it can be useful for analysis */ 226410337b07SRichard Fitzgerald wm_adsp2_show_fw_status(dsp); 226510337b07SRichard Fitzgerald 2266078e7183SCharles Keepax mutex_lock(&dsp->pwr_lock); 2267078e7183SCharles Keepax 2268f9f55e31SRichard Fitzgerald wm_adsp_debugfs_clear(dsp); 2269f9f55e31SRichard Fitzgerald 2270f9f55e31SRichard Fitzgerald dsp->fw_id = 0; 2271f9f55e31SRichard Fitzgerald dsp->fw_id_version = 0; 22721023dbd9SMark Brown dsp->running = false; 22731023dbd9SMark Brown 22742159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 2275a7f9be7eSMark Brown ADSP2_SYS_ENA | ADSP2_CORE_ENA | 2276a7f9be7eSMark Brown ADSP2_START, 0); 2277973838a0SMark Brown 22782d30b575SMark Brown /* Make sure DMAs are quiesced */ 22792d30b575SMark Brown regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_1, 0); 22802d30b575SMark Brown regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_2, 0); 22812d30b575SMark Brown regmap_write(dsp->regmap, dsp->base + ADSP2_RDMA_CONFIG_1, 0); 22822d30b575SMark Brown 228381ad93ecSDimitris Papastamos list_for_each_entry(ctl, &dsp->ctl_list, list) 22846ab2b7b4SDimitris Papastamos ctl->enabled = 0; 22856ab2b7b4SDimitris Papastamos 2286471f4885SMark Brown while (!list_empty(&dsp->alg_regions)) { 2287471f4885SMark Brown alg_region = list_first_entry(&dsp->alg_regions, 2288471f4885SMark Brown struct wm_adsp_alg_region, 2289471f4885SMark Brown list); 2290471f4885SMark Brown list_del(&alg_region->list); 2291471f4885SMark Brown kfree(alg_region); 2292471f4885SMark Brown } 2293ddbc5efeSCharles Keepax 22942cd19bdbSCharles Keepax if (wm_adsp_fw[dsp->fw].num_caps != 0) 22952cd19bdbSCharles Keepax wm_adsp_buffer_free(dsp); 22962cd19bdbSCharles Keepax 2297078e7183SCharles Keepax mutex_unlock(&dsp->pwr_lock); 2298078e7183SCharles Keepax 2299ddbc5efeSCharles Keepax adsp_dbg(dsp, "Shutdown complete\n"); 23002159ad93SMark Brown break; 23012159ad93SMark Brown 23022159ad93SMark Brown default: 23032159ad93SMark Brown break; 23042159ad93SMark Brown } 23052159ad93SMark Brown 23062159ad93SMark Brown return 0; 23072159ad93SMark Brown err: 23082159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 2309a7f9be7eSMark Brown ADSP2_SYS_ENA | ADSP2_CORE_ENA | ADSP2_START, 0); 23102159ad93SMark Brown return ret; 23112159ad93SMark Brown } 23122159ad93SMark Brown EXPORT_SYMBOL_GPL(wm_adsp2_event); 2313973838a0SMark Brown 2314f5e2ce92SRichard Fitzgerald int wm_adsp2_codec_probe(struct wm_adsp *dsp, struct snd_soc_codec *codec) 2315f5e2ce92SRichard Fitzgerald { 2316f9f55e31SRichard Fitzgerald wm_adsp2_init_debugfs(dsp, codec); 2317f9f55e31SRichard Fitzgerald 2318218e5087SRichard Fitzgerald return snd_soc_add_codec_controls(codec, 2319336d0442SRichard Fitzgerald &wm_adsp_fw_controls[dsp->num - 1], 2320336d0442SRichard Fitzgerald 1); 2321f5e2ce92SRichard Fitzgerald } 2322f5e2ce92SRichard Fitzgerald EXPORT_SYMBOL_GPL(wm_adsp2_codec_probe); 2323f5e2ce92SRichard Fitzgerald 2324f5e2ce92SRichard Fitzgerald int wm_adsp2_codec_remove(struct wm_adsp *dsp, struct snd_soc_codec *codec) 2325f5e2ce92SRichard Fitzgerald { 2326f9f55e31SRichard Fitzgerald wm_adsp2_cleanup_debugfs(dsp); 2327f9f55e31SRichard Fitzgerald 2328f5e2ce92SRichard Fitzgerald return 0; 2329f5e2ce92SRichard Fitzgerald } 2330f5e2ce92SRichard Fitzgerald EXPORT_SYMBOL_GPL(wm_adsp2_codec_remove); 2331f5e2ce92SRichard Fitzgerald 233281ac58b1SRichard Fitzgerald int wm_adsp2_init(struct wm_adsp *dsp) 2333973838a0SMark Brown { 2334973838a0SMark Brown int ret; 2335973838a0SMark Brown 233610a2b662SMark Brown /* 233710a2b662SMark Brown * Disable the DSP memory by default when in reset for a small 233810a2b662SMark Brown * power saving. 233910a2b662SMark Brown */ 23403809f001SCharles Keepax ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 234110a2b662SMark Brown ADSP2_MEM_ENA, 0); 234210a2b662SMark Brown if (ret != 0) { 23433809f001SCharles Keepax adsp_err(dsp, "Failed to clear memory retention: %d\n", ret); 234410a2b662SMark Brown return ret; 234510a2b662SMark Brown } 234610a2b662SMark Brown 23473809f001SCharles Keepax INIT_LIST_HEAD(&dsp->alg_regions); 23483809f001SCharles Keepax INIT_LIST_HEAD(&dsp->ctl_list); 23493809f001SCharles Keepax INIT_WORK(&dsp->boot_work, wm_adsp2_boot_work); 23506ab2b7b4SDimitris Papastamos 2351078e7183SCharles Keepax mutex_init(&dsp->pwr_lock); 2352078e7183SCharles Keepax 2353973838a0SMark Brown return 0; 2354973838a0SMark Brown } 2355973838a0SMark Brown EXPORT_SYMBOL_GPL(wm_adsp2_init); 23560a37c6efSPraveen Diwakar 2357406abc95SCharles Keepax int wm_adsp_compr_open(struct wm_adsp *dsp, struct snd_compr_stream *stream) 2358406abc95SCharles Keepax { 2359406abc95SCharles Keepax struct wm_adsp_compr *compr; 2360406abc95SCharles Keepax int ret = 0; 2361406abc95SCharles Keepax 2362406abc95SCharles Keepax mutex_lock(&dsp->pwr_lock); 2363406abc95SCharles Keepax 2364406abc95SCharles Keepax if (wm_adsp_fw[dsp->fw].num_caps == 0) { 2365406abc95SCharles Keepax adsp_err(dsp, "Firmware does not support compressed API\n"); 2366406abc95SCharles Keepax ret = -ENXIO; 2367406abc95SCharles Keepax goto out; 2368406abc95SCharles Keepax } 2369406abc95SCharles Keepax 2370406abc95SCharles Keepax if (wm_adsp_fw[dsp->fw].compr_direction != stream->direction) { 2371406abc95SCharles Keepax adsp_err(dsp, "Firmware does not support stream direction\n"); 2372406abc95SCharles Keepax ret = -EINVAL; 2373406abc95SCharles Keepax goto out; 2374406abc95SCharles Keepax } 2375406abc95SCharles Keepax 237695fe9597SCharles Keepax if (dsp->compr) { 237795fe9597SCharles Keepax /* It is expect this limitation will be removed in future */ 237895fe9597SCharles Keepax adsp_err(dsp, "Only a single stream supported per DSP\n"); 237995fe9597SCharles Keepax ret = -EBUSY; 238095fe9597SCharles Keepax goto out; 238195fe9597SCharles Keepax } 238295fe9597SCharles Keepax 2383406abc95SCharles Keepax compr = kzalloc(sizeof(*compr), GFP_KERNEL); 2384406abc95SCharles Keepax if (!compr) { 2385406abc95SCharles Keepax ret = -ENOMEM; 2386406abc95SCharles Keepax goto out; 2387406abc95SCharles Keepax } 2388406abc95SCharles Keepax 2389406abc95SCharles Keepax compr->dsp = dsp; 2390406abc95SCharles Keepax compr->stream = stream; 2391406abc95SCharles Keepax 2392406abc95SCharles Keepax dsp->compr = compr; 2393406abc95SCharles Keepax 2394406abc95SCharles Keepax stream->runtime->private_data = compr; 2395406abc95SCharles Keepax 2396406abc95SCharles Keepax out: 2397406abc95SCharles Keepax mutex_unlock(&dsp->pwr_lock); 2398406abc95SCharles Keepax 2399406abc95SCharles Keepax return ret; 2400406abc95SCharles Keepax } 2401406abc95SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_open); 2402406abc95SCharles Keepax 2403406abc95SCharles Keepax int wm_adsp_compr_free(struct snd_compr_stream *stream) 2404406abc95SCharles Keepax { 2405406abc95SCharles Keepax struct wm_adsp_compr *compr = stream->runtime->private_data; 2406406abc95SCharles Keepax struct wm_adsp *dsp = compr->dsp; 2407406abc95SCharles Keepax 2408406abc95SCharles Keepax mutex_lock(&dsp->pwr_lock); 2409406abc95SCharles Keepax 2410406abc95SCharles Keepax dsp->compr = NULL; 2411406abc95SCharles Keepax 241283a40ce9SCharles Keepax kfree(compr->raw_buf); 2413406abc95SCharles Keepax kfree(compr); 2414406abc95SCharles Keepax 2415406abc95SCharles Keepax mutex_unlock(&dsp->pwr_lock); 2416406abc95SCharles Keepax 2417406abc95SCharles Keepax return 0; 2418406abc95SCharles Keepax } 2419406abc95SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_free); 2420406abc95SCharles Keepax 2421406abc95SCharles Keepax static int wm_adsp_compr_check_params(struct snd_compr_stream *stream, 2422406abc95SCharles Keepax struct snd_compr_params *params) 2423406abc95SCharles Keepax { 2424406abc95SCharles Keepax struct wm_adsp_compr *compr = stream->runtime->private_data; 2425406abc95SCharles Keepax struct wm_adsp *dsp = compr->dsp; 2426406abc95SCharles Keepax const struct wm_adsp_fw_caps *caps; 2427406abc95SCharles Keepax const struct snd_codec_desc *desc; 2428406abc95SCharles Keepax int i, j; 2429406abc95SCharles Keepax 2430406abc95SCharles Keepax if (params->buffer.fragment_size < WM_ADSP_MIN_FRAGMENT_SIZE || 2431406abc95SCharles Keepax params->buffer.fragment_size > WM_ADSP_MAX_FRAGMENT_SIZE || 2432406abc95SCharles Keepax params->buffer.fragments < WM_ADSP_MIN_FRAGMENTS || 2433406abc95SCharles Keepax params->buffer.fragments > WM_ADSP_MAX_FRAGMENTS || 2434406abc95SCharles Keepax params->buffer.fragment_size % WM_ADSP_DATA_WORD_SIZE) { 2435406abc95SCharles Keepax adsp_err(dsp, "Invalid buffer fragsize=%d fragments=%d\n", 2436406abc95SCharles Keepax params->buffer.fragment_size, 2437406abc95SCharles Keepax params->buffer.fragments); 2438406abc95SCharles Keepax 2439406abc95SCharles Keepax return -EINVAL; 2440406abc95SCharles Keepax } 2441406abc95SCharles Keepax 2442406abc95SCharles Keepax for (i = 0; i < wm_adsp_fw[dsp->fw].num_caps; i++) { 2443406abc95SCharles Keepax caps = &wm_adsp_fw[dsp->fw].caps[i]; 2444406abc95SCharles Keepax desc = &caps->desc; 2445406abc95SCharles Keepax 2446406abc95SCharles Keepax if (caps->id != params->codec.id) 2447406abc95SCharles Keepax continue; 2448406abc95SCharles Keepax 2449406abc95SCharles Keepax if (stream->direction == SND_COMPRESS_PLAYBACK) { 2450406abc95SCharles Keepax if (desc->max_ch < params->codec.ch_out) 2451406abc95SCharles Keepax continue; 2452406abc95SCharles Keepax } else { 2453406abc95SCharles Keepax if (desc->max_ch < params->codec.ch_in) 2454406abc95SCharles Keepax continue; 2455406abc95SCharles Keepax } 2456406abc95SCharles Keepax 2457406abc95SCharles Keepax if (!(desc->formats & (1 << params->codec.format))) 2458406abc95SCharles Keepax continue; 2459406abc95SCharles Keepax 2460406abc95SCharles Keepax for (j = 0; j < desc->num_sample_rates; ++j) 2461406abc95SCharles Keepax if (desc->sample_rates[j] == params->codec.sample_rate) 2462406abc95SCharles Keepax return 0; 2463406abc95SCharles Keepax } 2464406abc95SCharles Keepax 2465406abc95SCharles Keepax adsp_err(dsp, "Invalid params id=%u ch=%u,%u rate=%u fmt=%u\n", 2466406abc95SCharles Keepax params->codec.id, params->codec.ch_in, params->codec.ch_out, 2467406abc95SCharles Keepax params->codec.sample_rate, params->codec.format); 2468406abc95SCharles Keepax return -EINVAL; 2469406abc95SCharles Keepax } 2470406abc95SCharles Keepax 2471565ace46SCharles Keepax static inline unsigned int wm_adsp_compr_frag_words(struct wm_adsp_compr *compr) 2472565ace46SCharles Keepax { 2473565ace46SCharles Keepax return compr->size.fragment_size / WM_ADSP_DATA_WORD_SIZE; 2474565ace46SCharles Keepax } 2475565ace46SCharles Keepax 2476406abc95SCharles Keepax int wm_adsp_compr_set_params(struct snd_compr_stream *stream, 2477406abc95SCharles Keepax struct snd_compr_params *params) 2478406abc95SCharles Keepax { 2479406abc95SCharles Keepax struct wm_adsp_compr *compr = stream->runtime->private_data; 248083a40ce9SCharles Keepax unsigned int size; 2481406abc95SCharles Keepax int ret; 2482406abc95SCharles Keepax 2483406abc95SCharles Keepax ret = wm_adsp_compr_check_params(stream, params); 2484406abc95SCharles Keepax if (ret) 2485406abc95SCharles Keepax return ret; 2486406abc95SCharles Keepax 2487406abc95SCharles Keepax compr->size = params->buffer; 2488406abc95SCharles Keepax 2489406abc95SCharles Keepax adsp_dbg(compr->dsp, "fragment_size=%d fragments=%d\n", 2490406abc95SCharles Keepax compr->size.fragment_size, compr->size.fragments); 2491406abc95SCharles Keepax 249283a40ce9SCharles Keepax size = wm_adsp_compr_frag_words(compr) * sizeof(*compr->raw_buf); 249383a40ce9SCharles Keepax compr->raw_buf = kmalloc(size, GFP_DMA | GFP_KERNEL); 249483a40ce9SCharles Keepax if (!compr->raw_buf) 249583a40ce9SCharles Keepax return -ENOMEM; 249683a40ce9SCharles Keepax 2497406abc95SCharles Keepax return 0; 2498406abc95SCharles Keepax } 2499406abc95SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_set_params); 2500406abc95SCharles Keepax 2501406abc95SCharles Keepax int wm_adsp_compr_get_caps(struct snd_compr_stream *stream, 2502406abc95SCharles Keepax struct snd_compr_caps *caps) 2503406abc95SCharles Keepax { 2504406abc95SCharles Keepax struct wm_adsp_compr *compr = stream->runtime->private_data; 2505406abc95SCharles Keepax int fw = compr->dsp->fw; 2506406abc95SCharles Keepax int i; 2507406abc95SCharles Keepax 2508406abc95SCharles Keepax if (wm_adsp_fw[fw].caps) { 2509406abc95SCharles Keepax for (i = 0; i < wm_adsp_fw[fw].num_caps; i++) 2510406abc95SCharles Keepax caps->codecs[i] = wm_adsp_fw[fw].caps[i].id; 2511406abc95SCharles Keepax 2512406abc95SCharles Keepax caps->num_codecs = i; 2513406abc95SCharles Keepax caps->direction = wm_adsp_fw[fw].compr_direction; 2514406abc95SCharles Keepax 2515406abc95SCharles Keepax caps->min_fragment_size = WM_ADSP_MIN_FRAGMENT_SIZE; 2516406abc95SCharles Keepax caps->max_fragment_size = WM_ADSP_MAX_FRAGMENT_SIZE; 2517406abc95SCharles Keepax caps->min_fragments = WM_ADSP_MIN_FRAGMENTS; 2518406abc95SCharles Keepax caps->max_fragments = WM_ADSP_MAX_FRAGMENTS; 2519406abc95SCharles Keepax } 2520406abc95SCharles Keepax 2521406abc95SCharles Keepax return 0; 2522406abc95SCharles Keepax } 2523406abc95SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_get_caps); 2524406abc95SCharles Keepax 25252cd19bdbSCharles Keepax static int wm_adsp_read_data_block(struct wm_adsp *dsp, int mem_type, 25262cd19bdbSCharles Keepax unsigned int mem_addr, 25272cd19bdbSCharles Keepax unsigned int num_words, u32 *data) 25282cd19bdbSCharles Keepax { 25292cd19bdbSCharles Keepax struct wm_adsp_region const *mem = wm_adsp_find_region(dsp, mem_type); 25302cd19bdbSCharles Keepax unsigned int i, reg; 25312cd19bdbSCharles Keepax int ret; 25322cd19bdbSCharles Keepax 25332cd19bdbSCharles Keepax if (!mem) 25342cd19bdbSCharles Keepax return -EINVAL; 25352cd19bdbSCharles Keepax 25362cd19bdbSCharles Keepax reg = wm_adsp_region_to_reg(mem, mem_addr); 25372cd19bdbSCharles Keepax 25382cd19bdbSCharles Keepax ret = regmap_raw_read(dsp->regmap, reg, data, 25392cd19bdbSCharles Keepax sizeof(*data) * num_words); 25402cd19bdbSCharles Keepax if (ret < 0) 25412cd19bdbSCharles Keepax return ret; 25422cd19bdbSCharles Keepax 25432cd19bdbSCharles Keepax for (i = 0; i < num_words; ++i) 25442cd19bdbSCharles Keepax data[i] = be32_to_cpu(data[i]) & 0x00ffffffu; 25452cd19bdbSCharles Keepax 25462cd19bdbSCharles Keepax return 0; 25472cd19bdbSCharles Keepax } 25482cd19bdbSCharles Keepax 25492cd19bdbSCharles Keepax static inline int wm_adsp_read_data_word(struct wm_adsp *dsp, int mem_type, 25502cd19bdbSCharles Keepax unsigned int mem_addr, u32 *data) 25512cd19bdbSCharles Keepax { 25522cd19bdbSCharles Keepax return wm_adsp_read_data_block(dsp, mem_type, mem_addr, 1, data); 25532cd19bdbSCharles Keepax } 25542cd19bdbSCharles Keepax 25552cd19bdbSCharles Keepax static int wm_adsp_write_data_word(struct wm_adsp *dsp, int mem_type, 25562cd19bdbSCharles Keepax unsigned int mem_addr, u32 data) 25572cd19bdbSCharles Keepax { 25582cd19bdbSCharles Keepax struct wm_adsp_region const *mem = wm_adsp_find_region(dsp, mem_type); 25592cd19bdbSCharles Keepax unsigned int reg; 25602cd19bdbSCharles Keepax 25612cd19bdbSCharles Keepax if (!mem) 25622cd19bdbSCharles Keepax return -EINVAL; 25632cd19bdbSCharles Keepax 25642cd19bdbSCharles Keepax reg = wm_adsp_region_to_reg(mem, mem_addr); 25652cd19bdbSCharles Keepax 25662cd19bdbSCharles Keepax data = cpu_to_be32(data & 0x00ffffffu); 25672cd19bdbSCharles Keepax 25682cd19bdbSCharles Keepax return regmap_raw_write(dsp->regmap, reg, &data, sizeof(data)); 25692cd19bdbSCharles Keepax } 25702cd19bdbSCharles Keepax 25712cd19bdbSCharles Keepax static inline int wm_adsp_buffer_read(struct wm_adsp_compr_buf *buf, 25722cd19bdbSCharles Keepax unsigned int field_offset, u32 *data) 25732cd19bdbSCharles Keepax { 25742cd19bdbSCharles Keepax return wm_adsp_read_data_word(buf->dsp, WMFW_ADSP2_XM, 25752cd19bdbSCharles Keepax buf->host_buf_ptr + field_offset, data); 25762cd19bdbSCharles Keepax } 25772cd19bdbSCharles Keepax 25782cd19bdbSCharles Keepax static inline int wm_adsp_buffer_write(struct wm_adsp_compr_buf *buf, 25792cd19bdbSCharles Keepax unsigned int field_offset, u32 data) 25802cd19bdbSCharles Keepax { 25812cd19bdbSCharles Keepax return wm_adsp_write_data_word(buf->dsp, WMFW_ADSP2_XM, 25822cd19bdbSCharles Keepax buf->host_buf_ptr + field_offset, data); 25832cd19bdbSCharles Keepax } 25842cd19bdbSCharles Keepax 25852cd19bdbSCharles Keepax static int wm_adsp_buffer_locate(struct wm_adsp_compr_buf *buf) 25862cd19bdbSCharles Keepax { 25872cd19bdbSCharles Keepax struct wm_adsp_alg_region *alg_region; 25882cd19bdbSCharles Keepax struct wm_adsp *dsp = buf->dsp; 25892cd19bdbSCharles Keepax u32 xmalg, addr, magic; 25902cd19bdbSCharles Keepax int i, ret; 25912cd19bdbSCharles Keepax 25922cd19bdbSCharles Keepax alg_region = wm_adsp_find_alg_region(dsp, WMFW_ADSP2_XM, dsp->fw_id); 25932cd19bdbSCharles Keepax xmalg = sizeof(struct wm_adsp_system_config_xm_hdr) / sizeof(__be32); 25942cd19bdbSCharles Keepax 25952cd19bdbSCharles Keepax addr = alg_region->base + xmalg + ALG_XM_FIELD(magic); 25962cd19bdbSCharles Keepax ret = wm_adsp_read_data_word(dsp, WMFW_ADSP2_XM, addr, &magic); 25972cd19bdbSCharles Keepax if (ret < 0) 25982cd19bdbSCharles Keepax return ret; 25992cd19bdbSCharles Keepax 26002cd19bdbSCharles Keepax if (magic != WM_ADSP_ALG_XM_STRUCT_MAGIC) 26012cd19bdbSCharles Keepax return -EINVAL; 26022cd19bdbSCharles Keepax 26032cd19bdbSCharles Keepax addr = alg_region->base + xmalg + ALG_XM_FIELD(host_buf_ptr); 26042cd19bdbSCharles Keepax for (i = 0; i < 5; ++i) { 26052cd19bdbSCharles Keepax ret = wm_adsp_read_data_word(dsp, WMFW_ADSP2_XM, addr, 26062cd19bdbSCharles Keepax &buf->host_buf_ptr); 26072cd19bdbSCharles Keepax if (ret < 0) 26082cd19bdbSCharles Keepax return ret; 26092cd19bdbSCharles Keepax 26102cd19bdbSCharles Keepax if (buf->host_buf_ptr) 26112cd19bdbSCharles Keepax break; 26122cd19bdbSCharles Keepax 26132cd19bdbSCharles Keepax usleep_range(1000, 2000); 26142cd19bdbSCharles Keepax } 26152cd19bdbSCharles Keepax 26162cd19bdbSCharles Keepax if (!buf->host_buf_ptr) 26172cd19bdbSCharles Keepax return -EIO; 26182cd19bdbSCharles Keepax 26192cd19bdbSCharles Keepax adsp_dbg(dsp, "host_buf_ptr=%x\n", buf->host_buf_ptr); 26202cd19bdbSCharles Keepax 26212cd19bdbSCharles Keepax return 0; 26222cd19bdbSCharles Keepax } 26232cd19bdbSCharles Keepax 26242cd19bdbSCharles Keepax static int wm_adsp_buffer_populate(struct wm_adsp_compr_buf *buf) 26252cd19bdbSCharles Keepax { 26262cd19bdbSCharles Keepax const struct wm_adsp_fw_caps *caps = wm_adsp_fw[buf->dsp->fw].caps; 26272cd19bdbSCharles Keepax struct wm_adsp_buffer_region *region; 26282cd19bdbSCharles Keepax u32 offset = 0; 26292cd19bdbSCharles Keepax int i, ret; 26302cd19bdbSCharles Keepax 26312cd19bdbSCharles Keepax for (i = 0; i < caps->num_regions; ++i) { 26322cd19bdbSCharles Keepax region = &buf->regions[i]; 26332cd19bdbSCharles Keepax 26342cd19bdbSCharles Keepax region->offset = offset; 26352cd19bdbSCharles Keepax region->mem_type = caps->region_defs[i].mem_type; 26362cd19bdbSCharles Keepax 26372cd19bdbSCharles Keepax ret = wm_adsp_buffer_read(buf, caps->region_defs[i].base_offset, 26382cd19bdbSCharles Keepax ®ion->base_addr); 26392cd19bdbSCharles Keepax if (ret < 0) 26402cd19bdbSCharles Keepax return ret; 26412cd19bdbSCharles Keepax 26422cd19bdbSCharles Keepax ret = wm_adsp_buffer_read(buf, caps->region_defs[i].size_offset, 26432cd19bdbSCharles Keepax &offset); 26442cd19bdbSCharles Keepax if (ret < 0) 26452cd19bdbSCharles Keepax return ret; 26462cd19bdbSCharles Keepax 26472cd19bdbSCharles Keepax region->cumulative_size = offset; 26482cd19bdbSCharles Keepax 26492cd19bdbSCharles Keepax adsp_dbg(buf->dsp, 26502cd19bdbSCharles Keepax "region=%d type=%d base=%04x off=%04x size=%04x\n", 26512cd19bdbSCharles Keepax i, region->mem_type, region->base_addr, 26522cd19bdbSCharles Keepax region->offset, region->cumulative_size); 26532cd19bdbSCharles Keepax } 26542cd19bdbSCharles Keepax 26552cd19bdbSCharles Keepax return 0; 26562cd19bdbSCharles Keepax } 26572cd19bdbSCharles Keepax 26582cd19bdbSCharles Keepax static int wm_adsp_buffer_init(struct wm_adsp *dsp) 26592cd19bdbSCharles Keepax { 26602cd19bdbSCharles Keepax struct wm_adsp_compr_buf *buf; 26612cd19bdbSCharles Keepax int ret; 26622cd19bdbSCharles Keepax 26632cd19bdbSCharles Keepax buf = kzalloc(sizeof(*buf), GFP_KERNEL); 26642cd19bdbSCharles Keepax if (!buf) 26652cd19bdbSCharles Keepax return -ENOMEM; 26662cd19bdbSCharles Keepax 26672cd19bdbSCharles Keepax buf->dsp = dsp; 2668565ace46SCharles Keepax buf->read_index = -1; 2669565ace46SCharles Keepax buf->irq_count = 0xFFFFFFFF; 26702cd19bdbSCharles Keepax 26712cd19bdbSCharles Keepax ret = wm_adsp_buffer_locate(buf); 26722cd19bdbSCharles Keepax if (ret < 0) { 26732cd19bdbSCharles Keepax adsp_err(dsp, "Failed to acquire host buffer: %d\n", ret); 26742cd19bdbSCharles Keepax goto err_buffer; 26752cd19bdbSCharles Keepax } 26762cd19bdbSCharles Keepax 26772cd19bdbSCharles Keepax buf->regions = kcalloc(wm_adsp_fw[dsp->fw].caps->num_regions, 26782cd19bdbSCharles Keepax sizeof(*buf->regions), GFP_KERNEL); 26792cd19bdbSCharles Keepax if (!buf->regions) { 26802cd19bdbSCharles Keepax ret = -ENOMEM; 26812cd19bdbSCharles Keepax goto err_buffer; 26822cd19bdbSCharles Keepax } 26832cd19bdbSCharles Keepax 26842cd19bdbSCharles Keepax ret = wm_adsp_buffer_populate(buf); 26852cd19bdbSCharles Keepax if (ret < 0) { 26862cd19bdbSCharles Keepax adsp_err(dsp, "Failed to populate host buffer: %d\n", ret); 26872cd19bdbSCharles Keepax goto err_regions; 26882cd19bdbSCharles Keepax } 26892cd19bdbSCharles Keepax 26902cd19bdbSCharles Keepax dsp->buffer = buf; 26912cd19bdbSCharles Keepax 26922cd19bdbSCharles Keepax return 0; 26932cd19bdbSCharles Keepax 26942cd19bdbSCharles Keepax err_regions: 26952cd19bdbSCharles Keepax kfree(buf->regions); 26962cd19bdbSCharles Keepax err_buffer: 26972cd19bdbSCharles Keepax kfree(buf); 26982cd19bdbSCharles Keepax return ret; 26992cd19bdbSCharles Keepax } 27002cd19bdbSCharles Keepax 27012cd19bdbSCharles Keepax static int wm_adsp_buffer_free(struct wm_adsp *dsp) 27022cd19bdbSCharles Keepax { 27032cd19bdbSCharles Keepax if (dsp->buffer) { 27042cd19bdbSCharles Keepax kfree(dsp->buffer->regions); 27052cd19bdbSCharles Keepax kfree(dsp->buffer); 27062cd19bdbSCharles Keepax 27072cd19bdbSCharles Keepax dsp->buffer = NULL; 27082cd19bdbSCharles Keepax } 27092cd19bdbSCharles Keepax 27102cd19bdbSCharles Keepax return 0; 27112cd19bdbSCharles Keepax } 27122cd19bdbSCharles Keepax 271395fe9597SCharles Keepax static inline int wm_adsp_compr_attached(struct wm_adsp_compr *compr) 271495fe9597SCharles Keepax { 271595fe9597SCharles Keepax return compr->buf != NULL; 271695fe9597SCharles Keepax } 271795fe9597SCharles Keepax 271895fe9597SCharles Keepax static int wm_adsp_compr_attach(struct wm_adsp_compr *compr) 271995fe9597SCharles Keepax { 272095fe9597SCharles Keepax /* 272195fe9597SCharles Keepax * Note this will be more complex once each DSP can support multiple 272295fe9597SCharles Keepax * streams 272395fe9597SCharles Keepax */ 272495fe9597SCharles Keepax if (!compr->dsp->buffer) 272595fe9597SCharles Keepax return -EINVAL; 272695fe9597SCharles Keepax 272795fe9597SCharles Keepax compr->buf = compr->dsp->buffer; 272895fe9597SCharles Keepax 272995fe9597SCharles Keepax return 0; 273095fe9597SCharles Keepax } 273195fe9597SCharles Keepax 273295fe9597SCharles Keepax int wm_adsp_compr_trigger(struct snd_compr_stream *stream, int cmd) 273395fe9597SCharles Keepax { 273495fe9597SCharles Keepax struct wm_adsp_compr *compr = stream->runtime->private_data; 273595fe9597SCharles Keepax struct wm_adsp *dsp = compr->dsp; 273695fe9597SCharles Keepax int ret = 0; 273795fe9597SCharles Keepax 273895fe9597SCharles Keepax adsp_dbg(dsp, "Trigger: %d\n", cmd); 273995fe9597SCharles Keepax 274095fe9597SCharles Keepax mutex_lock(&dsp->pwr_lock); 274195fe9597SCharles Keepax 274295fe9597SCharles Keepax switch (cmd) { 274395fe9597SCharles Keepax case SNDRV_PCM_TRIGGER_START: 274495fe9597SCharles Keepax if (wm_adsp_compr_attached(compr)) 274595fe9597SCharles Keepax break; 274695fe9597SCharles Keepax 274795fe9597SCharles Keepax ret = wm_adsp_compr_attach(compr); 274895fe9597SCharles Keepax if (ret < 0) { 274995fe9597SCharles Keepax adsp_err(dsp, "Failed to link buffer and stream: %d\n", 275095fe9597SCharles Keepax ret); 275195fe9597SCharles Keepax break; 275295fe9597SCharles Keepax } 2753565ace46SCharles Keepax 2754565ace46SCharles Keepax /* Trigger the IRQ at one fragment of data */ 2755565ace46SCharles Keepax ret = wm_adsp_buffer_write(compr->buf, 2756565ace46SCharles Keepax HOST_BUFFER_FIELD(high_water_mark), 2757565ace46SCharles Keepax wm_adsp_compr_frag_words(compr)); 2758565ace46SCharles Keepax if (ret < 0) { 2759565ace46SCharles Keepax adsp_err(dsp, "Failed to set high water mark: %d\n", 2760565ace46SCharles Keepax ret); 2761565ace46SCharles Keepax break; 2762565ace46SCharles Keepax } 276395fe9597SCharles Keepax break; 276495fe9597SCharles Keepax case SNDRV_PCM_TRIGGER_STOP: 276595fe9597SCharles Keepax break; 276695fe9597SCharles Keepax default: 276795fe9597SCharles Keepax ret = -EINVAL; 276895fe9597SCharles Keepax break; 276995fe9597SCharles Keepax } 277095fe9597SCharles Keepax 277195fe9597SCharles Keepax mutex_unlock(&dsp->pwr_lock); 277295fe9597SCharles Keepax 277395fe9597SCharles Keepax return ret; 277495fe9597SCharles Keepax } 277595fe9597SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_trigger); 277695fe9597SCharles Keepax 2777565ace46SCharles Keepax static inline int wm_adsp_buffer_size(struct wm_adsp_compr_buf *buf) 2778565ace46SCharles Keepax { 2779565ace46SCharles Keepax int last_region = wm_adsp_fw[buf->dsp->fw].caps->num_regions - 1; 2780565ace46SCharles Keepax 2781565ace46SCharles Keepax return buf->regions[last_region].cumulative_size; 2782565ace46SCharles Keepax } 2783565ace46SCharles Keepax 2784565ace46SCharles Keepax static int wm_adsp_buffer_update_avail(struct wm_adsp_compr_buf *buf) 2785565ace46SCharles Keepax { 2786565ace46SCharles Keepax u32 next_read_index, next_write_index; 2787565ace46SCharles Keepax int write_index, read_index, avail; 2788565ace46SCharles Keepax int ret; 2789565ace46SCharles Keepax 2790565ace46SCharles Keepax /* Only sync read index if we haven't already read a valid index */ 2791565ace46SCharles Keepax if (buf->read_index < 0) { 2792565ace46SCharles Keepax ret = wm_adsp_buffer_read(buf, 2793565ace46SCharles Keepax HOST_BUFFER_FIELD(next_read_index), 2794565ace46SCharles Keepax &next_read_index); 2795565ace46SCharles Keepax if (ret < 0) 2796565ace46SCharles Keepax return ret; 2797565ace46SCharles Keepax 2798565ace46SCharles Keepax read_index = sign_extend32(next_read_index, 23); 2799565ace46SCharles Keepax 2800565ace46SCharles Keepax if (read_index < 0) { 2801565ace46SCharles Keepax adsp_dbg(buf->dsp, "Avail check on unstarted stream\n"); 2802565ace46SCharles Keepax return 0; 2803565ace46SCharles Keepax } 2804565ace46SCharles Keepax 2805565ace46SCharles Keepax buf->read_index = read_index; 2806565ace46SCharles Keepax } 2807565ace46SCharles Keepax 2808565ace46SCharles Keepax ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(next_write_index), 2809565ace46SCharles Keepax &next_write_index); 2810565ace46SCharles Keepax if (ret < 0) 2811565ace46SCharles Keepax return ret; 2812565ace46SCharles Keepax 2813565ace46SCharles Keepax write_index = sign_extend32(next_write_index, 23); 2814565ace46SCharles Keepax 2815565ace46SCharles Keepax avail = write_index - buf->read_index; 2816565ace46SCharles Keepax if (avail < 0) 2817565ace46SCharles Keepax avail += wm_adsp_buffer_size(buf); 2818565ace46SCharles Keepax 2819565ace46SCharles Keepax adsp_dbg(buf->dsp, "readindex=0x%x, writeindex=0x%x, avail=%d\n", 2820565ace46SCharles Keepax buf->read_index, write_index, avail); 2821565ace46SCharles Keepax 2822565ace46SCharles Keepax buf->avail = avail; 2823565ace46SCharles Keepax 2824565ace46SCharles Keepax return 0; 2825565ace46SCharles Keepax } 2826565ace46SCharles Keepax 2827565ace46SCharles Keepax int wm_adsp_compr_handle_irq(struct wm_adsp *dsp) 2828565ace46SCharles Keepax { 2829565ace46SCharles Keepax struct wm_adsp_compr_buf *buf = dsp->buffer; 283083a40ce9SCharles Keepax struct wm_adsp_compr *compr = dsp->compr; 2831565ace46SCharles Keepax int ret = 0; 2832565ace46SCharles Keepax 2833565ace46SCharles Keepax mutex_lock(&dsp->pwr_lock); 2834565ace46SCharles Keepax 2835565ace46SCharles Keepax if (!buf) { 2836565ace46SCharles Keepax adsp_err(dsp, "Spurious buffer IRQ\n"); 2837565ace46SCharles Keepax ret = -ENODEV; 2838565ace46SCharles Keepax goto out; 2839565ace46SCharles Keepax } 2840565ace46SCharles Keepax 2841565ace46SCharles Keepax adsp_dbg(dsp, "Handling buffer IRQ\n"); 2842565ace46SCharles Keepax 2843565ace46SCharles Keepax ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(error), &buf->error); 2844565ace46SCharles Keepax if (ret < 0) { 2845565ace46SCharles Keepax adsp_err(dsp, "Failed to check buffer error: %d\n", ret); 2846565ace46SCharles Keepax goto out; 2847565ace46SCharles Keepax } 2848565ace46SCharles Keepax if (buf->error != 0) { 2849565ace46SCharles Keepax adsp_err(dsp, "Buffer error occurred: %d\n", buf->error); 2850565ace46SCharles Keepax ret = -EIO; 2851565ace46SCharles Keepax goto out; 2852565ace46SCharles Keepax } 2853565ace46SCharles Keepax 2854565ace46SCharles Keepax ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(irq_count), 2855565ace46SCharles Keepax &buf->irq_count); 2856565ace46SCharles Keepax if (ret < 0) { 2857565ace46SCharles Keepax adsp_err(dsp, "Failed to get irq_count: %d\n", ret); 2858565ace46SCharles Keepax goto out; 2859565ace46SCharles Keepax } 2860565ace46SCharles Keepax 2861565ace46SCharles Keepax ret = wm_adsp_buffer_update_avail(buf); 2862565ace46SCharles Keepax if (ret < 0) { 2863565ace46SCharles Keepax adsp_err(dsp, "Error reading avail: %d\n", ret); 2864565ace46SCharles Keepax goto out; 2865565ace46SCharles Keepax } 2866565ace46SCharles Keepax 286783a40ce9SCharles Keepax if (compr->stream) 286883a40ce9SCharles Keepax snd_compr_fragment_elapsed(compr->stream); 286983a40ce9SCharles Keepax 2870565ace46SCharles Keepax out: 2871565ace46SCharles Keepax mutex_unlock(&dsp->pwr_lock); 2872565ace46SCharles Keepax 2873565ace46SCharles Keepax return ret; 2874565ace46SCharles Keepax } 2875565ace46SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_handle_irq); 2876565ace46SCharles Keepax 2877565ace46SCharles Keepax static int wm_adsp_buffer_reenable_irq(struct wm_adsp_compr_buf *buf) 2878565ace46SCharles Keepax { 2879565ace46SCharles Keepax if (buf->irq_count & 0x01) 2880565ace46SCharles Keepax return 0; 2881565ace46SCharles Keepax 2882565ace46SCharles Keepax adsp_dbg(buf->dsp, "Enable IRQ(0x%x) for next fragment\n", 2883565ace46SCharles Keepax buf->irq_count); 2884565ace46SCharles Keepax 2885565ace46SCharles Keepax buf->irq_count |= 0x01; 2886565ace46SCharles Keepax 2887565ace46SCharles Keepax return wm_adsp_buffer_write(buf, HOST_BUFFER_FIELD(irq_ack), 2888565ace46SCharles Keepax buf->irq_count); 2889565ace46SCharles Keepax } 2890565ace46SCharles Keepax 2891565ace46SCharles Keepax int wm_adsp_compr_pointer(struct snd_compr_stream *stream, 2892565ace46SCharles Keepax struct snd_compr_tstamp *tstamp) 2893565ace46SCharles Keepax { 2894565ace46SCharles Keepax struct wm_adsp_compr *compr = stream->runtime->private_data; 2895565ace46SCharles Keepax struct wm_adsp_compr_buf *buf = compr->buf; 2896565ace46SCharles Keepax struct wm_adsp *dsp = compr->dsp; 2897565ace46SCharles Keepax int ret = 0; 2898565ace46SCharles Keepax 2899565ace46SCharles Keepax adsp_dbg(dsp, "Pointer request\n"); 2900565ace46SCharles Keepax 2901565ace46SCharles Keepax mutex_lock(&dsp->pwr_lock); 2902565ace46SCharles Keepax 2903565ace46SCharles Keepax if (!compr->buf) { 2904565ace46SCharles Keepax ret = -ENXIO; 2905565ace46SCharles Keepax goto out; 2906565ace46SCharles Keepax } 2907565ace46SCharles Keepax 2908565ace46SCharles Keepax if (compr->buf->error) { 2909565ace46SCharles Keepax ret = -EIO; 2910565ace46SCharles Keepax goto out; 2911565ace46SCharles Keepax } 2912565ace46SCharles Keepax 2913565ace46SCharles Keepax if (buf->avail < wm_adsp_compr_frag_words(compr)) { 2914565ace46SCharles Keepax ret = wm_adsp_buffer_update_avail(buf); 2915565ace46SCharles Keepax if (ret < 0) { 2916565ace46SCharles Keepax adsp_err(dsp, "Error reading avail: %d\n", ret); 2917565ace46SCharles Keepax goto out; 2918565ace46SCharles Keepax } 2919565ace46SCharles Keepax 2920565ace46SCharles Keepax /* 2921565ace46SCharles Keepax * If we really have less than 1 fragment available tell the 2922565ace46SCharles Keepax * DSP to inform us once a whole fragment is available. 2923565ace46SCharles Keepax */ 2924565ace46SCharles Keepax if (buf->avail < wm_adsp_compr_frag_words(compr)) { 2925565ace46SCharles Keepax ret = wm_adsp_buffer_reenable_irq(buf); 2926565ace46SCharles Keepax if (ret < 0) { 2927565ace46SCharles Keepax adsp_err(dsp, 2928565ace46SCharles Keepax "Failed to re-enable buffer IRQ: %d\n", 2929565ace46SCharles Keepax ret); 2930565ace46SCharles Keepax goto out; 2931565ace46SCharles Keepax } 2932565ace46SCharles Keepax } 2933565ace46SCharles Keepax } 2934565ace46SCharles Keepax 2935565ace46SCharles Keepax tstamp->copied_total = compr->copied_total; 2936565ace46SCharles Keepax tstamp->copied_total += buf->avail * WM_ADSP_DATA_WORD_SIZE; 2937565ace46SCharles Keepax 2938565ace46SCharles Keepax out: 2939565ace46SCharles Keepax mutex_unlock(&dsp->pwr_lock); 2940565ace46SCharles Keepax 2941565ace46SCharles Keepax return ret; 2942565ace46SCharles Keepax } 2943565ace46SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_pointer); 2944565ace46SCharles Keepax 294583a40ce9SCharles Keepax static int wm_adsp_buffer_capture_block(struct wm_adsp_compr *compr, int target) 294683a40ce9SCharles Keepax { 294783a40ce9SCharles Keepax struct wm_adsp_compr_buf *buf = compr->buf; 294883a40ce9SCharles Keepax u8 *pack_in = (u8 *)compr->raw_buf; 294983a40ce9SCharles Keepax u8 *pack_out = (u8 *)compr->raw_buf; 295083a40ce9SCharles Keepax unsigned int adsp_addr; 295183a40ce9SCharles Keepax int mem_type, nwords, max_read; 295283a40ce9SCharles Keepax int i, j, ret; 295383a40ce9SCharles Keepax 295483a40ce9SCharles Keepax /* Calculate read parameters */ 295583a40ce9SCharles Keepax for (i = 0; i < wm_adsp_fw[buf->dsp->fw].caps->num_regions; ++i) 295683a40ce9SCharles Keepax if (buf->read_index < buf->regions[i].cumulative_size) 295783a40ce9SCharles Keepax break; 295883a40ce9SCharles Keepax 295983a40ce9SCharles Keepax if (i == wm_adsp_fw[buf->dsp->fw].caps->num_regions) 296083a40ce9SCharles Keepax return -EINVAL; 296183a40ce9SCharles Keepax 296283a40ce9SCharles Keepax mem_type = buf->regions[i].mem_type; 296383a40ce9SCharles Keepax adsp_addr = buf->regions[i].base_addr + 296483a40ce9SCharles Keepax (buf->read_index - buf->regions[i].offset); 296583a40ce9SCharles Keepax 296683a40ce9SCharles Keepax max_read = wm_adsp_compr_frag_words(compr); 296783a40ce9SCharles Keepax nwords = buf->regions[i].cumulative_size - buf->read_index; 296883a40ce9SCharles Keepax 296983a40ce9SCharles Keepax if (nwords > target) 297083a40ce9SCharles Keepax nwords = target; 297183a40ce9SCharles Keepax if (nwords > buf->avail) 297283a40ce9SCharles Keepax nwords = buf->avail; 297383a40ce9SCharles Keepax if (nwords > max_read) 297483a40ce9SCharles Keepax nwords = max_read; 297583a40ce9SCharles Keepax if (!nwords) 297683a40ce9SCharles Keepax return 0; 297783a40ce9SCharles Keepax 297883a40ce9SCharles Keepax /* Read data from DSP */ 297983a40ce9SCharles Keepax ret = wm_adsp_read_data_block(buf->dsp, mem_type, adsp_addr, 298083a40ce9SCharles Keepax nwords, compr->raw_buf); 298183a40ce9SCharles Keepax if (ret < 0) 298283a40ce9SCharles Keepax return ret; 298383a40ce9SCharles Keepax 298483a40ce9SCharles Keepax /* Remove the padding bytes from the data read from the DSP */ 298583a40ce9SCharles Keepax for (i = 0; i < nwords; i++) { 298683a40ce9SCharles Keepax for (j = 0; j < WM_ADSP_DATA_WORD_SIZE; j++) 298783a40ce9SCharles Keepax *pack_out++ = *pack_in++; 298883a40ce9SCharles Keepax 298983a40ce9SCharles Keepax pack_in += sizeof(*(compr->raw_buf)) - WM_ADSP_DATA_WORD_SIZE; 299083a40ce9SCharles Keepax } 299183a40ce9SCharles Keepax 299283a40ce9SCharles Keepax /* update read index to account for words read */ 299383a40ce9SCharles Keepax buf->read_index += nwords; 299483a40ce9SCharles Keepax if (buf->read_index == wm_adsp_buffer_size(buf)) 299583a40ce9SCharles Keepax buf->read_index = 0; 299683a40ce9SCharles Keepax 299783a40ce9SCharles Keepax ret = wm_adsp_buffer_write(buf, HOST_BUFFER_FIELD(next_read_index), 299883a40ce9SCharles Keepax buf->read_index); 299983a40ce9SCharles Keepax if (ret < 0) 300083a40ce9SCharles Keepax return ret; 300183a40ce9SCharles Keepax 300283a40ce9SCharles Keepax /* update avail to account for words read */ 300383a40ce9SCharles Keepax buf->avail -= nwords; 300483a40ce9SCharles Keepax 300583a40ce9SCharles Keepax return nwords; 300683a40ce9SCharles Keepax } 300783a40ce9SCharles Keepax 300883a40ce9SCharles Keepax static int wm_adsp_compr_read(struct wm_adsp_compr *compr, 300983a40ce9SCharles Keepax char __user *buf, size_t count) 301083a40ce9SCharles Keepax { 301183a40ce9SCharles Keepax struct wm_adsp *dsp = compr->dsp; 301283a40ce9SCharles Keepax int ntotal = 0; 301383a40ce9SCharles Keepax int nwords, nbytes; 301483a40ce9SCharles Keepax 301583a40ce9SCharles Keepax adsp_dbg(dsp, "Requested read of %zu bytes\n", count); 301683a40ce9SCharles Keepax 301783a40ce9SCharles Keepax if (!compr->buf) 301883a40ce9SCharles Keepax return -ENXIO; 301983a40ce9SCharles Keepax 302083a40ce9SCharles Keepax if (compr->buf->error) 302183a40ce9SCharles Keepax return -EIO; 302283a40ce9SCharles Keepax 302383a40ce9SCharles Keepax count /= WM_ADSP_DATA_WORD_SIZE; 302483a40ce9SCharles Keepax 302583a40ce9SCharles Keepax do { 302683a40ce9SCharles Keepax nwords = wm_adsp_buffer_capture_block(compr, count); 302783a40ce9SCharles Keepax if (nwords < 0) { 302883a40ce9SCharles Keepax adsp_err(dsp, "Failed to capture block: %d\n", nwords); 302983a40ce9SCharles Keepax return nwords; 303083a40ce9SCharles Keepax } 303183a40ce9SCharles Keepax 303283a40ce9SCharles Keepax nbytes = nwords * WM_ADSP_DATA_WORD_SIZE; 303383a40ce9SCharles Keepax 303483a40ce9SCharles Keepax adsp_dbg(dsp, "Read %d bytes\n", nbytes); 303583a40ce9SCharles Keepax 303683a40ce9SCharles Keepax if (copy_to_user(buf + ntotal, compr->raw_buf, nbytes)) { 303783a40ce9SCharles Keepax adsp_err(dsp, "Failed to copy data to user: %d, %d\n", 303883a40ce9SCharles Keepax ntotal, nbytes); 303983a40ce9SCharles Keepax return -EFAULT; 304083a40ce9SCharles Keepax } 304183a40ce9SCharles Keepax 304283a40ce9SCharles Keepax count -= nwords; 304383a40ce9SCharles Keepax ntotal += nbytes; 304483a40ce9SCharles Keepax } while (nwords > 0 && count > 0); 304583a40ce9SCharles Keepax 304683a40ce9SCharles Keepax compr->copied_total += ntotal; 304783a40ce9SCharles Keepax 304883a40ce9SCharles Keepax return ntotal; 304983a40ce9SCharles Keepax } 305083a40ce9SCharles Keepax 305183a40ce9SCharles Keepax int wm_adsp_compr_copy(struct snd_compr_stream *stream, char __user *buf, 305283a40ce9SCharles Keepax size_t count) 305383a40ce9SCharles Keepax { 305483a40ce9SCharles Keepax struct wm_adsp_compr *compr = stream->runtime->private_data; 305583a40ce9SCharles Keepax struct wm_adsp *dsp = compr->dsp; 305683a40ce9SCharles Keepax int ret; 305783a40ce9SCharles Keepax 305883a40ce9SCharles Keepax mutex_lock(&dsp->pwr_lock); 305983a40ce9SCharles Keepax 306083a40ce9SCharles Keepax if (stream->direction == SND_COMPRESS_CAPTURE) 306183a40ce9SCharles Keepax ret = wm_adsp_compr_read(compr, buf, count); 306283a40ce9SCharles Keepax else 306383a40ce9SCharles Keepax ret = -ENOTSUPP; 306483a40ce9SCharles Keepax 306583a40ce9SCharles Keepax mutex_unlock(&dsp->pwr_lock); 306683a40ce9SCharles Keepax 306783a40ce9SCharles Keepax return ret; 306883a40ce9SCharles Keepax } 306983a40ce9SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_copy); 307083a40ce9SCharles Keepax 30710a37c6efSPraveen Diwakar MODULE_LICENSE("GPL v2"); 3072