12159ad93SMark Brown /* 22159ad93SMark Brown * wm_adsp.c -- Wolfson ADSP support 32159ad93SMark Brown * 42159ad93SMark Brown * Copyright 2012 Wolfson Microelectronics plc 52159ad93SMark Brown * 62159ad93SMark Brown * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> 72159ad93SMark Brown * 82159ad93SMark Brown * This program is free software; you can redistribute it and/or modify 92159ad93SMark Brown * it under the terms of the GNU General Public License version 2 as 102159ad93SMark Brown * published by the Free Software Foundation. 112159ad93SMark Brown */ 122159ad93SMark Brown 132159ad93SMark Brown #include <linux/module.h> 142159ad93SMark Brown #include <linux/moduleparam.h> 152159ad93SMark Brown #include <linux/init.h> 162159ad93SMark Brown #include <linux/delay.h> 172159ad93SMark Brown #include <linux/firmware.h> 18cf17c83cSMark Brown #include <linux/list.h> 192159ad93SMark Brown #include <linux/pm.h> 202159ad93SMark Brown #include <linux/pm_runtime.h> 212159ad93SMark Brown #include <linux/regmap.h> 22973838a0SMark Brown #include <linux/regulator/consumer.h> 232159ad93SMark Brown #include <linux/slab.h> 24cdcd7f72SCharles Keepax #include <linux/vmalloc.h> 256ab2b7b4SDimitris Papastamos #include <linux/workqueue.h> 26f9f55e31SRichard Fitzgerald #include <linux/debugfs.h> 272159ad93SMark Brown #include <sound/core.h> 282159ad93SMark Brown #include <sound/pcm.h> 292159ad93SMark Brown #include <sound/pcm_params.h> 302159ad93SMark Brown #include <sound/soc.h> 312159ad93SMark Brown #include <sound/jack.h> 322159ad93SMark Brown #include <sound/initval.h> 332159ad93SMark Brown #include <sound/tlv.h> 342159ad93SMark Brown 352159ad93SMark Brown #include "wm_adsp.h" 362159ad93SMark Brown 372159ad93SMark Brown #define adsp_crit(_dsp, fmt, ...) \ 382159ad93SMark Brown dev_crit(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__) 392159ad93SMark Brown #define adsp_err(_dsp, fmt, ...) \ 402159ad93SMark Brown dev_err(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__) 412159ad93SMark Brown #define adsp_warn(_dsp, fmt, ...) \ 422159ad93SMark Brown dev_warn(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__) 432159ad93SMark Brown #define adsp_info(_dsp, fmt, ...) \ 442159ad93SMark Brown dev_info(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__) 452159ad93SMark Brown #define adsp_dbg(_dsp, fmt, ...) \ 462159ad93SMark Brown dev_dbg(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__) 472159ad93SMark Brown 482159ad93SMark Brown #define ADSP1_CONTROL_1 0x00 492159ad93SMark Brown #define ADSP1_CONTROL_2 0x02 502159ad93SMark Brown #define ADSP1_CONTROL_3 0x03 512159ad93SMark Brown #define ADSP1_CONTROL_4 0x04 522159ad93SMark Brown #define ADSP1_CONTROL_5 0x06 532159ad93SMark Brown #define ADSP1_CONTROL_6 0x07 542159ad93SMark Brown #define ADSP1_CONTROL_7 0x08 552159ad93SMark Brown #define ADSP1_CONTROL_8 0x09 562159ad93SMark Brown #define ADSP1_CONTROL_9 0x0A 572159ad93SMark Brown #define ADSP1_CONTROL_10 0x0B 582159ad93SMark Brown #define ADSP1_CONTROL_11 0x0C 592159ad93SMark Brown #define ADSP1_CONTROL_12 0x0D 602159ad93SMark Brown #define ADSP1_CONTROL_13 0x0F 612159ad93SMark Brown #define ADSP1_CONTROL_14 0x10 622159ad93SMark Brown #define ADSP1_CONTROL_15 0x11 632159ad93SMark Brown #define ADSP1_CONTROL_16 0x12 642159ad93SMark Brown #define ADSP1_CONTROL_17 0x13 652159ad93SMark Brown #define ADSP1_CONTROL_18 0x14 662159ad93SMark Brown #define ADSP1_CONTROL_19 0x16 672159ad93SMark Brown #define ADSP1_CONTROL_20 0x17 682159ad93SMark Brown #define ADSP1_CONTROL_21 0x18 692159ad93SMark Brown #define ADSP1_CONTROL_22 0x1A 702159ad93SMark Brown #define ADSP1_CONTROL_23 0x1B 712159ad93SMark Brown #define ADSP1_CONTROL_24 0x1C 722159ad93SMark Brown #define ADSP1_CONTROL_25 0x1E 732159ad93SMark Brown #define ADSP1_CONTROL_26 0x20 742159ad93SMark Brown #define ADSP1_CONTROL_27 0x21 752159ad93SMark Brown #define ADSP1_CONTROL_28 0x22 762159ad93SMark Brown #define ADSP1_CONTROL_29 0x23 772159ad93SMark Brown #define ADSP1_CONTROL_30 0x24 782159ad93SMark Brown #define ADSP1_CONTROL_31 0x26 792159ad93SMark Brown 802159ad93SMark Brown /* 812159ad93SMark Brown * ADSP1 Control 19 822159ad93SMark Brown */ 832159ad93SMark Brown #define ADSP1_WDMA_BUFFER_LENGTH_MASK 0x00FF /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */ 842159ad93SMark Brown #define ADSP1_WDMA_BUFFER_LENGTH_SHIFT 0 /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */ 852159ad93SMark Brown #define ADSP1_WDMA_BUFFER_LENGTH_WIDTH 8 /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */ 862159ad93SMark Brown 872159ad93SMark Brown 882159ad93SMark Brown /* 892159ad93SMark Brown * ADSP1 Control 30 902159ad93SMark Brown */ 912159ad93SMark Brown #define ADSP1_DBG_CLK_ENA 0x0008 /* DSP1_DBG_CLK_ENA */ 922159ad93SMark Brown #define ADSP1_DBG_CLK_ENA_MASK 0x0008 /* DSP1_DBG_CLK_ENA */ 932159ad93SMark Brown #define ADSP1_DBG_CLK_ENA_SHIFT 3 /* DSP1_DBG_CLK_ENA */ 942159ad93SMark Brown #define ADSP1_DBG_CLK_ENA_WIDTH 1 /* DSP1_DBG_CLK_ENA */ 952159ad93SMark Brown #define ADSP1_SYS_ENA 0x0004 /* DSP1_SYS_ENA */ 962159ad93SMark Brown #define ADSP1_SYS_ENA_MASK 0x0004 /* DSP1_SYS_ENA */ 972159ad93SMark Brown #define ADSP1_SYS_ENA_SHIFT 2 /* DSP1_SYS_ENA */ 982159ad93SMark Brown #define ADSP1_SYS_ENA_WIDTH 1 /* DSP1_SYS_ENA */ 992159ad93SMark Brown #define ADSP1_CORE_ENA 0x0002 /* DSP1_CORE_ENA */ 1002159ad93SMark Brown #define ADSP1_CORE_ENA_MASK 0x0002 /* DSP1_CORE_ENA */ 1012159ad93SMark Brown #define ADSP1_CORE_ENA_SHIFT 1 /* DSP1_CORE_ENA */ 1022159ad93SMark Brown #define ADSP1_CORE_ENA_WIDTH 1 /* DSP1_CORE_ENA */ 1032159ad93SMark Brown #define ADSP1_START 0x0001 /* DSP1_START */ 1042159ad93SMark Brown #define ADSP1_START_MASK 0x0001 /* DSP1_START */ 1052159ad93SMark Brown #define ADSP1_START_SHIFT 0 /* DSP1_START */ 1062159ad93SMark Brown #define ADSP1_START_WIDTH 1 /* DSP1_START */ 1072159ad93SMark Brown 10894e205bfSChris Rattray /* 10994e205bfSChris Rattray * ADSP1 Control 31 11094e205bfSChris Rattray */ 11194e205bfSChris Rattray #define ADSP1_CLK_SEL_MASK 0x0007 /* CLK_SEL_ENA */ 11294e205bfSChris Rattray #define ADSP1_CLK_SEL_SHIFT 0 /* CLK_SEL_ENA */ 11394e205bfSChris Rattray #define ADSP1_CLK_SEL_WIDTH 3 /* CLK_SEL_ENA */ 11494e205bfSChris Rattray 1152d30b575SMark Brown #define ADSP2_CONTROL 0x0 1162d30b575SMark Brown #define ADSP2_CLOCKING 0x1 1172d30b575SMark Brown #define ADSP2_STATUS1 0x4 1182d30b575SMark Brown #define ADSP2_WDMA_CONFIG_1 0x30 1192d30b575SMark Brown #define ADSP2_WDMA_CONFIG_2 0x31 1202d30b575SMark Brown #define ADSP2_RDMA_CONFIG_1 0x34 1212159ad93SMark Brown 12210337b07SRichard Fitzgerald #define ADSP2_SCRATCH0 0x40 12310337b07SRichard Fitzgerald #define ADSP2_SCRATCH1 0x41 12410337b07SRichard Fitzgerald #define ADSP2_SCRATCH2 0x42 12510337b07SRichard Fitzgerald #define ADSP2_SCRATCH3 0x43 12610337b07SRichard Fitzgerald 1272159ad93SMark Brown /* 1282159ad93SMark Brown * ADSP2 Control 1292159ad93SMark Brown */ 1302159ad93SMark Brown 1312159ad93SMark Brown #define ADSP2_MEM_ENA 0x0010 /* DSP1_MEM_ENA */ 1322159ad93SMark Brown #define ADSP2_MEM_ENA_MASK 0x0010 /* DSP1_MEM_ENA */ 1332159ad93SMark Brown #define ADSP2_MEM_ENA_SHIFT 4 /* DSP1_MEM_ENA */ 1342159ad93SMark Brown #define ADSP2_MEM_ENA_WIDTH 1 /* DSP1_MEM_ENA */ 1352159ad93SMark Brown #define ADSP2_SYS_ENA 0x0004 /* DSP1_SYS_ENA */ 1362159ad93SMark Brown #define ADSP2_SYS_ENA_MASK 0x0004 /* DSP1_SYS_ENA */ 1372159ad93SMark Brown #define ADSP2_SYS_ENA_SHIFT 2 /* DSP1_SYS_ENA */ 1382159ad93SMark Brown #define ADSP2_SYS_ENA_WIDTH 1 /* DSP1_SYS_ENA */ 1392159ad93SMark Brown #define ADSP2_CORE_ENA 0x0002 /* DSP1_CORE_ENA */ 1402159ad93SMark Brown #define ADSP2_CORE_ENA_MASK 0x0002 /* DSP1_CORE_ENA */ 1412159ad93SMark Brown #define ADSP2_CORE_ENA_SHIFT 1 /* DSP1_CORE_ENA */ 1422159ad93SMark Brown #define ADSP2_CORE_ENA_WIDTH 1 /* DSP1_CORE_ENA */ 1432159ad93SMark Brown #define ADSP2_START 0x0001 /* DSP1_START */ 1442159ad93SMark Brown #define ADSP2_START_MASK 0x0001 /* DSP1_START */ 1452159ad93SMark Brown #define ADSP2_START_SHIFT 0 /* DSP1_START */ 1462159ad93SMark Brown #define ADSP2_START_WIDTH 1 /* DSP1_START */ 1472159ad93SMark Brown 1482159ad93SMark Brown /* 149973838a0SMark Brown * ADSP2 clocking 150973838a0SMark Brown */ 151973838a0SMark Brown #define ADSP2_CLK_SEL_MASK 0x0007 /* CLK_SEL_ENA */ 152973838a0SMark Brown #define ADSP2_CLK_SEL_SHIFT 0 /* CLK_SEL_ENA */ 153973838a0SMark Brown #define ADSP2_CLK_SEL_WIDTH 3 /* CLK_SEL_ENA */ 154973838a0SMark Brown 155973838a0SMark Brown /* 1562159ad93SMark Brown * ADSP2 Status 1 1572159ad93SMark Brown */ 1582159ad93SMark Brown #define ADSP2_RAM_RDY 0x0001 1592159ad93SMark Brown #define ADSP2_RAM_RDY_MASK 0x0001 1602159ad93SMark Brown #define ADSP2_RAM_RDY_SHIFT 0 1612159ad93SMark Brown #define ADSP2_RAM_RDY_WIDTH 1 1622159ad93SMark Brown 1639ee78757SCharles Keepax #define ADSP_MAX_STD_CTRL_SIZE 512 1649ee78757SCharles Keepax 165f4f0c4c6SRichard Fitzgerald #define WM_ADSP_ACKED_CTL_TIMEOUT_MS 100 166f4f0c4c6SRichard Fitzgerald #define WM_ADSP_ACKED_CTL_N_QUICKPOLLS 10 167a23ebba8SRichard Fitzgerald #define WM_ADSP_ACKED_CTL_MIN_VALUE 0 168a23ebba8SRichard Fitzgerald #define WM_ADSP_ACKED_CTL_MAX_VALUE 0xFFFFFF 169f4f0c4c6SRichard Fitzgerald 170f4f0c4c6SRichard Fitzgerald /* 171f4f0c4c6SRichard Fitzgerald * Event control messages 172f4f0c4c6SRichard Fitzgerald */ 173f4f0c4c6SRichard Fitzgerald #define WM_ADSP_FW_EVENT_SHUTDOWN 0x000001 174f4f0c4c6SRichard Fitzgerald 175cf17c83cSMark Brown struct wm_adsp_buf { 176cf17c83cSMark Brown struct list_head list; 177cf17c83cSMark Brown void *buf; 178cf17c83cSMark Brown }; 179cf17c83cSMark Brown 180cf17c83cSMark Brown static struct wm_adsp_buf *wm_adsp_buf_alloc(const void *src, size_t len, 181cf17c83cSMark Brown struct list_head *list) 182cf17c83cSMark Brown { 183cf17c83cSMark Brown struct wm_adsp_buf *buf = kzalloc(sizeof(*buf), GFP_KERNEL); 184cf17c83cSMark Brown 185cf17c83cSMark Brown if (buf == NULL) 186cf17c83cSMark Brown return NULL; 187cf17c83cSMark Brown 188cdcd7f72SCharles Keepax buf->buf = vmalloc(len); 189cf17c83cSMark Brown if (!buf->buf) { 190cdcd7f72SCharles Keepax vfree(buf); 191cf17c83cSMark Brown return NULL; 192cf17c83cSMark Brown } 193cdcd7f72SCharles Keepax memcpy(buf->buf, src, len); 194cf17c83cSMark Brown 195cf17c83cSMark Brown if (list) 196cf17c83cSMark Brown list_add_tail(&buf->list, list); 197cf17c83cSMark Brown 198cf17c83cSMark Brown return buf; 199cf17c83cSMark Brown } 200cf17c83cSMark Brown 201cf17c83cSMark Brown static void wm_adsp_buf_free(struct list_head *list) 202cf17c83cSMark Brown { 203cf17c83cSMark Brown while (!list_empty(list)) { 204cf17c83cSMark Brown struct wm_adsp_buf *buf = list_first_entry(list, 205cf17c83cSMark Brown struct wm_adsp_buf, 206cf17c83cSMark Brown list); 207cf17c83cSMark Brown list_del(&buf->list); 208cdcd7f72SCharles Keepax vfree(buf->buf); 209cf17c83cSMark Brown kfree(buf); 210cf17c83cSMark Brown } 211cf17c83cSMark Brown } 212cf17c83cSMark Brown 213dd84f925SMark Brown #define WM_ADSP_FW_MBC_VSS 0 21404d1300fSCharles Keepax #define WM_ADSP_FW_HIFI 1 21504d1300fSCharles Keepax #define WM_ADSP_FW_TX 2 21604d1300fSCharles Keepax #define WM_ADSP_FW_TX_SPK 3 21704d1300fSCharles Keepax #define WM_ADSP_FW_RX 4 21804d1300fSCharles Keepax #define WM_ADSP_FW_RX_ANC 5 21904d1300fSCharles Keepax #define WM_ADSP_FW_CTRL 6 22004d1300fSCharles Keepax #define WM_ADSP_FW_ASR 7 22104d1300fSCharles Keepax #define WM_ADSP_FW_TRACE 8 22204d1300fSCharles Keepax #define WM_ADSP_FW_SPK_PROT 9 22304d1300fSCharles Keepax #define WM_ADSP_FW_MISC 10 22404d1300fSCharles Keepax 22504d1300fSCharles Keepax #define WM_ADSP_NUM_FW 11 226dd84f925SMark Brown 2271023dbd9SMark Brown static const char *wm_adsp_fw_text[WM_ADSP_NUM_FW] = { 228dd84f925SMark Brown [WM_ADSP_FW_MBC_VSS] = "MBC/VSS", 22904d1300fSCharles Keepax [WM_ADSP_FW_HIFI] = "MasterHiFi", 230dd84f925SMark Brown [WM_ADSP_FW_TX] = "Tx", 231dd84f925SMark Brown [WM_ADSP_FW_TX_SPK] = "Tx Speaker", 23204d1300fSCharles Keepax [WM_ADSP_FW_RX] = "Rx", 233dd84f925SMark Brown [WM_ADSP_FW_RX_ANC] = "Rx ANC", 23404d1300fSCharles Keepax [WM_ADSP_FW_CTRL] = "Voice Ctrl", 23504d1300fSCharles Keepax [WM_ADSP_FW_ASR] = "ASR Assist", 23604d1300fSCharles Keepax [WM_ADSP_FW_TRACE] = "Dbg Trace", 23704d1300fSCharles Keepax [WM_ADSP_FW_SPK_PROT] = "Protection", 23804d1300fSCharles Keepax [WM_ADSP_FW_MISC] = "Misc", 2391023dbd9SMark Brown }; 2401023dbd9SMark Brown 2412cd19bdbSCharles Keepax struct wm_adsp_system_config_xm_hdr { 2422cd19bdbSCharles Keepax __be32 sys_enable; 2432cd19bdbSCharles Keepax __be32 fw_id; 2442cd19bdbSCharles Keepax __be32 fw_rev; 2452cd19bdbSCharles Keepax __be32 boot_status; 2462cd19bdbSCharles Keepax __be32 watchdog; 2472cd19bdbSCharles Keepax __be32 dma_buffer_size; 2482cd19bdbSCharles Keepax __be32 rdma[6]; 2492cd19bdbSCharles Keepax __be32 wdma[8]; 2502cd19bdbSCharles Keepax __be32 build_job_name[3]; 2512cd19bdbSCharles Keepax __be32 build_job_number; 2522cd19bdbSCharles Keepax }; 2532cd19bdbSCharles Keepax 2542cd19bdbSCharles Keepax struct wm_adsp_alg_xm_struct { 2552cd19bdbSCharles Keepax __be32 magic; 2562cd19bdbSCharles Keepax __be32 smoothing; 2572cd19bdbSCharles Keepax __be32 threshold; 2582cd19bdbSCharles Keepax __be32 host_buf_ptr; 2592cd19bdbSCharles Keepax __be32 start_seq; 2602cd19bdbSCharles Keepax __be32 high_water_mark; 2612cd19bdbSCharles Keepax __be32 low_water_mark; 2622cd19bdbSCharles Keepax __be64 smoothed_power; 2632cd19bdbSCharles Keepax }; 2642cd19bdbSCharles Keepax 2652cd19bdbSCharles Keepax struct wm_adsp_buffer { 2662cd19bdbSCharles Keepax __be32 X_buf_base; /* XM base addr of first X area */ 2672cd19bdbSCharles Keepax __be32 X_buf_size; /* Size of 1st X area in words */ 2682cd19bdbSCharles Keepax __be32 X_buf_base2; /* XM base addr of 2nd X area */ 2692cd19bdbSCharles Keepax __be32 X_buf_brk; /* Total X size in words */ 2702cd19bdbSCharles Keepax __be32 Y_buf_base; /* YM base addr of Y area */ 2712cd19bdbSCharles Keepax __be32 wrap; /* Total size X and Y in words */ 2722cd19bdbSCharles Keepax __be32 high_water_mark; /* Point at which IRQ is asserted */ 2732cd19bdbSCharles Keepax __be32 irq_count; /* bits 1-31 count IRQ assertions */ 2742cd19bdbSCharles Keepax __be32 irq_ack; /* acked IRQ count, bit 0 enables IRQ */ 2752cd19bdbSCharles Keepax __be32 next_write_index; /* word index of next write */ 2762cd19bdbSCharles Keepax __be32 next_read_index; /* word index of next read */ 2772cd19bdbSCharles Keepax __be32 error; /* error if any */ 2782cd19bdbSCharles Keepax __be32 oldest_block_index; /* word index of oldest surviving */ 2792cd19bdbSCharles Keepax __be32 requested_rewind; /* how many blocks rewind was done */ 2802cd19bdbSCharles Keepax __be32 reserved_space; /* internal */ 2812cd19bdbSCharles Keepax __be32 min_free; /* min free space since stream start */ 2822cd19bdbSCharles Keepax __be32 blocks_written[2]; /* total blocks written (64 bit) */ 2832cd19bdbSCharles Keepax __be32 words_written[2]; /* total words written (64 bit) */ 2842cd19bdbSCharles Keepax }; 2852cd19bdbSCharles Keepax 286721be3beSCharles Keepax struct wm_adsp_compr; 287721be3beSCharles Keepax 2882cd19bdbSCharles Keepax struct wm_adsp_compr_buf { 2892cd19bdbSCharles Keepax struct wm_adsp *dsp; 290721be3beSCharles Keepax struct wm_adsp_compr *compr; 2912cd19bdbSCharles Keepax 2922cd19bdbSCharles Keepax struct wm_adsp_buffer_region *regions; 2932cd19bdbSCharles Keepax u32 host_buf_ptr; 294565ace46SCharles Keepax 295565ace46SCharles Keepax u32 error; 296565ace46SCharles Keepax u32 irq_count; 297565ace46SCharles Keepax int read_index; 298565ace46SCharles Keepax int avail; 2992cd19bdbSCharles Keepax }; 3002cd19bdbSCharles Keepax 301406abc95SCharles Keepax struct wm_adsp_compr { 302406abc95SCharles Keepax struct wm_adsp *dsp; 30395fe9597SCharles Keepax struct wm_adsp_compr_buf *buf; 304406abc95SCharles Keepax 305406abc95SCharles Keepax struct snd_compr_stream *stream; 306406abc95SCharles Keepax struct snd_compressed_buffer size; 307565ace46SCharles Keepax 30883a40ce9SCharles Keepax u32 *raw_buf; 309565ace46SCharles Keepax unsigned int copied_total; 310da2b3358SCharles Keepax 311da2b3358SCharles Keepax unsigned int sample_rate; 312406abc95SCharles Keepax }; 313406abc95SCharles Keepax 314406abc95SCharles Keepax #define WM_ADSP_DATA_WORD_SIZE 3 315406abc95SCharles Keepax 316406abc95SCharles Keepax #define WM_ADSP_MIN_FRAGMENTS 1 317406abc95SCharles Keepax #define WM_ADSP_MAX_FRAGMENTS 256 318406abc95SCharles Keepax #define WM_ADSP_MIN_FRAGMENT_SIZE (64 * WM_ADSP_DATA_WORD_SIZE) 319406abc95SCharles Keepax #define WM_ADSP_MAX_FRAGMENT_SIZE (4096 * WM_ADSP_DATA_WORD_SIZE) 320406abc95SCharles Keepax 3212cd19bdbSCharles Keepax #define WM_ADSP_ALG_XM_STRUCT_MAGIC 0x49aec7 3222cd19bdbSCharles Keepax 3232cd19bdbSCharles Keepax #define HOST_BUFFER_FIELD(field) \ 3242cd19bdbSCharles Keepax (offsetof(struct wm_adsp_buffer, field) / sizeof(__be32)) 3252cd19bdbSCharles Keepax 3262cd19bdbSCharles Keepax #define ALG_XM_FIELD(field) \ 3272cd19bdbSCharles Keepax (offsetof(struct wm_adsp_alg_xm_struct, field) / sizeof(__be32)) 3282cd19bdbSCharles Keepax 3292cd19bdbSCharles Keepax static int wm_adsp_buffer_init(struct wm_adsp *dsp); 3302cd19bdbSCharles Keepax static int wm_adsp_buffer_free(struct wm_adsp *dsp); 3312cd19bdbSCharles Keepax 3322cd19bdbSCharles Keepax struct wm_adsp_buffer_region { 3332cd19bdbSCharles Keepax unsigned int offset; 3342cd19bdbSCharles Keepax unsigned int cumulative_size; 3352cd19bdbSCharles Keepax unsigned int mem_type; 3362cd19bdbSCharles Keepax unsigned int base_addr; 3372cd19bdbSCharles Keepax }; 3382cd19bdbSCharles Keepax 3392cd19bdbSCharles Keepax struct wm_adsp_buffer_region_def { 3402cd19bdbSCharles Keepax unsigned int mem_type; 3412cd19bdbSCharles Keepax unsigned int base_offset; 3422cd19bdbSCharles Keepax unsigned int size_offset; 3432cd19bdbSCharles Keepax }; 3442cd19bdbSCharles Keepax 3453a9686c4SCharles Keepax static const struct wm_adsp_buffer_region_def default_regions[] = { 3462cd19bdbSCharles Keepax { 3472cd19bdbSCharles Keepax .mem_type = WMFW_ADSP2_XM, 3482cd19bdbSCharles Keepax .base_offset = HOST_BUFFER_FIELD(X_buf_base), 3492cd19bdbSCharles Keepax .size_offset = HOST_BUFFER_FIELD(X_buf_size), 3502cd19bdbSCharles Keepax }, 3512cd19bdbSCharles Keepax { 3522cd19bdbSCharles Keepax .mem_type = WMFW_ADSP2_XM, 3532cd19bdbSCharles Keepax .base_offset = HOST_BUFFER_FIELD(X_buf_base2), 3542cd19bdbSCharles Keepax .size_offset = HOST_BUFFER_FIELD(X_buf_brk), 3552cd19bdbSCharles Keepax }, 3562cd19bdbSCharles Keepax { 3572cd19bdbSCharles Keepax .mem_type = WMFW_ADSP2_YM, 3582cd19bdbSCharles Keepax .base_offset = HOST_BUFFER_FIELD(Y_buf_base), 3592cd19bdbSCharles Keepax .size_offset = HOST_BUFFER_FIELD(wrap), 3602cd19bdbSCharles Keepax }, 3612cd19bdbSCharles Keepax }; 3622cd19bdbSCharles Keepax 363406abc95SCharles Keepax struct wm_adsp_fw_caps { 364406abc95SCharles Keepax u32 id; 365406abc95SCharles Keepax struct snd_codec_desc desc; 3662cd19bdbSCharles Keepax int num_regions; 3673a9686c4SCharles Keepax const struct wm_adsp_buffer_region_def *region_defs; 368406abc95SCharles Keepax }; 369406abc95SCharles Keepax 370e6d00f34SCharles Keepax static const struct wm_adsp_fw_caps ctrl_caps[] = { 371406abc95SCharles Keepax { 372406abc95SCharles Keepax .id = SND_AUDIOCODEC_BESPOKE, 373406abc95SCharles Keepax .desc = { 374406abc95SCharles Keepax .max_ch = 1, 375406abc95SCharles Keepax .sample_rates = { 16000 }, 376406abc95SCharles Keepax .num_sample_rates = 1, 377406abc95SCharles Keepax .formats = SNDRV_PCM_FMTBIT_S16_LE, 378406abc95SCharles Keepax }, 379e6d00f34SCharles Keepax .num_regions = ARRAY_SIZE(default_regions), 380e6d00f34SCharles Keepax .region_defs = default_regions, 381406abc95SCharles Keepax }, 382406abc95SCharles Keepax }; 383406abc95SCharles Keepax 3847ce4283cSCharles Keepax static const struct wm_adsp_fw_caps trace_caps[] = { 3857ce4283cSCharles Keepax { 3867ce4283cSCharles Keepax .id = SND_AUDIOCODEC_BESPOKE, 3877ce4283cSCharles Keepax .desc = { 3887ce4283cSCharles Keepax .max_ch = 8, 3897ce4283cSCharles Keepax .sample_rates = { 3907ce4283cSCharles Keepax 4000, 8000, 11025, 12000, 16000, 22050, 3917ce4283cSCharles Keepax 24000, 32000, 44100, 48000, 64000, 88200, 3927ce4283cSCharles Keepax 96000, 176400, 192000 3937ce4283cSCharles Keepax }, 3947ce4283cSCharles Keepax .num_sample_rates = 15, 3957ce4283cSCharles Keepax .formats = SNDRV_PCM_FMTBIT_S16_LE, 3967ce4283cSCharles Keepax }, 3977ce4283cSCharles Keepax .num_regions = ARRAY_SIZE(default_regions), 3987ce4283cSCharles Keepax .region_defs = default_regions, 399406abc95SCharles Keepax }, 400406abc95SCharles Keepax }; 401406abc95SCharles Keepax 402406abc95SCharles Keepax static const struct { 4031023dbd9SMark Brown const char *file; 404406abc95SCharles Keepax int compr_direction; 405406abc95SCharles Keepax int num_caps; 406406abc95SCharles Keepax const struct wm_adsp_fw_caps *caps; 40720b7f7c5SCharles Keepax bool voice_trigger; 4081023dbd9SMark Brown } wm_adsp_fw[WM_ADSP_NUM_FW] = { 409dd84f925SMark Brown [WM_ADSP_FW_MBC_VSS] = { .file = "mbc-vss" }, 41004d1300fSCharles Keepax [WM_ADSP_FW_HIFI] = { .file = "hifi" }, 411dd84f925SMark Brown [WM_ADSP_FW_TX] = { .file = "tx" }, 412dd84f925SMark Brown [WM_ADSP_FW_TX_SPK] = { .file = "tx-spk" }, 41304d1300fSCharles Keepax [WM_ADSP_FW_RX] = { .file = "rx" }, 414dd84f925SMark Brown [WM_ADSP_FW_RX_ANC] = { .file = "rx-anc" }, 415406abc95SCharles Keepax [WM_ADSP_FW_CTRL] = { 416406abc95SCharles Keepax .file = "ctrl", 417406abc95SCharles Keepax .compr_direction = SND_COMPRESS_CAPTURE, 418e6d00f34SCharles Keepax .num_caps = ARRAY_SIZE(ctrl_caps), 419e6d00f34SCharles Keepax .caps = ctrl_caps, 42020b7f7c5SCharles Keepax .voice_trigger = true, 421406abc95SCharles Keepax }, 42204d1300fSCharles Keepax [WM_ADSP_FW_ASR] = { .file = "asr" }, 4237ce4283cSCharles Keepax [WM_ADSP_FW_TRACE] = { 4247ce4283cSCharles Keepax .file = "trace", 4257ce4283cSCharles Keepax .compr_direction = SND_COMPRESS_CAPTURE, 4267ce4283cSCharles Keepax .num_caps = ARRAY_SIZE(trace_caps), 4277ce4283cSCharles Keepax .caps = trace_caps, 4287ce4283cSCharles Keepax }, 42904d1300fSCharles Keepax [WM_ADSP_FW_SPK_PROT] = { .file = "spk-prot" }, 43004d1300fSCharles Keepax [WM_ADSP_FW_MISC] = { .file = "misc" }, 4311023dbd9SMark Brown }; 4321023dbd9SMark Brown 4336ab2b7b4SDimitris Papastamos struct wm_coeff_ctl_ops { 4346ab2b7b4SDimitris Papastamos int (*xget)(struct snd_kcontrol *kcontrol, 4356ab2b7b4SDimitris Papastamos struct snd_ctl_elem_value *ucontrol); 4366ab2b7b4SDimitris Papastamos int (*xput)(struct snd_kcontrol *kcontrol, 4376ab2b7b4SDimitris Papastamos struct snd_ctl_elem_value *ucontrol); 4386ab2b7b4SDimitris Papastamos int (*xinfo)(struct snd_kcontrol *kcontrol, 4396ab2b7b4SDimitris Papastamos struct snd_ctl_elem_info *uinfo); 4406ab2b7b4SDimitris Papastamos }; 4416ab2b7b4SDimitris Papastamos 4426ab2b7b4SDimitris Papastamos struct wm_coeff_ctl { 4436ab2b7b4SDimitris Papastamos const char *name; 4442323736dSCharles Keepax const char *fw_name; 4453809f001SCharles Keepax struct wm_adsp_alg_region alg_region; 4466ab2b7b4SDimitris Papastamos struct wm_coeff_ctl_ops ops; 4473809f001SCharles Keepax struct wm_adsp *dsp; 4486ab2b7b4SDimitris Papastamos unsigned int enabled:1; 4496ab2b7b4SDimitris Papastamos struct list_head list; 4506ab2b7b4SDimitris Papastamos void *cache; 4512323736dSCharles Keepax unsigned int offset; 4526ab2b7b4SDimitris Papastamos size_t len; 4530c2e3f34SDimitris Papastamos unsigned int set:1; 4546ab2b7b4SDimitris Papastamos struct snd_kcontrol *kcontrol; 4559ee78757SCharles Keepax struct soc_bytes_ext bytes_ext; 45626c22a19SCharles Keepax unsigned int flags; 4578eb084d0SStuart Henderson unsigned int type; 4586ab2b7b4SDimitris Papastamos }; 4596ab2b7b4SDimitris Papastamos 4609ce5e6e6SRichard Fitzgerald static const char *wm_adsp_mem_region_name(unsigned int type) 4619ce5e6e6SRichard Fitzgerald { 4629ce5e6e6SRichard Fitzgerald switch (type) { 4639ce5e6e6SRichard Fitzgerald case WMFW_ADSP1_PM: 4649ce5e6e6SRichard Fitzgerald return "PM"; 4659ce5e6e6SRichard Fitzgerald case WMFW_ADSP1_DM: 4669ce5e6e6SRichard Fitzgerald return "DM"; 4679ce5e6e6SRichard Fitzgerald case WMFW_ADSP2_XM: 4689ce5e6e6SRichard Fitzgerald return "XM"; 4699ce5e6e6SRichard Fitzgerald case WMFW_ADSP2_YM: 4709ce5e6e6SRichard Fitzgerald return "YM"; 4719ce5e6e6SRichard Fitzgerald case WMFW_ADSP1_ZM: 4729ce5e6e6SRichard Fitzgerald return "ZM"; 4739ce5e6e6SRichard Fitzgerald default: 4749ce5e6e6SRichard Fitzgerald return NULL; 4759ce5e6e6SRichard Fitzgerald } 4769ce5e6e6SRichard Fitzgerald } 4779ce5e6e6SRichard Fitzgerald 478f9f55e31SRichard Fitzgerald #ifdef CONFIG_DEBUG_FS 479f9f55e31SRichard Fitzgerald static void wm_adsp_debugfs_save_wmfwname(struct wm_adsp *dsp, const char *s) 480f9f55e31SRichard Fitzgerald { 481f9f55e31SRichard Fitzgerald char *tmp = kasprintf(GFP_KERNEL, "%s\n", s); 482f9f55e31SRichard Fitzgerald 483f9f55e31SRichard Fitzgerald kfree(dsp->wmfw_file_name); 484f9f55e31SRichard Fitzgerald dsp->wmfw_file_name = tmp; 485f9f55e31SRichard Fitzgerald } 486f9f55e31SRichard Fitzgerald 487f9f55e31SRichard Fitzgerald static void wm_adsp_debugfs_save_binname(struct wm_adsp *dsp, const char *s) 488f9f55e31SRichard Fitzgerald { 489f9f55e31SRichard Fitzgerald char *tmp = kasprintf(GFP_KERNEL, "%s\n", s); 490f9f55e31SRichard Fitzgerald 491f9f55e31SRichard Fitzgerald kfree(dsp->bin_file_name); 492f9f55e31SRichard Fitzgerald dsp->bin_file_name = tmp; 493f9f55e31SRichard Fitzgerald } 494f9f55e31SRichard Fitzgerald 495f9f55e31SRichard Fitzgerald static void wm_adsp_debugfs_clear(struct wm_adsp *dsp) 496f9f55e31SRichard Fitzgerald { 497f9f55e31SRichard Fitzgerald kfree(dsp->wmfw_file_name); 498f9f55e31SRichard Fitzgerald kfree(dsp->bin_file_name); 499f9f55e31SRichard Fitzgerald dsp->wmfw_file_name = NULL; 500f9f55e31SRichard Fitzgerald dsp->bin_file_name = NULL; 501f9f55e31SRichard Fitzgerald } 502f9f55e31SRichard Fitzgerald 503f9f55e31SRichard Fitzgerald static ssize_t wm_adsp_debugfs_wmfw_read(struct file *file, 504f9f55e31SRichard Fitzgerald char __user *user_buf, 505f9f55e31SRichard Fitzgerald size_t count, loff_t *ppos) 506f9f55e31SRichard Fitzgerald { 507f9f55e31SRichard Fitzgerald struct wm_adsp *dsp = file->private_data; 508f9f55e31SRichard Fitzgerald ssize_t ret; 509f9f55e31SRichard Fitzgerald 510078e7183SCharles Keepax mutex_lock(&dsp->pwr_lock); 511f9f55e31SRichard Fitzgerald 51228823ebaSCharles Keepax if (!dsp->wmfw_file_name || !dsp->booted) 513f9f55e31SRichard Fitzgerald ret = 0; 514f9f55e31SRichard Fitzgerald else 515f9f55e31SRichard Fitzgerald ret = simple_read_from_buffer(user_buf, count, ppos, 516f9f55e31SRichard Fitzgerald dsp->wmfw_file_name, 517f9f55e31SRichard Fitzgerald strlen(dsp->wmfw_file_name)); 518f9f55e31SRichard Fitzgerald 519078e7183SCharles Keepax mutex_unlock(&dsp->pwr_lock); 520f9f55e31SRichard Fitzgerald return ret; 521f9f55e31SRichard Fitzgerald } 522f9f55e31SRichard Fitzgerald 523f9f55e31SRichard Fitzgerald static ssize_t wm_adsp_debugfs_bin_read(struct file *file, 524f9f55e31SRichard Fitzgerald char __user *user_buf, 525f9f55e31SRichard Fitzgerald size_t count, loff_t *ppos) 526f9f55e31SRichard Fitzgerald { 527f9f55e31SRichard Fitzgerald struct wm_adsp *dsp = file->private_data; 528f9f55e31SRichard Fitzgerald ssize_t ret; 529f9f55e31SRichard Fitzgerald 530078e7183SCharles Keepax mutex_lock(&dsp->pwr_lock); 531f9f55e31SRichard Fitzgerald 53228823ebaSCharles Keepax if (!dsp->bin_file_name || !dsp->booted) 533f9f55e31SRichard Fitzgerald ret = 0; 534f9f55e31SRichard Fitzgerald else 535f9f55e31SRichard Fitzgerald ret = simple_read_from_buffer(user_buf, count, ppos, 536f9f55e31SRichard Fitzgerald dsp->bin_file_name, 537f9f55e31SRichard Fitzgerald strlen(dsp->bin_file_name)); 538f9f55e31SRichard Fitzgerald 539078e7183SCharles Keepax mutex_unlock(&dsp->pwr_lock); 540f9f55e31SRichard Fitzgerald return ret; 541f9f55e31SRichard Fitzgerald } 542f9f55e31SRichard Fitzgerald 543f9f55e31SRichard Fitzgerald static const struct { 544f9f55e31SRichard Fitzgerald const char *name; 545f9f55e31SRichard Fitzgerald const struct file_operations fops; 546f9f55e31SRichard Fitzgerald } wm_adsp_debugfs_fops[] = { 547f9f55e31SRichard Fitzgerald { 548f9f55e31SRichard Fitzgerald .name = "wmfw_file_name", 549f9f55e31SRichard Fitzgerald .fops = { 550f9f55e31SRichard Fitzgerald .open = simple_open, 551f9f55e31SRichard Fitzgerald .read = wm_adsp_debugfs_wmfw_read, 552f9f55e31SRichard Fitzgerald }, 553f9f55e31SRichard Fitzgerald }, 554f9f55e31SRichard Fitzgerald { 555f9f55e31SRichard Fitzgerald .name = "bin_file_name", 556f9f55e31SRichard Fitzgerald .fops = { 557f9f55e31SRichard Fitzgerald .open = simple_open, 558f9f55e31SRichard Fitzgerald .read = wm_adsp_debugfs_bin_read, 559f9f55e31SRichard Fitzgerald }, 560f9f55e31SRichard Fitzgerald }, 561f9f55e31SRichard Fitzgerald }; 562f9f55e31SRichard Fitzgerald 563f9f55e31SRichard Fitzgerald static void wm_adsp2_init_debugfs(struct wm_adsp *dsp, 564f9f55e31SRichard Fitzgerald struct snd_soc_codec *codec) 565f9f55e31SRichard Fitzgerald { 566f9f55e31SRichard Fitzgerald struct dentry *root = NULL; 567f9f55e31SRichard Fitzgerald char *root_name; 568f9f55e31SRichard Fitzgerald int i; 569f9f55e31SRichard Fitzgerald 570f9f55e31SRichard Fitzgerald if (!codec->component.debugfs_root) { 571f9f55e31SRichard Fitzgerald adsp_err(dsp, "No codec debugfs root\n"); 572f9f55e31SRichard Fitzgerald goto err; 573f9f55e31SRichard Fitzgerald } 574f9f55e31SRichard Fitzgerald 575f9f55e31SRichard Fitzgerald root_name = kmalloc(PAGE_SIZE, GFP_KERNEL); 576f9f55e31SRichard Fitzgerald if (!root_name) 577f9f55e31SRichard Fitzgerald goto err; 578f9f55e31SRichard Fitzgerald 579f9f55e31SRichard Fitzgerald snprintf(root_name, PAGE_SIZE, "dsp%d", dsp->num); 580f9f55e31SRichard Fitzgerald root = debugfs_create_dir(root_name, codec->component.debugfs_root); 581f9f55e31SRichard Fitzgerald kfree(root_name); 582f9f55e31SRichard Fitzgerald 583f9f55e31SRichard Fitzgerald if (!root) 584f9f55e31SRichard Fitzgerald goto err; 585f9f55e31SRichard Fitzgerald 58628823ebaSCharles Keepax if (!debugfs_create_bool("booted", S_IRUGO, root, &dsp->booted)) 58728823ebaSCharles Keepax goto err; 58828823ebaSCharles Keepax 589f9f55e31SRichard Fitzgerald if (!debugfs_create_bool("running", S_IRUGO, root, &dsp->running)) 590f9f55e31SRichard Fitzgerald goto err; 591f9f55e31SRichard Fitzgerald 592f9f55e31SRichard Fitzgerald if (!debugfs_create_x32("fw_id", S_IRUGO, root, &dsp->fw_id)) 593f9f55e31SRichard Fitzgerald goto err; 594f9f55e31SRichard Fitzgerald 595f9f55e31SRichard Fitzgerald if (!debugfs_create_x32("fw_version", S_IRUGO, root, 596f9f55e31SRichard Fitzgerald &dsp->fw_id_version)) 597f9f55e31SRichard Fitzgerald goto err; 598f9f55e31SRichard Fitzgerald 599f9f55e31SRichard Fitzgerald for (i = 0; i < ARRAY_SIZE(wm_adsp_debugfs_fops); ++i) { 600f9f55e31SRichard Fitzgerald if (!debugfs_create_file(wm_adsp_debugfs_fops[i].name, 601f9f55e31SRichard Fitzgerald S_IRUGO, root, dsp, 602f9f55e31SRichard Fitzgerald &wm_adsp_debugfs_fops[i].fops)) 603f9f55e31SRichard Fitzgerald goto err; 604f9f55e31SRichard Fitzgerald } 605f9f55e31SRichard Fitzgerald 606f9f55e31SRichard Fitzgerald dsp->debugfs_root = root; 607f9f55e31SRichard Fitzgerald return; 608f9f55e31SRichard Fitzgerald 609f9f55e31SRichard Fitzgerald err: 610f9f55e31SRichard Fitzgerald debugfs_remove_recursive(root); 611f9f55e31SRichard Fitzgerald adsp_err(dsp, "Failed to create debugfs\n"); 612f9f55e31SRichard Fitzgerald } 613f9f55e31SRichard Fitzgerald 614f9f55e31SRichard Fitzgerald static void wm_adsp2_cleanup_debugfs(struct wm_adsp *dsp) 615f9f55e31SRichard Fitzgerald { 616f9f55e31SRichard Fitzgerald wm_adsp_debugfs_clear(dsp); 617f9f55e31SRichard Fitzgerald debugfs_remove_recursive(dsp->debugfs_root); 618f9f55e31SRichard Fitzgerald } 619f9f55e31SRichard Fitzgerald #else 620f9f55e31SRichard Fitzgerald static inline void wm_adsp2_init_debugfs(struct wm_adsp *dsp, 621f9f55e31SRichard Fitzgerald struct snd_soc_codec *codec) 622f9f55e31SRichard Fitzgerald { 623f9f55e31SRichard Fitzgerald } 624f9f55e31SRichard Fitzgerald 625f9f55e31SRichard Fitzgerald static inline void wm_adsp2_cleanup_debugfs(struct wm_adsp *dsp) 626f9f55e31SRichard Fitzgerald { 627f9f55e31SRichard Fitzgerald } 628f9f55e31SRichard Fitzgerald 629f9f55e31SRichard Fitzgerald static inline void wm_adsp_debugfs_save_wmfwname(struct wm_adsp *dsp, 630f9f55e31SRichard Fitzgerald const char *s) 631f9f55e31SRichard Fitzgerald { 632f9f55e31SRichard Fitzgerald } 633f9f55e31SRichard Fitzgerald 634f9f55e31SRichard Fitzgerald static inline void wm_adsp_debugfs_save_binname(struct wm_adsp *dsp, 635f9f55e31SRichard Fitzgerald const char *s) 636f9f55e31SRichard Fitzgerald { 637f9f55e31SRichard Fitzgerald } 638f9f55e31SRichard Fitzgerald 639f9f55e31SRichard Fitzgerald static inline void wm_adsp_debugfs_clear(struct wm_adsp *dsp) 640f9f55e31SRichard Fitzgerald { 641f9f55e31SRichard Fitzgerald } 642f9f55e31SRichard Fitzgerald #endif 643f9f55e31SRichard Fitzgerald 6441023dbd9SMark Brown static int wm_adsp_fw_get(struct snd_kcontrol *kcontrol, 6451023dbd9SMark Brown struct snd_ctl_elem_value *ucontrol) 6461023dbd9SMark Brown { 647ea53bf77SLars-Peter Clausen struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); 6481023dbd9SMark Brown struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; 6493809f001SCharles Keepax struct wm_adsp *dsp = snd_soc_codec_get_drvdata(codec); 6501023dbd9SMark Brown 65115c66570STakashi Iwai ucontrol->value.enumerated.item[0] = dsp[e->shift_l].fw; 6521023dbd9SMark Brown 6531023dbd9SMark Brown return 0; 6541023dbd9SMark Brown } 6551023dbd9SMark Brown 6561023dbd9SMark Brown static int wm_adsp_fw_put(struct snd_kcontrol *kcontrol, 6571023dbd9SMark Brown struct snd_ctl_elem_value *ucontrol) 6581023dbd9SMark Brown { 659ea53bf77SLars-Peter Clausen struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); 6601023dbd9SMark Brown struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; 6613809f001SCharles Keepax struct wm_adsp *dsp = snd_soc_codec_get_drvdata(codec); 662d27c5e15SCharles Keepax int ret = 0; 6631023dbd9SMark Brown 66415c66570STakashi Iwai if (ucontrol->value.enumerated.item[0] == dsp[e->shift_l].fw) 6651023dbd9SMark Brown return 0; 6661023dbd9SMark Brown 66715c66570STakashi Iwai if (ucontrol->value.enumerated.item[0] >= WM_ADSP_NUM_FW) 6681023dbd9SMark Brown return -EINVAL; 6691023dbd9SMark Brown 670d27c5e15SCharles Keepax mutex_lock(&dsp[e->shift_l].pwr_lock); 6711023dbd9SMark Brown 67228823ebaSCharles Keepax if (dsp[e->shift_l].booted || dsp[e->shift_l].compr) 673d27c5e15SCharles Keepax ret = -EBUSY; 674d27c5e15SCharles Keepax else 67515c66570STakashi Iwai dsp[e->shift_l].fw = ucontrol->value.enumerated.item[0]; 6761023dbd9SMark Brown 677d27c5e15SCharles Keepax mutex_unlock(&dsp[e->shift_l].pwr_lock); 678d27c5e15SCharles Keepax 679d27c5e15SCharles Keepax return ret; 6801023dbd9SMark Brown } 6811023dbd9SMark Brown 6821023dbd9SMark Brown static const struct soc_enum wm_adsp_fw_enum[] = { 6831023dbd9SMark Brown SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 6841023dbd9SMark Brown SOC_ENUM_SINGLE(0, 1, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 6851023dbd9SMark Brown SOC_ENUM_SINGLE(0, 2, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 6861023dbd9SMark Brown SOC_ENUM_SINGLE(0, 3, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 6871023dbd9SMark Brown }; 6881023dbd9SMark Brown 689336d0442SRichard Fitzgerald const struct snd_kcontrol_new wm_adsp_fw_controls[] = { 6901023dbd9SMark Brown SOC_ENUM_EXT("DSP1 Firmware", wm_adsp_fw_enum[0], 6911023dbd9SMark Brown wm_adsp_fw_get, wm_adsp_fw_put), 6921023dbd9SMark Brown SOC_ENUM_EXT("DSP2 Firmware", wm_adsp_fw_enum[1], 6931023dbd9SMark Brown wm_adsp_fw_get, wm_adsp_fw_put), 6941023dbd9SMark Brown SOC_ENUM_EXT("DSP3 Firmware", wm_adsp_fw_enum[2], 6951023dbd9SMark Brown wm_adsp_fw_get, wm_adsp_fw_put), 6961023dbd9SMark Brown SOC_ENUM_EXT("DSP4 Firmware", wm_adsp_fw_enum[3], 6971023dbd9SMark Brown wm_adsp_fw_get, wm_adsp_fw_put), 6981023dbd9SMark Brown }; 699336d0442SRichard Fitzgerald EXPORT_SYMBOL_GPL(wm_adsp_fw_controls); 7002159ad93SMark Brown 7012159ad93SMark Brown static struct wm_adsp_region const *wm_adsp_find_region(struct wm_adsp *dsp, 7022159ad93SMark Brown int type) 7032159ad93SMark Brown { 7042159ad93SMark Brown int i; 7052159ad93SMark Brown 7062159ad93SMark Brown for (i = 0; i < dsp->num_mems; i++) 7072159ad93SMark Brown if (dsp->mem[i].type == type) 7082159ad93SMark Brown return &dsp->mem[i]; 7092159ad93SMark Brown 7102159ad93SMark Brown return NULL; 7112159ad93SMark Brown } 7122159ad93SMark Brown 7133809f001SCharles Keepax static unsigned int wm_adsp_region_to_reg(struct wm_adsp_region const *mem, 71445b9ee72SMark Brown unsigned int offset) 71545b9ee72SMark Brown { 7163809f001SCharles Keepax if (WARN_ON(!mem)) 7176c452bdaSTakashi Iwai return offset; 7183809f001SCharles Keepax switch (mem->type) { 71945b9ee72SMark Brown case WMFW_ADSP1_PM: 7203809f001SCharles Keepax return mem->base + (offset * 3); 72145b9ee72SMark Brown case WMFW_ADSP1_DM: 7223809f001SCharles Keepax return mem->base + (offset * 2); 72345b9ee72SMark Brown case WMFW_ADSP2_XM: 7243809f001SCharles Keepax return mem->base + (offset * 2); 72545b9ee72SMark Brown case WMFW_ADSP2_YM: 7263809f001SCharles Keepax return mem->base + (offset * 2); 72745b9ee72SMark Brown case WMFW_ADSP1_ZM: 7283809f001SCharles Keepax return mem->base + (offset * 2); 72945b9ee72SMark Brown default: 7306c452bdaSTakashi Iwai WARN(1, "Unknown memory region type"); 73145b9ee72SMark Brown return offset; 73245b9ee72SMark Brown } 73345b9ee72SMark Brown } 73445b9ee72SMark Brown 73510337b07SRichard Fitzgerald static void wm_adsp2_show_fw_status(struct wm_adsp *dsp) 73610337b07SRichard Fitzgerald { 73710337b07SRichard Fitzgerald u16 scratch[4]; 73810337b07SRichard Fitzgerald int ret; 73910337b07SRichard Fitzgerald 74010337b07SRichard Fitzgerald ret = regmap_raw_read(dsp->regmap, dsp->base + ADSP2_SCRATCH0, 74110337b07SRichard Fitzgerald scratch, sizeof(scratch)); 74210337b07SRichard Fitzgerald if (ret) { 74310337b07SRichard Fitzgerald adsp_err(dsp, "Failed to read SCRATCH regs: %d\n", ret); 74410337b07SRichard Fitzgerald return; 74510337b07SRichard Fitzgerald } 74610337b07SRichard Fitzgerald 74710337b07SRichard Fitzgerald adsp_dbg(dsp, "FW SCRATCH 0:0x%x 1:0x%x 2:0x%x 3:0x%x\n", 74810337b07SRichard Fitzgerald be16_to_cpu(scratch[0]), 74910337b07SRichard Fitzgerald be16_to_cpu(scratch[1]), 75010337b07SRichard Fitzgerald be16_to_cpu(scratch[2]), 75110337b07SRichard Fitzgerald be16_to_cpu(scratch[3])); 75210337b07SRichard Fitzgerald } 75310337b07SRichard Fitzgerald 7549ee78757SCharles Keepax static inline struct wm_coeff_ctl *bytes_ext_to_ctl(struct soc_bytes_ext *ext) 7559ee78757SCharles Keepax { 7569ee78757SCharles Keepax return container_of(ext, struct wm_coeff_ctl, bytes_ext); 7579ee78757SCharles Keepax } 7589ee78757SCharles Keepax 759b396ebcaSRichard Fitzgerald static int wm_coeff_base_reg(struct wm_coeff_ctl *ctl, unsigned int *reg) 760b396ebcaSRichard Fitzgerald { 761b396ebcaSRichard Fitzgerald const struct wm_adsp_alg_region *alg_region = &ctl->alg_region; 762b396ebcaSRichard Fitzgerald struct wm_adsp *dsp = ctl->dsp; 763b396ebcaSRichard Fitzgerald const struct wm_adsp_region *mem; 764b396ebcaSRichard Fitzgerald 765b396ebcaSRichard Fitzgerald mem = wm_adsp_find_region(dsp, alg_region->type); 766b396ebcaSRichard Fitzgerald if (!mem) { 767b396ebcaSRichard Fitzgerald adsp_err(dsp, "No base for region %x\n", 768b396ebcaSRichard Fitzgerald alg_region->type); 769b396ebcaSRichard Fitzgerald return -EINVAL; 770b396ebcaSRichard Fitzgerald } 771b396ebcaSRichard Fitzgerald 772b396ebcaSRichard Fitzgerald *reg = wm_adsp_region_to_reg(mem, ctl->alg_region.base + ctl->offset); 773b396ebcaSRichard Fitzgerald 774b396ebcaSRichard Fitzgerald return 0; 775b396ebcaSRichard Fitzgerald } 776b396ebcaSRichard Fitzgerald 7777585a5b0SCharles Keepax static int wm_coeff_info(struct snd_kcontrol *kctl, 7786ab2b7b4SDimitris Papastamos struct snd_ctl_elem_info *uinfo) 7796ab2b7b4SDimitris Papastamos { 7809ee78757SCharles Keepax struct soc_bytes_ext *bytes_ext = 7819ee78757SCharles Keepax (struct soc_bytes_ext *)kctl->private_value; 7829ee78757SCharles Keepax struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); 7836ab2b7b4SDimitris Papastamos 784a23ebba8SRichard Fitzgerald switch (ctl->type) { 785a23ebba8SRichard Fitzgerald case WMFW_CTL_TYPE_ACKED: 786a23ebba8SRichard Fitzgerald uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 787a23ebba8SRichard Fitzgerald uinfo->value.integer.min = WM_ADSP_ACKED_CTL_MIN_VALUE; 788a23ebba8SRichard Fitzgerald uinfo->value.integer.max = WM_ADSP_ACKED_CTL_MAX_VALUE; 789a23ebba8SRichard Fitzgerald uinfo->value.integer.step = 1; 790a23ebba8SRichard Fitzgerald uinfo->count = 1; 791a23ebba8SRichard Fitzgerald break; 792a23ebba8SRichard Fitzgerald default: 7936ab2b7b4SDimitris Papastamos uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; 7946ab2b7b4SDimitris Papastamos uinfo->count = ctl->len; 795a23ebba8SRichard Fitzgerald break; 796a23ebba8SRichard Fitzgerald } 797a23ebba8SRichard Fitzgerald 7986ab2b7b4SDimitris Papastamos return 0; 7996ab2b7b4SDimitris Papastamos } 8006ab2b7b4SDimitris Papastamos 801f4f0c4c6SRichard Fitzgerald static int wm_coeff_write_acked_control(struct wm_coeff_ctl *ctl, 802f4f0c4c6SRichard Fitzgerald unsigned int event_id) 803f4f0c4c6SRichard Fitzgerald { 804f4f0c4c6SRichard Fitzgerald struct wm_adsp *dsp = ctl->dsp; 805f4f0c4c6SRichard Fitzgerald u32 val = cpu_to_be32(event_id); 806f4f0c4c6SRichard Fitzgerald unsigned int reg; 807f4f0c4c6SRichard Fitzgerald int i, ret; 808f4f0c4c6SRichard Fitzgerald 809f4f0c4c6SRichard Fitzgerald ret = wm_coeff_base_reg(ctl, ®); 810f4f0c4c6SRichard Fitzgerald if (ret) 811f4f0c4c6SRichard Fitzgerald return ret; 812f4f0c4c6SRichard Fitzgerald 813f4f0c4c6SRichard Fitzgerald adsp_dbg(dsp, "Sending 0x%x to acked control alg 0x%x %s:0x%x\n", 814f4f0c4c6SRichard Fitzgerald event_id, ctl->alg_region.alg, 815f4f0c4c6SRichard Fitzgerald wm_adsp_mem_region_name(ctl->alg_region.type), ctl->offset); 816f4f0c4c6SRichard Fitzgerald 817f4f0c4c6SRichard Fitzgerald ret = regmap_raw_write(dsp->regmap, reg, &val, sizeof(val)); 818f4f0c4c6SRichard Fitzgerald if (ret) { 819f4f0c4c6SRichard Fitzgerald adsp_err(dsp, "Failed to write %x: %d\n", reg, ret); 820f4f0c4c6SRichard Fitzgerald return ret; 821f4f0c4c6SRichard Fitzgerald } 822f4f0c4c6SRichard Fitzgerald 823f4f0c4c6SRichard Fitzgerald /* 824f4f0c4c6SRichard Fitzgerald * Poll for ack, we initially poll at ~1ms intervals for firmwares 825f4f0c4c6SRichard Fitzgerald * that respond quickly, then go to ~10ms polls. A firmware is unlikely 826f4f0c4c6SRichard Fitzgerald * to ack instantly so we do the first 1ms delay before reading the 827f4f0c4c6SRichard Fitzgerald * control to avoid a pointless bus transaction 828f4f0c4c6SRichard Fitzgerald */ 829f4f0c4c6SRichard Fitzgerald for (i = 0; i < WM_ADSP_ACKED_CTL_TIMEOUT_MS;) { 830f4f0c4c6SRichard Fitzgerald switch (i) { 831f4f0c4c6SRichard Fitzgerald case 0 ... WM_ADSP_ACKED_CTL_N_QUICKPOLLS - 1: 832f4f0c4c6SRichard Fitzgerald usleep_range(1000, 2000); 833f4f0c4c6SRichard Fitzgerald i++; 834f4f0c4c6SRichard Fitzgerald break; 835f4f0c4c6SRichard Fitzgerald default: 836f4f0c4c6SRichard Fitzgerald usleep_range(10000, 20000); 837f4f0c4c6SRichard Fitzgerald i += 10; 838f4f0c4c6SRichard Fitzgerald break; 839f4f0c4c6SRichard Fitzgerald } 840f4f0c4c6SRichard Fitzgerald 841f4f0c4c6SRichard Fitzgerald ret = regmap_raw_read(dsp->regmap, reg, &val, sizeof(val)); 842f4f0c4c6SRichard Fitzgerald if (ret) { 843f4f0c4c6SRichard Fitzgerald adsp_err(dsp, "Failed to read %x: %d\n", reg, ret); 844f4f0c4c6SRichard Fitzgerald return ret; 845f4f0c4c6SRichard Fitzgerald } 846f4f0c4c6SRichard Fitzgerald 847f4f0c4c6SRichard Fitzgerald if (val == 0) { 848f4f0c4c6SRichard Fitzgerald adsp_dbg(dsp, "Acked control ACKED at poll %u\n", i); 849f4f0c4c6SRichard Fitzgerald return 0; 850f4f0c4c6SRichard Fitzgerald } 851f4f0c4c6SRichard Fitzgerald } 852f4f0c4c6SRichard Fitzgerald 853f4f0c4c6SRichard Fitzgerald adsp_warn(dsp, "Acked control @0x%x alg:0x%x %s:0x%x timed out\n", 854f4f0c4c6SRichard Fitzgerald reg, ctl->alg_region.alg, 855f4f0c4c6SRichard Fitzgerald wm_adsp_mem_region_name(ctl->alg_region.type), 856f4f0c4c6SRichard Fitzgerald ctl->offset); 857f4f0c4c6SRichard Fitzgerald 858f4f0c4c6SRichard Fitzgerald return -ETIMEDOUT; 859f4f0c4c6SRichard Fitzgerald } 860f4f0c4c6SRichard Fitzgerald 861c9f8dd71SCharles Keepax static int wm_coeff_write_control(struct wm_coeff_ctl *ctl, 8626ab2b7b4SDimitris Papastamos const void *buf, size_t len) 8636ab2b7b4SDimitris Papastamos { 8643809f001SCharles Keepax struct wm_adsp *dsp = ctl->dsp; 8656ab2b7b4SDimitris Papastamos void *scratch; 8666ab2b7b4SDimitris Papastamos int ret; 8676ab2b7b4SDimitris Papastamos unsigned int reg; 8686ab2b7b4SDimitris Papastamos 869b396ebcaSRichard Fitzgerald ret = wm_coeff_base_reg(ctl, ®); 870b396ebcaSRichard Fitzgerald if (ret) 871b396ebcaSRichard Fitzgerald return ret; 8726ab2b7b4SDimitris Papastamos 8734f8ea6d7SCharles Keepax scratch = kmemdup(buf, len, GFP_KERNEL | GFP_DMA); 8746ab2b7b4SDimitris Papastamos if (!scratch) 8756ab2b7b4SDimitris Papastamos return -ENOMEM; 8766ab2b7b4SDimitris Papastamos 8773809f001SCharles Keepax ret = regmap_raw_write(dsp->regmap, reg, scratch, 8784f8ea6d7SCharles Keepax len); 8796ab2b7b4SDimitris Papastamos if (ret) { 8803809f001SCharles Keepax adsp_err(dsp, "Failed to write %zu bytes to %x: %d\n", 8814f8ea6d7SCharles Keepax len, reg, ret); 8826ab2b7b4SDimitris Papastamos kfree(scratch); 8836ab2b7b4SDimitris Papastamos return ret; 8846ab2b7b4SDimitris Papastamos } 8854f8ea6d7SCharles Keepax adsp_dbg(dsp, "Wrote %zu bytes to %x\n", len, reg); 8866ab2b7b4SDimitris Papastamos 8876ab2b7b4SDimitris Papastamos kfree(scratch); 8886ab2b7b4SDimitris Papastamos 8896ab2b7b4SDimitris Papastamos return 0; 8906ab2b7b4SDimitris Papastamos } 8916ab2b7b4SDimitris Papastamos 8927585a5b0SCharles Keepax static int wm_coeff_put(struct snd_kcontrol *kctl, 8936ab2b7b4SDimitris Papastamos struct snd_ctl_elem_value *ucontrol) 8946ab2b7b4SDimitris Papastamos { 8959ee78757SCharles Keepax struct soc_bytes_ext *bytes_ext = 8969ee78757SCharles Keepax (struct soc_bytes_ext *)kctl->private_value; 8979ee78757SCharles Keepax struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); 8986ab2b7b4SDimitris Papastamos char *p = ucontrol->value.bytes.data; 899168d10e7SCharles Keepax int ret = 0; 900168d10e7SCharles Keepax 901168d10e7SCharles Keepax mutex_lock(&ctl->dsp->pwr_lock); 9026ab2b7b4SDimitris Papastamos 9036ab2b7b4SDimitris Papastamos memcpy(ctl->cache, p, ctl->len); 9046ab2b7b4SDimitris Papastamos 9050c2e3f34SDimitris Papastamos ctl->set = 1; 906cef45771SCharles Keepax if (ctl->enabled && ctl->dsp->running) 907168d10e7SCharles Keepax ret = wm_coeff_write_control(ctl, p, ctl->len); 9086ab2b7b4SDimitris Papastamos 909168d10e7SCharles Keepax mutex_unlock(&ctl->dsp->pwr_lock); 910168d10e7SCharles Keepax 911168d10e7SCharles Keepax return ret; 9126ab2b7b4SDimitris Papastamos } 9136ab2b7b4SDimitris Papastamos 9149ee78757SCharles Keepax static int wm_coeff_tlv_put(struct snd_kcontrol *kctl, 9159ee78757SCharles Keepax const unsigned int __user *bytes, unsigned int size) 9169ee78757SCharles Keepax { 9179ee78757SCharles Keepax struct soc_bytes_ext *bytes_ext = 9189ee78757SCharles Keepax (struct soc_bytes_ext *)kctl->private_value; 9199ee78757SCharles Keepax struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); 9209ee78757SCharles Keepax int ret = 0; 9219ee78757SCharles Keepax 9229ee78757SCharles Keepax mutex_lock(&ctl->dsp->pwr_lock); 9239ee78757SCharles Keepax 9249ee78757SCharles Keepax if (copy_from_user(ctl->cache, bytes, size)) { 9259ee78757SCharles Keepax ret = -EFAULT; 9269ee78757SCharles Keepax } else { 9279ee78757SCharles Keepax ctl->set = 1; 928cef45771SCharles Keepax if (ctl->enabled && ctl->dsp->running) 9299ee78757SCharles Keepax ret = wm_coeff_write_control(ctl, ctl->cache, size); 9309ee78757SCharles Keepax } 9319ee78757SCharles Keepax 9329ee78757SCharles Keepax mutex_unlock(&ctl->dsp->pwr_lock); 9339ee78757SCharles Keepax 9349ee78757SCharles Keepax return ret; 9359ee78757SCharles Keepax } 9369ee78757SCharles Keepax 937a23ebba8SRichard Fitzgerald static int wm_coeff_put_acked(struct snd_kcontrol *kctl, 938a23ebba8SRichard Fitzgerald struct snd_ctl_elem_value *ucontrol) 939a23ebba8SRichard Fitzgerald { 940a23ebba8SRichard Fitzgerald struct soc_bytes_ext *bytes_ext = 941a23ebba8SRichard Fitzgerald (struct soc_bytes_ext *)kctl->private_value; 942a23ebba8SRichard Fitzgerald struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); 943a23ebba8SRichard Fitzgerald unsigned int val = ucontrol->value.integer.value[0]; 944a23ebba8SRichard Fitzgerald int ret; 945a23ebba8SRichard Fitzgerald 946a23ebba8SRichard Fitzgerald if (val == 0) 947a23ebba8SRichard Fitzgerald return 0; /* 0 means no event */ 948a23ebba8SRichard Fitzgerald 949a23ebba8SRichard Fitzgerald mutex_lock(&ctl->dsp->pwr_lock); 950a23ebba8SRichard Fitzgerald 951a23ebba8SRichard Fitzgerald if (ctl->enabled) 952a23ebba8SRichard Fitzgerald ret = wm_coeff_write_acked_control(ctl, val); 953a23ebba8SRichard Fitzgerald else 954a23ebba8SRichard Fitzgerald ret = -EPERM; 955a23ebba8SRichard Fitzgerald 956a23ebba8SRichard Fitzgerald mutex_unlock(&ctl->dsp->pwr_lock); 957a23ebba8SRichard Fitzgerald 958a23ebba8SRichard Fitzgerald return ret; 959a23ebba8SRichard Fitzgerald } 960a23ebba8SRichard Fitzgerald 961c9f8dd71SCharles Keepax static int wm_coeff_read_control(struct wm_coeff_ctl *ctl, 9626ab2b7b4SDimitris Papastamos void *buf, size_t len) 9636ab2b7b4SDimitris Papastamos { 9643809f001SCharles Keepax struct wm_adsp *dsp = ctl->dsp; 9656ab2b7b4SDimitris Papastamos void *scratch; 9666ab2b7b4SDimitris Papastamos int ret; 9676ab2b7b4SDimitris Papastamos unsigned int reg; 9686ab2b7b4SDimitris Papastamos 969b396ebcaSRichard Fitzgerald ret = wm_coeff_base_reg(ctl, ®); 970b396ebcaSRichard Fitzgerald if (ret) 971b396ebcaSRichard Fitzgerald return ret; 9726ab2b7b4SDimitris Papastamos 9734f8ea6d7SCharles Keepax scratch = kmalloc(len, GFP_KERNEL | GFP_DMA); 9746ab2b7b4SDimitris Papastamos if (!scratch) 9756ab2b7b4SDimitris Papastamos return -ENOMEM; 9766ab2b7b4SDimitris Papastamos 9774f8ea6d7SCharles Keepax ret = regmap_raw_read(dsp->regmap, reg, scratch, len); 9786ab2b7b4SDimitris Papastamos if (ret) { 9793809f001SCharles Keepax adsp_err(dsp, "Failed to read %zu bytes from %x: %d\n", 9805602a643SCharles Keepax len, reg, ret); 9816ab2b7b4SDimitris Papastamos kfree(scratch); 9826ab2b7b4SDimitris Papastamos return ret; 9836ab2b7b4SDimitris Papastamos } 9844f8ea6d7SCharles Keepax adsp_dbg(dsp, "Read %zu bytes from %x\n", len, reg); 9856ab2b7b4SDimitris Papastamos 9864f8ea6d7SCharles Keepax memcpy(buf, scratch, len); 9876ab2b7b4SDimitris Papastamos kfree(scratch); 9886ab2b7b4SDimitris Papastamos 9896ab2b7b4SDimitris Papastamos return 0; 9906ab2b7b4SDimitris Papastamos } 9916ab2b7b4SDimitris Papastamos 9927585a5b0SCharles Keepax static int wm_coeff_get(struct snd_kcontrol *kctl, 9936ab2b7b4SDimitris Papastamos struct snd_ctl_elem_value *ucontrol) 9946ab2b7b4SDimitris Papastamos { 9959ee78757SCharles Keepax struct soc_bytes_ext *bytes_ext = 9969ee78757SCharles Keepax (struct soc_bytes_ext *)kctl->private_value; 9979ee78757SCharles Keepax struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); 9986ab2b7b4SDimitris Papastamos char *p = ucontrol->value.bytes.data; 999168d10e7SCharles Keepax int ret = 0; 1000168d10e7SCharles Keepax 1001168d10e7SCharles Keepax mutex_lock(&ctl->dsp->pwr_lock); 10026ab2b7b4SDimitris Papastamos 100326c22a19SCharles Keepax if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) { 1004cef45771SCharles Keepax if (ctl->enabled && ctl->dsp->running) 1005168d10e7SCharles Keepax ret = wm_coeff_read_control(ctl, p, ctl->len); 100626c22a19SCharles Keepax else 1007168d10e7SCharles Keepax ret = -EPERM; 1008168d10e7SCharles Keepax } else { 1009cef45771SCharles Keepax if (!ctl->flags && ctl->enabled && ctl->dsp->running) 1010bc1765d6SCharles Keepax ret = wm_coeff_read_control(ctl, ctl->cache, ctl->len); 1011bc1765d6SCharles Keepax 1012168d10e7SCharles Keepax memcpy(p, ctl->cache, ctl->len); 101326c22a19SCharles Keepax } 101426c22a19SCharles Keepax 1015168d10e7SCharles Keepax mutex_unlock(&ctl->dsp->pwr_lock); 101626c22a19SCharles Keepax 1017168d10e7SCharles Keepax return ret; 10186ab2b7b4SDimitris Papastamos } 10196ab2b7b4SDimitris Papastamos 10209ee78757SCharles Keepax static int wm_coeff_tlv_get(struct snd_kcontrol *kctl, 10219ee78757SCharles Keepax unsigned int __user *bytes, unsigned int size) 10229ee78757SCharles Keepax { 10239ee78757SCharles Keepax struct soc_bytes_ext *bytes_ext = 10249ee78757SCharles Keepax (struct soc_bytes_ext *)kctl->private_value; 10259ee78757SCharles Keepax struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); 10269ee78757SCharles Keepax int ret = 0; 10279ee78757SCharles Keepax 10289ee78757SCharles Keepax mutex_lock(&ctl->dsp->pwr_lock); 10299ee78757SCharles Keepax 10309ee78757SCharles Keepax if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) { 1031cef45771SCharles Keepax if (ctl->enabled && ctl->dsp->running) 10329ee78757SCharles Keepax ret = wm_coeff_read_control(ctl, ctl->cache, size); 10339ee78757SCharles Keepax else 10349ee78757SCharles Keepax ret = -EPERM; 10359ee78757SCharles Keepax } else { 1036cef45771SCharles Keepax if (!ctl->flags && ctl->enabled && ctl->dsp->running) 10379ee78757SCharles Keepax ret = wm_coeff_read_control(ctl, ctl->cache, size); 10389ee78757SCharles Keepax } 10399ee78757SCharles Keepax 10409ee78757SCharles Keepax if (!ret && copy_to_user(bytes, ctl->cache, size)) 10419ee78757SCharles Keepax ret = -EFAULT; 10429ee78757SCharles Keepax 10439ee78757SCharles Keepax mutex_unlock(&ctl->dsp->pwr_lock); 10449ee78757SCharles Keepax 10459ee78757SCharles Keepax return ret; 10469ee78757SCharles Keepax } 10479ee78757SCharles Keepax 1048a23ebba8SRichard Fitzgerald static int wm_coeff_get_acked(struct snd_kcontrol *kcontrol, 1049a23ebba8SRichard Fitzgerald struct snd_ctl_elem_value *ucontrol) 1050a23ebba8SRichard Fitzgerald { 1051a23ebba8SRichard Fitzgerald /* 1052a23ebba8SRichard Fitzgerald * Although it's not useful to read an acked control, we must satisfy 1053a23ebba8SRichard Fitzgerald * user-side assumptions that all controls are readable and that a 1054a23ebba8SRichard Fitzgerald * write of the same value should be filtered out (it's valid to send 1055a23ebba8SRichard Fitzgerald * the same event number again to the firmware). We therefore return 0, 1056a23ebba8SRichard Fitzgerald * meaning "no event" so valid event numbers will always be a change 1057a23ebba8SRichard Fitzgerald */ 1058a23ebba8SRichard Fitzgerald ucontrol->value.integer.value[0] = 0; 1059a23ebba8SRichard Fitzgerald 1060a23ebba8SRichard Fitzgerald return 0; 1061a23ebba8SRichard Fitzgerald } 1062a23ebba8SRichard Fitzgerald 10636ab2b7b4SDimitris Papastamos struct wmfw_ctl_work { 10643809f001SCharles Keepax struct wm_adsp *dsp; 10656ab2b7b4SDimitris Papastamos struct wm_coeff_ctl *ctl; 10666ab2b7b4SDimitris Papastamos struct work_struct work; 10676ab2b7b4SDimitris Papastamos }; 10686ab2b7b4SDimitris Papastamos 10699ee78757SCharles Keepax static unsigned int wmfw_convert_flags(unsigned int in, unsigned int len) 10709ee78757SCharles Keepax { 10719ee78757SCharles Keepax unsigned int out, rd, wr, vol; 10729ee78757SCharles Keepax 10739ee78757SCharles Keepax if (len > ADSP_MAX_STD_CTRL_SIZE) { 10749ee78757SCharles Keepax rd = SNDRV_CTL_ELEM_ACCESS_TLV_READ; 10759ee78757SCharles Keepax wr = SNDRV_CTL_ELEM_ACCESS_TLV_WRITE; 10769ee78757SCharles Keepax vol = SNDRV_CTL_ELEM_ACCESS_VOLATILE; 10779ee78757SCharles Keepax 10789ee78757SCharles Keepax out = SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK; 10799ee78757SCharles Keepax } else { 10809ee78757SCharles Keepax rd = SNDRV_CTL_ELEM_ACCESS_READ; 10819ee78757SCharles Keepax wr = SNDRV_CTL_ELEM_ACCESS_WRITE; 10829ee78757SCharles Keepax vol = SNDRV_CTL_ELEM_ACCESS_VOLATILE; 10839ee78757SCharles Keepax 10849ee78757SCharles Keepax out = 0; 10859ee78757SCharles Keepax } 10869ee78757SCharles Keepax 10879ee78757SCharles Keepax if (in) { 10889ee78757SCharles Keepax if (in & WMFW_CTL_FLAG_READABLE) 10899ee78757SCharles Keepax out |= rd; 10909ee78757SCharles Keepax if (in & WMFW_CTL_FLAG_WRITEABLE) 10919ee78757SCharles Keepax out |= wr; 10929ee78757SCharles Keepax if (in & WMFW_CTL_FLAG_VOLATILE) 10939ee78757SCharles Keepax out |= vol; 10949ee78757SCharles Keepax } else { 10959ee78757SCharles Keepax out |= rd | wr | vol; 10969ee78757SCharles Keepax } 10979ee78757SCharles Keepax 10989ee78757SCharles Keepax return out; 10999ee78757SCharles Keepax } 11009ee78757SCharles Keepax 11013809f001SCharles Keepax static int wmfw_add_ctl(struct wm_adsp *dsp, struct wm_coeff_ctl *ctl) 11026ab2b7b4SDimitris Papastamos { 11036ab2b7b4SDimitris Papastamos struct snd_kcontrol_new *kcontrol; 11046ab2b7b4SDimitris Papastamos int ret; 11056ab2b7b4SDimitris Papastamos 110692bb4c32SDimitris Papastamos if (!ctl || !ctl->name) 11076ab2b7b4SDimitris Papastamos return -EINVAL; 11086ab2b7b4SDimitris Papastamos 11096ab2b7b4SDimitris Papastamos kcontrol = kzalloc(sizeof(*kcontrol), GFP_KERNEL); 11106ab2b7b4SDimitris Papastamos if (!kcontrol) 11116ab2b7b4SDimitris Papastamos return -ENOMEM; 11126ab2b7b4SDimitris Papastamos 11136ab2b7b4SDimitris Papastamos kcontrol->name = ctl->name; 11146ab2b7b4SDimitris Papastamos kcontrol->info = wm_coeff_info; 11159ee78757SCharles Keepax kcontrol->iface = SNDRV_CTL_ELEM_IFACE_MIXER; 11169ee78757SCharles Keepax kcontrol->tlv.c = snd_soc_bytes_tlv_callback; 11179ee78757SCharles Keepax kcontrol->private_value = (unsigned long)&ctl->bytes_ext; 1118a23ebba8SRichard Fitzgerald kcontrol->access = wmfw_convert_flags(ctl->flags, ctl->len); 1119a23ebba8SRichard Fitzgerald 1120a23ebba8SRichard Fitzgerald switch (ctl->type) { 1121a23ebba8SRichard Fitzgerald case WMFW_CTL_TYPE_ACKED: 1122a23ebba8SRichard Fitzgerald kcontrol->get = wm_coeff_get_acked; 1123a23ebba8SRichard Fitzgerald kcontrol->put = wm_coeff_put_acked; 1124a23ebba8SRichard Fitzgerald break; 1125a23ebba8SRichard Fitzgerald default: 1126a23ebba8SRichard Fitzgerald kcontrol->get = wm_coeff_get; 1127a23ebba8SRichard Fitzgerald kcontrol->put = wm_coeff_put; 11286ab2b7b4SDimitris Papastamos 11299ee78757SCharles Keepax ctl->bytes_ext.max = ctl->len; 11309ee78757SCharles Keepax ctl->bytes_ext.get = wm_coeff_tlv_get; 11319ee78757SCharles Keepax ctl->bytes_ext.put = wm_coeff_tlv_put; 1132a23ebba8SRichard Fitzgerald break; 1133a23ebba8SRichard Fitzgerald } 113426c22a19SCharles Keepax 11357d00cd97SCharles Keepax ret = snd_soc_add_card_controls(dsp->card, kcontrol, 1); 11366ab2b7b4SDimitris Papastamos if (ret < 0) 11376ab2b7b4SDimitris Papastamos goto err_kcontrol; 11386ab2b7b4SDimitris Papastamos 11396ab2b7b4SDimitris Papastamos kfree(kcontrol); 11406ab2b7b4SDimitris Papastamos 11417d00cd97SCharles Keepax ctl->kcontrol = snd_soc_card_get_kcontrol(dsp->card, ctl->name); 114281ad93ecSDimitris Papastamos 11436ab2b7b4SDimitris Papastamos return 0; 11446ab2b7b4SDimitris Papastamos 11456ab2b7b4SDimitris Papastamos err_kcontrol: 11466ab2b7b4SDimitris Papastamos kfree(kcontrol); 11476ab2b7b4SDimitris Papastamos return ret; 11486ab2b7b4SDimitris Papastamos } 11496ab2b7b4SDimitris Papastamos 1150b21acc1cSCharles Keepax static int wm_coeff_init_control_caches(struct wm_adsp *dsp) 1151b21acc1cSCharles Keepax { 1152b21acc1cSCharles Keepax struct wm_coeff_ctl *ctl; 1153b21acc1cSCharles Keepax int ret; 1154b21acc1cSCharles Keepax 1155b21acc1cSCharles Keepax list_for_each_entry(ctl, &dsp->ctl_list, list) { 1156b21acc1cSCharles Keepax if (!ctl->enabled || ctl->set) 1157b21acc1cSCharles Keepax continue; 115826c22a19SCharles Keepax if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) 115926c22a19SCharles Keepax continue; 116026c22a19SCharles Keepax 11617d00cd97SCharles Keepax ret = wm_coeff_read_control(ctl, ctl->cache, ctl->len); 1162b21acc1cSCharles Keepax if (ret < 0) 1163b21acc1cSCharles Keepax return ret; 1164b21acc1cSCharles Keepax } 1165b21acc1cSCharles Keepax 1166b21acc1cSCharles Keepax return 0; 1167b21acc1cSCharles Keepax } 1168b21acc1cSCharles Keepax 1169b21acc1cSCharles Keepax static int wm_coeff_sync_controls(struct wm_adsp *dsp) 1170b21acc1cSCharles Keepax { 1171b21acc1cSCharles Keepax struct wm_coeff_ctl *ctl; 1172b21acc1cSCharles Keepax int ret; 1173b21acc1cSCharles Keepax 1174b21acc1cSCharles Keepax list_for_each_entry(ctl, &dsp->ctl_list, list) { 1175b21acc1cSCharles Keepax if (!ctl->enabled) 1176b21acc1cSCharles Keepax continue; 117726c22a19SCharles Keepax if (ctl->set && !(ctl->flags & WMFW_CTL_FLAG_VOLATILE)) { 11787d00cd97SCharles Keepax ret = wm_coeff_write_control(ctl, ctl->cache, ctl->len); 1179b21acc1cSCharles Keepax if (ret < 0) 1180b21acc1cSCharles Keepax return ret; 1181b21acc1cSCharles Keepax } 1182b21acc1cSCharles Keepax } 1183b21acc1cSCharles Keepax 1184b21acc1cSCharles Keepax return 0; 1185b21acc1cSCharles Keepax } 1186b21acc1cSCharles Keepax 1187f4f0c4c6SRichard Fitzgerald static void wm_adsp_signal_event_controls(struct wm_adsp *dsp, 1188f4f0c4c6SRichard Fitzgerald unsigned int event) 1189f4f0c4c6SRichard Fitzgerald { 1190f4f0c4c6SRichard Fitzgerald struct wm_coeff_ctl *ctl; 1191f4f0c4c6SRichard Fitzgerald int ret; 1192f4f0c4c6SRichard Fitzgerald 1193f4f0c4c6SRichard Fitzgerald list_for_each_entry(ctl, &dsp->ctl_list, list) { 1194f4f0c4c6SRichard Fitzgerald if (ctl->type != WMFW_CTL_TYPE_HOSTEVENT) 1195f4f0c4c6SRichard Fitzgerald continue; 1196f4f0c4c6SRichard Fitzgerald 1197f4f0c4c6SRichard Fitzgerald ret = wm_coeff_write_acked_control(ctl, event); 1198f4f0c4c6SRichard Fitzgerald if (ret) 1199f4f0c4c6SRichard Fitzgerald adsp_warn(dsp, 1200f4f0c4c6SRichard Fitzgerald "Failed to send 0x%x event to alg 0x%x (%d)\n", 1201f4f0c4c6SRichard Fitzgerald event, ctl->alg_region.alg, ret); 1202f4f0c4c6SRichard Fitzgerald } 1203f4f0c4c6SRichard Fitzgerald } 1204f4f0c4c6SRichard Fitzgerald 1205b21acc1cSCharles Keepax static void wm_adsp_ctl_work(struct work_struct *work) 1206b21acc1cSCharles Keepax { 1207b21acc1cSCharles Keepax struct wmfw_ctl_work *ctl_work = container_of(work, 1208b21acc1cSCharles Keepax struct wmfw_ctl_work, 1209b21acc1cSCharles Keepax work); 1210b21acc1cSCharles Keepax 1211b21acc1cSCharles Keepax wmfw_add_ctl(ctl_work->dsp, ctl_work->ctl); 1212b21acc1cSCharles Keepax kfree(ctl_work); 1213b21acc1cSCharles Keepax } 1214b21acc1cSCharles Keepax 121566225e98SRichard Fitzgerald static void wm_adsp_free_ctl_blk(struct wm_coeff_ctl *ctl) 121666225e98SRichard Fitzgerald { 121766225e98SRichard Fitzgerald kfree(ctl->cache); 121866225e98SRichard Fitzgerald kfree(ctl->name); 121966225e98SRichard Fitzgerald kfree(ctl); 122066225e98SRichard Fitzgerald } 122166225e98SRichard Fitzgerald 1222b21acc1cSCharles Keepax static int wm_adsp_create_control(struct wm_adsp *dsp, 1223b21acc1cSCharles Keepax const struct wm_adsp_alg_region *alg_region, 12242323736dSCharles Keepax unsigned int offset, unsigned int len, 122526c22a19SCharles Keepax const char *subname, unsigned int subname_len, 12268eb084d0SStuart Henderson unsigned int flags, unsigned int type) 1227b21acc1cSCharles Keepax { 1228b21acc1cSCharles Keepax struct wm_coeff_ctl *ctl; 1229b21acc1cSCharles Keepax struct wmfw_ctl_work *ctl_work; 1230b21acc1cSCharles Keepax char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; 12319ce5e6e6SRichard Fitzgerald const char *region_name; 1232b21acc1cSCharles Keepax int ret; 1233b21acc1cSCharles Keepax 12349ce5e6e6SRichard Fitzgerald region_name = wm_adsp_mem_region_name(alg_region->type); 12359ce5e6e6SRichard Fitzgerald if (!region_name) { 12362323736dSCharles Keepax adsp_err(dsp, "Unknown region type: %d\n", alg_region->type); 1237b21acc1cSCharles Keepax return -EINVAL; 1238b21acc1cSCharles Keepax } 1239b21acc1cSCharles Keepax 1240cb5b57a9SCharles Keepax switch (dsp->fw_ver) { 1241cb5b57a9SCharles Keepax case 0: 1242cb5b57a9SCharles Keepax case 1: 1243b21acc1cSCharles Keepax snprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "DSP%d %s %x", 1244b21acc1cSCharles Keepax dsp->num, region_name, alg_region->alg); 1245cb5b57a9SCharles Keepax break; 1246cb5b57a9SCharles Keepax default: 1247cb5b57a9SCharles Keepax ret = snprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, 1248cb5b57a9SCharles Keepax "DSP%d%c %.12s %x", dsp->num, *region_name, 1249cb5b57a9SCharles Keepax wm_adsp_fw_text[dsp->fw], alg_region->alg); 1250cb5b57a9SCharles Keepax 1251cb5b57a9SCharles Keepax /* Truncate the subname from the start if it is too long */ 1252cb5b57a9SCharles Keepax if (subname) { 1253cb5b57a9SCharles Keepax int avail = SNDRV_CTL_ELEM_ID_NAME_MAXLEN - ret - 2; 1254cb5b57a9SCharles Keepax int skip = 0; 1255cb5b57a9SCharles Keepax 1256cb5b57a9SCharles Keepax if (subname_len > avail) 1257cb5b57a9SCharles Keepax skip = subname_len - avail; 1258cb5b57a9SCharles Keepax 1259cb5b57a9SCharles Keepax snprintf(name + ret, 1260cb5b57a9SCharles Keepax SNDRV_CTL_ELEM_ID_NAME_MAXLEN - ret, " %.*s", 1261cb5b57a9SCharles Keepax subname_len - skip, subname + skip); 1262cb5b57a9SCharles Keepax } 1263cb5b57a9SCharles Keepax break; 1264cb5b57a9SCharles Keepax } 1265b21acc1cSCharles Keepax 12667585a5b0SCharles Keepax list_for_each_entry(ctl, &dsp->ctl_list, list) { 1267b21acc1cSCharles Keepax if (!strcmp(ctl->name, name)) { 1268b21acc1cSCharles Keepax if (!ctl->enabled) 1269b21acc1cSCharles Keepax ctl->enabled = 1; 1270b21acc1cSCharles Keepax return 0; 1271b21acc1cSCharles Keepax } 1272b21acc1cSCharles Keepax } 1273b21acc1cSCharles Keepax 1274b21acc1cSCharles Keepax ctl = kzalloc(sizeof(*ctl), GFP_KERNEL); 1275b21acc1cSCharles Keepax if (!ctl) 1276b21acc1cSCharles Keepax return -ENOMEM; 12772323736dSCharles Keepax ctl->fw_name = wm_adsp_fw_text[dsp->fw]; 1278b21acc1cSCharles Keepax ctl->alg_region = *alg_region; 1279b21acc1cSCharles Keepax ctl->name = kmemdup(name, strlen(name) + 1, GFP_KERNEL); 1280b21acc1cSCharles Keepax if (!ctl->name) { 1281b21acc1cSCharles Keepax ret = -ENOMEM; 1282b21acc1cSCharles Keepax goto err_ctl; 1283b21acc1cSCharles Keepax } 1284b21acc1cSCharles Keepax ctl->enabled = 1; 1285b21acc1cSCharles Keepax ctl->set = 0; 1286b21acc1cSCharles Keepax ctl->ops.xget = wm_coeff_get; 1287b21acc1cSCharles Keepax ctl->ops.xput = wm_coeff_put; 1288b21acc1cSCharles Keepax ctl->dsp = dsp; 1289b21acc1cSCharles Keepax 129026c22a19SCharles Keepax ctl->flags = flags; 12918eb084d0SStuart Henderson ctl->type = type; 12922323736dSCharles Keepax ctl->offset = offset; 1293b21acc1cSCharles Keepax ctl->len = len; 1294b21acc1cSCharles Keepax ctl->cache = kzalloc(ctl->len, GFP_KERNEL); 1295b21acc1cSCharles Keepax if (!ctl->cache) { 1296b21acc1cSCharles Keepax ret = -ENOMEM; 1297b21acc1cSCharles Keepax goto err_ctl_name; 1298b21acc1cSCharles Keepax } 1299b21acc1cSCharles Keepax 13002323736dSCharles Keepax list_add(&ctl->list, &dsp->ctl_list); 13012323736dSCharles Keepax 13028eb084d0SStuart Henderson if (flags & WMFW_CTL_FLAG_SYS) 13038eb084d0SStuart Henderson return 0; 13048eb084d0SStuart Henderson 1305b21acc1cSCharles Keepax ctl_work = kzalloc(sizeof(*ctl_work), GFP_KERNEL); 1306b21acc1cSCharles Keepax if (!ctl_work) { 1307b21acc1cSCharles Keepax ret = -ENOMEM; 1308b21acc1cSCharles Keepax goto err_ctl_cache; 1309b21acc1cSCharles Keepax } 1310b21acc1cSCharles Keepax 1311b21acc1cSCharles Keepax ctl_work->dsp = dsp; 1312b21acc1cSCharles Keepax ctl_work->ctl = ctl; 1313b21acc1cSCharles Keepax INIT_WORK(&ctl_work->work, wm_adsp_ctl_work); 1314b21acc1cSCharles Keepax schedule_work(&ctl_work->work); 1315b21acc1cSCharles Keepax 1316b21acc1cSCharles Keepax return 0; 1317b21acc1cSCharles Keepax 1318b21acc1cSCharles Keepax err_ctl_cache: 1319b21acc1cSCharles Keepax kfree(ctl->cache); 1320b21acc1cSCharles Keepax err_ctl_name: 1321b21acc1cSCharles Keepax kfree(ctl->name); 1322b21acc1cSCharles Keepax err_ctl: 1323b21acc1cSCharles Keepax kfree(ctl); 1324b21acc1cSCharles Keepax 1325b21acc1cSCharles Keepax return ret; 1326b21acc1cSCharles Keepax } 1327b21acc1cSCharles Keepax 13282323736dSCharles Keepax struct wm_coeff_parsed_alg { 13292323736dSCharles Keepax int id; 13302323736dSCharles Keepax const u8 *name; 13312323736dSCharles Keepax int name_len; 13322323736dSCharles Keepax int ncoeff; 13332323736dSCharles Keepax }; 13342323736dSCharles Keepax 13352323736dSCharles Keepax struct wm_coeff_parsed_coeff { 13362323736dSCharles Keepax int offset; 13372323736dSCharles Keepax int mem_type; 13382323736dSCharles Keepax const u8 *name; 13392323736dSCharles Keepax int name_len; 13402323736dSCharles Keepax int ctl_type; 13412323736dSCharles Keepax int flags; 13422323736dSCharles Keepax int len; 13432323736dSCharles Keepax }; 13442323736dSCharles Keepax 1345cb5b57a9SCharles Keepax static int wm_coeff_parse_string(int bytes, const u8 **pos, const u8 **str) 1346cb5b57a9SCharles Keepax { 1347cb5b57a9SCharles Keepax int length; 1348cb5b57a9SCharles Keepax 1349cb5b57a9SCharles Keepax switch (bytes) { 1350cb5b57a9SCharles Keepax case 1: 1351cb5b57a9SCharles Keepax length = **pos; 1352cb5b57a9SCharles Keepax break; 1353cb5b57a9SCharles Keepax case 2: 13548299ee81SCharles Keepax length = le16_to_cpu(*((__le16 *)*pos)); 1355cb5b57a9SCharles Keepax break; 1356cb5b57a9SCharles Keepax default: 1357cb5b57a9SCharles Keepax return 0; 1358cb5b57a9SCharles Keepax } 1359cb5b57a9SCharles Keepax 1360cb5b57a9SCharles Keepax if (str) 1361cb5b57a9SCharles Keepax *str = *pos + bytes; 1362cb5b57a9SCharles Keepax 1363cb5b57a9SCharles Keepax *pos += ((length + bytes) + 3) & ~0x03; 1364cb5b57a9SCharles Keepax 1365cb5b57a9SCharles Keepax return length; 1366cb5b57a9SCharles Keepax } 1367cb5b57a9SCharles Keepax 1368cb5b57a9SCharles Keepax static int wm_coeff_parse_int(int bytes, const u8 **pos) 1369cb5b57a9SCharles Keepax { 1370cb5b57a9SCharles Keepax int val = 0; 1371cb5b57a9SCharles Keepax 1372cb5b57a9SCharles Keepax switch (bytes) { 1373cb5b57a9SCharles Keepax case 2: 13748299ee81SCharles Keepax val = le16_to_cpu(*((__le16 *)*pos)); 1375cb5b57a9SCharles Keepax break; 1376cb5b57a9SCharles Keepax case 4: 13778299ee81SCharles Keepax val = le32_to_cpu(*((__le32 *)*pos)); 1378cb5b57a9SCharles Keepax break; 1379cb5b57a9SCharles Keepax default: 1380cb5b57a9SCharles Keepax break; 1381cb5b57a9SCharles Keepax } 1382cb5b57a9SCharles Keepax 1383cb5b57a9SCharles Keepax *pos += bytes; 1384cb5b57a9SCharles Keepax 1385cb5b57a9SCharles Keepax return val; 1386cb5b57a9SCharles Keepax } 1387cb5b57a9SCharles Keepax 13882323736dSCharles Keepax static inline void wm_coeff_parse_alg(struct wm_adsp *dsp, const u8 **data, 13892323736dSCharles Keepax struct wm_coeff_parsed_alg *blk) 13902323736dSCharles Keepax { 13912323736dSCharles Keepax const struct wmfw_adsp_alg_data *raw; 13922323736dSCharles Keepax 1393cb5b57a9SCharles Keepax switch (dsp->fw_ver) { 1394cb5b57a9SCharles Keepax case 0: 1395cb5b57a9SCharles Keepax case 1: 13962323736dSCharles Keepax raw = (const struct wmfw_adsp_alg_data *)*data; 13972323736dSCharles Keepax *data = raw->data; 13982323736dSCharles Keepax 13992323736dSCharles Keepax blk->id = le32_to_cpu(raw->id); 14002323736dSCharles Keepax blk->name = raw->name; 14012323736dSCharles Keepax blk->name_len = strlen(raw->name); 14022323736dSCharles Keepax blk->ncoeff = le32_to_cpu(raw->ncoeff); 1403cb5b57a9SCharles Keepax break; 1404cb5b57a9SCharles Keepax default: 1405cb5b57a9SCharles Keepax blk->id = wm_coeff_parse_int(sizeof(raw->id), data); 1406cb5b57a9SCharles Keepax blk->name_len = wm_coeff_parse_string(sizeof(u8), data, 1407cb5b57a9SCharles Keepax &blk->name); 1408cb5b57a9SCharles Keepax wm_coeff_parse_string(sizeof(u16), data, NULL); 1409cb5b57a9SCharles Keepax blk->ncoeff = wm_coeff_parse_int(sizeof(raw->ncoeff), data); 1410cb5b57a9SCharles Keepax break; 1411cb5b57a9SCharles Keepax } 14122323736dSCharles Keepax 14132323736dSCharles Keepax adsp_dbg(dsp, "Algorithm ID: %#x\n", blk->id); 14142323736dSCharles Keepax adsp_dbg(dsp, "Algorithm name: %.*s\n", blk->name_len, blk->name); 14152323736dSCharles Keepax adsp_dbg(dsp, "# of coefficient descriptors: %#x\n", blk->ncoeff); 14162323736dSCharles Keepax } 14172323736dSCharles Keepax 14182323736dSCharles Keepax static inline void wm_coeff_parse_coeff(struct wm_adsp *dsp, const u8 **data, 14192323736dSCharles Keepax struct wm_coeff_parsed_coeff *blk) 14202323736dSCharles Keepax { 14212323736dSCharles Keepax const struct wmfw_adsp_coeff_data *raw; 1422cb5b57a9SCharles Keepax const u8 *tmp; 1423cb5b57a9SCharles Keepax int length; 14242323736dSCharles Keepax 1425cb5b57a9SCharles Keepax switch (dsp->fw_ver) { 1426cb5b57a9SCharles Keepax case 0: 1427cb5b57a9SCharles Keepax case 1: 14282323736dSCharles Keepax raw = (const struct wmfw_adsp_coeff_data *)*data; 14292323736dSCharles Keepax *data = *data + sizeof(raw->hdr) + le32_to_cpu(raw->hdr.size); 14302323736dSCharles Keepax 14312323736dSCharles Keepax blk->offset = le16_to_cpu(raw->hdr.offset); 14322323736dSCharles Keepax blk->mem_type = le16_to_cpu(raw->hdr.type); 14332323736dSCharles Keepax blk->name = raw->name; 14342323736dSCharles Keepax blk->name_len = strlen(raw->name); 14352323736dSCharles Keepax blk->ctl_type = le16_to_cpu(raw->ctl_type); 14362323736dSCharles Keepax blk->flags = le16_to_cpu(raw->flags); 14372323736dSCharles Keepax blk->len = le32_to_cpu(raw->len); 1438cb5b57a9SCharles Keepax break; 1439cb5b57a9SCharles Keepax default: 1440cb5b57a9SCharles Keepax tmp = *data; 1441cb5b57a9SCharles Keepax blk->offset = wm_coeff_parse_int(sizeof(raw->hdr.offset), &tmp); 1442cb5b57a9SCharles Keepax blk->mem_type = wm_coeff_parse_int(sizeof(raw->hdr.type), &tmp); 1443cb5b57a9SCharles Keepax length = wm_coeff_parse_int(sizeof(raw->hdr.size), &tmp); 1444cb5b57a9SCharles Keepax blk->name_len = wm_coeff_parse_string(sizeof(u8), &tmp, 1445cb5b57a9SCharles Keepax &blk->name); 1446cb5b57a9SCharles Keepax wm_coeff_parse_string(sizeof(u8), &tmp, NULL); 1447cb5b57a9SCharles Keepax wm_coeff_parse_string(sizeof(u16), &tmp, NULL); 1448cb5b57a9SCharles Keepax blk->ctl_type = wm_coeff_parse_int(sizeof(raw->ctl_type), &tmp); 1449cb5b57a9SCharles Keepax blk->flags = wm_coeff_parse_int(sizeof(raw->flags), &tmp); 1450cb5b57a9SCharles Keepax blk->len = wm_coeff_parse_int(sizeof(raw->len), &tmp); 1451cb5b57a9SCharles Keepax 1452cb5b57a9SCharles Keepax *data = *data + sizeof(raw->hdr) + length; 1453cb5b57a9SCharles Keepax break; 1454cb5b57a9SCharles Keepax } 14552323736dSCharles Keepax 14562323736dSCharles Keepax adsp_dbg(dsp, "\tCoefficient type: %#x\n", blk->mem_type); 14572323736dSCharles Keepax adsp_dbg(dsp, "\tCoefficient offset: %#x\n", blk->offset); 14582323736dSCharles Keepax adsp_dbg(dsp, "\tCoefficient name: %.*s\n", blk->name_len, blk->name); 14592323736dSCharles Keepax adsp_dbg(dsp, "\tCoefficient flags: %#x\n", blk->flags); 14602323736dSCharles Keepax adsp_dbg(dsp, "\tALSA control type: %#x\n", blk->ctl_type); 14612323736dSCharles Keepax adsp_dbg(dsp, "\tALSA control len: %#x\n", blk->len); 14622323736dSCharles Keepax } 14632323736dSCharles Keepax 1464f4f0c4c6SRichard Fitzgerald static int wm_adsp_check_coeff_flags(struct wm_adsp *dsp, 1465f4f0c4c6SRichard Fitzgerald const struct wm_coeff_parsed_coeff *coeff_blk, 1466f4f0c4c6SRichard Fitzgerald unsigned int f_required, 1467f4f0c4c6SRichard Fitzgerald unsigned int f_illegal) 1468f4f0c4c6SRichard Fitzgerald { 1469f4f0c4c6SRichard Fitzgerald if ((coeff_blk->flags & f_illegal) || 1470f4f0c4c6SRichard Fitzgerald ((coeff_blk->flags & f_required) != f_required)) { 1471f4f0c4c6SRichard Fitzgerald adsp_err(dsp, "Illegal flags 0x%x for control type 0x%x\n", 1472f4f0c4c6SRichard Fitzgerald coeff_blk->flags, coeff_blk->ctl_type); 1473f4f0c4c6SRichard Fitzgerald return -EINVAL; 1474f4f0c4c6SRichard Fitzgerald } 1475f4f0c4c6SRichard Fitzgerald 1476f4f0c4c6SRichard Fitzgerald return 0; 1477f4f0c4c6SRichard Fitzgerald } 1478f4f0c4c6SRichard Fitzgerald 14792323736dSCharles Keepax static int wm_adsp_parse_coeff(struct wm_adsp *dsp, 14802323736dSCharles Keepax const struct wmfw_region *region) 14812323736dSCharles Keepax { 14822323736dSCharles Keepax struct wm_adsp_alg_region alg_region = {}; 14832323736dSCharles Keepax struct wm_coeff_parsed_alg alg_blk; 14842323736dSCharles Keepax struct wm_coeff_parsed_coeff coeff_blk; 14852323736dSCharles Keepax const u8 *data = region->data; 14862323736dSCharles Keepax int i, ret; 14872323736dSCharles Keepax 14882323736dSCharles Keepax wm_coeff_parse_alg(dsp, &data, &alg_blk); 14892323736dSCharles Keepax for (i = 0; i < alg_blk.ncoeff; i++) { 14902323736dSCharles Keepax wm_coeff_parse_coeff(dsp, &data, &coeff_blk); 14912323736dSCharles Keepax 14922323736dSCharles Keepax switch (coeff_blk.ctl_type) { 14932323736dSCharles Keepax case SNDRV_CTL_ELEM_TYPE_BYTES: 14942323736dSCharles Keepax break; 1495a23ebba8SRichard Fitzgerald case WMFW_CTL_TYPE_ACKED: 1496a23ebba8SRichard Fitzgerald if (coeff_blk.flags & WMFW_CTL_FLAG_SYS) 1497a23ebba8SRichard Fitzgerald continue; /* ignore */ 1498a23ebba8SRichard Fitzgerald 1499a23ebba8SRichard Fitzgerald ret = wm_adsp_check_coeff_flags(dsp, &coeff_blk, 1500a23ebba8SRichard Fitzgerald WMFW_CTL_FLAG_VOLATILE | 1501a23ebba8SRichard Fitzgerald WMFW_CTL_FLAG_WRITEABLE | 1502a23ebba8SRichard Fitzgerald WMFW_CTL_FLAG_READABLE, 1503a23ebba8SRichard Fitzgerald 0); 1504a23ebba8SRichard Fitzgerald if (ret) 1505a23ebba8SRichard Fitzgerald return -EINVAL; 1506a23ebba8SRichard Fitzgerald break; 1507f4f0c4c6SRichard Fitzgerald case WMFW_CTL_TYPE_HOSTEVENT: 1508f4f0c4c6SRichard Fitzgerald ret = wm_adsp_check_coeff_flags(dsp, &coeff_blk, 1509f4f0c4c6SRichard Fitzgerald WMFW_CTL_FLAG_SYS | 1510f4f0c4c6SRichard Fitzgerald WMFW_CTL_FLAG_VOLATILE | 1511f4f0c4c6SRichard Fitzgerald WMFW_CTL_FLAG_WRITEABLE | 1512f4f0c4c6SRichard Fitzgerald WMFW_CTL_FLAG_READABLE, 1513f4f0c4c6SRichard Fitzgerald 0); 1514f4f0c4c6SRichard Fitzgerald if (ret) 1515f4f0c4c6SRichard Fitzgerald return -EINVAL; 1516f4f0c4c6SRichard Fitzgerald break; 15172323736dSCharles Keepax default: 15182323736dSCharles Keepax adsp_err(dsp, "Unknown control type: %d\n", 15192323736dSCharles Keepax coeff_blk.ctl_type); 15202323736dSCharles Keepax return -EINVAL; 15212323736dSCharles Keepax } 15222323736dSCharles Keepax 15232323736dSCharles Keepax alg_region.type = coeff_blk.mem_type; 15242323736dSCharles Keepax alg_region.alg = alg_blk.id; 15252323736dSCharles Keepax 15262323736dSCharles Keepax ret = wm_adsp_create_control(dsp, &alg_region, 15272323736dSCharles Keepax coeff_blk.offset, 15282323736dSCharles Keepax coeff_blk.len, 15292323736dSCharles Keepax coeff_blk.name, 153026c22a19SCharles Keepax coeff_blk.name_len, 15318eb084d0SStuart Henderson coeff_blk.flags, 15328eb084d0SStuart Henderson coeff_blk.ctl_type); 15332323736dSCharles Keepax if (ret < 0) 15342323736dSCharles Keepax adsp_err(dsp, "Failed to create control: %.*s, %d\n", 15352323736dSCharles Keepax coeff_blk.name_len, coeff_blk.name, ret); 15362323736dSCharles Keepax } 15372323736dSCharles Keepax 15382323736dSCharles Keepax return 0; 15392323736dSCharles Keepax } 15402323736dSCharles Keepax 15412159ad93SMark Brown static int wm_adsp_load(struct wm_adsp *dsp) 15422159ad93SMark Brown { 1543cf17c83cSMark Brown LIST_HEAD(buf_list); 15442159ad93SMark Brown const struct firmware *firmware; 15452159ad93SMark Brown struct regmap *regmap = dsp->regmap; 15462159ad93SMark Brown unsigned int pos = 0; 15472159ad93SMark Brown const struct wmfw_header *header; 15482159ad93SMark Brown const struct wmfw_adsp1_sizes *adsp1_sizes; 15492159ad93SMark Brown const struct wmfw_adsp2_sizes *adsp2_sizes; 15502159ad93SMark Brown const struct wmfw_footer *footer; 15512159ad93SMark Brown const struct wmfw_region *region; 15522159ad93SMark Brown const struct wm_adsp_region *mem; 15532159ad93SMark Brown const char *region_name; 15542159ad93SMark Brown char *file, *text; 1555cf17c83cSMark Brown struct wm_adsp_buf *buf; 15562159ad93SMark Brown unsigned int reg; 15572159ad93SMark Brown int regions = 0; 15582159ad93SMark Brown int ret, offset, type, sizes; 15592159ad93SMark Brown 15602159ad93SMark Brown file = kzalloc(PAGE_SIZE, GFP_KERNEL); 15612159ad93SMark Brown if (file == NULL) 15622159ad93SMark Brown return -ENOMEM; 15632159ad93SMark Brown 15641023dbd9SMark Brown snprintf(file, PAGE_SIZE, "%s-dsp%d-%s.wmfw", dsp->part, dsp->num, 15651023dbd9SMark Brown wm_adsp_fw[dsp->fw].file); 15662159ad93SMark Brown file[PAGE_SIZE - 1] = '\0'; 15672159ad93SMark Brown 15682159ad93SMark Brown ret = request_firmware(&firmware, file, dsp->dev); 15692159ad93SMark Brown if (ret != 0) { 15702159ad93SMark Brown adsp_err(dsp, "Failed to request '%s'\n", file); 15712159ad93SMark Brown goto out; 15722159ad93SMark Brown } 15732159ad93SMark Brown ret = -EINVAL; 15742159ad93SMark Brown 15752159ad93SMark Brown pos = sizeof(*header) + sizeof(*adsp1_sizes) + sizeof(*footer); 15762159ad93SMark Brown if (pos >= firmware->size) { 15772159ad93SMark Brown adsp_err(dsp, "%s: file too short, %zu bytes\n", 15782159ad93SMark Brown file, firmware->size); 15792159ad93SMark Brown goto out_fw; 15802159ad93SMark Brown } 15812159ad93SMark Brown 15822159ad93SMark Brown header = (void *)&firmware->data[0]; 15832159ad93SMark Brown 15842159ad93SMark Brown if (memcmp(&header->magic[0], "WMFW", 4) != 0) { 15852159ad93SMark Brown adsp_err(dsp, "%s: invalid magic\n", file); 15862159ad93SMark Brown goto out_fw; 15872159ad93SMark Brown } 15882159ad93SMark Brown 15892323736dSCharles Keepax switch (header->ver) { 15902323736dSCharles Keepax case 0: 1591c61e59feSCharles Keepax adsp_warn(dsp, "%s: Depreciated file format %d\n", 1592c61e59feSCharles Keepax file, header->ver); 1593c61e59feSCharles Keepax break; 15942323736dSCharles Keepax case 1: 1595cb5b57a9SCharles Keepax case 2: 15962323736dSCharles Keepax break; 15972323736dSCharles Keepax default: 15982159ad93SMark Brown adsp_err(dsp, "%s: unknown file format %d\n", 15992159ad93SMark Brown file, header->ver); 16002159ad93SMark Brown goto out_fw; 16012159ad93SMark Brown } 16022323736dSCharles Keepax 16033626992aSDimitris Papastamos adsp_info(dsp, "Firmware version: %d\n", header->ver); 16042323736dSCharles Keepax dsp->fw_ver = header->ver; 16052159ad93SMark Brown 16062159ad93SMark Brown if (header->core != dsp->type) { 16072159ad93SMark Brown adsp_err(dsp, "%s: invalid core %d != %d\n", 16082159ad93SMark Brown file, header->core, dsp->type); 16092159ad93SMark Brown goto out_fw; 16102159ad93SMark Brown } 16112159ad93SMark Brown 16122159ad93SMark Brown switch (dsp->type) { 16132159ad93SMark Brown case WMFW_ADSP1: 16142159ad93SMark Brown pos = sizeof(*header) + sizeof(*adsp1_sizes) + sizeof(*footer); 16152159ad93SMark Brown adsp1_sizes = (void *)&(header[1]); 16162159ad93SMark Brown footer = (void *)&(adsp1_sizes[1]); 16172159ad93SMark Brown sizes = sizeof(*adsp1_sizes); 16182159ad93SMark Brown 16192159ad93SMark Brown adsp_dbg(dsp, "%s: %d DM, %d PM, %d ZM\n", 16202159ad93SMark Brown file, le32_to_cpu(adsp1_sizes->dm), 16212159ad93SMark Brown le32_to_cpu(adsp1_sizes->pm), 16222159ad93SMark Brown le32_to_cpu(adsp1_sizes->zm)); 16232159ad93SMark Brown break; 16242159ad93SMark Brown 16252159ad93SMark Brown case WMFW_ADSP2: 16262159ad93SMark Brown pos = sizeof(*header) + sizeof(*adsp2_sizes) + sizeof(*footer); 16272159ad93SMark Brown adsp2_sizes = (void *)&(header[1]); 16282159ad93SMark Brown footer = (void *)&(adsp2_sizes[1]); 16292159ad93SMark Brown sizes = sizeof(*adsp2_sizes); 16302159ad93SMark Brown 16312159ad93SMark Brown adsp_dbg(dsp, "%s: %d XM, %d YM %d PM, %d ZM\n", 16322159ad93SMark Brown file, le32_to_cpu(adsp2_sizes->xm), 16332159ad93SMark Brown le32_to_cpu(adsp2_sizes->ym), 16342159ad93SMark Brown le32_to_cpu(adsp2_sizes->pm), 16352159ad93SMark Brown le32_to_cpu(adsp2_sizes->zm)); 16362159ad93SMark Brown break; 16372159ad93SMark Brown 16382159ad93SMark Brown default: 16396c452bdaSTakashi Iwai WARN(1, "Unknown DSP type"); 16402159ad93SMark Brown goto out_fw; 16412159ad93SMark Brown } 16422159ad93SMark Brown 16432159ad93SMark Brown if (le32_to_cpu(header->len) != sizeof(*header) + 16442159ad93SMark Brown sizes + sizeof(*footer)) { 16452159ad93SMark Brown adsp_err(dsp, "%s: unexpected header length %d\n", 16462159ad93SMark Brown file, le32_to_cpu(header->len)); 16472159ad93SMark Brown goto out_fw; 16482159ad93SMark Brown } 16492159ad93SMark Brown 16502159ad93SMark Brown adsp_dbg(dsp, "%s: timestamp %llu\n", file, 16512159ad93SMark Brown le64_to_cpu(footer->timestamp)); 16522159ad93SMark Brown 16532159ad93SMark Brown while (pos < firmware->size && 16542159ad93SMark Brown pos - firmware->size > sizeof(*region)) { 16552159ad93SMark Brown region = (void *)&(firmware->data[pos]); 16562159ad93SMark Brown region_name = "Unknown"; 16572159ad93SMark Brown reg = 0; 16582159ad93SMark Brown text = NULL; 16592159ad93SMark Brown offset = le32_to_cpu(region->offset) & 0xffffff; 16602159ad93SMark Brown type = be32_to_cpu(region->type) & 0xff; 16612159ad93SMark Brown mem = wm_adsp_find_region(dsp, type); 16622159ad93SMark Brown 16632159ad93SMark Brown switch (type) { 16642159ad93SMark Brown case WMFW_NAME_TEXT: 16652159ad93SMark Brown region_name = "Firmware name"; 16662159ad93SMark Brown text = kzalloc(le32_to_cpu(region->len) + 1, 16672159ad93SMark Brown GFP_KERNEL); 16682159ad93SMark Brown break; 16692323736dSCharles Keepax case WMFW_ALGORITHM_DATA: 16702323736dSCharles Keepax region_name = "Algorithm"; 16712323736dSCharles Keepax ret = wm_adsp_parse_coeff(dsp, region); 16722323736dSCharles Keepax if (ret != 0) 16732323736dSCharles Keepax goto out_fw; 16742323736dSCharles Keepax break; 16752159ad93SMark Brown case WMFW_INFO_TEXT: 16762159ad93SMark Brown region_name = "Information"; 16772159ad93SMark Brown text = kzalloc(le32_to_cpu(region->len) + 1, 16782159ad93SMark Brown GFP_KERNEL); 16792159ad93SMark Brown break; 16802159ad93SMark Brown case WMFW_ABSOLUTE: 16812159ad93SMark Brown region_name = "Absolute"; 16822159ad93SMark Brown reg = offset; 16832159ad93SMark Brown break; 16842159ad93SMark Brown case WMFW_ADSP1_PM: 16852159ad93SMark Brown case WMFW_ADSP1_DM: 16862159ad93SMark Brown case WMFW_ADSP2_XM: 16872159ad93SMark Brown case WMFW_ADSP2_YM: 16882159ad93SMark Brown case WMFW_ADSP1_ZM: 16899ce5e6e6SRichard Fitzgerald region_name = wm_adsp_mem_region_name(type); 169045b9ee72SMark Brown reg = wm_adsp_region_to_reg(mem, offset); 16912159ad93SMark Brown break; 16922159ad93SMark Brown default: 16932159ad93SMark Brown adsp_warn(dsp, 16942159ad93SMark Brown "%s.%d: Unknown region type %x at %d(%x)\n", 16952159ad93SMark Brown file, regions, type, pos, pos); 16962159ad93SMark Brown break; 16972159ad93SMark Brown } 16982159ad93SMark Brown 16992159ad93SMark Brown adsp_dbg(dsp, "%s.%d: %d bytes at %d in %s\n", file, 17002159ad93SMark Brown regions, le32_to_cpu(region->len), offset, 17012159ad93SMark Brown region_name); 17022159ad93SMark Brown 17032159ad93SMark Brown if (text) { 17042159ad93SMark Brown memcpy(text, region->data, le32_to_cpu(region->len)); 17052159ad93SMark Brown adsp_info(dsp, "%s: %s\n", file, text); 17062159ad93SMark Brown kfree(text); 17072159ad93SMark Brown } 17082159ad93SMark Brown 17092159ad93SMark Brown if (reg) { 1710cdcd7f72SCharles Keepax buf = wm_adsp_buf_alloc(region->data, 1711cdcd7f72SCharles Keepax le32_to_cpu(region->len), 1712cf17c83cSMark Brown &buf_list); 1713a76fefabSMark Brown if (!buf) { 1714a76fefabSMark Brown adsp_err(dsp, "Out of memory\n"); 17157328823dSDimitris Papastamos ret = -ENOMEM; 17167328823dSDimitris Papastamos goto out_fw; 1717a76fefabSMark Brown } 1718a76fefabSMark Brown 1719cdcd7f72SCharles Keepax ret = regmap_raw_write_async(regmap, reg, buf->buf, 1720cdcd7f72SCharles Keepax le32_to_cpu(region->len)); 17212159ad93SMark Brown if (ret != 0) { 17222159ad93SMark Brown adsp_err(dsp, 1723cdcd7f72SCharles Keepax "%s.%d: Failed to write %d bytes at %d in %s: %d\n", 17242159ad93SMark Brown file, regions, 1725cdcd7f72SCharles Keepax le32_to_cpu(region->len), offset, 17262159ad93SMark Brown region_name, ret); 17272159ad93SMark Brown goto out_fw; 17282159ad93SMark Brown } 17292159ad93SMark Brown } 17302159ad93SMark Brown 17312159ad93SMark Brown pos += le32_to_cpu(region->len) + sizeof(*region); 17322159ad93SMark Brown regions++; 17332159ad93SMark Brown } 17342159ad93SMark Brown 1735cf17c83cSMark Brown ret = regmap_async_complete(regmap); 1736cf17c83cSMark Brown if (ret != 0) { 1737cf17c83cSMark Brown adsp_err(dsp, "Failed to complete async write: %d\n", ret); 1738cf17c83cSMark Brown goto out_fw; 1739cf17c83cSMark Brown } 1740cf17c83cSMark Brown 17412159ad93SMark Brown if (pos > firmware->size) 17422159ad93SMark Brown adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n", 17432159ad93SMark Brown file, regions, pos - firmware->size); 17442159ad93SMark Brown 1745f9f55e31SRichard Fitzgerald wm_adsp_debugfs_save_wmfwname(dsp, file); 1746f9f55e31SRichard Fitzgerald 17472159ad93SMark Brown out_fw: 1748cf17c83cSMark Brown regmap_async_complete(regmap); 1749cf17c83cSMark Brown wm_adsp_buf_free(&buf_list); 17502159ad93SMark Brown release_firmware(firmware); 17512159ad93SMark Brown out: 17522159ad93SMark Brown kfree(file); 17532159ad93SMark Brown 17542159ad93SMark Brown return ret; 17552159ad93SMark Brown } 17562159ad93SMark Brown 17572323736dSCharles Keepax static void wm_adsp_ctl_fixup_base(struct wm_adsp *dsp, 17582323736dSCharles Keepax const struct wm_adsp_alg_region *alg_region) 17592323736dSCharles Keepax { 17602323736dSCharles Keepax struct wm_coeff_ctl *ctl; 17612323736dSCharles Keepax 17622323736dSCharles Keepax list_for_each_entry(ctl, &dsp->ctl_list, list) { 17632323736dSCharles Keepax if (ctl->fw_name == wm_adsp_fw_text[dsp->fw] && 17642323736dSCharles Keepax alg_region->alg == ctl->alg_region.alg && 17652323736dSCharles Keepax alg_region->type == ctl->alg_region.type) { 17662323736dSCharles Keepax ctl->alg_region.base = alg_region->base; 17672323736dSCharles Keepax } 17682323736dSCharles Keepax } 17692323736dSCharles Keepax } 17702323736dSCharles Keepax 17713809f001SCharles Keepax static void *wm_adsp_read_algs(struct wm_adsp *dsp, size_t n_algs, 1772b618a185SCharles Keepax unsigned int pos, unsigned int len) 1773db40517cSMark Brown { 1774b618a185SCharles Keepax void *alg; 1775b618a185SCharles Keepax int ret; 1776db40517cSMark Brown __be32 val; 1777db40517cSMark Brown 17783809f001SCharles Keepax if (n_algs == 0) { 1779b618a185SCharles Keepax adsp_err(dsp, "No algorithms\n"); 1780b618a185SCharles Keepax return ERR_PTR(-EINVAL); 1781db40517cSMark Brown } 1782db40517cSMark Brown 17833809f001SCharles Keepax if (n_algs > 1024) { 17843809f001SCharles Keepax adsp_err(dsp, "Algorithm count %zx excessive\n", n_algs); 1785b618a185SCharles Keepax return ERR_PTR(-EINVAL); 1786b618a185SCharles Keepax } 1787b618a185SCharles Keepax 1788b618a185SCharles Keepax /* Read the terminator first to validate the length */ 1789b618a185SCharles Keepax ret = regmap_raw_read(dsp->regmap, pos + len, &val, sizeof(val)); 1790b618a185SCharles Keepax if (ret != 0) { 1791b618a185SCharles Keepax adsp_err(dsp, "Failed to read algorithm list end: %d\n", 1792b618a185SCharles Keepax ret); 1793b618a185SCharles Keepax return ERR_PTR(ret); 1794b618a185SCharles Keepax } 1795b618a185SCharles Keepax 1796b618a185SCharles Keepax if (be32_to_cpu(val) != 0xbedead) 1797b618a185SCharles Keepax adsp_warn(dsp, "Algorithm list end %x 0x%x != 0xbeadead\n", 1798b618a185SCharles Keepax pos + len, be32_to_cpu(val)); 1799b618a185SCharles Keepax 1800b618a185SCharles Keepax alg = kzalloc(len * 2, GFP_KERNEL | GFP_DMA); 1801b618a185SCharles Keepax if (!alg) 1802b618a185SCharles Keepax return ERR_PTR(-ENOMEM); 1803b618a185SCharles Keepax 1804b618a185SCharles Keepax ret = regmap_raw_read(dsp->regmap, pos, alg, len * 2); 1805b618a185SCharles Keepax if (ret != 0) { 18067d00cd97SCharles Keepax adsp_err(dsp, "Failed to read algorithm list: %d\n", ret); 1807b618a185SCharles Keepax kfree(alg); 1808b618a185SCharles Keepax return ERR_PTR(ret); 1809b618a185SCharles Keepax } 1810b618a185SCharles Keepax 1811b618a185SCharles Keepax return alg; 1812b618a185SCharles Keepax } 1813b618a185SCharles Keepax 181414197095SCharles Keepax static struct wm_adsp_alg_region * 181514197095SCharles Keepax wm_adsp_find_alg_region(struct wm_adsp *dsp, int type, unsigned int id) 181614197095SCharles Keepax { 181714197095SCharles Keepax struct wm_adsp_alg_region *alg_region; 181814197095SCharles Keepax 181914197095SCharles Keepax list_for_each_entry(alg_region, &dsp->alg_regions, list) { 182014197095SCharles Keepax if (id == alg_region->alg && type == alg_region->type) 182114197095SCharles Keepax return alg_region; 182214197095SCharles Keepax } 182314197095SCharles Keepax 182414197095SCharles Keepax return NULL; 182514197095SCharles Keepax } 182614197095SCharles Keepax 1827d9d20e17SCharles Keepax static struct wm_adsp_alg_region *wm_adsp_create_region(struct wm_adsp *dsp, 1828d9d20e17SCharles Keepax int type, __be32 id, 1829d9d20e17SCharles Keepax __be32 base) 1830d9d20e17SCharles Keepax { 1831d9d20e17SCharles Keepax struct wm_adsp_alg_region *alg_region; 1832d9d20e17SCharles Keepax 1833d9d20e17SCharles Keepax alg_region = kzalloc(sizeof(*alg_region), GFP_KERNEL); 1834d9d20e17SCharles Keepax if (!alg_region) 1835d9d20e17SCharles Keepax return ERR_PTR(-ENOMEM); 1836d9d20e17SCharles Keepax 1837d9d20e17SCharles Keepax alg_region->type = type; 1838d9d20e17SCharles Keepax alg_region->alg = be32_to_cpu(id); 1839d9d20e17SCharles Keepax alg_region->base = be32_to_cpu(base); 1840d9d20e17SCharles Keepax 1841d9d20e17SCharles Keepax list_add_tail(&alg_region->list, &dsp->alg_regions); 1842d9d20e17SCharles Keepax 18432323736dSCharles Keepax if (dsp->fw_ver > 0) 18442323736dSCharles Keepax wm_adsp_ctl_fixup_base(dsp, alg_region); 18452323736dSCharles Keepax 1846d9d20e17SCharles Keepax return alg_region; 1847d9d20e17SCharles Keepax } 1848d9d20e17SCharles Keepax 184956574d54SRichard Fitzgerald static void wm_adsp_free_alg_regions(struct wm_adsp *dsp) 185056574d54SRichard Fitzgerald { 185156574d54SRichard Fitzgerald struct wm_adsp_alg_region *alg_region; 185256574d54SRichard Fitzgerald 185356574d54SRichard Fitzgerald while (!list_empty(&dsp->alg_regions)) { 185456574d54SRichard Fitzgerald alg_region = list_first_entry(&dsp->alg_regions, 185556574d54SRichard Fitzgerald struct wm_adsp_alg_region, 185656574d54SRichard Fitzgerald list); 185756574d54SRichard Fitzgerald list_del(&alg_region->list); 185856574d54SRichard Fitzgerald kfree(alg_region); 185956574d54SRichard Fitzgerald } 186056574d54SRichard Fitzgerald } 186156574d54SRichard Fitzgerald 1862b618a185SCharles Keepax static int wm_adsp1_setup_algs(struct wm_adsp *dsp) 1863b618a185SCharles Keepax { 1864b618a185SCharles Keepax struct wmfw_adsp1_id_hdr adsp1_id; 1865b618a185SCharles Keepax struct wmfw_adsp1_alg_hdr *adsp1_alg; 18663809f001SCharles Keepax struct wm_adsp_alg_region *alg_region; 1867b618a185SCharles Keepax const struct wm_adsp_region *mem; 1868b618a185SCharles Keepax unsigned int pos, len; 18693809f001SCharles Keepax size_t n_algs; 1870b618a185SCharles Keepax int i, ret; 1871b618a185SCharles Keepax 1872b618a185SCharles Keepax mem = wm_adsp_find_region(dsp, WMFW_ADSP1_DM); 18736c452bdaSTakashi Iwai if (WARN_ON(!mem)) 1874db40517cSMark Brown return -EINVAL; 1875db40517cSMark Brown 1876b618a185SCharles Keepax ret = regmap_raw_read(dsp->regmap, mem->base, &adsp1_id, 1877db40517cSMark Brown sizeof(adsp1_id)); 1878db40517cSMark Brown if (ret != 0) { 1879db40517cSMark Brown adsp_err(dsp, "Failed to read algorithm info: %d\n", 1880db40517cSMark Brown ret); 1881db40517cSMark Brown return ret; 1882db40517cSMark Brown } 1883db40517cSMark Brown 18843809f001SCharles Keepax n_algs = be32_to_cpu(adsp1_id.n_algs); 1885f395a218SMark Brown dsp->fw_id = be32_to_cpu(adsp1_id.fw.id); 1886db40517cSMark Brown adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n", 1887f395a218SMark Brown dsp->fw_id, 1888db40517cSMark Brown (be32_to_cpu(adsp1_id.fw.ver) & 0xff0000) >> 16, 1889db40517cSMark Brown (be32_to_cpu(adsp1_id.fw.ver) & 0xff00) >> 8, 1890db40517cSMark Brown be32_to_cpu(adsp1_id.fw.ver) & 0xff, 18913809f001SCharles Keepax n_algs); 1892db40517cSMark Brown 1893d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_ZM, 1894d9d20e17SCharles Keepax adsp1_id.fw.id, adsp1_id.zm); 1895d9d20e17SCharles Keepax if (IS_ERR(alg_region)) 1896d9d20e17SCharles Keepax return PTR_ERR(alg_region); 1897ac50009fSMark Brown 1898d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_DM, 1899d9d20e17SCharles Keepax adsp1_id.fw.id, adsp1_id.dm); 1900d9d20e17SCharles Keepax if (IS_ERR(alg_region)) 1901d9d20e17SCharles Keepax return PTR_ERR(alg_region); 1902ac50009fSMark Brown 1903db40517cSMark Brown pos = sizeof(adsp1_id) / 2; 19043809f001SCharles Keepax len = (sizeof(*adsp1_alg) * n_algs) / 2; 1905db40517cSMark Brown 19063809f001SCharles Keepax adsp1_alg = wm_adsp_read_algs(dsp, n_algs, mem->base + pos, len); 1907b618a185SCharles Keepax if (IS_ERR(adsp1_alg)) 1908b618a185SCharles Keepax return PTR_ERR(adsp1_alg); 1909db40517cSMark Brown 19103809f001SCharles Keepax for (i = 0; i < n_algs; i++) { 1911471f4885SMark Brown adsp_info(dsp, "%d: ID %x v%d.%d.%d DM@%x ZM@%x\n", 1912db40517cSMark Brown i, be32_to_cpu(adsp1_alg[i].alg.id), 1913db40517cSMark Brown (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff0000) >> 16, 1914db40517cSMark Brown (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff00) >> 8, 1915471f4885SMark Brown be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff, 1916471f4885SMark Brown be32_to_cpu(adsp1_alg[i].dm), 1917471f4885SMark Brown be32_to_cpu(adsp1_alg[i].zm)); 1918471f4885SMark Brown 1919d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_DM, 1920d9d20e17SCharles Keepax adsp1_alg[i].alg.id, 1921d9d20e17SCharles Keepax adsp1_alg[i].dm); 1922d9d20e17SCharles Keepax if (IS_ERR(alg_region)) { 1923d9d20e17SCharles Keepax ret = PTR_ERR(alg_region); 1924d6d52179SJS Park goto out; 1925d6d52179SJS Park } 19262323736dSCharles Keepax if (dsp->fw_ver == 0) { 19273809f001SCharles Keepax if (i + 1 < n_algs) { 19286958eb2aSCharles Keepax len = be32_to_cpu(adsp1_alg[i + 1].dm); 19296958eb2aSCharles Keepax len -= be32_to_cpu(adsp1_alg[i].dm); 19306958eb2aSCharles Keepax len *= 4; 19312323736dSCharles Keepax wm_adsp_create_control(dsp, alg_region, 0, 19328eb084d0SStuart Henderson len, NULL, 0, 0, 19338eb084d0SStuart Henderson SNDRV_CTL_ELEM_TYPE_BYTES); 19346ab2b7b4SDimitris Papastamos } else { 19356ab2b7b4SDimitris Papastamos adsp_warn(dsp, "Missing length info for region DM with ID %x\n", 19366ab2b7b4SDimitris Papastamos be32_to_cpu(adsp1_alg[i].alg.id)); 19376ab2b7b4SDimitris Papastamos } 19382323736dSCharles Keepax } 1939471f4885SMark Brown 1940d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_ZM, 1941d9d20e17SCharles Keepax adsp1_alg[i].alg.id, 1942d9d20e17SCharles Keepax adsp1_alg[i].zm); 1943d9d20e17SCharles Keepax if (IS_ERR(alg_region)) { 1944d9d20e17SCharles Keepax ret = PTR_ERR(alg_region); 1945d6d52179SJS Park goto out; 1946d6d52179SJS Park } 19472323736dSCharles Keepax if (dsp->fw_ver == 0) { 19483809f001SCharles Keepax if (i + 1 < n_algs) { 19496958eb2aSCharles Keepax len = be32_to_cpu(adsp1_alg[i + 1].zm); 19506958eb2aSCharles Keepax len -= be32_to_cpu(adsp1_alg[i].zm); 19516958eb2aSCharles Keepax len *= 4; 19522323736dSCharles Keepax wm_adsp_create_control(dsp, alg_region, 0, 19538eb084d0SStuart Henderson len, NULL, 0, 0, 19548eb084d0SStuart Henderson SNDRV_CTL_ELEM_TYPE_BYTES); 19556ab2b7b4SDimitris Papastamos } else { 19566ab2b7b4SDimitris Papastamos adsp_warn(dsp, "Missing length info for region ZM with ID %x\n", 19576ab2b7b4SDimitris Papastamos be32_to_cpu(adsp1_alg[i].alg.id)); 19586ab2b7b4SDimitris Papastamos } 1959b618a185SCharles Keepax } 19602323736dSCharles Keepax } 1961db40517cSMark Brown 1962b618a185SCharles Keepax out: 1963b618a185SCharles Keepax kfree(adsp1_alg); 1964b618a185SCharles Keepax return ret; 1965b618a185SCharles Keepax } 1966b618a185SCharles Keepax 1967b618a185SCharles Keepax static int wm_adsp2_setup_algs(struct wm_adsp *dsp) 1968b618a185SCharles Keepax { 1969b618a185SCharles Keepax struct wmfw_adsp2_id_hdr adsp2_id; 1970b618a185SCharles Keepax struct wmfw_adsp2_alg_hdr *adsp2_alg; 19713809f001SCharles Keepax struct wm_adsp_alg_region *alg_region; 1972b618a185SCharles Keepax const struct wm_adsp_region *mem; 1973b618a185SCharles Keepax unsigned int pos, len; 19743809f001SCharles Keepax size_t n_algs; 1975b618a185SCharles Keepax int i, ret; 1976b618a185SCharles Keepax 1977b618a185SCharles Keepax mem = wm_adsp_find_region(dsp, WMFW_ADSP2_XM); 1978b618a185SCharles Keepax if (WARN_ON(!mem)) 1979b618a185SCharles Keepax return -EINVAL; 1980b618a185SCharles Keepax 1981b618a185SCharles Keepax ret = regmap_raw_read(dsp->regmap, mem->base, &adsp2_id, 1982b618a185SCharles Keepax sizeof(adsp2_id)); 1983b618a185SCharles Keepax if (ret != 0) { 1984b618a185SCharles Keepax adsp_err(dsp, "Failed to read algorithm info: %d\n", 1985b618a185SCharles Keepax ret); 1986b618a185SCharles Keepax return ret; 1987b618a185SCharles Keepax } 1988b618a185SCharles Keepax 19893809f001SCharles Keepax n_algs = be32_to_cpu(adsp2_id.n_algs); 1990b618a185SCharles Keepax dsp->fw_id = be32_to_cpu(adsp2_id.fw.id); 1991f9f55e31SRichard Fitzgerald dsp->fw_id_version = be32_to_cpu(adsp2_id.fw.ver); 1992b618a185SCharles Keepax adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n", 1993b618a185SCharles Keepax dsp->fw_id, 1994f9f55e31SRichard Fitzgerald (dsp->fw_id_version & 0xff0000) >> 16, 1995f9f55e31SRichard Fitzgerald (dsp->fw_id_version & 0xff00) >> 8, 1996f9f55e31SRichard Fitzgerald dsp->fw_id_version & 0xff, 19973809f001SCharles Keepax n_algs); 1998b618a185SCharles Keepax 1999d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_XM, 2000d9d20e17SCharles Keepax adsp2_id.fw.id, adsp2_id.xm); 2001d9d20e17SCharles Keepax if (IS_ERR(alg_region)) 2002d9d20e17SCharles Keepax return PTR_ERR(alg_region); 2003b618a185SCharles Keepax 2004d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_YM, 2005d9d20e17SCharles Keepax adsp2_id.fw.id, adsp2_id.ym); 2006d9d20e17SCharles Keepax if (IS_ERR(alg_region)) 2007d9d20e17SCharles Keepax return PTR_ERR(alg_region); 2008b618a185SCharles Keepax 2009d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_ZM, 2010d9d20e17SCharles Keepax adsp2_id.fw.id, adsp2_id.zm); 2011d9d20e17SCharles Keepax if (IS_ERR(alg_region)) 2012d9d20e17SCharles Keepax return PTR_ERR(alg_region); 2013b618a185SCharles Keepax 2014b618a185SCharles Keepax pos = sizeof(adsp2_id) / 2; 20153809f001SCharles Keepax len = (sizeof(*adsp2_alg) * n_algs) / 2; 2016b618a185SCharles Keepax 20173809f001SCharles Keepax adsp2_alg = wm_adsp_read_algs(dsp, n_algs, mem->base + pos, len); 2018b618a185SCharles Keepax if (IS_ERR(adsp2_alg)) 2019b618a185SCharles Keepax return PTR_ERR(adsp2_alg); 2020b618a185SCharles Keepax 20213809f001SCharles Keepax for (i = 0; i < n_algs; i++) { 2022471f4885SMark Brown adsp_info(dsp, 2023471f4885SMark Brown "%d: ID %x v%d.%d.%d XM@%x YM@%x ZM@%x\n", 2024db40517cSMark Brown i, be32_to_cpu(adsp2_alg[i].alg.id), 2025db40517cSMark Brown (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff0000) >> 16, 2026db40517cSMark Brown (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff00) >> 8, 2027471f4885SMark Brown be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff, 2028471f4885SMark Brown be32_to_cpu(adsp2_alg[i].xm), 2029471f4885SMark Brown be32_to_cpu(adsp2_alg[i].ym), 2030471f4885SMark Brown be32_to_cpu(adsp2_alg[i].zm)); 2031471f4885SMark Brown 2032d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_XM, 2033d9d20e17SCharles Keepax adsp2_alg[i].alg.id, 2034d9d20e17SCharles Keepax adsp2_alg[i].xm); 2035d9d20e17SCharles Keepax if (IS_ERR(alg_region)) { 2036d9d20e17SCharles Keepax ret = PTR_ERR(alg_region); 2037d6d52179SJS Park goto out; 2038d6d52179SJS Park } 20392323736dSCharles Keepax if (dsp->fw_ver == 0) { 20403809f001SCharles Keepax if (i + 1 < n_algs) { 20416958eb2aSCharles Keepax len = be32_to_cpu(adsp2_alg[i + 1].xm); 20426958eb2aSCharles Keepax len -= be32_to_cpu(adsp2_alg[i].xm); 20436958eb2aSCharles Keepax len *= 4; 20442323736dSCharles Keepax wm_adsp_create_control(dsp, alg_region, 0, 20458eb084d0SStuart Henderson len, NULL, 0, 0, 20468eb084d0SStuart Henderson SNDRV_CTL_ELEM_TYPE_BYTES); 20476ab2b7b4SDimitris Papastamos } else { 20486ab2b7b4SDimitris Papastamos adsp_warn(dsp, "Missing length info for region XM with ID %x\n", 20496ab2b7b4SDimitris Papastamos be32_to_cpu(adsp2_alg[i].alg.id)); 20506ab2b7b4SDimitris Papastamos } 20512323736dSCharles Keepax } 2052471f4885SMark Brown 2053d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_YM, 2054d9d20e17SCharles Keepax adsp2_alg[i].alg.id, 2055d9d20e17SCharles Keepax adsp2_alg[i].ym); 2056d9d20e17SCharles Keepax if (IS_ERR(alg_region)) { 2057d9d20e17SCharles Keepax ret = PTR_ERR(alg_region); 2058d6d52179SJS Park goto out; 2059d6d52179SJS Park } 20602323736dSCharles Keepax if (dsp->fw_ver == 0) { 20613809f001SCharles Keepax if (i + 1 < n_algs) { 20626958eb2aSCharles Keepax len = be32_to_cpu(adsp2_alg[i + 1].ym); 20636958eb2aSCharles Keepax len -= be32_to_cpu(adsp2_alg[i].ym); 20646958eb2aSCharles Keepax len *= 4; 20652323736dSCharles Keepax wm_adsp_create_control(dsp, alg_region, 0, 20668eb084d0SStuart Henderson len, NULL, 0, 0, 20678eb084d0SStuart Henderson SNDRV_CTL_ELEM_TYPE_BYTES); 20686ab2b7b4SDimitris Papastamos } else { 20696ab2b7b4SDimitris Papastamos adsp_warn(dsp, "Missing length info for region YM with ID %x\n", 20706ab2b7b4SDimitris Papastamos be32_to_cpu(adsp2_alg[i].alg.id)); 20716ab2b7b4SDimitris Papastamos } 20722323736dSCharles Keepax } 2073471f4885SMark Brown 2074d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_ZM, 2075d9d20e17SCharles Keepax adsp2_alg[i].alg.id, 2076d9d20e17SCharles Keepax adsp2_alg[i].zm); 2077d9d20e17SCharles Keepax if (IS_ERR(alg_region)) { 2078d9d20e17SCharles Keepax ret = PTR_ERR(alg_region); 2079d6d52179SJS Park goto out; 2080d6d52179SJS Park } 20812323736dSCharles Keepax if (dsp->fw_ver == 0) { 20823809f001SCharles Keepax if (i + 1 < n_algs) { 20836958eb2aSCharles Keepax len = be32_to_cpu(adsp2_alg[i + 1].zm); 20846958eb2aSCharles Keepax len -= be32_to_cpu(adsp2_alg[i].zm); 20856958eb2aSCharles Keepax len *= 4; 20862323736dSCharles Keepax wm_adsp_create_control(dsp, alg_region, 0, 20878eb084d0SStuart Henderson len, NULL, 0, 0, 20888eb084d0SStuart Henderson SNDRV_CTL_ELEM_TYPE_BYTES); 20896ab2b7b4SDimitris Papastamos } else { 20906ab2b7b4SDimitris Papastamos adsp_warn(dsp, "Missing length info for region ZM with ID %x\n", 20916ab2b7b4SDimitris Papastamos be32_to_cpu(adsp2_alg[i].alg.id)); 20926ab2b7b4SDimitris Papastamos } 2093db40517cSMark Brown } 20942323736dSCharles Keepax } 2095db40517cSMark Brown 2096db40517cSMark Brown out: 2097b618a185SCharles Keepax kfree(adsp2_alg); 2098db40517cSMark Brown return ret; 2099db40517cSMark Brown } 2100db40517cSMark Brown 21012159ad93SMark Brown static int wm_adsp_load_coeff(struct wm_adsp *dsp) 21022159ad93SMark Brown { 2103cf17c83cSMark Brown LIST_HEAD(buf_list); 21042159ad93SMark Brown struct regmap *regmap = dsp->regmap; 21052159ad93SMark Brown struct wmfw_coeff_hdr *hdr; 21062159ad93SMark Brown struct wmfw_coeff_item *blk; 21072159ad93SMark Brown const struct firmware *firmware; 2108471f4885SMark Brown const struct wm_adsp_region *mem; 2109471f4885SMark Brown struct wm_adsp_alg_region *alg_region; 21102159ad93SMark Brown const char *region_name; 21112159ad93SMark Brown int ret, pos, blocks, type, offset, reg; 21122159ad93SMark Brown char *file; 2113cf17c83cSMark Brown struct wm_adsp_buf *buf; 21142159ad93SMark Brown 21152159ad93SMark Brown file = kzalloc(PAGE_SIZE, GFP_KERNEL); 21162159ad93SMark Brown if (file == NULL) 21172159ad93SMark Brown return -ENOMEM; 21182159ad93SMark Brown 21191023dbd9SMark Brown snprintf(file, PAGE_SIZE, "%s-dsp%d-%s.bin", dsp->part, dsp->num, 21201023dbd9SMark Brown wm_adsp_fw[dsp->fw].file); 21212159ad93SMark Brown file[PAGE_SIZE - 1] = '\0'; 21222159ad93SMark Brown 21232159ad93SMark Brown ret = request_firmware(&firmware, file, dsp->dev); 21242159ad93SMark Brown if (ret != 0) { 21252159ad93SMark Brown adsp_warn(dsp, "Failed to request '%s'\n", file); 21262159ad93SMark Brown ret = 0; 21272159ad93SMark Brown goto out; 21282159ad93SMark Brown } 21292159ad93SMark Brown ret = -EINVAL; 21302159ad93SMark Brown 21312159ad93SMark Brown if (sizeof(*hdr) >= firmware->size) { 21322159ad93SMark Brown adsp_err(dsp, "%s: file too short, %zu bytes\n", 21332159ad93SMark Brown file, firmware->size); 21342159ad93SMark Brown goto out_fw; 21352159ad93SMark Brown } 21362159ad93SMark Brown 21372159ad93SMark Brown hdr = (void *)&firmware->data[0]; 21382159ad93SMark Brown if (memcmp(hdr->magic, "WMDR", 4) != 0) { 21392159ad93SMark Brown adsp_err(dsp, "%s: invalid magic\n", file); 2140a4cdbec7SCharles Keepax goto out_fw; 21412159ad93SMark Brown } 21422159ad93SMark Brown 2143c712326dSMark Brown switch (be32_to_cpu(hdr->rev) & 0xff) { 2144c712326dSMark Brown case 1: 2145c712326dSMark Brown break; 2146c712326dSMark Brown default: 2147c712326dSMark Brown adsp_err(dsp, "%s: Unsupported coefficient file format %d\n", 2148c712326dSMark Brown file, be32_to_cpu(hdr->rev) & 0xff); 2149c712326dSMark Brown ret = -EINVAL; 2150c712326dSMark Brown goto out_fw; 2151c712326dSMark Brown } 2152c712326dSMark Brown 21532159ad93SMark Brown adsp_dbg(dsp, "%s: v%d.%d.%d\n", file, 21542159ad93SMark Brown (le32_to_cpu(hdr->ver) >> 16) & 0xff, 21552159ad93SMark Brown (le32_to_cpu(hdr->ver) >> 8) & 0xff, 21562159ad93SMark Brown le32_to_cpu(hdr->ver) & 0xff); 21572159ad93SMark Brown 21582159ad93SMark Brown pos = le32_to_cpu(hdr->len); 21592159ad93SMark Brown 21602159ad93SMark Brown blocks = 0; 21612159ad93SMark Brown while (pos < firmware->size && 21622159ad93SMark Brown pos - firmware->size > sizeof(*blk)) { 21632159ad93SMark Brown blk = (void *)(&firmware->data[pos]); 21642159ad93SMark Brown 2165c712326dSMark Brown type = le16_to_cpu(blk->type); 2166c712326dSMark Brown offset = le16_to_cpu(blk->offset); 21672159ad93SMark Brown 21682159ad93SMark Brown adsp_dbg(dsp, "%s.%d: %x v%d.%d.%d\n", 21692159ad93SMark Brown file, blocks, le32_to_cpu(blk->id), 21702159ad93SMark Brown (le32_to_cpu(blk->ver) >> 16) & 0xff, 21712159ad93SMark Brown (le32_to_cpu(blk->ver) >> 8) & 0xff, 21722159ad93SMark Brown le32_to_cpu(blk->ver) & 0xff); 21732159ad93SMark Brown adsp_dbg(dsp, "%s.%d: %d bytes at 0x%x in %x\n", 21742159ad93SMark Brown file, blocks, le32_to_cpu(blk->len), offset, type); 21752159ad93SMark Brown 21762159ad93SMark Brown reg = 0; 21772159ad93SMark Brown region_name = "Unknown"; 21782159ad93SMark Brown switch (type) { 2179c712326dSMark Brown case (WMFW_NAME_TEXT << 8): 2180c712326dSMark Brown case (WMFW_INFO_TEXT << 8): 21812159ad93SMark Brown break; 2182c712326dSMark Brown case (WMFW_ABSOLUTE << 8): 2183f395a218SMark Brown /* 2184f395a218SMark Brown * Old files may use this for global 2185f395a218SMark Brown * coefficients. 2186f395a218SMark Brown */ 2187f395a218SMark Brown if (le32_to_cpu(blk->id) == dsp->fw_id && 2188f395a218SMark Brown offset == 0) { 2189f395a218SMark Brown region_name = "global coefficients"; 2190f395a218SMark Brown mem = wm_adsp_find_region(dsp, type); 2191f395a218SMark Brown if (!mem) { 2192f395a218SMark Brown adsp_err(dsp, "No ZM\n"); 2193f395a218SMark Brown break; 2194f395a218SMark Brown } 2195f395a218SMark Brown reg = wm_adsp_region_to_reg(mem, 0); 2196f395a218SMark Brown 2197f395a218SMark Brown } else { 21982159ad93SMark Brown region_name = "register"; 21992159ad93SMark Brown reg = offset; 2200f395a218SMark Brown } 22012159ad93SMark Brown break; 2202471f4885SMark Brown 2203471f4885SMark Brown case WMFW_ADSP1_DM: 2204471f4885SMark Brown case WMFW_ADSP1_ZM: 2205471f4885SMark Brown case WMFW_ADSP2_XM: 2206471f4885SMark Brown case WMFW_ADSP2_YM: 2207471f4885SMark Brown adsp_dbg(dsp, "%s.%d: %d bytes in %x for %x\n", 2208471f4885SMark Brown file, blocks, le32_to_cpu(blk->len), 2209471f4885SMark Brown type, le32_to_cpu(blk->id)); 2210471f4885SMark Brown 2211471f4885SMark Brown mem = wm_adsp_find_region(dsp, type); 2212471f4885SMark Brown if (!mem) { 2213471f4885SMark Brown adsp_err(dsp, "No base for region %x\n", type); 2214471f4885SMark Brown break; 2215471f4885SMark Brown } 2216471f4885SMark Brown 221714197095SCharles Keepax alg_region = wm_adsp_find_alg_region(dsp, type, 221814197095SCharles Keepax le32_to_cpu(blk->id)); 221914197095SCharles Keepax if (alg_region) { 2220338c5188SMark Brown reg = alg_region->base; 222114197095SCharles Keepax reg = wm_adsp_region_to_reg(mem, reg); 2222338c5188SMark Brown reg += offset; 222314197095SCharles Keepax } else { 2224471f4885SMark Brown adsp_err(dsp, "No %x for algorithm %x\n", 2225471f4885SMark Brown type, le32_to_cpu(blk->id)); 222614197095SCharles Keepax } 2227471f4885SMark Brown break; 2228471f4885SMark Brown 22292159ad93SMark Brown default: 223025c62f7eSMark Brown adsp_err(dsp, "%s.%d: Unknown region type %x at %d\n", 223125c62f7eSMark Brown file, blocks, type, pos); 22322159ad93SMark Brown break; 22332159ad93SMark Brown } 22342159ad93SMark Brown 22352159ad93SMark Brown if (reg) { 2236cf17c83cSMark Brown buf = wm_adsp_buf_alloc(blk->data, 2237cf17c83cSMark Brown le32_to_cpu(blk->len), 2238cf17c83cSMark Brown &buf_list); 2239a76fefabSMark Brown if (!buf) { 2240a76fefabSMark Brown adsp_err(dsp, "Out of memory\n"); 2241f4b82812SWei Yongjun ret = -ENOMEM; 2242f4b82812SWei Yongjun goto out_fw; 2243a76fefabSMark Brown } 2244a76fefabSMark Brown 224520da6d5aSMark Brown adsp_dbg(dsp, "%s.%d: Writing %d bytes at %x\n", 224620da6d5aSMark Brown file, blocks, le32_to_cpu(blk->len), 224720da6d5aSMark Brown reg); 2248cf17c83cSMark Brown ret = regmap_raw_write_async(regmap, reg, buf->buf, 22492159ad93SMark Brown le32_to_cpu(blk->len)); 22502159ad93SMark Brown if (ret != 0) { 22512159ad93SMark Brown adsp_err(dsp, 225243bc3bf6SDimitris Papastamos "%s.%d: Failed to write to %x in %s: %d\n", 225343bc3bf6SDimitris Papastamos file, blocks, reg, region_name, ret); 22542159ad93SMark Brown } 22552159ad93SMark Brown } 22562159ad93SMark Brown 2257be951017SCharles Keepax pos += (le32_to_cpu(blk->len) + sizeof(*blk) + 3) & ~0x03; 22582159ad93SMark Brown blocks++; 22592159ad93SMark Brown } 22602159ad93SMark Brown 2261cf17c83cSMark Brown ret = regmap_async_complete(regmap); 2262cf17c83cSMark Brown if (ret != 0) 2263cf17c83cSMark Brown adsp_err(dsp, "Failed to complete async write: %d\n", ret); 2264cf17c83cSMark Brown 22652159ad93SMark Brown if (pos > firmware->size) 22662159ad93SMark Brown adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n", 22672159ad93SMark Brown file, blocks, pos - firmware->size); 22682159ad93SMark Brown 2269f9f55e31SRichard Fitzgerald wm_adsp_debugfs_save_binname(dsp, file); 2270f9f55e31SRichard Fitzgerald 22712159ad93SMark Brown out_fw: 22729da7a5a9SCharles Keepax regmap_async_complete(regmap); 22732159ad93SMark Brown release_firmware(firmware); 2274cf17c83cSMark Brown wm_adsp_buf_free(&buf_list); 22752159ad93SMark Brown out: 22762159ad93SMark Brown kfree(file); 2277f4b82812SWei Yongjun return ret; 22782159ad93SMark Brown } 22792159ad93SMark Brown 22803809f001SCharles Keepax int wm_adsp1_init(struct wm_adsp *dsp) 22815e7a7a22SMark Brown { 22823809f001SCharles Keepax INIT_LIST_HEAD(&dsp->alg_regions); 22835e7a7a22SMark Brown 2284078e7183SCharles Keepax mutex_init(&dsp->pwr_lock); 2285078e7183SCharles Keepax 22865e7a7a22SMark Brown return 0; 22875e7a7a22SMark Brown } 22885e7a7a22SMark Brown EXPORT_SYMBOL_GPL(wm_adsp1_init); 22895e7a7a22SMark Brown 22902159ad93SMark Brown int wm_adsp1_event(struct snd_soc_dapm_widget *w, 22912159ad93SMark Brown struct snd_kcontrol *kcontrol, 22922159ad93SMark Brown int event) 22932159ad93SMark Brown { 229472718517SLars-Peter Clausen struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); 22952159ad93SMark Brown struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec); 22962159ad93SMark Brown struct wm_adsp *dsp = &dsps[w->shift]; 22976ab2b7b4SDimitris Papastamos struct wm_coeff_ctl *ctl; 22982159ad93SMark Brown int ret; 22997585a5b0SCharles Keepax unsigned int val; 23002159ad93SMark Brown 230100200107SLars-Peter Clausen dsp->card = codec->component.card; 230292bb4c32SDimitris Papastamos 2303078e7183SCharles Keepax mutex_lock(&dsp->pwr_lock); 2304078e7183SCharles Keepax 23052159ad93SMark Brown switch (event) { 23062159ad93SMark Brown case SND_SOC_DAPM_POST_PMU: 23072159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 23082159ad93SMark Brown ADSP1_SYS_ENA, ADSP1_SYS_ENA); 23092159ad93SMark Brown 231094e205bfSChris Rattray /* 231194e205bfSChris Rattray * For simplicity set the DSP clock rate to be the 231294e205bfSChris Rattray * SYSCLK rate rather than making it configurable. 231394e205bfSChris Rattray */ 231494e205bfSChris Rattray if (dsp->sysclk_reg) { 231594e205bfSChris Rattray ret = regmap_read(dsp->regmap, dsp->sysclk_reg, &val); 231694e205bfSChris Rattray if (ret != 0) { 231794e205bfSChris Rattray adsp_err(dsp, "Failed to read SYSCLK state: %d\n", 231894e205bfSChris Rattray ret); 2319078e7183SCharles Keepax goto err_mutex; 232094e205bfSChris Rattray } 232194e205bfSChris Rattray 23227d00cd97SCharles Keepax val = (val & dsp->sysclk_mask) >> dsp->sysclk_shift; 232394e205bfSChris Rattray 232494e205bfSChris Rattray ret = regmap_update_bits(dsp->regmap, 232594e205bfSChris Rattray dsp->base + ADSP1_CONTROL_31, 232694e205bfSChris Rattray ADSP1_CLK_SEL_MASK, val); 232794e205bfSChris Rattray if (ret != 0) { 232894e205bfSChris Rattray adsp_err(dsp, "Failed to set clock rate: %d\n", 232994e205bfSChris Rattray ret); 2330078e7183SCharles Keepax goto err_mutex; 233194e205bfSChris Rattray } 233294e205bfSChris Rattray } 233394e205bfSChris Rattray 23342159ad93SMark Brown ret = wm_adsp_load(dsp); 23352159ad93SMark Brown if (ret != 0) 2336078e7183SCharles Keepax goto err_ena; 23372159ad93SMark Brown 2338b618a185SCharles Keepax ret = wm_adsp1_setup_algs(dsp); 2339db40517cSMark Brown if (ret != 0) 2340078e7183SCharles Keepax goto err_ena; 2341db40517cSMark Brown 23422159ad93SMark Brown ret = wm_adsp_load_coeff(dsp); 23432159ad93SMark Brown if (ret != 0) 2344078e7183SCharles Keepax goto err_ena; 23452159ad93SMark Brown 23460c2e3f34SDimitris Papastamos /* Initialize caches for enabled and unset controls */ 234781ad93ecSDimitris Papastamos ret = wm_coeff_init_control_caches(dsp); 23486ab2b7b4SDimitris Papastamos if (ret != 0) 2349078e7183SCharles Keepax goto err_ena; 23506ab2b7b4SDimitris Papastamos 23510c2e3f34SDimitris Papastamos /* Sync set controls */ 235281ad93ecSDimitris Papastamos ret = wm_coeff_sync_controls(dsp); 23536ab2b7b4SDimitris Papastamos if (ret != 0) 2354078e7183SCharles Keepax goto err_ena; 23556ab2b7b4SDimitris Papastamos 235628823ebaSCharles Keepax dsp->booted = true; 235728823ebaSCharles Keepax 23582159ad93SMark Brown /* Start the core running */ 23592159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 23602159ad93SMark Brown ADSP1_CORE_ENA | ADSP1_START, 23612159ad93SMark Brown ADSP1_CORE_ENA | ADSP1_START); 236228823ebaSCharles Keepax 236328823ebaSCharles Keepax dsp->running = true; 23642159ad93SMark Brown break; 23652159ad93SMark Brown 23662159ad93SMark Brown case SND_SOC_DAPM_PRE_PMD: 236728823ebaSCharles Keepax dsp->running = false; 236828823ebaSCharles Keepax dsp->booted = false; 236928823ebaSCharles Keepax 23702159ad93SMark Brown /* Halt the core */ 23712159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 23722159ad93SMark Brown ADSP1_CORE_ENA | ADSP1_START, 0); 23732159ad93SMark Brown 23742159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_19, 23752159ad93SMark Brown ADSP1_WDMA_BUFFER_LENGTH_MASK, 0); 23762159ad93SMark Brown 23772159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 23782159ad93SMark Brown ADSP1_SYS_ENA, 0); 23796ab2b7b4SDimitris Papastamos 238081ad93ecSDimitris Papastamos list_for_each_entry(ctl, &dsp->ctl_list, list) 23816ab2b7b4SDimitris Papastamos ctl->enabled = 0; 2382b0101b4fSDimitris Papastamos 238356574d54SRichard Fitzgerald 238456574d54SRichard Fitzgerald wm_adsp_free_alg_regions(dsp); 23852159ad93SMark Brown break; 23862159ad93SMark Brown 23872159ad93SMark Brown default: 23882159ad93SMark Brown break; 23892159ad93SMark Brown } 23902159ad93SMark Brown 2391078e7183SCharles Keepax mutex_unlock(&dsp->pwr_lock); 2392078e7183SCharles Keepax 23932159ad93SMark Brown return 0; 23942159ad93SMark Brown 2395078e7183SCharles Keepax err_ena: 23962159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 23972159ad93SMark Brown ADSP1_SYS_ENA, 0); 2398078e7183SCharles Keepax err_mutex: 2399078e7183SCharles Keepax mutex_unlock(&dsp->pwr_lock); 2400078e7183SCharles Keepax 24012159ad93SMark Brown return ret; 24022159ad93SMark Brown } 24032159ad93SMark Brown EXPORT_SYMBOL_GPL(wm_adsp1_event); 24042159ad93SMark Brown 24052159ad93SMark Brown static int wm_adsp2_ena(struct wm_adsp *dsp) 24062159ad93SMark Brown { 24072159ad93SMark Brown unsigned int val; 24082159ad93SMark Brown int ret, count; 24092159ad93SMark Brown 24101552c325SMark Brown ret = regmap_update_bits_async(dsp->regmap, dsp->base + ADSP2_CONTROL, 24112159ad93SMark Brown ADSP2_SYS_ENA, ADSP2_SYS_ENA); 24122159ad93SMark Brown if (ret != 0) 24132159ad93SMark Brown return ret; 24142159ad93SMark Brown 24152159ad93SMark Brown /* Wait for the RAM to start, should be near instantaneous */ 2416939fd1e8SCharles Keepax for (count = 0; count < 10; ++count) { 24177d00cd97SCharles Keepax ret = regmap_read(dsp->regmap, dsp->base + ADSP2_STATUS1, &val); 24182159ad93SMark Brown if (ret != 0) 24192159ad93SMark Brown return ret; 2420939fd1e8SCharles Keepax 2421939fd1e8SCharles Keepax if (val & ADSP2_RAM_RDY) 2422939fd1e8SCharles Keepax break; 2423939fd1e8SCharles Keepax 24241fa96f3fSCharles Keepax usleep_range(250, 500); 2425939fd1e8SCharles Keepax } 24262159ad93SMark Brown 24272159ad93SMark Brown if (!(val & ADSP2_RAM_RDY)) { 24282159ad93SMark Brown adsp_err(dsp, "Failed to start DSP RAM\n"); 24292159ad93SMark Brown return -EBUSY; 24302159ad93SMark Brown } 24312159ad93SMark Brown 24322159ad93SMark Brown adsp_dbg(dsp, "RAM ready after %d polls\n", count); 24332159ad93SMark Brown 24342159ad93SMark Brown return 0; 24352159ad93SMark Brown } 24362159ad93SMark Brown 243718b1a902SCharles Keepax static void wm_adsp2_boot_work(struct work_struct *work) 24382159ad93SMark Brown { 2439d8a64d6aSCharles Keepax struct wm_adsp *dsp = container_of(work, 2440d8a64d6aSCharles Keepax struct wm_adsp, 2441d8a64d6aSCharles Keepax boot_work); 24422159ad93SMark Brown int ret; 24432159ad93SMark Brown 2444078e7183SCharles Keepax mutex_lock(&dsp->pwr_lock); 2445078e7183SCharles Keepax 244690d19ba5SCharles Keepax ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 244790d19ba5SCharles Keepax ADSP2_MEM_ENA, ADSP2_MEM_ENA); 244890d19ba5SCharles Keepax if (ret != 0) 244990d19ba5SCharles Keepax goto err_mutex; 245090d19ba5SCharles Keepax 24512159ad93SMark Brown ret = wm_adsp2_ena(dsp); 24522159ad93SMark Brown if (ret != 0) 2453078e7183SCharles Keepax goto err_mutex; 24542159ad93SMark Brown 24552159ad93SMark Brown ret = wm_adsp_load(dsp); 24562159ad93SMark Brown if (ret != 0) 2457078e7183SCharles Keepax goto err_ena; 24582159ad93SMark Brown 2459b618a185SCharles Keepax ret = wm_adsp2_setup_algs(dsp); 2460db40517cSMark Brown if (ret != 0) 2461078e7183SCharles Keepax goto err_ena; 2462db40517cSMark Brown 24632159ad93SMark Brown ret = wm_adsp_load_coeff(dsp); 24642159ad93SMark Brown if (ret != 0) 2465078e7183SCharles Keepax goto err_ena; 24662159ad93SMark Brown 24670c2e3f34SDimitris Papastamos /* Initialize caches for enabled and unset controls */ 246881ad93ecSDimitris Papastamos ret = wm_coeff_init_control_caches(dsp); 24696ab2b7b4SDimitris Papastamos if (ret != 0) 2470078e7183SCharles Keepax goto err_ena; 24716ab2b7b4SDimitris Papastamos 247228823ebaSCharles Keepax dsp->booted = true; 2473d8a64d6aSCharles Keepax 247490d19ba5SCharles Keepax /* Turn DSP back off until we are ready to run */ 247590d19ba5SCharles Keepax ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 247690d19ba5SCharles Keepax ADSP2_SYS_ENA, 0); 247790d19ba5SCharles Keepax if (ret != 0) 247890d19ba5SCharles Keepax goto err_ena; 247990d19ba5SCharles Keepax 2480078e7183SCharles Keepax mutex_unlock(&dsp->pwr_lock); 2481078e7183SCharles Keepax 2482d8a64d6aSCharles Keepax return; 2483d8a64d6aSCharles Keepax 2484078e7183SCharles Keepax err_ena: 2485d8a64d6aSCharles Keepax regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 2486d8a64d6aSCharles Keepax ADSP2_SYS_ENA | ADSP2_CORE_ENA | ADSP2_START, 0); 2487078e7183SCharles Keepax err_mutex: 2488078e7183SCharles Keepax mutex_unlock(&dsp->pwr_lock); 2489d8a64d6aSCharles Keepax } 2490d8a64d6aSCharles Keepax 2491d82d767fSCharles Keepax static void wm_adsp2_set_dspclk(struct wm_adsp *dsp, unsigned int freq) 2492d82d767fSCharles Keepax { 2493d82d767fSCharles Keepax int ret; 2494d82d767fSCharles Keepax 2495d82d767fSCharles Keepax ret = regmap_update_bits_async(dsp->regmap, 2496d82d767fSCharles Keepax dsp->base + ADSP2_CLOCKING, 2497d82d767fSCharles Keepax ADSP2_CLK_SEL_MASK, 2498d82d767fSCharles Keepax freq << ADSP2_CLK_SEL_SHIFT); 2499d82d767fSCharles Keepax if (ret != 0) 2500d82d767fSCharles Keepax adsp_err(dsp, "Failed to set clock rate: %d\n", ret); 2501d82d767fSCharles Keepax } 2502d82d767fSCharles Keepax 250312db5eddSCharles Keepax int wm_adsp2_early_event(struct snd_soc_dapm_widget *w, 2504d82d767fSCharles Keepax struct snd_kcontrol *kcontrol, int event, 2505d82d767fSCharles Keepax unsigned int freq) 250612db5eddSCharles Keepax { 250772718517SLars-Peter Clausen struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); 250812db5eddSCharles Keepax struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec); 250912db5eddSCharles Keepax struct wm_adsp *dsp = &dsps[w->shift]; 251057a60cc3SCharles Keepax struct wm_coeff_ctl *ctl; 251112db5eddSCharles Keepax 251200200107SLars-Peter Clausen dsp->card = codec->component.card; 251312db5eddSCharles Keepax 251412db5eddSCharles Keepax switch (event) { 251512db5eddSCharles Keepax case SND_SOC_DAPM_PRE_PMU: 2516d82d767fSCharles Keepax wm_adsp2_set_dspclk(dsp, freq); 251712db5eddSCharles Keepax queue_work(system_unbound_wq, &dsp->boot_work); 251812db5eddSCharles Keepax break; 251957a60cc3SCharles Keepax case SND_SOC_DAPM_PRE_PMD: 252057a60cc3SCharles Keepax wm_adsp_debugfs_clear(dsp); 252157a60cc3SCharles Keepax 252257a60cc3SCharles Keepax dsp->fw_id = 0; 252357a60cc3SCharles Keepax dsp->fw_id_version = 0; 252457a60cc3SCharles Keepax 252557a60cc3SCharles Keepax dsp->booted = false; 252657a60cc3SCharles Keepax 252757a60cc3SCharles Keepax regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 252857a60cc3SCharles Keepax ADSP2_MEM_ENA, 0); 252957a60cc3SCharles Keepax 253057a60cc3SCharles Keepax list_for_each_entry(ctl, &dsp->ctl_list, list) 253157a60cc3SCharles Keepax ctl->enabled = 0; 253257a60cc3SCharles Keepax 253357a60cc3SCharles Keepax wm_adsp_free_alg_regions(dsp); 253457a60cc3SCharles Keepax 253557a60cc3SCharles Keepax adsp_dbg(dsp, "Shutdown complete\n"); 253657a60cc3SCharles Keepax break; 253712db5eddSCharles Keepax default: 253812db5eddSCharles Keepax break; 2539cab27258SCharles Keepax } 254012db5eddSCharles Keepax 254112db5eddSCharles Keepax return 0; 254212db5eddSCharles Keepax } 254312db5eddSCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp2_early_event); 254412db5eddSCharles Keepax 2545d8a64d6aSCharles Keepax int wm_adsp2_event(struct snd_soc_dapm_widget *w, 2546d8a64d6aSCharles Keepax struct snd_kcontrol *kcontrol, int event) 2547d8a64d6aSCharles Keepax { 254872718517SLars-Peter Clausen struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); 2549d8a64d6aSCharles Keepax struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec); 2550d8a64d6aSCharles Keepax struct wm_adsp *dsp = &dsps[w->shift]; 2551d8a64d6aSCharles Keepax int ret; 2552d8a64d6aSCharles Keepax 2553d8a64d6aSCharles Keepax switch (event) { 2554d8a64d6aSCharles Keepax case SND_SOC_DAPM_POST_PMU: 2555d8a64d6aSCharles Keepax flush_work(&dsp->boot_work); 2556d8a64d6aSCharles Keepax 255728823ebaSCharles Keepax if (!dsp->booted) 2558d8a64d6aSCharles Keepax return -EIO; 2559d8a64d6aSCharles Keepax 256090d19ba5SCharles Keepax ret = wm_adsp2_ena(dsp); 256190d19ba5SCharles Keepax if (ret != 0) 256290d19ba5SCharles Keepax goto err; 256390d19ba5SCharles Keepax 2564cef45771SCharles Keepax /* Sync set controls */ 2565cef45771SCharles Keepax ret = wm_coeff_sync_controls(dsp); 2566cef45771SCharles Keepax if (ret != 0) 2567cef45771SCharles Keepax goto err; 2568cef45771SCharles Keepax 2569d8a64d6aSCharles Keepax ret = regmap_update_bits(dsp->regmap, 2570d8a64d6aSCharles Keepax dsp->base + ADSP2_CONTROL, 257100e4c3b6SCharles Keepax ADSP2_CORE_ENA | ADSP2_START, 257200e4c3b6SCharles Keepax ADSP2_CORE_ENA | ADSP2_START); 2573d8a64d6aSCharles Keepax if (ret != 0) 2574d8a64d6aSCharles Keepax goto err; 25752cd19bdbSCharles Keepax 257628823ebaSCharles Keepax dsp->running = true; 257728823ebaSCharles Keepax 2578612047f0SCharles Keepax mutex_lock(&dsp->pwr_lock); 2579612047f0SCharles Keepax 25802cd19bdbSCharles Keepax if (wm_adsp_fw[dsp->fw].num_caps != 0) 25812cd19bdbSCharles Keepax ret = wm_adsp_buffer_init(dsp); 25822cd19bdbSCharles Keepax 2583612047f0SCharles Keepax mutex_unlock(&dsp->pwr_lock); 2584612047f0SCharles Keepax 25852159ad93SMark Brown break; 25862159ad93SMark Brown 25872159ad93SMark Brown case SND_SOC_DAPM_PRE_PMD: 2588f4f0c4c6SRichard Fitzgerald /* Tell the firmware to cleanup */ 2589f4f0c4c6SRichard Fitzgerald wm_adsp_signal_event_controls(dsp, WM_ADSP_FW_EVENT_SHUTDOWN); 2590f4f0c4c6SRichard Fitzgerald 259110337b07SRichard Fitzgerald /* Log firmware state, it can be useful for analysis */ 259210337b07SRichard Fitzgerald wm_adsp2_show_fw_status(dsp); 259310337b07SRichard Fitzgerald 2594078e7183SCharles Keepax mutex_lock(&dsp->pwr_lock); 2595078e7183SCharles Keepax 25961023dbd9SMark Brown dsp->running = false; 25971023dbd9SMark Brown 25982159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 259957a60cc3SCharles Keepax ADSP2_CORE_ENA | ADSP2_START, 0); 2600973838a0SMark Brown 26012d30b575SMark Brown /* Make sure DMAs are quiesced */ 26026facd2d1SSimon Trimmer regmap_write(dsp->regmap, dsp->base + ADSP2_RDMA_CONFIG_1, 0); 26032d30b575SMark Brown regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_1, 0); 26042d30b575SMark Brown regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_2, 0); 26056facd2d1SSimon Trimmer 26066facd2d1SSimon Trimmer regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 26076facd2d1SSimon Trimmer ADSP2_SYS_ENA, 0); 26082d30b575SMark Brown 26092cd19bdbSCharles Keepax if (wm_adsp_fw[dsp->fw].num_caps != 0) 26102cd19bdbSCharles Keepax wm_adsp_buffer_free(dsp); 26112cd19bdbSCharles Keepax 2612078e7183SCharles Keepax mutex_unlock(&dsp->pwr_lock); 2613078e7183SCharles Keepax 261457a60cc3SCharles Keepax adsp_dbg(dsp, "Execution stopped\n"); 26152159ad93SMark Brown break; 26162159ad93SMark Brown 26172159ad93SMark Brown default: 26182159ad93SMark Brown break; 26192159ad93SMark Brown } 26202159ad93SMark Brown 26212159ad93SMark Brown return 0; 26222159ad93SMark Brown err: 26232159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 2624a7f9be7eSMark Brown ADSP2_SYS_ENA | ADSP2_CORE_ENA | ADSP2_START, 0); 26252159ad93SMark Brown return ret; 26262159ad93SMark Brown } 26272159ad93SMark Brown EXPORT_SYMBOL_GPL(wm_adsp2_event); 2628973838a0SMark Brown 2629f5e2ce92SRichard Fitzgerald int wm_adsp2_codec_probe(struct wm_adsp *dsp, struct snd_soc_codec *codec) 2630f5e2ce92SRichard Fitzgerald { 2631f9f55e31SRichard Fitzgerald wm_adsp2_init_debugfs(dsp, codec); 2632f9f55e31SRichard Fitzgerald 2633218e5087SRichard Fitzgerald return snd_soc_add_codec_controls(codec, 2634336d0442SRichard Fitzgerald &wm_adsp_fw_controls[dsp->num - 1], 2635336d0442SRichard Fitzgerald 1); 2636f5e2ce92SRichard Fitzgerald } 2637f5e2ce92SRichard Fitzgerald EXPORT_SYMBOL_GPL(wm_adsp2_codec_probe); 2638f5e2ce92SRichard Fitzgerald 2639f5e2ce92SRichard Fitzgerald int wm_adsp2_codec_remove(struct wm_adsp *dsp, struct snd_soc_codec *codec) 2640f5e2ce92SRichard Fitzgerald { 2641f9f55e31SRichard Fitzgerald wm_adsp2_cleanup_debugfs(dsp); 2642f9f55e31SRichard Fitzgerald 2643f5e2ce92SRichard Fitzgerald return 0; 2644f5e2ce92SRichard Fitzgerald } 2645f5e2ce92SRichard Fitzgerald EXPORT_SYMBOL_GPL(wm_adsp2_codec_remove); 2646f5e2ce92SRichard Fitzgerald 264781ac58b1SRichard Fitzgerald int wm_adsp2_init(struct wm_adsp *dsp) 2648973838a0SMark Brown { 2649973838a0SMark Brown int ret; 2650973838a0SMark Brown 265110a2b662SMark Brown /* 265210a2b662SMark Brown * Disable the DSP memory by default when in reset for a small 265310a2b662SMark Brown * power saving. 265410a2b662SMark Brown */ 26553809f001SCharles Keepax ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 265610a2b662SMark Brown ADSP2_MEM_ENA, 0); 265710a2b662SMark Brown if (ret != 0) { 26583809f001SCharles Keepax adsp_err(dsp, "Failed to clear memory retention: %d\n", ret); 265910a2b662SMark Brown return ret; 266010a2b662SMark Brown } 266110a2b662SMark Brown 26623809f001SCharles Keepax INIT_LIST_HEAD(&dsp->alg_regions); 26633809f001SCharles Keepax INIT_LIST_HEAD(&dsp->ctl_list); 26643809f001SCharles Keepax INIT_WORK(&dsp->boot_work, wm_adsp2_boot_work); 26656ab2b7b4SDimitris Papastamos 2666078e7183SCharles Keepax mutex_init(&dsp->pwr_lock); 2667078e7183SCharles Keepax 2668973838a0SMark Brown return 0; 2669973838a0SMark Brown } 2670973838a0SMark Brown EXPORT_SYMBOL_GPL(wm_adsp2_init); 26710a37c6efSPraveen Diwakar 267266225e98SRichard Fitzgerald void wm_adsp2_remove(struct wm_adsp *dsp) 267366225e98SRichard Fitzgerald { 267466225e98SRichard Fitzgerald struct wm_coeff_ctl *ctl; 267566225e98SRichard Fitzgerald 267666225e98SRichard Fitzgerald while (!list_empty(&dsp->ctl_list)) { 267766225e98SRichard Fitzgerald ctl = list_first_entry(&dsp->ctl_list, struct wm_coeff_ctl, 267866225e98SRichard Fitzgerald list); 267966225e98SRichard Fitzgerald list_del(&ctl->list); 268066225e98SRichard Fitzgerald wm_adsp_free_ctl_blk(ctl); 268166225e98SRichard Fitzgerald } 268266225e98SRichard Fitzgerald } 268366225e98SRichard Fitzgerald EXPORT_SYMBOL_GPL(wm_adsp2_remove); 268466225e98SRichard Fitzgerald 2685edd71350SCharles Keepax static inline int wm_adsp_compr_attached(struct wm_adsp_compr *compr) 2686edd71350SCharles Keepax { 2687edd71350SCharles Keepax return compr->buf != NULL; 2688edd71350SCharles Keepax } 2689edd71350SCharles Keepax 2690edd71350SCharles Keepax static int wm_adsp_compr_attach(struct wm_adsp_compr *compr) 2691edd71350SCharles Keepax { 2692edd71350SCharles Keepax /* 2693edd71350SCharles Keepax * Note this will be more complex once each DSP can support multiple 2694edd71350SCharles Keepax * streams 2695edd71350SCharles Keepax */ 2696edd71350SCharles Keepax if (!compr->dsp->buffer) 2697edd71350SCharles Keepax return -EINVAL; 2698edd71350SCharles Keepax 2699edd71350SCharles Keepax compr->buf = compr->dsp->buffer; 2700721be3beSCharles Keepax compr->buf->compr = compr; 2701edd71350SCharles Keepax 2702edd71350SCharles Keepax return 0; 2703edd71350SCharles Keepax } 2704edd71350SCharles Keepax 2705721be3beSCharles Keepax static void wm_adsp_compr_detach(struct wm_adsp_compr *compr) 2706721be3beSCharles Keepax { 2707721be3beSCharles Keepax if (!compr) 2708721be3beSCharles Keepax return; 2709721be3beSCharles Keepax 2710721be3beSCharles Keepax /* Wake the poll so it can see buffer is no longer attached */ 2711721be3beSCharles Keepax if (compr->stream) 2712721be3beSCharles Keepax snd_compr_fragment_elapsed(compr->stream); 2713721be3beSCharles Keepax 2714721be3beSCharles Keepax if (wm_adsp_compr_attached(compr)) { 2715721be3beSCharles Keepax compr->buf->compr = NULL; 2716721be3beSCharles Keepax compr->buf = NULL; 2717721be3beSCharles Keepax } 2718721be3beSCharles Keepax } 2719721be3beSCharles Keepax 2720406abc95SCharles Keepax int wm_adsp_compr_open(struct wm_adsp *dsp, struct snd_compr_stream *stream) 2721406abc95SCharles Keepax { 2722406abc95SCharles Keepax struct wm_adsp_compr *compr; 2723406abc95SCharles Keepax int ret = 0; 2724406abc95SCharles Keepax 2725406abc95SCharles Keepax mutex_lock(&dsp->pwr_lock); 2726406abc95SCharles Keepax 2727406abc95SCharles Keepax if (wm_adsp_fw[dsp->fw].num_caps == 0) { 2728406abc95SCharles Keepax adsp_err(dsp, "Firmware does not support compressed API\n"); 2729406abc95SCharles Keepax ret = -ENXIO; 2730406abc95SCharles Keepax goto out; 2731406abc95SCharles Keepax } 2732406abc95SCharles Keepax 2733406abc95SCharles Keepax if (wm_adsp_fw[dsp->fw].compr_direction != stream->direction) { 2734406abc95SCharles Keepax adsp_err(dsp, "Firmware does not support stream direction\n"); 2735406abc95SCharles Keepax ret = -EINVAL; 2736406abc95SCharles Keepax goto out; 2737406abc95SCharles Keepax } 2738406abc95SCharles Keepax 273995fe9597SCharles Keepax if (dsp->compr) { 274095fe9597SCharles Keepax /* It is expect this limitation will be removed in future */ 274195fe9597SCharles Keepax adsp_err(dsp, "Only a single stream supported per DSP\n"); 274295fe9597SCharles Keepax ret = -EBUSY; 274395fe9597SCharles Keepax goto out; 274495fe9597SCharles Keepax } 274595fe9597SCharles Keepax 2746406abc95SCharles Keepax compr = kzalloc(sizeof(*compr), GFP_KERNEL); 2747406abc95SCharles Keepax if (!compr) { 2748406abc95SCharles Keepax ret = -ENOMEM; 2749406abc95SCharles Keepax goto out; 2750406abc95SCharles Keepax } 2751406abc95SCharles Keepax 2752406abc95SCharles Keepax compr->dsp = dsp; 2753406abc95SCharles Keepax compr->stream = stream; 2754406abc95SCharles Keepax 2755406abc95SCharles Keepax dsp->compr = compr; 2756406abc95SCharles Keepax 2757406abc95SCharles Keepax stream->runtime->private_data = compr; 2758406abc95SCharles Keepax 2759406abc95SCharles Keepax out: 2760406abc95SCharles Keepax mutex_unlock(&dsp->pwr_lock); 2761406abc95SCharles Keepax 2762406abc95SCharles Keepax return ret; 2763406abc95SCharles Keepax } 2764406abc95SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_open); 2765406abc95SCharles Keepax 2766406abc95SCharles Keepax int wm_adsp_compr_free(struct snd_compr_stream *stream) 2767406abc95SCharles Keepax { 2768406abc95SCharles Keepax struct wm_adsp_compr *compr = stream->runtime->private_data; 2769406abc95SCharles Keepax struct wm_adsp *dsp = compr->dsp; 2770406abc95SCharles Keepax 2771406abc95SCharles Keepax mutex_lock(&dsp->pwr_lock); 2772406abc95SCharles Keepax 2773721be3beSCharles Keepax wm_adsp_compr_detach(compr); 2774406abc95SCharles Keepax dsp->compr = NULL; 2775406abc95SCharles Keepax 277683a40ce9SCharles Keepax kfree(compr->raw_buf); 2777406abc95SCharles Keepax kfree(compr); 2778406abc95SCharles Keepax 2779406abc95SCharles Keepax mutex_unlock(&dsp->pwr_lock); 2780406abc95SCharles Keepax 2781406abc95SCharles Keepax return 0; 2782406abc95SCharles Keepax } 2783406abc95SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_free); 2784406abc95SCharles Keepax 2785406abc95SCharles Keepax static int wm_adsp_compr_check_params(struct snd_compr_stream *stream, 2786406abc95SCharles Keepax struct snd_compr_params *params) 2787406abc95SCharles Keepax { 2788406abc95SCharles Keepax struct wm_adsp_compr *compr = stream->runtime->private_data; 2789406abc95SCharles Keepax struct wm_adsp *dsp = compr->dsp; 2790406abc95SCharles Keepax const struct wm_adsp_fw_caps *caps; 2791406abc95SCharles Keepax const struct snd_codec_desc *desc; 2792406abc95SCharles Keepax int i, j; 2793406abc95SCharles Keepax 2794406abc95SCharles Keepax if (params->buffer.fragment_size < WM_ADSP_MIN_FRAGMENT_SIZE || 2795406abc95SCharles Keepax params->buffer.fragment_size > WM_ADSP_MAX_FRAGMENT_SIZE || 2796406abc95SCharles Keepax params->buffer.fragments < WM_ADSP_MIN_FRAGMENTS || 2797406abc95SCharles Keepax params->buffer.fragments > WM_ADSP_MAX_FRAGMENTS || 2798406abc95SCharles Keepax params->buffer.fragment_size % WM_ADSP_DATA_WORD_SIZE) { 2799406abc95SCharles Keepax adsp_err(dsp, "Invalid buffer fragsize=%d fragments=%d\n", 2800406abc95SCharles Keepax params->buffer.fragment_size, 2801406abc95SCharles Keepax params->buffer.fragments); 2802406abc95SCharles Keepax 2803406abc95SCharles Keepax return -EINVAL; 2804406abc95SCharles Keepax } 2805406abc95SCharles Keepax 2806406abc95SCharles Keepax for (i = 0; i < wm_adsp_fw[dsp->fw].num_caps; i++) { 2807406abc95SCharles Keepax caps = &wm_adsp_fw[dsp->fw].caps[i]; 2808406abc95SCharles Keepax desc = &caps->desc; 2809406abc95SCharles Keepax 2810406abc95SCharles Keepax if (caps->id != params->codec.id) 2811406abc95SCharles Keepax continue; 2812406abc95SCharles Keepax 2813406abc95SCharles Keepax if (stream->direction == SND_COMPRESS_PLAYBACK) { 2814406abc95SCharles Keepax if (desc->max_ch < params->codec.ch_out) 2815406abc95SCharles Keepax continue; 2816406abc95SCharles Keepax } else { 2817406abc95SCharles Keepax if (desc->max_ch < params->codec.ch_in) 2818406abc95SCharles Keepax continue; 2819406abc95SCharles Keepax } 2820406abc95SCharles Keepax 2821406abc95SCharles Keepax if (!(desc->formats & (1 << params->codec.format))) 2822406abc95SCharles Keepax continue; 2823406abc95SCharles Keepax 2824406abc95SCharles Keepax for (j = 0; j < desc->num_sample_rates; ++j) 2825406abc95SCharles Keepax if (desc->sample_rates[j] == params->codec.sample_rate) 2826406abc95SCharles Keepax return 0; 2827406abc95SCharles Keepax } 2828406abc95SCharles Keepax 2829406abc95SCharles Keepax adsp_err(dsp, "Invalid params id=%u ch=%u,%u rate=%u fmt=%u\n", 2830406abc95SCharles Keepax params->codec.id, params->codec.ch_in, params->codec.ch_out, 2831406abc95SCharles Keepax params->codec.sample_rate, params->codec.format); 2832406abc95SCharles Keepax return -EINVAL; 2833406abc95SCharles Keepax } 2834406abc95SCharles Keepax 2835565ace46SCharles Keepax static inline unsigned int wm_adsp_compr_frag_words(struct wm_adsp_compr *compr) 2836565ace46SCharles Keepax { 2837565ace46SCharles Keepax return compr->size.fragment_size / WM_ADSP_DATA_WORD_SIZE; 2838565ace46SCharles Keepax } 2839565ace46SCharles Keepax 2840406abc95SCharles Keepax int wm_adsp_compr_set_params(struct snd_compr_stream *stream, 2841406abc95SCharles Keepax struct snd_compr_params *params) 2842406abc95SCharles Keepax { 2843406abc95SCharles Keepax struct wm_adsp_compr *compr = stream->runtime->private_data; 284483a40ce9SCharles Keepax unsigned int size; 2845406abc95SCharles Keepax int ret; 2846406abc95SCharles Keepax 2847406abc95SCharles Keepax ret = wm_adsp_compr_check_params(stream, params); 2848406abc95SCharles Keepax if (ret) 2849406abc95SCharles Keepax return ret; 2850406abc95SCharles Keepax 2851406abc95SCharles Keepax compr->size = params->buffer; 2852406abc95SCharles Keepax 2853406abc95SCharles Keepax adsp_dbg(compr->dsp, "fragment_size=%d fragments=%d\n", 2854406abc95SCharles Keepax compr->size.fragment_size, compr->size.fragments); 2855406abc95SCharles Keepax 285683a40ce9SCharles Keepax size = wm_adsp_compr_frag_words(compr) * sizeof(*compr->raw_buf); 285783a40ce9SCharles Keepax compr->raw_buf = kmalloc(size, GFP_DMA | GFP_KERNEL); 285883a40ce9SCharles Keepax if (!compr->raw_buf) 285983a40ce9SCharles Keepax return -ENOMEM; 286083a40ce9SCharles Keepax 2861da2b3358SCharles Keepax compr->sample_rate = params->codec.sample_rate; 2862da2b3358SCharles Keepax 2863406abc95SCharles Keepax return 0; 2864406abc95SCharles Keepax } 2865406abc95SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_set_params); 2866406abc95SCharles Keepax 2867406abc95SCharles Keepax int wm_adsp_compr_get_caps(struct snd_compr_stream *stream, 2868406abc95SCharles Keepax struct snd_compr_caps *caps) 2869406abc95SCharles Keepax { 2870406abc95SCharles Keepax struct wm_adsp_compr *compr = stream->runtime->private_data; 2871406abc95SCharles Keepax int fw = compr->dsp->fw; 2872406abc95SCharles Keepax int i; 2873406abc95SCharles Keepax 2874406abc95SCharles Keepax if (wm_adsp_fw[fw].caps) { 2875406abc95SCharles Keepax for (i = 0; i < wm_adsp_fw[fw].num_caps; i++) 2876406abc95SCharles Keepax caps->codecs[i] = wm_adsp_fw[fw].caps[i].id; 2877406abc95SCharles Keepax 2878406abc95SCharles Keepax caps->num_codecs = i; 2879406abc95SCharles Keepax caps->direction = wm_adsp_fw[fw].compr_direction; 2880406abc95SCharles Keepax 2881406abc95SCharles Keepax caps->min_fragment_size = WM_ADSP_MIN_FRAGMENT_SIZE; 2882406abc95SCharles Keepax caps->max_fragment_size = WM_ADSP_MAX_FRAGMENT_SIZE; 2883406abc95SCharles Keepax caps->min_fragments = WM_ADSP_MIN_FRAGMENTS; 2884406abc95SCharles Keepax caps->max_fragments = WM_ADSP_MAX_FRAGMENTS; 2885406abc95SCharles Keepax } 2886406abc95SCharles Keepax 2887406abc95SCharles Keepax return 0; 2888406abc95SCharles Keepax } 2889406abc95SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_get_caps); 2890406abc95SCharles Keepax 28912cd19bdbSCharles Keepax static int wm_adsp_read_data_block(struct wm_adsp *dsp, int mem_type, 28922cd19bdbSCharles Keepax unsigned int mem_addr, 28932cd19bdbSCharles Keepax unsigned int num_words, u32 *data) 28942cd19bdbSCharles Keepax { 28952cd19bdbSCharles Keepax struct wm_adsp_region const *mem = wm_adsp_find_region(dsp, mem_type); 28962cd19bdbSCharles Keepax unsigned int i, reg; 28972cd19bdbSCharles Keepax int ret; 28982cd19bdbSCharles Keepax 28992cd19bdbSCharles Keepax if (!mem) 29002cd19bdbSCharles Keepax return -EINVAL; 29012cd19bdbSCharles Keepax 29022cd19bdbSCharles Keepax reg = wm_adsp_region_to_reg(mem, mem_addr); 29032cd19bdbSCharles Keepax 29042cd19bdbSCharles Keepax ret = regmap_raw_read(dsp->regmap, reg, data, 29052cd19bdbSCharles Keepax sizeof(*data) * num_words); 29062cd19bdbSCharles Keepax if (ret < 0) 29072cd19bdbSCharles Keepax return ret; 29082cd19bdbSCharles Keepax 29092cd19bdbSCharles Keepax for (i = 0; i < num_words; ++i) 29102cd19bdbSCharles Keepax data[i] = be32_to_cpu(data[i]) & 0x00ffffffu; 29112cd19bdbSCharles Keepax 29122cd19bdbSCharles Keepax return 0; 29132cd19bdbSCharles Keepax } 29142cd19bdbSCharles Keepax 29152cd19bdbSCharles Keepax static inline int wm_adsp_read_data_word(struct wm_adsp *dsp, int mem_type, 29162cd19bdbSCharles Keepax unsigned int mem_addr, u32 *data) 29172cd19bdbSCharles Keepax { 29182cd19bdbSCharles Keepax return wm_adsp_read_data_block(dsp, mem_type, mem_addr, 1, data); 29192cd19bdbSCharles Keepax } 29202cd19bdbSCharles Keepax 29212cd19bdbSCharles Keepax static int wm_adsp_write_data_word(struct wm_adsp *dsp, int mem_type, 29222cd19bdbSCharles Keepax unsigned int mem_addr, u32 data) 29232cd19bdbSCharles Keepax { 29242cd19bdbSCharles Keepax struct wm_adsp_region const *mem = wm_adsp_find_region(dsp, mem_type); 29252cd19bdbSCharles Keepax unsigned int reg; 29262cd19bdbSCharles Keepax 29272cd19bdbSCharles Keepax if (!mem) 29282cd19bdbSCharles Keepax return -EINVAL; 29292cd19bdbSCharles Keepax 29302cd19bdbSCharles Keepax reg = wm_adsp_region_to_reg(mem, mem_addr); 29312cd19bdbSCharles Keepax 29322cd19bdbSCharles Keepax data = cpu_to_be32(data & 0x00ffffffu); 29332cd19bdbSCharles Keepax 29342cd19bdbSCharles Keepax return regmap_raw_write(dsp->regmap, reg, &data, sizeof(data)); 29352cd19bdbSCharles Keepax } 29362cd19bdbSCharles Keepax 29372cd19bdbSCharles Keepax static inline int wm_adsp_buffer_read(struct wm_adsp_compr_buf *buf, 29382cd19bdbSCharles Keepax unsigned int field_offset, u32 *data) 29392cd19bdbSCharles Keepax { 29402cd19bdbSCharles Keepax return wm_adsp_read_data_word(buf->dsp, WMFW_ADSP2_XM, 29412cd19bdbSCharles Keepax buf->host_buf_ptr + field_offset, data); 29422cd19bdbSCharles Keepax } 29432cd19bdbSCharles Keepax 29442cd19bdbSCharles Keepax static inline int wm_adsp_buffer_write(struct wm_adsp_compr_buf *buf, 29452cd19bdbSCharles Keepax unsigned int field_offset, u32 data) 29462cd19bdbSCharles Keepax { 29472cd19bdbSCharles Keepax return wm_adsp_write_data_word(buf->dsp, WMFW_ADSP2_XM, 29482cd19bdbSCharles Keepax buf->host_buf_ptr + field_offset, data); 29492cd19bdbSCharles Keepax } 29502cd19bdbSCharles Keepax 29512cd19bdbSCharles Keepax static int wm_adsp_buffer_locate(struct wm_adsp_compr_buf *buf) 29522cd19bdbSCharles Keepax { 29532cd19bdbSCharles Keepax struct wm_adsp_alg_region *alg_region; 29542cd19bdbSCharles Keepax struct wm_adsp *dsp = buf->dsp; 29552cd19bdbSCharles Keepax u32 xmalg, addr, magic; 29562cd19bdbSCharles Keepax int i, ret; 29572cd19bdbSCharles Keepax 29582cd19bdbSCharles Keepax alg_region = wm_adsp_find_alg_region(dsp, WMFW_ADSP2_XM, dsp->fw_id); 29592cd19bdbSCharles Keepax xmalg = sizeof(struct wm_adsp_system_config_xm_hdr) / sizeof(__be32); 29602cd19bdbSCharles Keepax 29612cd19bdbSCharles Keepax addr = alg_region->base + xmalg + ALG_XM_FIELD(magic); 29622cd19bdbSCharles Keepax ret = wm_adsp_read_data_word(dsp, WMFW_ADSP2_XM, addr, &magic); 29632cd19bdbSCharles Keepax if (ret < 0) 29642cd19bdbSCharles Keepax return ret; 29652cd19bdbSCharles Keepax 29662cd19bdbSCharles Keepax if (magic != WM_ADSP_ALG_XM_STRUCT_MAGIC) 29672cd19bdbSCharles Keepax return -EINVAL; 29682cd19bdbSCharles Keepax 29692cd19bdbSCharles Keepax addr = alg_region->base + xmalg + ALG_XM_FIELD(host_buf_ptr); 29702cd19bdbSCharles Keepax for (i = 0; i < 5; ++i) { 29712cd19bdbSCharles Keepax ret = wm_adsp_read_data_word(dsp, WMFW_ADSP2_XM, addr, 29722cd19bdbSCharles Keepax &buf->host_buf_ptr); 29732cd19bdbSCharles Keepax if (ret < 0) 29742cd19bdbSCharles Keepax return ret; 29752cd19bdbSCharles Keepax 29762cd19bdbSCharles Keepax if (buf->host_buf_ptr) 29772cd19bdbSCharles Keepax break; 29782cd19bdbSCharles Keepax 29792cd19bdbSCharles Keepax usleep_range(1000, 2000); 29802cd19bdbSCharles Keepax } 29812cd19bdbSCharles Keepax 29822cd19bdbSCharles Keepax if (!buf->host_buf_ptr) 29832cd19bdbSCharles Keepax return -EIO; 29842cd19bdbSCharles Keepax 29852cd19bdbSCharles Keepax adsp_dbg(dsp, "host_buf_ptr=%x\n", buf->host_buf_ptr); 29862cd19bdbSCharles Keepax 29872cd19bdbSCharles Keepax return 0; 29882cd19bdbSCharles Keepax } 29892cd19bdbSCharles Keepax 29902cd19bdbSCharles Keepax static int wm_adsp_buffer_populate(struct wm_adsp_compr_buf *buf) 29912cd19bdbSCharles Keepax { 29922cd19bdbSCharles Keepax const struct wm_adsp_fw_caps *caps = wm_adsp_fw[buf->dsp->fw].caps; 29932cd19bdbSCharles Keepax struct wm_adsp_buffer_region *region; 29942cd19bdbSCharles Keepax u32 offset = 0; 29952cd19bdbSCharles Keepax int i, ret; 29962cd19bdbSCharles Keepax 29972cd19bdbSCharles Keepax for (i = 0; i < caps->num_regions; ++i) { 29982cd19bdbSCharles Keepax region = &buf->regions[i]; 29992cd19bdbSCharles Keepax 30002cd19bdbSCharles Keepax region->offset = offset; 30012cd19bdbSCharles Keepax region->mem_type = caps->region_defs[i].mem_type; 30022cd19bdbSCharles Keepax 30032cd19bdbSCharles Keepax ret = wm_adsp_buffer_read(buf, caps->region_defs[i].base_offset, 30042cd19bdbSCharles Keepax ®ion->base_addr); 30052cd19bdbSCharles Keepax if (ret < 0) 30062cd19bdbSCharles Keepax return ret; 30072cd19bdbSCharles Keepax 30082cd19bdbSCharles Keepax ret = wm_adsp_buffer_read(buf, caps->region_defs[i].size_offset, 30092cd19bdbSCharles Keepax &offset); 30102cd19bdbSCharles Keepax if (ret < 0) 30112cd19bdbSCharles Keepax return ret; 30122cd19bdbSCharles Keepax 30132cd19bdbSCharles Keepax region->cumulative_size = offset; 30142cd19bdbSCharles Keepax 30152cd19bdbSCharles Keepax adsp_dbg(buf->dsp, 30162cd19bdbSCharles Keepax "region=%d type=%d base=%04x off=%04x size=%04x\n", 30172cd19bdbSCharles Keepax i, region->mem_type, region->base_addr, 30182cd19bdbSCharles Keepax region->offset, region->cumulative_size); 30192cd19bdbSCharles Keepax } 30202cd19bdbSCharles Keepax 30212cd19bdbSCharles Keepax return 0; 30222cd19bdbSCharles Keepax } 30232cd19bdbSCharles Keepax 30242cd19bdbSCharles Keepax static int wm_adsp_buffer_init(struct wm_adsp *dsp) 30252cd19bdbSCharles Keepax { 30262cd19bdbSCharles Keepax struct wm_adsp_compr_buf *buf; 30272cd19bdbSCharles Keepax int ret; 30282cd19bdbSCharles Keepax 30292cd19bdbSCharles Keepax buf = kzalloc(sizeof(*buf), GFP_KERNEL); 30302cd19bdbSCharles Keepax if (!buf) 30312cd19bdbSCharles Keepax return -ENOMEM; 30322cd19bdbSCharles Keepax 30332cd19bdbSCharles Keepax buf->dsp = dsp; 3034565ace46SCharles Keepax buf->read_index = -1; 3035565ace46SCharles Keepax buf->irq_count = 0xFFFFFFFF; 30362cd19bdbSCharles Keepax 30372cd19bdbSCharles Keepax ret = wm_adsp_buffer_locate(buf); 30382cd19bdbSCharles Keepax if (ret < 0) { 30392cd19bdbSCharles Keepax adsp_err(dsp, "Failed to acquire host buffer: %d\n", ret); 30402cd19bdbSCharles Keepax goto err_buffer; 30412cd19bdbSCharles Keepax } 30422cd19bdbSCharles Keepax 30432cd19bdbSCharles Keepax buf->regions = kcalloc(wm_adsp_fw[dsp->fw].caps->num_regions, 30442cd19bdbSCharles Keepax sizeof(*buf->regions), GFP_KERNEL); 30452cd19bdbSCharles Keepax if (!buf->regions) { 30462cd19bdbSCharles Keepax ret = -ENOMEM; 30472cd19bdbSCharles Keepax goto err_buffer; 30482cd19bdbSCharles Keepax } 30492cd19bdbSCharles Keepax 30502cd19bdbSCharles Keepax ret = wm_adsp_buffer_populate(buf); 30512cd19bdbSCharles Keepax if (ret < 0) { 30522cd19bdbSCharles Keepax adsp_err(dsp, "Failed to populate host buffer: %d\n", ret); 30532cd19bdbSCharles Keepax goto err_regions; 30542cd19bdbSCharles Keepax } 30552cd19bdbSCharles Keepax 30562cd19bdbSCharles Keepax dsp->buffer = buf; 30572cd19bdbSCharles Keepax 30582cd19bdbSCharles Keepax return 0; 30592cd19bdbSCharles Keepax 30602cd19bdbSCharles Keepax err_regions: 30612cd19bdbSCharles Keepax kfree(buf->regions); 30622cd19bdbSCharles Keepax err_buffer: 30632cd19bdbSCharles Keepax kfree(buf); 30642cd19bdbSCharles Keepax return ret; 30652cd19bdbSCharles Keepax } 30662cd19bdbSCharles Keepax 30672cd19bdbSCharles Keepax static int wm_adsp_buffer_free(struct wm_adsp *dsp) 30682cd19bdbSCharles Keepax { 30692cd19bdbSCharles Keepax if (dsp->buffer) { 3070721be3beSCharles Keepax wm_adsp_compr_detach(dsp->buffer->compr); 3071721be3beSCharles Keepax 30722cd19bdbSCharles Keepax kfree(dsp->buffer->regions); 30732cd19bdbSCharles Keepax kfree(dsp->buffer); 30742cd19bdbSCharles Keepax 30752cd19bdbSCharles Keepax dsp->buffer = NULL; 30762cd19bdbSCharles Keepax } 30772cd19bdbSCharles Keepax 30782cd19bdbSCharles Keepax return 0; 30792cd19bdbSCharles Keepax } 30802cd19bdbSCharles Keepax 308195fe9597SCharles Keepax int wm_adsp_compr_trigger(struct snd_compr_stream *stream, int cmd) 308295fe9597SCharles Keepax { 308395fe9597SCharles Keepax struct wm_adsp_compr *compr = stream->runtime->private_data; 308495fe9597SCharles Keepax struct wm_adsp *dsp = compr->dsp; 308595fe9597SCharles Keepax int ret = 0; 308695fe9597SCharles Keepax 308795fe9597SCharles Keepax adsp_dbg(dsp, "Trigger: %d\n", cmd); 308895fe9597SCharles Keepax 308995fe9597SCharles Keepax mutex_lock(&dsp->pwr_lock); 309095fe9597SCharles Keepax 309195fe9597SCharles Keepax switch (cmd) { 309295fe9597SCharles Keepax case SNDRV_PCM_TRIGGER_START: 309395fe9597SCharles Keepax if (wm_adsp_compr_attached(compr)) 309495fe9597SCharles Keepax break; 309595fe9597SCharles Keepax 309695fe9597SCharles Keepax ret = wm_adsp_compr_attach(compr); 309795fe9597SCharles Keepax if (ret < 0) { 309895fe9597SCharles Keepax adsp_err(dsp, "Failed to link buffer and stream: %d\n", 309995fe9597SCharles Keepax ret); 310095fe9597SCharles Keepax break; 310195fe9597SCharles Keepax } 3102565ace46SCharles Keepax 3103565ace46SCharles Keepax /* Trigger the IRQ at one fragment of data */ 3104565ace46SCharles Keepax ret = wm_adsp_buffer_write(compr->buf, 3105565ace46SCharles Keepax HOST_BUFFER_FIELD(high_water_mark), 3106565ace46SCharles Keepax wm_adsp_compr_frag_words(compr)); 3107565ace46SCharles Keepax if (ret < 0) { 3108565ace46SCharles Keepax adsp_err(dsp, "Failed to set high water mark: %d\n", 3109565ace46SCharles Keepax ret); 3110565ace46SCharles Keepax break; 3111565ace46SCharles Keepax } 311295fe9597SCharles Keepax break; 311395fe9597SCharles Keepax case SNDRV_PCM_TRIGGER_STOP: 311495fe9597SCharles Keepax break; 311595fe9597SCharles Keepax default: 311695fe9597SCharles Keepax ret = -EINVAL; 311795fe9597SCharles Keepax break; 311895fe9597SCharles Keepax } 311995fe9597SCharles Keepax 312095fe9597SCharles Keepax mutex_unlock(&dsp->pwr_lock); 312195fe9597SCharles Keepax 312295fe9597SCharles Keepax return ret; 312395fe9597SCharles Keepax } 312495fe9597SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_trigger); 312595fe9597SCharles Keepax 3126565ace46SCharles Keepax static inline int wm_adsp_buffer_size(struct wm_adsp_compr_buf *buf) 3127565ace46SCharles Keepax { 3128565ace46SCharles Keepax int last_region = wm_adsp_fw[buf->dsp->fw].caps->num_regions - 1; 3129565ace46SCharles Keepax 3130565ace46SCharles Keepax return buf->regions[last_region].cumulative_size; 3131565ace46SCharles Keepax } 3132565ace46SCharles Keepax 3133565ace46SCharles Keepax static int wm_adsp_buffer_update_avail(struct wm_adsp_compr_buf *buf) 3134565ace46SCharles Keepax { 3135565ace46SCharles Keepax u32 next_read_index, next_write_index; 3136565ace46SCharles Keepax int write_index, read_index, avail; 3137565ace46SCharles Keepax int ret; 3138565ace46SCharles Keepax 3139565ace46SCharles Keepax /* Only sync read index if we haven't already read a valid index */ 3140565ace46SCharles Keepax if (buf->read_index < 0) { 3141565ace46SCharles Keepax ret = wm_adsp_buffer_read(buf, 3142565ace46SCharles Keepax HOST_BUFFER_FIELD(next_read_index), 3143565ace46SCharles Keepax &next_read_index); 3144565ace46SCharles Keepax if (ret < 0) 3145565ace46SCharles Keepax return ret; 3146565ace46SCharles Keepax 3147565ace46SCharles Keepax read_index = sign_extend32(next_read_index, 23); 3148565ace46SCharles Keepax 3149565ace46SCharles Keepax if (read_index < 0) { 3150565ace46SCharles Keepax adsp_dbg(buf->dsp, "Avail check on unstarted stream\n"); 3151565ace46SCharles Keepax return 0; 3152565ace46SCharles Keepax } 3153565ace46SCharles Keepax 3154565ace46SCharles Keepax buf->read_index = read_index; 3155565ace46SCharles Keepax } 3156565ace46SCharles Keepax 3157565ace46SCharles Keepax ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(next_write_index), 3158565ace46SCharles Keepax &next_write_index); 3159565ace46SCharles Keepax if (ret < 0) 3160565ace46SCharles Keepax return ret; 3161565ace46SCharles Keepax 3162565ace46SCharles Keepax write_index = sign_extend32(next_write_index, 23); 3163565ace46SCharles Keepax 3164565ace46SCharles Keepax avail = write_index - buf->read_index; 3165565ace46SCharles Keepax if (avail < 0) 3166565ace46SCharles Keepax avail += wm_adsp_buffer_size(buf); 3167565ace46SCharles Keepax 3168565ace46SCharles Keepax adsp_dbg(buf->dsp, "readindex=0x%x, writeindex=0x%x, avail=%d\n", 316933d740e0SCharles Keepax buf->read_index, write_index, avail * WM_ADSP_DATA_WORD_SIZE); 3170565ace46SCharles Keepax 3171565ace46SCharles Keepax buf->avail = avail; 3172565ace46SCharles Keepax 3173565ace46SCharles Keepax return 0; 3174565ace46SCharles Keepax } 3175565ace46SCharles Keepax 31769771b18aSCharles Keepax static int wm_adsp_buffer_get_error(struct wm_adsp_compr_buf *buf) 31779771b18aSCharles Keepax { 31789771b18aSCharles Keepax int ret; 31799771b18aSCharles Keepax 31809771b18aSCharles Keepax ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(error), &buf->error); 31819771b18aSCharles Keepax if (ret < 0) { 31829771b18aSCharles Keepax adsp_err(buf->dsp, "Failed to check buffer error: %d\n", ret); 31839771b18aSCharles Keepax return ret; 31849771b18aSCharles Keepax } 31859771b18aSCharles Keepax if (buf->error != 0) { 31869771b18aSCharles Keepax adsp_err(buf->dsp, "Buffer error occurred: %d\n", buf->error); 31879771b18aSCharles Keepax return -EIO; 31889771b18aSCharles Keepax } 31899771b18aSCharles Keepax 31909771b18aSCharles Keepax return 0; 31919771b18aSCharles Keepax } 31929771b18aSCharles Keepax 3193565ace46SCharles Keepax int wm_adsp_compr_handle_irq(struct wm_adsp *dsp) 3194565ace46SCharles Keepax { 3195612047f0SCharles Keepax struct wm_adsp_compr_buf *buf; 3196612047f0SCharles Keepax struct wm_adsp_compr *compr; 3197565ace46SCharles Keepax int ret = 0; 3198565ace46SCharles Keepax 3199565ace46SCharles Keepax mutex_lock(&dsp->pwr_lock); 3200565ace46SCharles Keepax 3201612047f0SCharles Keepax buf = dsp->buffer; 3202612047f0SCharles Keepax compr = dsp->compr; 3203612047f0SCharles Keepax 3204565ace46SCharles Keepax if (!buf) { 3205565ace46SCharles Keepax ret = -ENODEV; 3206565ace46SCharles Keepax goto out; 3207565ace46SCharles Keepax } 3208565ace46SCharles Keepax 3209565ace46SCharles Keepax adsp_dbg(dsp, "Handling buffer IRQ\n"); 3210565ace46SCharles Keepax 32119771b18aSCharles Keepax ret = wm_adsp_buffer_get_error(buf); 32129771b18aSCharles Keepax if (ret < 0) 32135847609eSCharles Keepax goto out_notify; /* Wake poll to report error */ 3214565ace46SCharles Keepax 3215565ace46SCharles Keepax ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(irq_count), 3216565ace46SCharles Keepax &buf->irq_count); 3217565ace46SCharles Keepax if (ret < 0) { 3218565ace46SCharles Keepax adsp_err(dsp, "Failed to get irq_count: %d\n", ret); 3219565ace46SCharles Keepax goto out; 3220565ace46SCharles Keepax } 3221565ace46SCharles Keepax 3222565ace46SCharles Keepax ret = wm_adsp_buffer_update_avail(buf); 3223565ace46SCharles Keepax if (ret < 0) { 3224565ace46SCharles Keepax adsp_err(dsp, "Error reading avail: %d\n", ret); 3225565ace46SCharles Keepax goto out; 3226565ace46SCharles Keepax } 3227565ace46SCharles Keepax 322820b7f7c5SCharles Keepax if (wm_adsp_fw[dsp->fw].voice_trigger && buf->irq_count == 2) 322920b7f7c5SCharles Keepax ret = WM_ADSP_COMPR_VOICE_TRIGGER; 323020b7f7c5SCharles Keepax 32315847609eSCharles Keepax out_notify: 3232c7dae7c4SCharles Keepax if (compr && compr->stream) 323383a40ce9SCharles Keepax snd_compr_fragment_elapsed(compr->stream); 323483a40ce9SCharles Keepax 3235565ace46SCharles Keepax out: 3236565ace46SCharles Keepax mutex_unlock(&dsp->pwr_lock); 3237565ace46SCharles Keepax 3238565ace46SCharles Keepax return ret; 3239565ace46SCharles Keepax } 3240565ace46SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_handle_irq); 3241565ace46SCharles Keepax 3242565ace46SCharles Keepax static int wm_adsp_buffer_reenable_irq(struct wm_adsp_compr_buf *buf) 3243565ace46SCharles Keepax { 3244565ace46SCharles Keepax if (buf->irq_count & 0x01) 3245565ace46SCharles Keepax return 0; 3246565ace46SCharles Keepax 3247565ace46SCharles Keepax adsp_dbg(buf->dsp, "Enable IRQ(0x%x) for next fragment\n", 3248565ace46SCharles Keepax buf->irq_count); 3249565ace46SCharles Keepax 3250565ace46SCharles Keepax buf->irq_count |= 0x01; 3251565ace46SCharles Keepax 3252565ace46SCharles Keepax return wm_adsp_buffer_write(buf, HOST_BUFFER_FIELD(irq_ack), 3253565ace46SCharles Keepax buf->irq_count); 3254565ace46SCharles Keepax } 3255565ace46SCharles Keepax 3256565ace46SCharles Keepax int wm_adsp_compr_pointer(struct snd_compr_stream *stream, 3257565ace46SCharles Keepax struct snd_compr_tstamp *tstamp) 3258565ace46SCharles Keepax { 3259565ace46SCharles Keepax struct wm_adsp_compr *compr = stream->runtime->private_data; 3260565ace46SCharles Keepax struct wm_adsp *dsp = compr->dsp; 3261612047f0SCharles Keepax struct wm_adsp_compr_buf *buf; 3262565ace46SCharles Keepax int ret = 0; 3263565ace46SCharles Keepax 3264565ace46SCharles Keepax adsp_dbg(dsp, "Pointer request\n"); 3265565ace46SCharles Keepax 3266565ace46SCharles Keepax mutex_lock(&dsp->pwr_lock); 3267565ace46SCharles Keepax 3268612047f0SCharles Keepax buf = compr->buf; 3269612047f0SCharles Keepax 327028ee3d73SCharles Keepax if (!compr->buf || compr->buf->error) { 32718d280664SCharles Keepax snd_compr_stop_error(stream, SNDRV_PCM_STATE_XRUN); 3272565ace46SCharles Keepax ret = -EIO; 3273565ace46SCharles Keepax goto out; 3274565ace46SCharles Keepax } 3275565ace46SCharles Keepax 3276565ace46SCharles Keepax if (buf->avail < wm_adsp_compr_frag_words(compr)) { 3277565ace46SCharles Keepax ret = wm_adsp_buffer_update_avail(buf); 3278565ace46SCharles Keepax if (ret < 0) { 3279565ace46SCharles Keepax adsp_err(dsp, "Error reading avail: %d\n", ret); 3280565ace46SCharles Keepax goto out; 3281565ace46SCharles Keepax } 3282565ace46SCharles Keepax 3283565ace46SCharles Keepax /* 3284565ace46SCharles Keepax * If we really have less than 1 fragment available tell the 3285565ace46SCharles Keepax * DSP to inform us once a whole fragment is available. 3286565ace46SCharles Keepax */ 3287565ace46SCharles Keepax if (buf->avail < wm_adsp_compr_frag_words(compr)) { 32885847609eSCharles Keepax ret = wm_adsp_buffer_get_error(buf); 32898d280664SCharles Keepax if (ret < 0) { 32908d280664SCharles Keepax if (compr->buf->error) 32918d280664SCharles Keepax snd_compr_stop_error(stream, 32928d280664SCharles Keepax SNDRV_PCM_STATE_XRUN); 32935847609eSCharles Keepax goto out; 32948d280664SCharles Keepax } 32955847609eSCharles Keepax 3296565ace46SCharles Keepax ret = wm_adsp_buffer_reenable_irq(buf); 3297565ace46SCharles Keepax if (ret < 0) { 3298565ace46SCharles Keepax adsp_err(dsp, 3299565ace46SCharles Keepax "Failed to re-enable buffer IRQ: %d\n", 3300565ace46SCharles Keepax ret); 3301565ace46SCharles Keepax goto out; 3302565ace46SCharles Keepax } 3303565ace46SCharles Keepax } 3304565ace46SCharles Keepax } 3305565ace46SCharles Keepax 3306565ace46SCharles Keepax tstamp->copied_total = compr->copied_total; 3307565ace46SCharles Keepax tstamp->copied_total += buf->avail * WM_ADSP_DATA_WORD_SIZE; 3308da2b3358SCharles Keepax tstamp->sampling_rate = compr->sample_rate; 3309565ace46SCharles Keepax 3310565ace46SCharles Keepax out: 3311565ace46SCharles Keepax mutex_unlock(&dsp->pwr_lock); 3312565ace46SCharles Keepax 3313565ace46SCharles Keepax return ret; 3314565ace46SCharles Keepax } 3315565ace46SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_pointer); 3316565ace46SCharles Keepax 331783a40ce9SCharles Keepax static int wm_adsp_buffer_capture_block(struct wm_adsp_compr *compr, int target) 331883a40ce9SCharles Keepax { 331983a40ce9SCharles Keepax struct wm_adsp_compr_buf *buf = compr->buf; 332083a40ce9SCharles Keepax u8 *pack_in = (u8 *)compr->raw_buf; 332183a40ce9SCharles Keepax u8 *pack_out = (u8 *)compr->raw_buf; 332283a40ce9SCharles Keepax unsigned int adsp_addr; 332383a40ce9SCharles Keepax int mem_type, nwords, max_read; 332483a40ce9SCharles Keepax int i, j, ret; 332583a40ce9SCharles Keepax 332683a40ce9SCharles Keepax /* Calculate read parameters */ 332783a40ce9SCharles Keepax for (i = 0; i < wm_adsp_fw[buf->dsp->fw].caps->num_regions; ++i) 332883a40ce9SCharles Keepax if (buf->read_index < buf->regions[i].cumulative_size) 332983a40ce9SCharles Keepax break; 333083a40ce9SCharles Keepax 333183a40ce9SCharles Keepax if (i == wm_adsp_fw[buf->dsp->fw].caps->num_regions) 333283a40ce9SCharles Keepax return -EINVAL; 333383a40ce9SCharles Keepax 333483a40ce9SCharles Keepax mem_type = buf->regions[i].mem_type; 333583a40ce9SCharles Keepax adsp_addr = buf->regions[i].base_addr + 333683a40ce9SCharles Keepax (buf->read_index - buf->regions[i].offset); 333783a40ce9SCharles Keepax 333883a40ce9SCharles Keepax max_read = wm_adsp_compr_frag_words(compr); 333983a40ce9SCharles Keepax nwords = buf->regions[i].cumulative_size - buf->read_index; 334083a40ce9SCharles Keepax 334183a40ce9SCharles Keepax if (nwords > target) 334283a40ce9SCharles Keepax nwords = target; 334383a40ce9SCharles Keepax if (nwords > buf->avail) 334483a40ce9SCharles Keepax nwords = buf->avail; 334583a40ce9SCharles Keepax if (nwords > max_read) 334683a40ce9SCharles Keepax nwords = max_read; 334783a40ce9SCharles Keepax if (!nwords) 334883a40ce9SCharles Keepax return 0; 334983a40ce9SCharles Keepax 335083a40ce9SCharles Keepax /* Read data from DSP */ 335183a40ce9SCharles Keepax ret = wm_adsp_read_data_block(buf->dsp, mem_type, adsp_addr, 335283a40ce9SCharles Keepax nwords, compr->raw_buf); 335383a40ce9SCharles Keepax if (ret < 0) 335483a40ce9SCharles Keepax return ret; 335583a40ce9SCharles Keepax 335683a40ce9SCharles Keepax /* Remove the padding bytes from the data read from the DSP */ 335783a40ce9SCharles Keepax for (i = 0; i < nwords; i++) { 335883a40ce9SCharles Keepax for (j = 0; j < WM_ADSP_DATA_WORD_SIZE; j++) 335983a40ce9SCharles Keepax *pack_out++ = *pack_in++; 336083a40ce9SCharles Keepax 336183a40ce9SCharles Keepax pack_in += sizeof(*(compr->raw_buf)) - WM_ADSP_DATA_WORD_SIZE; 336283a40ce9SCharles Keepax } 336383a40ce9SCharles Keepax 336483a40ce9SCharles Keepax /* update read index to account for words read */ 336583a40ce9SCharles Keepax buf->read_index += nwords; 336683a40ce9SCharles Keepax if (buf->read_index == wm_adsp_buffer_size(buf)) 336783a40ce9SCharles Keepax buf->read_index = 0; 336883a40ce9SCharles Keepax 336983a40ce9SCharles Keepax ret = wm_adsp_buffer_write(buf, HOST_BUFFER_FIELD(next_read_index), 337083a40ce9SCharles Keepax buf->read_index); 337183a40ce9SCharles Keepax if (ret < 0) 337283a40ce9SCharles Keepax return ret; 337383a40ce9SCharles Keepax 337483a40ce9SCharles Keepax /* update avail to account for words read */ 337583a40ce9SCharles Keepax buf->avail -= nwords; 337683a40ce9SCharles Keepax 337783a40ce9SCharles Keepax return nwords; 337883a40ce9SCharles Keepax } 337983a40ce9SCharles Keepax 338083a40ce9SCharles Keepax static int wm_adsp_compr_read(struct wm_adsp_compr *compr, 338183a40ce9SCharles Keepax char __user *buf, size_t count) 338283a40ce9SCharles Keepax { 338383a40ce9SCharles Keepax struct wm_adsp *dsp = compr->dsp; 338483a40ce9SCharles Keepax int ntotal = 0; 338583a40ce9SCharles Keepax int nwords, nbytes; 338683a40ce9SCharles Keepax 338783a40ce9SCharles Keepax adsp_dbg(dsp, "Requested read of %zu bytes\n", count); 338883a40ce9SCharles Keepax 338928ee3d73SCharles Keepax if (!compr->buf || compr->buf->error) { 33908d280664SCharles Keepax snd_compr_stop_error(compr->stream, SNDRV_PCM_STATE_XRUN); 339183a40ce9SCharles Keepax return -EIO; 33928d280664SCharles Keepax } 339383a40ce9SCharles Keepax 339483a40ce9SCharles Keepax count /= WM_ADSP_DATA_WORD_SIZE; 339583a40ce9SCharles Keepax 339683a40ce9SCharles Keepax do { 339783a40ce9SCharles Keepax nwords = wm_adsp_buffer_capture_block(compr, count); 339883a40ce9SCharles Keepax if (nwords < 0) { 339983a40ce9SCharles Keepax adsp_err(dsp, "Failed to capture block: %d\n", nwords); 340083a40ce9SCharles Keepax return nwords; 340183a40ce9SCharles Keepax } 340283a40ce9SCharles Keepax 340383a40ce9SCharles Keepax nbytes = nwords * WM_ADSP_DATA_WORD_SIZE; 340483a40ce9SCharles Keepax 340583a40ce9SCharles Keepax adsp_dbg(dsp, "Read %d bytes\n", nbytes); 340683a40ce9SCharles Keepax 340783a40ce9SCharles Keepax if (copy_to_user(buf + ntotal, compr->raw_buf, nbytes)) { 340883a40ce9SCharles Keepax adsp_err(dsp, "Failed to copy data to user: %d, %d\n", 340983a40ce9SCharles Keepax ntotal, nbytes); 341083a40ce9SCharles Keepax return -EFAULT; 341183a40ce9SCharles Keepax } 341283a40ce9SCharles Keepax 341383a40ce9SCharles Keepax count -= nwords; 341483a40ce9SCharles Keepax ntotal += nbytes; 341583a40ce9SCharles Keepax } while (nwords > 0 && count > 0); 341683a40ce9SCharles Keepax 341783a40ce9SCharles Keepax compr->copied_total += ntotal; 341883a40ce9SCharles Keepax 341983a40ce9SCharles Keepax return ntotal; 342083a40ce9SCharles Keepax } 342183a40ce9SCharles Keepax 342283a40ce9SCharles Keepax int wm_adsp_compr_copy(struct snd_compr_stream *stream, char __user *buf, 342383a40ce9SCharles Keepax size_t count) 342483a40ce9SCharles Keepax { 342583a40ce9SCharles Keepax struct wm_adsp_compr *compr = stream->runtime->private_data; 342683a40ce9SCharles Keepax struct wm_adsp *dsp = compr->dsp; 342783a40ce9SCharles Keepax int ret; 342883a40ce9SCharles Keepax 342983a40ce9SCharles Keepax mutex_lock(&dsp->pwr_lock); 343083a40ce9SCharles Keepax 343183a40ce9SCharles Keepax if (stream->direction == SND_COMPRESS_CAPTURE) 343283a40ce9SCharles Keepax ret = wm_adsp_compr_read(compr, buf, count); 343383a40ce9SCharles Keepax else 343483a40ce9SCharles Keepax ret = -ENOTSUPP; 343583a40ce9SCharles Keepax 343683a40ce9SCharles Keepax mutex_unlock(&dsp->pwr_lock); 343783a40ce9SCharles Keepax 343883a40ce9SCharles Keepax return ret; 343983a40ce9SCharles Keepax } 344083a40ce9SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_copy); 344183a40ce9SCharles Keepax 34420a37c6efSPraveen Diwakar MODULE_LICENSE("GPL v2"); 3443