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) { 1904d41c74dSRichard Fitzgerald kfree(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; 4549ee78757SCharles Keepax struct soc_bytes_ext bytes_ext; 45526c22a19SCharles Keepax unsigned int flags; 4568eb084d0SStuart Henderson unsigned int type; 4576ab2b7b4SDimitris Papastamos }; 4586ab2b7b4SDimitris Papastamos 4599ce5e6e6SRichard Fitzgerald static const char *wm_adsp_mem_region_name(unsigned int type) 4609ce5e6e6SRichard Fitzgerald { 4619ce5e6e6SRichard Fitzgerald switch (type) { 4629ce5e6e6SRichard Fitzgerald case WMFW_ADSP1_PM: 4639ce5e6e6SRichard Fitzgerald return "PM"; 4649ce5e6e6SRichard Fitzgerald case WMFW_ADSP1_DM: 4659ce5e6e6SRichard Fitzgerald return "DM"; 4669ce5e6e6SRichard Fitzgerald case WMFW_ADSP2_XM: 4679ce5e6e6SRichard Fitzgerald return "XM"; 4689ce5e6e6SRichard Fitzgerald case WMFW_ADSP2_YM: 4699ce5e6e6SRichard Fitzgerald return "YM"; 4709ce5e6e6SRichard Fitzgerald case WMFW_ADSP1_ZM: 4719ce5e6e6SRichard Fitzgerald return "ZM"; 4729ce5e6e6SRichard Fitzgerald default: 4739ce5e6e6SRichard Fitzgerald return NULL; 4749ce5e6e6SRichard Fitzgerald } 4759ce5e6e6SRichard Fitzgerald } 4769ce5e6e6SRichard Fitzgerald 477f9f55e31SRichard Fitzgerald #ifdef CONFIG_DEBUG_FS 478f9f55e31SRichard Fitzgerald static void wm_adsp_debugfs_save_wmfwname(struct wm_adsp *dsp, const char *s) 479f9f55e31SRichard Fitzgerald { 480f9f55e31SRichard Fitzgerald char *tmp = kasprintf(GFP_KERNEL, "%s\n", s); 481f9f55e31SRichard Fitzgerald 482f9f55e31SRichard Fitzgerald kfree(dsp->wmfw_file_name); 483f9f55e31SRichard Fitzgerald dsp->wmfw_file_name = tmp; 484f9f55e31SRichard Fitzgerald } 485f9f55e31SRichard Fitzgerald 486f9f55e31SRichard Fitzgerald static void wm_adsp_debugfs_save_binname(struct wm_adsp *dsp, const char *s) 487f9f55e31SRichard Fitzgerald { 488f9f55e31SRichard Fitzgerald char *tmp = kasprintf(GFP_KERNEL, "%s\n", s); 489f9f55e31SRichard Fitzgerald 490f9f55e31SRichard Fitzgerald kfree(dsp->bin_file_name); 491f9f55e31SRichard Fitzgerald dsp->bin_file_name = tmp; 492f9f55e31SRichard Fitzgerald } 493f9f55e31SRichard Fitzgerald 494f9f55e31SRichard Fitzgerald static void wm_adsp_debugfs_clear(struct wm_adsp *dsp) 495f9f55e31SRichard Fitzgerald { 496f9f55e31SRichard Fitzgerald kfree(dsp->wmfw_file_name); 497f9f55e31SRichard Fitzgerald kfree(dsp->bin_file_name); 498f9f55e31SRichard Fitzgerald dsp->wmfw_file_name = NULL; 499f9f55e31SRichard Fitzgerald dsp->bin_file_name = NULL; 500f9f55e31SRichard Fitzgerald } 501f9f55e31SRichard Fitzgerald 502f9f55e31SRichard Fitzgerald static ssize_t wm_adsp_debugfs_wmfw_read(struct file *file, 503f9f55e31SRichard Fitzgerald char __user *user_buf, 504f9f55e31SRichard Fitzgerald size_t count, loff_t *ppos) 505f9f55e31SRichard Fitzgerald { 506f9f55e31SRichard Fitzgerald struct wm_adsp *dsp = file->private_data; 507f9f55e31SRichard Fitzgerald ssize_t ret; 508f9f55e31SRichard Fitzgerald 509078e7183SCharles Keepax mutex_lock(&dsp->pwr_lock); 510f9f55e31SRichard Fitzgerald 51128823ebaSCharles Keepax if (!dsp->wmfw_file_name || !dsp->booted) 512f9f55e31SRichard Fitzgerald ret = 0; 513f9f55e31SRichard Fitzgerald else 514f9f55e31SRichard Fitzgerald ret = simple_read_from_buffer(user_buf, count, ppos, 515f9f55e31SRichard Fitzgerald dsp->wmfw_file_name, 516f9f55e31SRichard Fitzgerald strlen(dsp->wmfw_file_name)); 517f9f55e31SRichard Fitzgerald 518078e7183SCharles Keepax mutex_unlock(&dsp->pwr_lock); 519f9f55e31SRichard Fitzgerald return ret; 520f9f55e31SRichard Fitzgerald } 521f9f55e31SRichard Fitzgerald 522f9f55e31SRichard Fitzgerald static ssize_t wm_adsp_debugfs_bin_read(struct file *file, 523f9f55e31SRichard Fitzgerald char __user *user_buf, 524f9f55e31SRichard Fitzgerald size_t count, loff_t *ppos) 525f9f55e31SRichard Fitzgerald { 526f9f55e31SRichard Fitzgerald struct wm_adsp *dsp = file->private_data; 527f9f55e31SRichard Fitzgerald ssize_t ret; 528f9f55e31SRichard Fitzgerald 529078e7183SCharles Keepax mutex_lock(&dsp->pwr_lock); 530f9f55e31SRichard Fitzgerald 53128823ebaSCharles Keepax if (!dsp->bin_file_name || !dsp->booted) 532f9f55e31SRichard Fitzgerald ret = 0; 533f9f55e31SRichard Fitzgerald else 534f9f55e31SRichard Fitzgerald ret = simple_read_from_buffer(user_buf, count, ppos, 535f9f55e31SRichard Fitzgerald dsp->bin_file_name, 536f9f55e31SRichard Fitzgerald strlen(dsp->bin_file_name)); 537f9f55e31SRichard Fitzgerald 538078e7183SCharles Keepax mutex_unlock(&dsp->pwr_lock); 539f9f55e31SRichard Fitzgerald return ret; 540f9f55e31SRichard Fitzgerald } 541f9f55e31SRichard Fitzgerald 542f9f55e31SRichard Fitzgerald static const struct { 543f9f55e31SRichard Fitzgerald const char *name; 544f9f55e31SRichard Fitzgerald const struct file_operations fops; 545f9f55e31SRichard Fitzgerald } wm_adsp_debugfs_fops[] = { 546f9f55e31SRichard Fitzgerald { 547f9f55e31SRichard Fitzgerald .name = "wmfw_file_name", 548f9f55e31SRichard Fitzgerald .fops = { 549f9f55e31SRichard Fitzgerald .open = simple_open, 550f9f55e31SRichard Fitzgerald .read = wm_adsp_debugfs_wmfw_read, 551f9f55e31SRichard Fitzgerald }, 552f9f55e31SRichard Fitzgerald }, 553f9f55e31SRichard Fitzgerald { 554f9f55e31SRichard Fitzgerald .name = "bin_file_name", 555f9f55e31SRichard Fitzgerald .fops = { 556f9f55e31SRichard Fitzgerald .open = simple_open, 557f9f55e31SRichard Fitzgerald .read = wm_adsp_debugfs_bin_read, 558f9f55e31SRichard Fitzgerald }, 559f9f55e31SRichard Fitzgerald }, 560f9f55e31SRichard Fitzgerald }; 561f9f55e31SRichard Fitzgerald 562f9f55e31SRichard Fitzgerald static void wm_adsp2_init_debugfs(struct wm_adsp *dsp, 563f9f55e31SRichard Fitzgerald struct snd_soc_codec *codec) 564f9f55e31SRichard Fitzgerald { 565f9f55e31SRichard Fitzgerald struct dentry *root = NULL; 566f9f55e31SRichard Fitzgerald char *root_name; 567f9f55e31SRichard Fitzgerald int i; 568f9f55e31SRichard Fitzgerald 569f9f55e31SRichard Fitzgerald if (!codec->component.debugfs_root) { 570f9f55e31SRichard Fitzgerald adsp_err(dsp, "No codec debugfs root\n"); 571f9f55e31SRichard Fitzgerald goto err; 572f9f55e31SRichard Fitzgerald } 573f9f55e31SRichard Fitzgerald 574f9f55e31SRichard Fitzgerald root_name = kmalloc(PAGE_SIZE, GFP_KERNEL); 575f9f55e31SRichard Fitzgerald if (!root_name) 576f9f55e31SRichard Fitzgerald goto err; 577f9f55e31SRichard Fitzgerald 578f9f55e31SRichard Fitzgerald snprintf(root_name, PAGE_SIZE, "dsp%d", dsp->num); 579f9f55e31SRichard Fitzgerald root = debugfs_create_dir(root_name, codec->component.debugfs_root); 580f9f55e31SRichard Fitzgerald kfree(root_name); 581f9f55e31SRichard Fitzgerald 582f9f55e31SRichard Fitzgerald if (!root) 583f9f55e31SRichard Fitzgerald goto err; 584f9f55e31SRichard Fitzgerald 58528823ebaSCharles Keepax if (!debugfs_create_bool("booted", S_IRUGO, root, &dsp->booted)) 58628823ebaSCharles Keepax goto err; 58728823ebaSCharles Keepax 588f9f55e31SRichard Fitzgerald if (!debugfs_create_bool("running", S_IRUGO, root, &dsp->running)) 589f9f55e31SRichard Fitzgerald goto err; 590f9f55e31SRichard Fitzgerald 591f9f55e31SRichard Fitzgerald if (!debugfs_create_x32("fw_id", S_IRUGO, root, &dsp->fw_id)) 592f9f55e31SRichard Fitzgerald goto err; 593f9f55e31SRichard Fitzgerald 594f9f55e31SRichard Fitzgerald if (!debugfs_create_x32("fw_version", S_IRUGO, root, 595f9f55e31SRichard Fitzgerald &dsp->fw_id_version)) 596f9f55e31SRichard Fitzgerald goto err; 597f9f55e31SRichard Fitzgerald 598f9f55e31SRichard Fitzgerald for (i = 0; i < ARRAY_SIZE(wm_adsp_debugfs_fops); ++i) { 599f9f55e31SRichard Fitzgerald if (!debugfs_create_file(wm_adsp_debugfs_fops[i].name, 600f9f55e31SRichard Fitzgerald S_IRUGO, root, dsp, 601f9f55e31SRichard Fitzgerald &wm_adsp_debugfs_fops[i].fops)) 602f9f55e31SRichard Fitzgerald goto err; 603f9f55e31SRichard Fitzgerald } 604f9f55e31SRichard Fitzgerald 605f9f55e31SRichard Fitzgerald dsp->debugfs_root = root; 606f9f55e31SRichard Fitzgerald return; 607f9f55e31SRichard Fitzgerald 608f9f55e31SRichard Fitzgerald err: 609f9f55e31SRichard Fitzgerald debugfs_remove_recursive(root); 610f9f55e31SRichard Fitzgerald adsp_err(dsp, "Failed to create debugfs\n"); 611f9f55e31SRichard Fitzgerald } 612f9f55e31SRichard Fitzgerald 613f9f55e31SRichard Fitzgerald static void wm_adsp2_cleanup_debugfs(struct wm_adsp *dsp) 614f9f55e31SRichard Fitzgerald { 615f9f55e31SRichard Fitzgerald wm_adsp_debugfs_clear(dsp); 616f9f55e31SRichard Fitzgerald debugfs_remove_recursive(dsp->debugfs_root); 617f9f55e31SRichard Fitzgerald } 618f9f55e31SRichard Fitzgerald #else 619f9f55e31SRichard Fitzgerald static inline void wm_adsp2_init_debugfs(struct wm_adsp *dsp, 620f9f55e31SRichard Fitzgerald struct snd_soc_codec *codec) 621f9f55e31SRichard Fitzgerald { 622f9f55e31SRichard Fitzgerald } 623f9f55e31SRichard Fitzgerald 624f9f55e31SRichard Fitzgerald static inline void wm_adsp2_cleanup_debugfs(struct wm_adsp *dsp) 625f9f55e31SRichard Fitzgerald { 626f9f55e31SRichard Fitzgerald } 627f9f55e31SRichard Fitzgerald 628f9f55e31SRichard Fitzgerald static inline void wm_adsp_debugfs_save_wmfwname(struct wm_adsp *dsp, 629f9f55e31SRichard Fitzgerald const char *s) 630f9f55e31SRichard Fitzgerald { 631f9f55e31SRichard Fitzgerald } 632f9f55e31SRichard Fitzgerald 633f9f55e31SRichard Fitzgerald static inline void wm_adsp_debugfs_save_binname(struct wm_adsp *dsp, 634f9f55e31SRichard Fitzgerald const char *s) 635f9f55e31SRichard Fitzgerald { 636f9f55e31SRichard Fitzgerald } 637f9f55e31SRichard Fitzgerald 638f9f55e31SRichard Fitzgerald static inline void wm_adsp_debugfs_clear(struct wm_adsp *dsp) 639f9f55e31SRichard Fitzgerald { 640f9f55e31SRichard Fitzgerald } 641f9f55e31SRichard Fitzgerald #endif 642f9f55e31SRichard Fitzgerald 6431023dbd9SMark Brown static int wm_adsp_fw_get(struct snd_kcontrol *kcontrol, 6441023dbd9SMark Brown struct snd_ctl_elem_value *ucontrol) 6451023dbd9SMark Brown { 646ea53bf77SLars-Peter Clausen struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); 6471023dbd9SMark Brown struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; 6483809f001SCharles Keepax struct wm_adsp *dsp = snd_soc_codec_get_drvdata(codec); 6491023dbd9SMark Brown 65015c66570STakashi Iwai ucontrol->value.enumerated.item[0] = dsp[e->shift_l].fw; 6511023dbd9SMark Brown 6521023dbd9SMark Brown return 0; 6531023dbd9SMark Brown } 6541023dbd9SMark Brown 6551023dbd9SMark Brown static int wm_adsp_fw_put(struct snd_kcontrol *kcontrol, 6561023dbd9SMark Brown struct snd_ctl_elem_value *ucontrol) 6571023dbd9SMark Brown { 658ea53bf77SLars-Peter Clausen struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); 6591023dbd9SMark Brown struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; 6603809f001SCharles Keepax struct wm_adsp *dsp = snd_soc_codec_get_drvdata(codec); 661d27c5e15SCharles Keepax int ret = 0; 6621023dbd9SMark Brown 66315c66570STakashi Iwai if (ucontrol->value.enumerated.item[0] == dsp[e->shift_l].fw) 6641023dbd9SMark Brown return 0; 6651023dbd9SMark Brown 66615c66570STakashi Iwai if (ucontrol->value.enumerated.item[0] >= WM_ADSP_NUM_FW) 6671023dbd9SMark Brown return -EINVAL; 6681023dbd9SMark Brown 669d27c5e15SCharles Keepax mutex_lock(&dsp[e->shift_l].pwr_lock); 6701023dbd9SMark Brown 67128823ebaSCharles Keepax if (dsp[e->shift_l].booted || dsp[e->shift_l].compr) 672d27c5e15SCharles Keepax ret = -EBUSY; 673d27c5e15SCharles Keepax else 67415c66570STakashi Iwai dsp[e->shift_l].fw = ucontrol->value.enumerated.item[0]; 6751023dbd9SMark Brown 676d27c5e15SCharles Keepax mutex_unlock(&dsp[e->shift_l].pwr_lock); 677d27c5e15SCharles Keepax 678d27c5e15SCharles Keepax return ret; 6791023dbd9SMark Brown } 6801023dbd9SMark Brown 6811023dbd9SMark Brown static const struct soc_enum wm_adsp_fw_enum[] = { 6821023dbd9SMark Brown SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 6831023dbd9SMark Brown SOC_ENUM_SINGLE(0, 1, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 6841023dbd9SMark Brown SOC_ENUM_SINGLE(0, 2, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 6851023dbd9SMark Brown SOC_ENUM_SINGLE(0, 3, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 6861023dbd9SMark Brown }; 6871023dbd9SMark Brown 688336d0442SRichard Fitzgerald const struct snd_kcontrol_new wm_adsp_fw_controls[] = { 6891023dbd9SMark Brown SOC_ENUM_EXT("DSP1 Firmware", wm_adsp_fw_enum[0], 6901023dbd9SMark Brown wm_adsp_fw_get, wm_adsp_fw_put), 6911023dbd9SMark Brown SOC_ENUM_EXT("DSP2 Firmware", wm_adsp_fw_enum[1], 6921023dbd9SMark Brown wm_adsp_fw_get, wm_adsp_fw_put), 6931023dbd9SMark Brown SOC_ENUM_EXT("DSP3 Firmware", wm_adsp_fw_enum[2], 6941023dbd9SMark Brown wm_adsp_fw_get, wm_adsp_fw_put), 6951023dbd9SMark Brown SOC_ENUM_EXT("DSP4 Firmware", wm_adsp_fw_enum[3], 6961023dbd9SMark Brown wm_adsp_fw_get, wm_adsp_fw_put), 6971023dbd9SMark Brown }; 698336d0442SRichard Fitzgerald EXPORT_SYMBOL_GPL(wm_adsp_fw_controls); 6992159ad93SMark Brown 7002159ad93SMark Brown static struct wm_adsp_region const *wm_adsp_find_region(struct wm_adsp *dsp, 7012159ad93SMark Brown int type) 7022159ad93SMark Brown { 7032159ad93SMark Brown int i; 7042159ad93SMark Brown 7052159ad93SMark Brown for (i = 0; i < dsp->num_mems; i++) 7062159ad93SMark Brown if (dsp->mem[i].type == type) 7072159ad93SMark Brown return &dsp->mem[i]; 7082159ad93SMark Brown 7092159ad93SMark Brown return NULL; 7102159ad93SMark Brown } 7112159ad93SMark Brown 7123809f001SCharles Keepax static unsigned int wm_adsp_region_to_reg(struct wm_adsp_region const *mem, 71345b9ee72SMark Brown unsigned int offset) 71445b9ee72SMark Brown { 7153809f001SCharles Keepax if (WARN_ON(!mem)) 7166c452bdaSTakashi Iwai return offset; 7173809f001SCharles Keepax switch (mem->type) { 71845b9ee72SMark Brown case WMFW_ADSP1_PM: 7193809f001SCharles Keepax return mem->base + (offset * 3); 72045b9ee72SMark Brown case WMFW_ADSP1_DM: 7213809f001SCharles Keepax return mem->base + (offset * 2); 72245b9ee72SMark Brown case WMFW_ADSP2_XM: 7233809f001SCharles Keepax return mem->base + (offset * 2); 72445b9ee72SMark Brown case WMFW_ADSP2_YM: 7253809f001SCharles Keepax return mem->base + (offset * 2); 72645b9ee72SMark Brown case WMFW_ADSP1_ZM: 7273809f001SCharles Keepax return mem->base + (offset * 2); 72845b9ee72SMark Brown default: 7296c452bdaSTakashi Iwai WARN(1, "Unknown memory region type"); 73045b9ee72SMark Brown return offset; 73145b9ee72SMark Brown } 73245b9ee72SMark Brown } 73345b9ee72SMark Brown 73410337b07SRichard Fitzgerald static void wm_adsp2_show_fw_status(struct wm_adsp *dsp) 73510337b07SRichard Fitzgerald { 73610337b07SRichard Fitzgerald u16 scratch[4]; 73710337b07SRichard Fitzgerald int ret; 73810337b07SRichard Fitzgerald 73910337b07SRichard Fitzgerald ret = regmap_raw_read(dsp->regmap, dsp->base + ADSP2_SCRATCH0, 74010337b07SRichard Fitzgerald scratch, sizeof(scratch)); 74110337b07SRichard Fitzgerald if (ret) { 74210337b07SRichard Fitzgerald adsp_err(dsp, "Failed to read SCRATCH regs: %d\n", ret); 74310337b07SRichard Fitzgerald return; 74410337b07SRichard Fitzgerald } 74510337b07SRichard Fitzgerald 74610337b07SRichard Fitzgerald adsp_dbg(dsp, "FW SCRATCH 0:0x%x 1:0x%x 2:0x%x 3:0x%x\n", 74710337b07SRichard Fitzgerald be16_to_cpu(scratch[0]), 74810337b07SRichard Fitzgerald be16_to_cpu(scratch[1]), 74910337b07SRichard Fitzgerald be16_to_cpu(scratch[2]), 75010337b07SRichard Fitzgerald be16_to_cpu(scratch[3])); 75110337b07SRichard Fitzgerald } 75210337b07SRichard Fitzgerald 7539ee78757SCharles Keepax static inline struct wm_coeff_ctl *bytes_ext_to_ctl(struct soc_bytes_ext *ext) 7549ee78757SCharles Keepax { 7559ee78757SCharles Keepax return container_of(ext, struct wm_coeff_ctl, bytes_ext); 7569ee78757SCharles Keepax } 7579ee78757SCharles Keepax 758b396ebcaSRichard Fitzgerald static int wm_coeff_base_reg(struct wm_coeff_ctl *ctl, unsigned int *reg) 759b396ebcaSRichard Fitzgerald { 760b396ebcaSRichard Fitzgerald const struct wm_adsp_alg_region *alg_region = &ctl->alg_region; 761b396ebcaSRichard Fitzgerald struct wm_adsp *dsp = ctl->dsp; 762b396ebcaSRichard Fitzgerald const struct wm_adsp_region *mem; 763b396ebcaSRichard Fitzgerald 764b396ebcaSRichard Fitzgerald mem = wm_adsp_find_region(dsp, alg_region->type); 765b396ebcaSRichard Fitzgerald if (!mem) { 766b396ebcaSRichard Fitzgerald adsp_err(dsp, "No base for region %x\n", 767b396ebcaSRichard Fitzgerald alg_region->type); 768b396ebcaSRichard Fitzgerald return -EINVAL; 769b396ebcaSRichard Fitzgerald } 770b396ebcaSRichard Fitzgerald 771b396ebcaSRichard Fitzgerald *reg = wm_adsp_region_to_reg(mem, ctl->alg_region.base + ctl->offset); 772b396ebcaSRichard Fitzgerald 773b396ebcaSRichard Fitzgerald return 0; 774b396ebcaSRichard Fitzgerald } 775b396ebcaSRichard Fitzgerald 7767585a5b0SCharles Keepax static int wm_coeff_info(struct snd_kcontrol *kctl, 7776ab2b7b4SDimitris Papastamos struct snd_ctl_elem_info *uinfo) 7786ab2b7b4SDimitris Papastamos { 7799ee78757SCharles Keepax struct soc_bytes_ext *bytes_ext = 7809ee78757SCharles Keepax (struct soc_bytes_ext *)kctl->private_value; 7819ee78757SCharles Keepax struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); 7826ab2b7b4SDimitris Papastamos 783a23ebba8SRichard Fitzgerald switch (ctl->type) { 784a23ebba8SRichard Fitzgerald case WMFW_CTL_TYPE_ACKED: 785a23ebba8SRichard Fitzgerald uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 786a23ebba8SRichard Fitzgerald uinfo->value.integer.min = WM_ADSP_ACKED_CTL_MIN_VALUE; 787a23ebba8SRichard Fitzgerald uinfo->value.integer.max = WM_ADSP_ACKED_CTL_MAX_VALUE; 788a23ebba8SRichard Fitzgerald uinfo->value.integer.step = 1; 789a23ebba8SRichard Fitzgerald uinfo->count = 1; 790a23ebba8SRichard Fitzgerald break; 791a23ebba8SRichard Fitzgerald default: 7926ab2b7b4SDimitris Papastamos uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; 7936ab2b7b4SDimitris Papastamos uinfo->count = ctl->len; 794a23ebba8SRichard Fitzgerald break; 795a23ebba8SRichard Fitzgerald } 796a23ebba8SRichard Fitzgerald 7976ab2b7b4SDimitris Papastamos return 0; 7986ab2b7b4SDimitris Papastamos } 7996ab2b7b4SDimitris Papastamos 800f4f0c4c6SRichard Fitzgerald static int wm_coeff_write_acked_control(struct wm_coeff_ctl *ctl, 801f4f0c4c6SRichard Fitzgerald unsigned int event_id) 802f4f0c4c6SRichard Fitzgerald { 803f4f0c4c6SRichard Fitzgerald struct wm_adsp *dsp = ctl->dsp; 804f4f0c4c6SRichard Fitzgerald u32 val = cpu_to_be32(event_id); 805f4f0c4c6SRichard Fitzgerald unsigned int reg; 806f4f0c4c6SRichard Fitzgerald int i, ret; 807f4f0c4c6SRichard Fitzgerald 808f4f0c4c6SRichard Fitzgerald ret = wm_coeff_base_reg(ctl, ®); 809f4f0c4c6SRichard Fitzgerald if (ret) 810f4f0c4c6SRichard Fitzgerald return ret; 811f4f0c4c6SRichard Fitzgerald 812f4f0c4c6SRichard Fitzgerald adsp_dbg(dsp, "Sending 0x%x to acked control alg 0x%x %s:0x%x\n", 813f4f0c4c6SRichard Fitzgerald event_id, ctl->alg_region.alg, 814f4f0c4c6SRichard Fitzgerald wm_adsp_mem_region_name(ctl->alg_region.type), ctl->offset); 815f4f0c4c6SRichard Fitzgerald 816f4f0c4c6SRichard Fitzgerald ret = regmap_raw_write(dsp->regmap, reg, &val, sizeof(val)); 817f4f0c4c6SRichard Fitzgerald if (ret) { 818f4f0c4c6SRichard Fitzgerald adsp_err(dsp, "Failed to write %x: %d\n", reg, ret); 819f4f0c4c6SRichard Fitzgerald return ret; 820f4f0c4c6SRichard Fitzgerald } 821f4f0c4c6SRichard Fitzgerald 822f4f0c4c6SRichard Fitzgerald /* 823f4f0c4c6SRichard Fitzgerald * Poll for ack, we initially poll at ~1ms intervals for firmwares 824f4f0c4c6SRichard Fitzgerald * that respond quickly, then go to ~10ms polls. A firmware is unlikely 825f4f0c4c6SRichard Fitzgerald * to ack instantly so we do the first 1ms delay before reading the 826f4f0c4c6SRichard Fitzgerald * control to avoid a pointless bus transaction 827f4f0c4c6SRichard Fitzgerald */ 828f4f0c4c6SRichard Fitzgerald for (i = 0; i < WM_ADSP_ACKED_CTL_TIMEOUT_MS;) { 829f4f0c4c6SRichard Fitzgerald switch (i) { 830f4f0c4c6SRichard Fitzgerald case 0 ... WM_ADSP_ACKED_CTL_N_QUICKPOLLS - 1: 831f4f0c4c6SRichard Fitzgerald usleep_range(1000, 2000); 832f4f0c4c6SRichard Fitzgerald i++; 833f4f0c4c6SRichard Fitzgerald break; 834f4f0c4c6SRichard Fitzgerald default: 835f4f0c4c6SRichard Fitzgerald usleep_range(10000, 20000); 836f4f0c4c6SRichard Fitzgerald i += 10; 837f4f0c4c6SRichard Fitzgerald break; 838f4f0c4c6SRichard Fitzgerald } 839f4f0c4c6SRichard Fitzgerald 840f4f0c4c6SRichard Fitzgerald ret = regmap_raw_read(dsp->regmap, reg, &val, sizeof(val)); 841f4f0c4c6SRichard Fitzgerald if (ret) { 842f4f0c4c6SRichard Fitzgerald adsp_err(dsp, "Failed to read %x: %d\n", reg, ret); 843f4f0c4c6SRichard Fitzgerald return ret; 844f4f0c4c6SRichard Fitzgerald } 845f4f0c4c6SRichard Fitzgerald 846f4f0c4c6SRichard Fitzgerald if (val == 0) { 847f4f0c4c6SRichard Fitzgerald adsp_dbg(dsp, "Acked control ACKED at poll %u\n", i); 848f4f0c4c6SRichard Fitzgerald return 0; 849f4f0c4c6SRichard Fitzgerald } 850f4f0c4c6SRichard Fitzgerald } 851f4f0c4c6SRichard Fitzgerald 852f4f0c4c6SRichard Fitzgerald adsp_warn(dsp, "Acked control @0x%x alg:0x%x %s:0x%x timed out\n", 853f4f0c4c6SRichard Fitzgerald reg, ctl->alg_region.alg, 854f4f0c4c6SRichard Fitzgerald wm_adsp_mem_region_name(ctl->alg_region.type), 855f4f0c4c6SRichard Fitzgerald ctl->offset); 856f4f0c4c6SRichard Fitzgerald 857f4f0c4c6SRichard Fitzgerald return -ETIMEDOUT; 858f4f0c4c6SRichard Fitzgerald } 859f4f0c4c6SRichard Fitzgerald 860c9f8dd71SCharles Keepax static int wm_coeff_write_control(struct wm_coeff_ctl *ctl, 8616ab2b7b4SDimitris Papastamos const void *buf, size_t len) 8626ab2b7b4SDimitris Papastamos { 8633809f001SCharles Keepax struct wm_adsp *dsp = ctl->dsp; 8646ab2b7b4SDimitris Papastamos void *scratch; 8656ab2b7b4SDimitris Papastamos int ret; 8666ab2b7b4SDimitris Papastamos unsigned int reg; 8676ab2b7b4SDimitris Papastamos 868b396ebcaSRichard Fitzgerald ret = wm_coeff_base_reg(ctl, ®); 869b396ebcaSRichard Fitzgerald if (ret) 870b396ebcaSRichard Fitzgerald return ret; 8716ab2b7b4SDimitris Papastamos 8724f8ea6d7SCharles Keepax scratch = kmemdup(buf, len, GFP_KERNEL | GFP_DMA); 8736ab2b7b4SDimitris Papastamos if (!scratch) 8746ab2b7b4SDimitris Papastamos return -ENOMEM; 8756ab2b7b4SDimitris Papastamos 8763809f001SCharles Keepax ret = regmap_raw_write(dsp->regmap, reg, scratch, 8774f8ea6d7SCharles Keepax len); 8786ab2b7b4SDimitris Papastamos if (ret) { 8793809f001SCharles Keepax adsp_err(dsp, "Failed to write %zu bytes to %x: %d\n", 8804f8ea6d7SCharles Keepax len, reg, ret); 8816ab2b7b4SDimitris Papastamos kfree(scratch); 8826ab2b7b4SDimitris Papastamos return ret; 8836ab2b7b4SDimitris Papastamos } 8844f8ea6d7SCharles Keepax adsp_dbg(dsp, "Wrote %zu bytes to %x\n", len, reg); 8856ab2b7b4SDimitris Papastamos 8866ab2b7b4SDimitris Papastamos kfree(scratch); 8876ab2b7b4SDimitris Papastamos 8886ab2b7b4SDimitris Papastamos return 0; 8896ab2b7b4SDimitris Papastamos } 8906ab2b7b4SDimitris Papastamos 8917585a5b0SCharles Keepax static int wm_coeff_put(struct snd_kcontrol *kctl, 8926ab2b7b4SDimitris Papastamos struct snd_ctl_elem_value *ucontrol) 8936ab2b7b4SDimitris Papastamos { 8949ee78757SCharles Keepax struct soc_bytes_ext *bytes_ext = 8959ee78757SCharles Keepax (struct soc_bytes_ext *)kctl->private_value; 8969ee78757SCharles Keepax struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); 8976ab2b7b4SDimitris Papastamos char *p = ucontrol->value.bytes.data; 898168d10e7SCharles Keepax int ret = 0; 899168d10e7SCharles Keepax 900168d10e7SCharles Keepax mutex_lock(&ctl->dsp->pwr_lock); 9016ab2b7b4SDimitris Papastamos 9026ab2b7b4SDimitris Papastamos memcpy(ctl->cache, p, ctl->len); 9036ab2b7b4SDimitris Papastamos 9040c2e3f34SDimitris Papastamos ctl->set = 1; 905cef45771SCharles Keepax if (ctl->enabled && ctl->dsp->running) 906168d10e7SCharles Keepax ret = wm_coeff_write_control(ctl, p, ctl->len); 9076ab2b7b4SDimitris Papastamos 908168d10e7SCharles Keepax mutex_unlock(&ctl->dsp->pwr_lock); 909168d10e7SCharles Keepax 910168d10e7SCharles Keepax return ret; 9116ab2b7b4SDimitris Papastamos } 9126ab2b7b4SDimitris Papastamos 9139ee78757SCharles Keepax static int wm_coeff_tlv_put(struct snd_kcontrol *kctl, 9149ee78757SCharles Keepax const unsigned int __user *bytes, unsigned int size) 9159ee78757SCharles Keepax { 9169ee78757SCharles Keepax struct soc_bytes_ext *bytes_ext = 9179ee78757SCharles Keepax (struct soc_bytes_ext *)kctl->private_value; 9189ee78757SCharles Keepax struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); 9199ee78757SCharles Keepax int ret = 0; 9209ee78757SCharles Keepax 9219ee78757SCharles Keepax mutex_lock(&ctl->dsp->pwr_lock); 9229ee78757SCharles Keepax 9239ee78757SCharles Keepax if (copy_from_user(ctl->cache, bytes, size)) { 9249ee78757SCharles Keepax ret = -EFAULT; 9259ee78757SCharles Keepax } else { 9269ee78757SCharles Keepax ctl->set = 1; 927cef45771SCharles Keepax if (ctl->enabled && ctl->dsp->running) 9289ee78757SCharles Keepax ret = wm_coeff_write_control(ctl, ctl->cache, size); 9299ee78757SCharles Keepax } 9309ee78757SCharles Keepax 9319ee78757SCharles Keepax mutex_unlock(&ctl->dsp->pwr_lock); 9329ee78757SCharles Keepax 9339ee78757SCharles Keepax return ret; 9349ee78757SCharles Keepax } 9359ee78757SCharles Keepax 936a23ebba8SRichard Fitzgerald static int wm_coeff_put_acked(struct snd_kcontrol *kctl, 937a23ebba8SRichard Fitzgerald struct snd_ctl_elem_value *ucontrol) 938a23ebba8SRichard Fitzgerald { 939a23ebba8SRichard Fitzgerald struct soc_bytes_ext *bytes_ext = 940a23ebba8SRichard Fitzgerald (struct soc_bytes_ext *)kctl->private_value; 941a23ebba8SRichard Fitzgerald struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); 942a23ebba8SRichard Fitzgerald unsigned int val = ucontrol->value.integer.value[0]; 943a23ebba8SRichard Fitzgerald int ret; 944a23ebba8SRichard Fitzgerald 945a23ebba8SRichard Fitzgerald if (val == 0) 946a23ebba8SRichard Fitzgerald return 0; /* 0 means no event */ 947a23ebba8SRichard Fitzgerald 948a23ebba8SRichard Fitzgerald mutex_lock(&ctl->dsp->pwr_lock); 949a23ebba8SRichard Fitzgerald 950a23ebba8SRichard Fitzgerald if (ctl->enabled) 951a23ebba8SRichard Fitzgerald ret = wm_coeff_write_acked_control(ctl, val); 952a23ebba8SRichard Fitzgerald else 953a23ebba8SRichard Fitzgerald ret = -EPERM; 954a23ebba8SRichard Fitzgerald 955a23ebba8SRichard Fitzgerald mutex_unlock(&ctl->dsp->pwr_lock); 956a23ebba8SRichard Fitzgerald 957a23ebba8SRichard Fitzgerald return ret; 958a23ebba8SRichard Fitzgerald } 959a23ebba8SRichard Fitzgerald 960c9f8dd71SCharles Keepax static int wm_coeff_read_control(struct wm_coeff_ctl *ctl, 9616ab2b7b4SDimitris Papastamos void *buf, size_t len) 9626ab2b7b4SDimitris Papastamos { 9633809f001SCharles Keepax struct wm_adsp *dsp = ctl->dsp; 9646ab2b7b4SDimitris Papastamos void *scratch; 9656ab2b7b4SDimitris Papastamos int ret; 9666ab2b7b4SDimitris Papastamos unsigned int reg; 9676ab2b7b4SDimitris Papastamos 968b396ebcaSRichard Fitzgerald ret = wm_coeff_base_reg(ctl, ®); 969b396ebcaSRichard Fitzgerald if (ret) 970b396ebcaSRichard Fitzgerald return ret; 9716ab2b7b4SDimitris Papastamos 9724f8ea6d7SCharles Keepax scratch = kmalloc(len, GFP_KERNEL | GFP_DMA); 9736ab2b7b4SDimitris Papastamos if (!scratch) 9746ab2b7b4SDimitris Papastamos return -ENOMEM; 9756ab2b7b4SDimitris Papastamos 9764f8ea6d7SCharles Keepax ret = regmap_raw_read(dsp->regmap, reg, scratch, len); 9776ab2b7b4SDimitris Papastamos if (ret) { 9783809f001SCharles Keepax adsp_err(dsp, "Failed to read %zu bytes from %x: %d\n", 9795602a643SCharles Keepax len, reg, ret); 9806ab2b7b4SDimitris Papastamos kfree(scratch); 9816ab2b7b4SDimitris Papastamos return ret; 9826ab2b7b4SDimitris Papastamos } 9834f8ea6d7SCharles Keepax adsp_dbg(dsp, "Read %zu bytes from %x\n", len, reg); 9846ab2b7b4SDimitris Papastamos 9854f8ea6d7SCharles Keepax memcpy(buf, scratch, len); 9866ab2b7b4SDimitris Papastamos kfree(scratch); 9876ab2b7b4SDimitris Papastamos 9886ab2b7b4SDimitris Papastamos return 0; 9896ab2b7b4SDimitris Papastamos } 9906ab2b7b4SDimitris Papastamos 9917585a5b0SCharles Keepax static int wm_coeff_get(struct snd_kcontrol *kctl, 9926ab2b7b4SDimitris Papastamos struct snd_ctl_elem_value *ucontrol) 9936ab2b7b4SDimitris Papastamos { 9949ee78757SCharles Keepax struct soc_bytes_ext *bytes_ext = 9959ee78757SCharles Keepax (struct soc_bytes_ext *)kctl->private_value; 9969ee78757SCharles Keepax struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); 9976ab2b7b4SDimitris Papastamos char *p = ucontrol->value.bytes.data; 998168d10e7SCharles Keepax int ret = 0; 999168d10e7SCharles Keepax 1000168d10e7SCharles Keepax mutex_lock(&ctl->dsp->pwr_lock); 10016ab2b7b4SDimitris Papastamos 100226c22a19SCharles Keepax if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) { 1003cef45771SCharles Keepax if (ctl->enabled && ctl->dsp->running) 1004168d10e7SCharles Keepax ret = wm_coeff_read_control(ctl, p, ctl->len); 100526c22a19SCharles Keepax else 1006168d10e7SCharles Keepax ret = -EPERM; 1007168d10e7SCharles Keepax } else { 1008cef45771SCharles Keepax if (!ctl->flags && ctl->enabled && ctl->dsp->running) 1009bc1765d6SCharles Keepax ret = wm_coeff_read_control(ctl, ctl->cache, ctl->len); 1010bc1765d6SCharles Keepax 1011168d10e7SCharles Keepax memcpy(p, ctl->cache, ctl->len); 101226c22a19SCharles Keepax } 101326c22a19SCharles Keepax 1014168d10e7SCharles Keepax mutex_unlock(&ctl->dsp->pwr_lock); 101526c22a19SCharles Keepax 1016168d10e7SCharles Keepax return ret; 10176ab2b7b4SDimitris Papastamos } 10186ab2b7b4SDimitris Papastamos 10199ee78757SCharles Keepax static int wm_coeff_tlv_get(struct snd_kcontrol *kctl, 10209ee78757SCharles Keepax unsigned int __user *bytes, unsigned int size) 10219ee78757SCharles Keepax { 10229ee78757SCharles Keepax struct soc_bytes_ext *bytes_ext = 10239ee78757SCharles Keepax (struct soc_bytes_ext *)kctl->private_value; 10249ee78757SCharles Keepax struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); 10259ee78757SCharles Keepax int ret = 0; 10269ee78757SCharles Keepax 10279ee78757SCharles Keepax mutex_lock(&ctl->dsp->pwr_lock); 10289ee78757SCharles Keepax 10299ee78757SCharles Keepax if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) { 1030cef45771SCharles Keepax if (ctl->enabled && ctl->dsp->running) 10319ee78757SCharles Keepax ret = wm_coeff_read_control(ctl, ctl->cache, size); 10329ee78757SCharles Keepax else 10339ee78757SCharles Keepax ret = -EPERM; 10349ee78757SCharles Keepax } else { 1035cef45771SCharles Keepax if (!ctl->flags && ctl->enabled && ctl->dsp->running) 10369ee78757SCharles Keepax ret = wm_coeff_read_control(ctl, ctl->cache, size); 10379ee78757SCharles Keepax } 10389ee78757SCharles Keepax 10399ee78757SCharles Keepax if (!ret && copy_to_user(bytes, ctl->cache, size)) 10409ee78757SCharles Keepax ret = -EFAULT; 10419ee78757SCharles Keepax 10429ee78757SCharles Keepax mutex_unlock(&ctl->dsp->pwr_lock); 10439ee78757SCharles Keepax 10449ee78757SCharles Keepax return ret; 10459ee78757SCharles Keepax } 10469ee78757SCharles Keepax 1047a23ebba8SRichard Fitzgerald static int wm_coeff_get_acked(struct snd_kcontrol *kcontrol, 1048a23ebba8SRichard Fitzgerald struct snd_ctl_elem_value *ucontrol) 1049a23ebba8SRichard Fitzgerald { 1050a23ebba8SRichard Fitzgerald /* 1051a23ebba8SRichard Fitzgerald * Although it's not useful to read an acked control, we must satisfy 1052a23ebba8SRichard Fitzgerald * user-side assumptions that all controls are readable and that a 1053a23ebba8SRichard Fitzgerald * write of the same value should be filtered out (it's valid to send 1054a23ebba8SRichard Fitzgerald * the same event number again to the firmware). We therefore return 0, 1055a23ebba8SRichard Fitzgerald * meaning "no event" so valid event numbers will always be a change 1056a23ebba8SRichard Fitzgerald */ 1057a23ebba8SRichard Fitzgerald ucontrol->value.integer.value[0] = 0; 1058a23ebba8SRichard Fitzgerald 1059a23ebba8SRichard Fitzgerald return 0; 1060a23ebba8SRichard Fitzgerald } 1061a23ebba8SRichard Fitzgerald 10626ab2b7b4SDimitris Papastamos struct wmfw_ctl_work { 10633809f001SCharles Keepax struct wm_adsp *dsp; 10646ab2b7b4SDimitris Papastamos struct wm_coeff_ctl *ctl; 10656ab2b7b4SDimitris Papastamos struct work_struct work; 10666ab2b7b4SDimitris Papastamos }; 10676ab2b7b4SDimitris Papastamos 10689ee78757SCharles Keepax static unsigned int wmfw_convert_flags(unsigned int in, unsigned int len) 10699ee78757SCharles Keepax { 10709ee78757SCharles Keepax unsigned int out, rd, wr, vol; 10719ee78757SCharles Keepax 10729ee78757SCharles Keepax if (len > ADSP_MAX_STD_CTRL_SIZE) { 10739ee78757SCharles Keepax rd = SNDRV_CTL_ELEM_ACCESS_TLV_READ; 10749ee78757SCharles Keepax wr = SNDRV_CTL_ELEM_ACCESS_TLV_WRITE; 10759ee78757SCharles Keepax vol = SNDRV_CTL_ELEM_ACCESS_VOLATILE; 10769ee78757SCharles Keepax 10779ee78757SCharles Keepax out = SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK; 10789ee78757SCharles Keepax } else { 10799ee78757SCharles Keepax rd = SNDRV_CTL_ELEM_ACCESS_READ; 10809ee78757SCharles Keepax wr = SNDRV_CTL_ELEM_ACCESS_WRITE; 10819ee78757SCharles Keepax vol = SNDRV_CTL_ELEM_ACCESS_VOLATILE; 10829ee78757SCharles Keepax 10839ee78757SCharles Keepax out = 0; 10849ee78757SCharles Keepax } 10859ee78757SCharles Keepax 10869ee78757SCharles Keepax if (in) { 10879ee78757SCharles Keepax if (in & WMFW_CTL_FLAG_READABLE) 10889ee78757SCharles Keepax out |= rd; 10899ee78757SCharles Keepax if (in & WMFW_CTL_FLAG_WRITEABLE) 10909ee78757SCharles Keepax out |= wr; 10919ee78757SCharles Keepax if (in & WMFW_CTL_FLAG_VOLATILE) 10929ee78757SCharles Keepax out |= vol; 10939ee78757SCharles Keepax } else { 10949ee78757SCharles Keepax out |= rd | wr | vol; 10959ee78757SCharles Keepax } 10969ee78757SCharles Keepax 10979ee78757SCharles Keepax return out; 10989ee78757SCharles Keepax } 10999ee78757SCharles Keepax 11003809f001SCharles Keepax static int wmfw_add_ctl(struct wm_adsp *dsp, struct wm_coeff_ctl *ctl) 11016ab2b7b4SDimitris Papastamos { 11026ab2b7b4SDimitris Papastamos struct snd_kcontrol_new *kcontrol; 11036ab2b7b4SDimitris Papastamos int ret; 11046ab2b7b4SDimitris Papastamos 110592bb4c32SDimitris Papastamos if (!ctl || !ctl->name) 11066ab2b7b4SDimitris Papastamos return -EINVAL; 11076ab2b7b4SDimitris Papastamos 11086ab2b7b4SDimitris Papastamos kcontrol = kzalloc(sizeof(*kcontrol), GFP_KERNEL); 11096ab2b7b4SDimitris Papastamos if (!kcontrol) 11106ab2b7b4SDimitris Papastamos return -ENOMEM; 11116ab2b7b4SDimitris Papastamos 11126ab2b7b4SDimitris Papastamos kcontrol->name = ctl->name; 11136ab2b7b4SDimitris Papastamos kcontrol->info = wm_coeff_info; 11149ee78757SCharles Keepax kcontrol->iface = SNDRV_CTL_ELEM_IFACE_MIXER; 11159ee78757SCharles Keepax kcontrol->tlv.c = snd_soc_bytes_tlv_callback; 11169ee78757SCharles Keepax kcontrol->private_value = (unsigned long)&ctl->bytes_ext; 1117a23ebba8SRichard Fitzgerald kcontrol->access = wmfw_convert_flags(ctl->flags, ctl->len); 1118a23ebba8SRichard Fitzgerald 1119a23ebba8SRichard Fitzgerald switch (ctl->type) { 1120a23ebba8SRichard Fitzgerald case WMFW_CTL_TYPE_ACKED: 1121a23ebba8SRichard Fitzgerald kcontrol->get = wm_coeff_get_acked; 1122a23ebba8SRichard Fitzgerald kcontrol->put = wm_coeff_put_acked; 1123a23ebba8SRichard Fitzgerald break; 1124a23ebba8SRichard Fitzgerald default: 1125a23ebba8SRichard Fitzgerald kcontrol->get = wm_coeff_get; 1126a23ebba8SRichard Fitzgerald kcontrol->put = wm_coeff_put; 11276ab2b7b4SDimitris Papastamos 11289ee78757SCharles Keepax ctl->bytes_ext.max = ctl->len; 11299ee78757SCharles Keepax ctl->bytes_ext.get = wm_coeff_tlv_get; 11309ee78757SCharles Keepax ctl->bytes_ext.put = wm_coeff_tlv_put; 1131a23ebba8SRichard Fitzgerald break; 1132a23ebba8SRichard Fitzgerald } 113326c22a19SCharles Keepax 1134685f51a5SRichard Fitzgerald ret = snd_soc_add_codec_controls(dsp->codec, kcontrol, 1); 11356ab2b7b4SDimitris Papastamos if (ret < 0) 11366ab2b7b4SDimitris Papastamos goto err_kcontrol; 11376ab2b7b4SDimitris Papastamos 11386ab2b7b4SDimitris Papastamos kfree(kcontrol); 11396ab2b7b4SDimitris Papastamos 11406ab2b7b4SDimitris Papastamos return 0; 11416ab2b7b4SDimitris Papastamos 11426ab2b7b4SDimitris Papastamos err_kcontrol: 11436ab2b7b4SDimitris Papastamos kfree(kcontrol); 11446ab2b7b4SDimitris Papastamos return ret; 11456ab2b7b4SDimitris Papastamos } 11466ab2b7b4SDimitris Papastamos 1147b21acc1cSCharles Keepax static int wm_coeff_init_control_caches(struct wm_adsp *dsp) 1148b21acc1cSCharles Keepax { 1149b21acc1cSCharles Keepax struct wm_coeff_ctl *ctl; 1150b21acc1cSCharles Keepax int ret; 1151b21acc1cSCharles Keepax 1152b21acc1cSCharles Keepax list_for_each_entry(ctl, &dsp->ctl_list, list) { 1153b21acc1cSCharles Keepax if (!ctl->enabled || ctl->set) 1154b21acc1cSCharles Keepax continue; 115526c22a19SCharles Keepax if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) 115626c22a19SCharles Keepax continue; 115726c22a19SCharles Keepax 11587d00cd97SCharles Keepax ret = wm_coeff_read_control(ctl, ctl->cache, ctl->len); 1159b21acc1cSCharles Keepax if (ret < 0) 1160b21acc1cSCharles Keepax return ret; 1161b21acc1cSCharles Keepax } 1162b21acc1cSCharles Keepax 1163b21acc1cSCharles Keepax return 0; 1164b21acc1cSCharles Keepax } 1165b21acc1cSCharles Keepax 1166b21acc1cSCharles Keepax static int wm_coeff_sync_controls(struct wm_adsp *dsp) 1167b21acc1cSCharles Keepax { 1168b21acc1cSCharles Keepax struct wm_coeff_ctl *ctl; 1169b21acc1cSCharles Keepax int ret; 1170b21acc1cSCharles Keepax 1171b21acc1cSCharles Keepax list_for_each_entry(ctl, &dsp->ctl_list, list) { 1172b21acc1cSCharles Keepax if (!ctl->enabled) 1173b21acc1cSCharles Keepax continue; 117426c22a19SCharles Keepax if (ctl->set && !(ctl->flags & WMFW_CTL_FLAG_VOLATILE)) { 11757d00cd97SCharles Keepax ret = wm_coeff_write_control(ctl, ctl->cache, ctl->len); 1176b21acc1cSCharles Keepax if (ret < 0) 1177b21acc1cSCharles Keepax return ret; 1178b21acc1cSCharles Keepax } 1179b21acc1cSCharles Keepax } 1180b21acc1cSCharles Keepax 1181b21acc1cSCharles Keepax return 0; 1182b21acc1cSCharles Keepax } 1183b21acc1cSCharles Keepax 1184f4f0c4c6SRichard Fitzgerald static void wm_adsp_signal_event_controls(struct wm_adsp *dsp, 1185f4f0c4c6SRichard Fitzgerald unsigned int event) 1186f4f0c4c6SRichard Fitzgerald { 1187f4f0c4c6SRichard Fitzgerald struct wm_coeff_ctl *ctl; 1188f4f0c4c6SRichard Fitzgerald int ret; 1189f4f0c4c6SRichard Fitzgerald 1190f4f0c4c6SRichard Fitzgerald list_for_each_entry(ctl, &dsp->ctl_list, list) { 1191f4f0c4c6SRichard Fitzgerald if (ctl->type != WMFW_CTL_TYPE_HOSTEVENT) 1192f4f0c4c6SRichard Fitzgerald continue; 1193f4f0c4c6SRichard Fitzgerald 119487aa6374SCharles Keepax if (!ctl->enabled) 119587aa6374SCharles Keepax continue; 119687aa6374SCharles Keepax 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 2301685f51a5SRichard Fitzgerald dsp->codec = codec; 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) 2453d589d8b8SCharles Keepax goto err_mem; 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 247290d19ba5SCharles Keepax /* Turn DSP back off until we are ready to run */ 247390d19ba5SCharles Keepax ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 247490d19ba5SCharles Keepax ADSP2_SYS_ENA, 0); 247590d19ba5SCharles Keepax if (ret != 0) 247690d19ba5SCharles Keepax goto err_ena; 247790d19ba5SCharles Keepax 2478e779974bSCharles Keepax dsp->booted = true; 2479e779974bSCharles 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); 2487d589d8b8SCharles Keepax err_mem: 2488d589d8b8SCharles Keepax regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 2489d589d8b8SCharles Keepax ADSP2_MEM_ENA, 0); 2490078e7183SCharles Keepax err_mutex: 2491078e7183SCharles Keepax mutex_unlock(&dsp->pwr_lock); 2492d8a64d6aSCharles Keepax } 2493d8a64d6aSCharles Keepax 2494d82d767fSCharles Keepax static void wm_adsp2_set_dspclk(struct wm_adsp *dsp, unsigned int freq) 2495d82d767fSCharles Keepax { 2496d82d767fSCharles Keepax int ret; 2497d82d767fSCharles Keepax 2498d82d767fSCharles Keepax ret = regmap_update_bits_async(dsp->regmap, 2499d82d767fSCharles Keepax dsp->base + ADSP2_CLOCKING, 2500d82d767fSCharles Keepax ADSP2_CLK_SEL_MASK, 2501d82d767fSCharles Keepax freq << ADSP2_CLK_SEL_SHIFT); 2502d82d767fSCharles Keepax if (ret != 0) 2503d82d767fSCharles Keepax adsp_err(dsp, "Failed to set clock rate: %d\n", ret); 2504d82d767fSCharles Keepax } 2505d82d767fSCharles Keepax 2506af813a6fSCharles Keepax int wm_adsp2_preloader_get(struct snd_kcontrol *kcontrol, 2507af813a6fSCharles Keepax struct snd_ctl_elem_value *ucontrol) 2508af813a6fSCharles Keepax { 2509af813a6fSCharles Keepax struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); 2510af813a6fSCharles Keepax struct wm_adsp *dsp = snd_soc_codec_get_drvdata(codec); 2511af813a6fSCharles Keepax 2512af813a6fSCharles Keepax ucontrol->value.integer.value[0] = dsp->preloaded; 2513af813a6fSCharles Keepax 2514af813a6fSCharles Keepax return 0; 2515af813a6fSCharles Keepax } 2516af813a6fSCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp2_preloader_get); 2517af813a6fSCharles Keepax 2518af813a6fSCharles Keepax int wm_adsp2_preloader_put(struct snd_kcontrol *kcontrol, 2519af813a6fSCharles Keepax struct snd_ctl_elem_value *ucontrol) 2520af813a6fSCharles Keepax { 2521af813a6fSCharles Keepax struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); 2522af813a6fSCharles Keepax struct wm_adsp *dsp = snd_soc_codec_get_drvdata(codec); 2523af813a6fSCharles Keepax struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); 2524af813a6fSCharles Keepax struct soc_mixer_control *mc = 2525af813a6fSCharles Keepax (struct soc_mixer_control *)kcontrol->private_value; 2526af813a6fSCharles Keepax char preload[32]; 2527af813a6fSCharles Keepax 2528af813a6fSCharles Keepax snprintf(preload, ARRAY_SIZE(preload), "DSP%d Preload", mc->shift); 2529af813a6fSCharles Keepax 2530af813a6fSCharles Keepax dsp->preloaded = ucontrol->value.integer.value[0]; 2531af813a6fSCharles Keepax 2532af813a6fSCharles Keepax if (ucontrol->value.integer.value[0]) 2533af813a6fSCharles Keepax snd_soc_dapm_force_enable_pin(dapm, preload); 2534af813a6fSCharles Keepax else 2535af813a6fSCharles Keepax snd_soc_dapm_disable_pin(dapm, preload); 2536af813a6fSCharles Keepax 2537af813a6fSCharles Keepax snd_soc_dapm_sync(dapm); 2538af813a6fSCharles Keepax 2539af813a6fSCharles Keepax return 0; 2540af813a6fSCharles Keepax } 2541af813a6fSCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp2_preloader_put); 2542af813a6fSCharles Keepax 254312db5eddSCharles Keepax int wm_adsp2_early_event(struct snd_soc_dapm_widget *w, 2544d82d767fSCharles Keepax struct snd_kcontrol *kcontrol, int event, 2545d82d767fSCharles Keepax unsigned int freq) 254612db5eddSCharles Keepax { 254772718517SLars-Peter Clausen struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); 254812db5eddSCharles Keepax struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec); 254912db5eddSCharles Keepax struct wm_adsp *dsp = &dsps[w->shift]; 255057a60cc3SCharles Keepax struct wm_coeff_ctl *ctl; 255112db5eddSCharles Keepax 255212db5eddSCharles Keepax switch (event) { 255312db5eddSCharles Keepax case SND_SOC_DAPM_PRE_PMU: 2554d82d767fSCharles Keepax wm_adsp2_set_dspclk(dsp, freq); 255512db5eddSCharles Keepax queue_work(system_unbound_wq, &dsp->boot_work); 255612db5eddSCharles Keepax break; 255757a60cc3SCharles Keepax case SND_SOC_DAPM_PRE_PMD: 2558bb24ee41SCharles Keepax mutex_lock(&dsp->pwr_lock); 2559bb24ee41SCharles Keepax 256057a60cc3SCharles Keepax wm_adsp_debugfs_clear(dsp); 256157a60cc3SCharles Keepax 256257a60cc3SCharles Keepax dsp->fw_id = 0; 256357a60cc3SCharles Keepax dsp->fw_id_version = 0; 256457a60cc3SCharles Keepax 256557a60cc3SCharles Keepax dsp->booted = false; 256657a60cc3SCharles Keepax 256757a60cc3SCharles Keepax regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 256857a60cc3SCharles Keepax ADSP2_MEM_ENA, 0); 256957a60cc3SCharles Keepax 257057a60cc3SCharles Keepax list_for_each_entry(ctl, &dsp->ctl_list, list) 257157a60cc3SCharles Keepax ctl->enabled = 0; 257257a60cc3SCharles Keepax 257357a60cc3SCharles Keepax wm_adsp_free_alg_regions(dsp); 257457a60cc3SCharles Keepax 2575bb24ee41SCharles Keepax mutex_unlock(&dsp->pwr_lock); 2576bb24ee41SCharles Keepax 257757a60cc3SCharles Keepax adsp_dbg(dsp, "Shutdown complete\n"); 257857a60cc3SCharles Keepax break; 257912db5eddSCharles Keepax default: 258012db5eddSCharles Keepax break; 2581cab27258SCharles Keepax } 258212db5eddSCharles Keepax 258312db5eddSCharles Keepax return 0; 258412db5eddSCharles Keepax } 258512db5eddSCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp2_early_event); 258612db5eddSCharles Keepax 2587d8a64d6aSCharles Keepax int wm_adsp2_event(struct snd_soc_dapm_widget *w, 2588d8a64d6aSCharles Keepax struct snd_kcontrol *kcontrol, int event) 2589d8a64d6aSCharles Keepax { 259072718517SLars-Peter Clausen struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); 2591d8a64d6aSCharles Keepax struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec); 2592d8a64d6aSCharles Keepax struct wm_adsp *dsp = &dsps[w->shift]; 2593d8a64d6aSCharles Keepax int ret; 2594d8a64d6aSCharles Keepax 2595d8a64d6aSCharles Keepax switch (event) { 2596d8a64d6aSCharles Keepax case SND_SOC_DAPM_POST_PMU: 2597d8a64d6aSCharles Keepax flush_work(&dsp->boot_work); 2598d8a64d6aSCharles Keepax 2599bb24ee41SCharles Keepax mutex_lock(&dsp->pwr_lock); 2600bb24ee41SCharles Keepax 2601bb24ee41SCharles Keepax if (!dsp->booted) { 2602bb24ee41SCharles Keepax ret = -EIO; 2603bb24ee41SCharles Keepax goto err; 2604bb24ee41SCharles Keepax } 2605d8a64d6aSCharles Keepax 260690d19ba5SCharles Keepax ret = wm_adsp2_ena(dsp); 260790d19ba5SCharles Keepax if (ret != 0) 260890d19ba5SCharles Keepax goto err; 260990d19ba5SCharles Keepax 2610cef45771SCharles Keepax /* Sync set controls */ 2611cef45771SCharles Keepax ret = wm_coeff_sync_controls(dsp); 2612cef45771SCharles Keepax if (ret != 0) 2613cef45771SCharles Keepax goto err; 2614cef45771SCharles Keepax 2615d8a64d6aSCharles Keepax ret = regmap_update_bits(dsp->regmap, 2616d8a64d6aSCharles Keepax dsp->base + ADSP2_CONTROL, 261700e4c3b6SCharles Keepax ADSP2_CORE_ENA | ADSP2_START, 261800e4c3b6SCharles Keepax ADSP2_CORE_ENA | ADSP2_START); 2619d8a64d6aSCharles Keepax if (ret != 0) 2620d8a64d6aSCharles Keepax goto err; 26212cd19bdbSCharles Keepax 262248c2c993SCharles Keepax if (wm_adsp_fw[dsp->fw].num_caps != 0) { 26232cd19bdbSCharles Keepax ret = wm_adsp_buffer_init(dsp); 2624bb24ee41SCharles Keepax if (ret < 0) 262548c2c993SCharles Keepax goto err; 262648c2c993SCharles Keepax } 26272cd19bdbSCharles Keepax 2628e779974bSCharles Keepax dsp->running = true; 2629e779974bSCharles Keepax 2630612047f0SCharles Keepax mutex_unlock(&dsp->pwr_lock); 2631612047f0SCharles Keepax 26322159ad93SMark Brown break; 26332159ad93SMark Brown 26342159ad93SMark Brown case SND_SOC_DAPM_PRE_PMD: 2635f4f0c4c6SRichard Fitzgerald /* Tell the firmware to cleanup */ 2636f4f0c4c6SRichard Fitzgerald wm_adsp_signal_event_controls(dsp, WM_ADSP_FW_EVENT_SHUTDOWN); 2637f4f0c4c6SRichard Fitzgerald 263810337b07SRichard Fitzgerald /* Log firmware state, it can be useful for analysis */ 263910337b07SRichard Fitzgerald wm_adsp2_show_fw_status(dsp); 264010337b07SRichard Fitzgerald 2641078e7183SCharles Keepax mutex_lock(&dsp->pwr_lock); 2642078e7183SCharles Keepax 26431023dbd9SMark Brown dsp->running = false; 26441023dbd9SMark Brown 26452159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 264657a60cc3SCharles Keepax ADSP2_CORE_ENA | ADSP2_START, 0); 2647973838a0SMark Brown 26482d30b575SMark Brown /* Make sure DMAs are quiesced */ 26496facd2d1SSimon Trimmer regmap_write(dsp->regmap, dsp->base + ADSP2_RDMA_CONFIG_1, 0); 26502d30b575SMark Brown regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_1, 0); 26512d30b575SMark Brown regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_2, 0); 26526facd2d1SSimon Trimmer 26536facd2d1SSimon Trimmer regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 26546facd2d1SSimon Trimmer ADSP2_SYS_ENA, 0); 26552d30b575SMark Brown 26562cd19bdbSCharles Keepax if (wm_adsp_fw[dsp->fw].num_caps != 0) 26572cd19bdbSCharles Keepax wm_adsp_buffer_free(dsp); 26582cd19bdbSCharles Keepax 2659078e7183SCharles Keepax mutex_unlock(&dsp->pwr_lock); 2660078e7183SCharles Keepax 266157a60cc3SCharles Keepax adsp_dbg(dsp, "Execution stopped\n"); 26622159ad93SMark Brown break; 26632159ad93SMark Brown 26642159ad93SMark Brown default: 26652159ad93SMark Brown break; 26662159ad93SMark Brown } 26672159ad93SMark Brown 26682159ad93SMark Brown return 0; 26692159ad93SMark Brown err: 26702159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 2671a7f9be7eSMark Brown ADSP2_SYS_ENA | ADSP2_CORE_ENA | ADSP2_START, 0); 2672bb24ee41SCharles Keepax mutex_unlock(&dsp->pwr_lock); 26732159ad93SMark Brown return ret; 26742159ad93SMark Brown } 26752159ad93SMark Brown EXPORT_SYMBOL_GPL(wm_adsp2_event); 2676973838a0SMark Brown 2677f5e2ce92SRichard Fitzgerald int wm_adsp2_codec_probe(struct wm_adsp *dsp, struct snd_soc_codec *codec) 2678f5e2ce92SRichard Fitzgerald { 2679af813a6fSCharles Keepax struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); 2680af813a6fSCharles Keepax char preload[32]; 2681af813a6fSCharles Keepax 2682af813a6fSCharles Keepax snprintf(preload, ARRAY_SIZE(preload), "DSP%d Preload", dsp->num); 2683af813a6fSCharles Keepax snd_soc_dapm_disable_pin(dapm, preload); 2684685f51a5SRichard Fitzgerald 2685f9f55e31SRichard Fitzgerald wm_adsp2_init_debugfs(dsp, codec); 2686f9f55e31SRichard Fitzgerald 2687af813a6fSCharles Keepax dsp->codec = codec; 2688af813a6fSCharles Keepax 2689218e5087SRichard Fitzgerald return snd_soc_add_codec_controls(codec, 2690336d0442SRichard Fitzgerald &wm_adsp_fw_controls[dsp->num - 1], 2691336d0442SRichard Fitzgerald 1); 2692f5e2ce92SRichard Fitzgerald } 2693f5e2ce92SRichard Fitzgerald EXPORT_SYMBOL_GPL(wm_adsp2_codec_probe); 2694f5e2ce92SRichard Fitzgerald 2695f5e2ce92SRichard Fitzgerald int wm_adsp2_codec_remove(struct wm_adsp *dsp, struct snd_soc_codec *codec) 2696f5e2ce92SRichard Fitzgerald { 2697f9f55e31SRichard Fitzgerald wm_adsp2_cleanup_debugfs(dsp); 2698f9f55e31SRichard Fitzgerald 2699f5e2ce92SRichard Fitzgerald return 0; 2700f5e2ce92SRichard Fitzgerald } 2701f5e2ce92SRichard Fitzgerald EXPORT_SYMBOL_GPL(wm_adsp2_codec_remove); 2702f5e2ce92SRichard Fitzgerald 270381ac58b1SRichard Fitzgerald int wm_adsp2_init(struct wm_adsp *dsp) 2704973838a0SMark Brown { 2705973838a0SMark Brown int ret; 2706973838a0SMark Brown 270710a2b662SMark Brown /* 270810a2b662SMark Brown * Disable the DSP memory by default when in reset for a small 270910a2b662SMark Brown * power saving. 271010a2b662SMark Brown */ 27113809f001SCharles Keepax ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 271210a2b662SMark Brown ADSP2_MEM_ENA, 0); 271310a2b662SMark Brown if (ret != 0) { 27143809f001SCharles Keepax adsp_err(dsp, "Failed to clear memory retention: %d\n", ret); 271510a2b662SMark Brown return ret; 271610a2b662SMark Brown } 271710a2b662SMark Brown 27183809f001SCharles Keepax INIT_LIST_HEAD(&dsp->alg_regions); 27193809f001SCharles Keepax INIT_LIST_HEAD(&dsp->ctl_list); 27203809f001SCharles Keepax INIT_WORK(&dsp->boot_work, wm_adsp2_boot_work); 27216ab2b7b4SDimitris Papastamos 2722078e7183SCharles Keepax mutex_init(&dsp->pwr_lock); 2723078e7183SCharles Keepax 2724973838a0SMark Brown return 0; 2725973838a0SMark Brown } 2726973838a0SMark Brown EXPORT_SYMBOL_GPL(wm_adsp2_init); 27270a37c6efSPraveen Diwakar 272866225e98SRichard Fitzgerald void wm_adsp2_remove(struct wm_adsp *dsp) 272966225e98SRichard Fitzgerald { 273066225e98SRichard Fitzgerald struct wm_coeff_ctl *ctl; 273166225e98SRichard Fitzgerald 273266225e98SRichard Fitzgerald while (!list_empty(&dsp->ctl_list)) { 273366225e98SRichard Fitzgerald ctl = list_first_entry(&dsp->ctl_list, struct wm_coeff_ctl, 273466225e98SRichard Fitzgerald list); 273566225e98SRichard Fitzgerald list_del(&ctl->list); 273666225e98SRichard Fitzgerald wm_adsp_free_ctl_blk(ctl); 273766225e98SRichard Fitzgerald } 273866225e98SRichard Fitzgerald } 273966225e98SRichard Fitzgerald EXPORT_SYMBOL_GPL(wm_adsp2_remove); 274066225e98SRichard Fitzgerald 2741edd71350SCharles Keepax static inline int wm_adsp_compr_attached(struct wm_adsp_compr *compr) 2742edd71350SCharles Keepax { 2743edd71350SCharles Keepax return compr->buf != NULL; 2744edd71350SCharles Keepax } 2745edd71350SCharles Keepax 2746edd71350SCharles Keepax static int wm_adsp_compr_attach(struct wm_adsp_compr *compr) 2747edd71350SCharles Keepax { 2748edd71350SCharles Keepax /* 2749edd71350SCharles Keepax * Note this will be more complex once each DSP can support multiple 2750edd71350SCharles Keepax * streams 2751edd71350SCharles Keepax */ 2752edd71350SCharles Keepax if (!compr->dsp->buffer) 2753edd71350SCharles Keepax return -EINVAL; 2754edd71350SCharles Keepax 2755edd71350SCharles Keepax compr->buf = compr->dsp->buffer; 2756721be3beSCharles Keepax compr->buf->compr = compr; 2757edd71350SCharles Keepax 2758edd71350SCharles Keepax return 0; 2759edd71350SCharles Keepax } 2760edd71350SCharles Keepax 2761721be3beSCharles Keepax static void wm_adsp_compr_detach(struct wm_adsp_compr *compr) 2762721be3beSCharles Keepax { 2763721be3beSCharles Keepax if (!compr) 2764721be3beSCharles Keepax return; 2765721be3beSCharles Keepax 2766721be3beSCharles Keepax /* Wake the poll so it can see buffer is no longer attached */ 2767721be3beSCharles Keepax if (compr->stream) 2768721be3beSCharles Keepax snd_compr_fragment_elapsed(compr->stream); 2769721be3beSCharles Keepax 2770721be3beSCharles Keepax if (wm_adsp_compr_attached(compr)) { 2771721be3beSCharles Keepax compr->buf->compr = NULL; 2772721be3beSCharles Keepax compr->buf = NULL; 2773721be3beSCharles Keepax } 2774721be3beSCharles Keepax } 2775721be3beSCharles Keepax 2776406abc95SCharles Keepax int wm_adsp_compr_open(struct wm_adsp *dsp, struct snd_compr_stream *stream) 2777406abc95SCharles Keepax { 2778406abc95SCharles Keepax struct wm_adsp_compr *compr; 2779406abc95SCharles Keepax int ret = 0; 2780406abc95SCharles Keepax 2781406abc95SCharles Keepax mutex_lock(&dsp->pwr_lock); 2782406abc95SCharles Keepax 2783406abc95SCharles Keepax if (wm_adsp_fw[dsp->fw].num_caps == 0) { 2784406abc95SCharles Keepax adsp_err(dsp, "Firmware does not support compressed API\n"); 2785406abc95SCharles Keepax ret = -ENXIO; 2786406abc95SCharles Keepax goto out; 2787406abc95SCharles Keepax } 2788406abc95SCharles Keepax 2789406abc95SCharles Keepax if (wm_adsp_fw[dsp->fw].compr_direction != stream->direction) { 2790406abc95SCharles Keepax adsp_err(dsp, "Firmware does not support stream direction\n"); 2791406abc95SCharles Keepax ret = -EINVAL; 2792406abc95SCharles Keepax goto out; 2793406abc95SCharles Keepax } 2794406abc95SCharles Keepax 279595fe9597SCharles Keepax if (dsp->compr) { 279695fe9597SCharles Keepax /* It is expect this limitation will be removed in future */ 279795fe9597SCharles Keepax adsp_err(dsp, "Only a single stream supported per DSP\n"); 279895fe9597SCharles Keepax ret = -EBUSY; 279995fe9597SCharles Keepax goto out; 280095fe9597SCharles Keepax } 280195fe9597SCharles Keepax 2802406abc95SCharles Keepax compr = kzalloc(sizeof(*compr), GFP_KERNEL); 2803406abc95SCharles Keepax if (!compr) { 2804406abc95SCharles Keepax ret = -ENOMEM; 2805406abc95SCharles Keepax goto out; 2806406abc95SCharles Keepax } 2807406abc95SCharles Keepax 2808406abc95SCharles Keepax compr->dsp = dsp; 2809406abc95SCharles Keepax compr->stream = stream; 2810406abc95SCharles Keepax 2811406abc95SCharles Keepax dsp->compr = compr; 2812406abc95SCharles Keepax 2813406abc95SCharles Keepax stream->runtime->private_data = compr; 2814406abc95SCharles Keepax 2815406abc95SCharles Keepax out: 2816406abc95SCharles Keepax mutex_unlock(&dsp->pwr_lock); 2817406abc95SCharles Keepax 2818406abc95SCharles Keepax return ret; 2819406abc95SCharles Keepax } 2820406abc95SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_open); 2821406abc95SCharles Keepax 2822406abc95SCharles Keepax int wm_adsp_compr_free(struct snd_compr_stream *stream) 2823406abc95SCharles Keepax { 2824406abc95SCharles Keepax struct wm_adsp_compr *compr = stream->runtime->private_data; 2825406abc95SCharles Keepax struct wm_adsp *dsp = compr->dsp; 2826406abc95SCharles Keepax 2827406abc95SCharles Keepax mutex_lock(&dsp->pwr_lock); 2828406abc95SCharles Keepax 2829721be3beSCharles Keepax wm_adsp_compr_detach(compr); 2830406abc95SCharles Keepax dsp->compr = NULL; 2831406abc95SCharles Keepax 283283a40ce9SCharles Keepax kfree(compr->raw_buf); 2833406abc95SCharles Keepax kfree(compr); 2834406abc95SCharles Keepax 2835406abc95SCharles Keepax mutex_unlock(&dsp->pwr_lock); 2836406abc95SCharles Keepax 2837406abc95SCharles Keepax return 0; 2838406abc95SCharles Keepax } 2839406abc95SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_free); 2840406abc95SCharles Keepax 2841406abc95SCharles Keepax static int wm_adsp_compr_check_params(struct snd_compr_stream *stream, 2842406abc95SCharles Keepax struct snd_compr_params *params) 2843406abc95SCharles Keepax { 2844406abc95SCharles Keepax struct wm_adsp_compr *compr = stream->runtime->private_data; 2845406abc95SCharles Keepax struct wm_adsp *dsp = compr->dsp; 2846406abc95SCharles Keepax const struct wm_adsp_fw_caps *caps; 2847406abc95SCharles Keepax const struct snd_codec_desc *desc; 2848406abc95SCharles Keepax int i, j; 2849406abc95SCharles Keepax 2850406abc95SCharles Keepax if (params->buffer.fragment_size < WM_ADSP_MIN_FRAGMENT_SIZE || 2851406abc95SCharles Keepax params->buffer.fragment_size > WM_ADSP_MAX_FRAGMENT_SIZE || 2852406abc95SCharles Keepax params->buffer.fragments < WM_ADSP_MIN_FRAGMENTS || 2853406abc95SCharles Keepax params->buffer.fragments > WM_ADSP_MAX_FRAGMENTS || 2854406abc95SCharles Keepax params->buffer.fragment_size % WM_ADSP_DATA_WORD_SIZE) { 2855406abc95SCharles Keepax adsp_err(dsp, "Invalid buffer fragsize=%d fragments=%d\n", 2856406abc95SCharles Keepax params->buffer.fragment_size, 2857406abc95SCharles Keepax params->buffer.fragments); 2858406abc95SCharles Keepax 2859406abc95SCharles Keepax return -EINVAL; 2860406abc95SCharles Keepax } 2861406abc95SCharles Keepax 2862406abc95SCharles Keepax for (i = 0; i < wm_adsp_fw[dsp->fw].num_caps; i++) { 2863406abc95SCharles Keepax caps = &wm_adsp_fw[dsp->fw].caps[i]; 2864406abc95SCharles Keepax desc = &caps->desc; 2865406abc95SCharles Keepax 2866406abc95SCharles Keepax if (caps->id != params->codec.id) 2867406abc95SCharles Keepax continue; 2868406abc95SCharles Keepax 2869406abc95SCharles Keepax if (stream->direction == SND_COMPRESS_PLAYBACK) { 2870406abc95SCharles Keepax if (desc->max_ch < params->codec.ch_out) 2871406abc95SCharles Keepax continue; 2872406abc95SCharles Keepax } else { 2873406abc95SCharles Keepax if (desc->max_ch < params->codec.ch_in) 2874406abc95SCharles Keepax continue; 2875406abc95SCharles Keepax } 2876406abc95SCharles Keepax 2877406abc95SCharles Keepax if (!(desc->formats & (1 << params->codec.format))) 2878406abc95SCharles Keepax continue; 2879406abc95SCharles Keepax 2880406abc95SCharles Keepax for (j = 0; j < desc->num_sample_rates; ++j) 2881406abc95SCharles Keepax if (desc->sample_rates[j] == params->codec.sample_rate) 2882406abc95SCharles Keepax return 0; 2883406abc95SCharles Keepax } 2884406abc95SCharles Keepax 2885406abc95SCharles Keepax adsp_err(dsp, "Invalid params id=%u ch=%u,%u rate=%u fmt=%u\n", 2886406abc95SCharles Keepax params->codec.id, params->codec.ch_in, params->codec.ch_out, 2887406abc95SCharles Keepax params->codec.sample_rate, params->codec.format); 2888406abc95SCharles Keepax return -EINVAL; 2889406abc95SCharles Keepax } 2890406abc95SCharles Keepax 2891565ace46SCharles Keepax static inline unsigned int wm_adsp_compr_frag_words(struct wm_adsp_compr *compr) 2892565ace46SCharles Keepax { 2893565ace46SCharles Keepax return compr->size.fragment_size / WM_ADSP_DATA_WORD_SIZE; 2894565ace46SCharles Keepax } 2895565ace46SCharles Keepax 2896406abc95SCharles Keepax int wm_adsp_compr_set_params(struct snd_compr_stream *stream, 2897406abc95SCharles Keepax struct snd_compr_params *params) 2898406abc95SCharles Keepax { 2899406abc95SCharles Keepax struct wm_adsp_compr *compr = stream->runtime->private_data; 290083a40ce9SCharles Keepax unsigned int size; 2901406abc95SCharles Keepax int ret; 2902406abc95SCharles Keepax 2903406abc95SCharles Keepax ret = wm_adsp_compr_check_params(stream, params); 2904406abc95SCharles Keepax if (ret) 2905406abc95SCharles Keepax return ret; 2906406abc95SCharles Keepax 2907406abc95SCharles Keepax compr->size = params->buffer; 2908406abc95SCharles Keepax 2909406abc95SCharles Keepax adsp_dbg(compr->dsp, "fragment_size=%d fragments=%d\n", 2910406abc95SCharles Keepax compr->size.fragment_size, compr->size.fragments); 2911406abc95SCharles Keepax 291283a40ce9SCharles Keepax size = wm_adsp_compr_frag_words(compr) * sizeof(*compr->raw_buf); 291383a40ce9SCharles Keepax compr->raw_buf = kmalloc(size, GFP_DMA | GFP_KERNEL); 291483a40ce9SCharles Keepax if (!compr->raw_buf) 291583a40ce9SCharles Keepax return -ENOMEM; 291683a40ce9SCharles Keepax 2917da2b3358SCharles Keepax compr->sample_rate = params->codec.sample_rate; 2918da2b3358SCharles Keepax 2919406abc95SCharles Keepax return 0; 2920406abc95SCharles Keepax } 2921406abc95SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_set_params); 2922406abc95SCharles Keepax 2923406abc95SCharles Keepax int wm_adsp_compr_get_caps(struct snd_compr_stream *stream, 2924406abc95SCharles Keepax struct snd_compr_caps *caps) 2925406abc95SCharles Keepax { 2926406abc95SCharles Keepax struct wm_adsp_compr *compr = stream->runtime->private_data; 2927406abc95SCharles Keepax int fw = compr->dsp->fw; 2928406abc95SCharles Keepax int i; 2929406abc95SCharles Keepax 2930406abc95SCharles Keepax if (wm_adsp_fw[fw].caps) { 2931406abc95SCharles Keepax for (i = 0; i < wm_adsp_fw[fw].num_caps; i++) 2932406abc95SCharles Keepax caps->codecs[i] = wm_adsp_fw[fw].caps[i].id; 2933406abc95SCharles Keepax 2934406abc95SCharles Keepax caps->num_codecs = i; 2935406abc95SCharles Keepax caps->direction = wm_adsp_fw[fw].compr_direction; 2936406abc95SCharles Keepax 2937406abc95SCharles Keepax caps->min_fragment_size = WM_ADSP_MIN_FRAGMENT_SIZE; 2938406abc95SCharles Keepax caps->max_fragment_size = WM_ADSP_MAX_FRAGMENT_SIZE; 2939406abc95SCharles Keepax caps->min_fragments = WM_ADSP_MIN_FRAGMENTS; 2940406abc95SCharles Keepax caps->max_fragments = WM_ADSP_MAX_FRAGMENTS; 2941406abc95SCharles Keepax } 2942406abc95SCharles Keepax 2943406abc95SCharles Keepax return 0; 2944406abc95SCharles Keepax } 2945406abc95SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_get_caps); 2946406abc95SCharles Keepax 29472cd19bdbSCharles Keepax static int wm_adsp_read_data_block(struct wm_adsp *dsp, int mem_type, 29482cd19bdbSCharles Keepax unsigned int mem_addr, 29492cd19bdbSCharles Keepax unsigned int num_words, u32 *data) 29502cd19bdbSCharles Keepax { 29512cd19bdbSCharles Keepax struct wm_adsp_region const *mem = wm_adsp_find_region(dsp, mem_type); 29522cd19bdbSCharles Keepax unsigned int i, reg; 29532cd19bdbSCharles Keepax int ret; 29542cd19bdbSCharles Keepax 29552cd19bdbSCharles Keepax if (!mem) 29562cd19bdbSCharles Keepax return -EINVAL; 29572cd19bdbSCharles Keepax 29582cd19bdbSCharles Keepax reg = wm_adsp_region_to_reg(mem, mem_addr); 29592cd19bdbSCharles Keepax 29602cd19bdbSCharles Keepax ret = regmap_raw_read(dsp->regmap, reg, data, 29612cd19bdbSCharles Keepax sizeof(*data) * num_words); 29622cd19bdbSCharles Keepax if (ret < 0) 29632cd19bdbSCharles Keepax return ret; 29642cd19bdbSCharles Keepax 29652cd19bdbSCharles Keepax for (i = 0; i < num_words; ++i) 29662cd19bdbSCharles Keepax data[i] = be32_to_cpu(data[i]) & 0x00ffffffu; 29672cd19bdbSCharles Keepax 29682cd19bdbSCharles Keepax return 0; 29692cd19bdbSCharles Keepax } 29702cd19bdbSCharles Keepax 29712cd19bdbSCharles Keepax static inline int wm_adsp_read_data_word(struct wm_adsp *dsp, int mem_type, 29722cd19bdbSCharles Keepax unsigned int mem_addr, u32 *data) 29732cd19bdbSCharles Keepax { 29742cd19bdbSCharles Keepax return wm_adsp_read_data_block(dsp, mem_type, mem_addr, 1, data); 29752cd19bdbSCharles Keepax } 29762cd19bdbSCharles Keepax 29772cd19bdbSCharles Keepax static int wm_adsp_write_data_word(struct wm_adsp *dsp, int mem_type, 29782cd19bdbSCharles Keepax unsigned int mem_addr, u32 data) 29792cd19bdbSCharles Keepax { 29802cd19bdbSCharles Keepax struct wm_adsp_region const *mem = wm_adsp_find_region(dsp, mem_type); 29812cd19bdbSCharles Keepax unsigned int reg; 29822cd19bdbSCharles Keepax 29832cd19bdbSCharles Keepax if (!mem) 29842cd19bdbSCharles Keepax return -EINVAL; 29852cd19bdbSCharles Keepax 29862cd19bdbSCharles Keepax reg = wm_adsp_region_to_reg(mem, mem_addr); 29872cd19bdbSCharles Keepax 29882cd19bdbSCharles Keepax data = cpu_to_be32(data & 0x00ffffffu); 29892cd19bdbSCharles Keepax 29902cd19bdbSCharles Keepax return regmap_raw_write(dsp->regmap, reg, &data, sizeof(data)); 29912cd19bdbSCharles Keepax } 29922cd19bdbSCharles Keepax 29932cd19bdbSCharles Keepax static inline int wm_adsp_buffer_read(struct wm_adsp_compr_buf *buf, 29942cd19bdbSCharles Keepax unsigned int field_offset, u32 *data) 29952cd19bdbSCharles Keepax { 29962cd19bdbSCharles Keepax return wm_adsp_read_data_word(buf->dsp, WMFW_ADSP2_XM, 29972cd19bdbSCharles Keepax buf->host_buf_ptr + field_offset, data); 29982cd19bdbSCharles Keepax } 29992cd19bdbSCharles Keepax 30002cd19bdbSCharles Keepax static inline int wm_adsp_buffer_write(struct wm_adsp_compr_buf *buf, 30012cd19bdbSCharles Keepax unsigned int field_offset, u32 data) 30022cd19bdbSCharles Keepax { 30032cd19bdbSCharles Keepax return wm_adsp_write_data_word(buf->dsp, WMFW_ADSP2_XM, 30042cd19bdbSCharles Keepax buf->host_buf_ptr + field_offset, data); 30052cd19bdbSCharles Keepax } 30062cd19bdbSCharles Keepax 30072cd19bdbSCharles Keepax static int wm_adsp_buffer_locate(struct wm_adsp_compr_buf *buf) 30082cd19bdbSCharles Keepax { 30092cd19bdbSCharles Keepax struct wm_adsp_alg_region *alg_region; 30102cd19bdbSCharles Keepax struct wm_adsp *dsp = buf->dsp; 30112cd19bdbSCharles Keepax u32 xmalg, addr, magic; 30122cd19bdbSCharles Keepax int i, ret; 30132cd19bdbSCharles Keepax 30142cd19bdbSCharles Keepax alg_region = wm_adsp_find_alg_region(dsp, WMFW_ADSP2_XM, dsp->fw_id); 30152cd19bdbSCharles Keepax xmalg = sizeof(struct wm_adsp_system_config_xm_hdr) / sizeof(__be32); 30162cd19bdbSCharles Keepax 30172cd19bdbSCharles Keepax addr = alg_region->base + xmalg + ALG_XM_FIELD(magic); 30182cd19bdbSCharles Keepax ret = wm_adsp_read_data_word(dsp, WMFW_ADSP2_XM, addr, &magic); 30192cd19bdbSCharles Keepax if (ret < 0) 30202cd19bdbSCharles Keepax return ret; 30212cd19bdbSCharles Keepax 30222cd19bdbSCharles Keepax if (magic != WM_ADSP_ALG_XM_STRUCT_MAGIC) 30232cd19bdbSCharles Keepax return -EINVAL; 30242cd19bdbSCharles Keepax 30252cd19bdbSCharles Keepax addr = alg_region->base + xmalg + ALG_XM_FIELD(host_buf_ptr); 30262cd19bdbSCharles Keepax for (i = 0; i < 5; ++i) { 30272cd19bdbSCharles Keepax ret = wm_adsp_read_data_word(dsp, WMFW_ADSP2_XM, addr, 30282cd19bdbSCharles Keepax &buf->host_buf_ptr); 30292cd19bdbSCharles Keepax if (ret < 0) 30302cd19bdbSCharles Keepax return ret; 30312cd19bdbSCharles Keepax 30322cd19bdbSCharles Keepax if (buf->host_buf_ptr) 30332cd19bdbSCharles Keepax break; 30342cd19bdbSCharles Keepax 30352cd19bdbSCharles Keepax usleep_range(1000, 2000); 30362cd19bdbSCharles Keepax } 30372cd19bdbSCharles Keepax 30382cd19bdbSCharles Keepax if (!buf->host_buf_ptr) 30392cd19bdbSCharles Keepax return -EIO; 30402cd19bdbSCharles Keepax 30412cd19bdbSCharles Keepax adsp_dbg(dsp, "host_buf_ptr=%x\n", buf->host_buf_ptr); 30422cd19bdbSCharles Keepax 30432cd19bdbSCharles Keepax return 0; 30442cd19bdbSCharles Keepax } 30452cd19bdbSCharles Keepax 30462cd19bdbSCharles Keepax static int wm_adsp_buffer_populate(struct wm_adsp_compr_buf *buf) 30472cd19bdbSCharles Keepax { 30482cd19bdbSCharles Keepax const struct wm_adsp_fw_caps *caps = wm_adsp_fw[buf->dsp->fw].caps; 30492cd19bdbSCharles Keepax struct wm_adsp_buffer_region *region; 30502cd19bdbSCharles Keepax u32 offset = 0; 30512cd19bdbSCharles Keepax int i, ret; 30522cd19bdbSCharles Keepax 30532cd19bdbSCharles Keepax for (i = 0; i < caps->num_regions; ++i) { 30542cd19bdbSCharles Keepax region = &buf->regions[i]; 30552cd19bdbSCharles Keepax 30562cd19bdbSCharles Keepax region->offset = offset; 30572cd19bdbSCharles Keepax region->mem_type = caps->region_defs[i].mem_type; 30582cd19bdbSCharles Keepax 30592cd19bdbSCharles Keepax ret = wm_adsp_buffer_read(buf, caps->region_defs[i].base_offset, 30602cd19bdbSCharles Keepax ®ion->base_addr); 30612cd19bdbSCharles Keepax if (ret < 0) 30622cd19bdbSCharles Keepax return ret; 30632cd19bdbSCharles Keepax 30642cd19bdbSCharles Keepax ret = wm_adsp_buffer_read(buf, caps->region_defs[i].size_offset, 30652cd19bdbSCharles Keepax &offset); 30662cd19bdbSCharles Keepax if (ret < 0) 30672cd19bdbSCharles Keepax return ret; 30682cd19bdbSCharles Keepax 30692cd19bdbSCharles Keepax region->cumulative_size = offset; 30702cd19bdbSCharles Keepax 30712cd19bdbSCharles Keepax adsp_dbg(buf->dsp, 30722cd19bdbSCharles Keepax "region=%d type=%d base=%04x off=%04x size=%04x\n", 30732cd19bdbSCharles Keepax i, region->mem_type, region->base_addr, 30742cd19bdbSCharles Keepax region->offset, region->cumulative_size); 30752cd19bdbSCharles Keepax } 30762cd19bdbSCharles Keepax 30772cd19bdbSCharles Keepax return 0; 30782cd19bdbSCharles Keepax } 30792cd19bdbSCharles Keepax 30802cd19bdbSCharles Keepax static int wm_adsp_buffer_init(struct wm_adsp *dsp) 30812cd19bdbSCharles Keepax { 30822cd19bdbSCharles Keepax struct wm_adsp_compr_buf *buf; 30832cd19bdbSCharles Keepax int ret; 30842cd19bdbSCharles Keepax 30852cd19bdbSCharles Keepax buf = kzalloc(sizeof(*buf), GFP_KERNEL); 30862cd19bdbSCharles Keepax if (!buf) 30872cd19bdbSCharles Keepax return -ENOMEM; 30882cd19bdbSCharles Keepax 30892cd19bdbSCharles Keepax buf->dsp = dsp; 3090565ace46SCharles Keepax buf->read_index = -1; 3091565ace46SCharles Keepax buf->irq_count = 0xFFFFFFFF; 30922cd19bdbSCharles Keepax 30932cd19bdbSCharles Keepax ret = wm_adsp_buffer_locate(buf); 30942cd19bdbSCharles Keepax if (ret < 0) { 30952cd19bdbSCharles Keepax adsp_err(dsp, "Failed to acquire host buffer: %d\n", ret); 30962cd19bdbSCharles Keepax goto err_buffer; 30972cd19bdbSCharles Keepax } 30982cd19bdbSCharles Keepax 30992cd19bdbSCharles Keepax buf->regions = kcalloc(wm_adsp_fw[dsp->fw].caps->num_regions, 31002cd19bdbSCharles Keepax sizeof(*buf->regions), GFP_KERNEL); 31012cd19bdbSCharles Keepax if (!buf->regions) { 31022cd19bdbSCharles Keepax ret = -ENOMEM; 31032cd19bdbSCharles Keepax goto err_buffer; 31042cd19bdbSCharles Keepax } 31052cd19bdbSCharles Keepax 31062cd19bdbSCharles Keepax ret = wm_adsp_buffer_populate(buf); 31072cd19bdbSCharles Keepax if (ret < 0) { 31082cd19bdbSCharles Keepax adsp_err(dsp, "Failed to populate host buffer: %d\n", ret); 31092cd19bdbSCharles Keepax goto err_regions; 31102cd19bdbSCharles Keepax } 31112cd19bdbSCharles Keepax 31122cd19bdbSCharles Keepax dsp->buffer = buf; 31132cd19bdbSCharles Keepax 31142cd19bdbSCharles Keepax return 0; 31152cd19bdbSCharles Keepax 31162cd19bdbSCharles Keepax err_regions: 31172cd19bdbSCharles Keepax kfree(buf->regions); 31182cd19bdbSCharles Keepax err_buffer: 31192cd19bdbSCharles Keepax kfree(buf); 31202cd19bdbSCharles Keepax return ret; 31212cd19bdbSCharles Keepax } 31222cd19bdbSCharles Keepax 31232cd19bdbSCharles Keepax static int wm_adsp_buffer_free(struct wm_adsp *dsp) 31242cd19bdbSCharles Keepax { 31252cd19bdbSCharles Keepax if (dsp->buffer) { 3126721be3beSCharles Keepax wm_adsp_compr_detach(dsp->buffer->compr); 3127721be3beSCharles Keepax 31282cd19bdbSCharles Keepax kfree(dsp->buffer->regions); 31292cd19bdbSCharles Keepax kfree(dsp->buffer); 31302cd19bdbSCharles Keepax 31312cd19bdbSCharles Keepax dsp->buffer = NULL; 31322cd19bdbSCharles Keepax } 31332cd19bdbSCharles Keepax 31342cd19bdbSCharles Keepax return 0; 31352cd19bdbSCharles Keepax } 31362cd19bdbSCharles Keepax 313795fe9597SCharles Keepax int wm_adsp_compr_trigger(struct snd_compr_stream *stream, int cmd) 313895fe9597SCharles Keepax { 313995fe9597SCharles Keepax struct wm_adsp_compr *compr = stream->runtime->private_data; 314095fe9597SCharles Keepax struct wm_adsp *dsp = compr->dsp; 314195fe9597SCharles Keepax int ret = 0; 314295fe9597SCharles Keepax 314395fe9597SCharles Keepax adsp_dbg(dsp, "Trigger: %d\n", cmd); 314495fe9597SCharles Keepax 314595fe9597SCharles Keepax mutex_lock(&dsp->pwr_lock); 314695fe9597SCharles Keepax 314795fe9597SCharles Keepax switch (cmd) { 314895fe9597SCharles Keepax case SNDRV_PCM_TRIGGER_START: 314995fe9597SCharles Keepax if (wm_adsp_compr_attached(compr)) 315095fe9597SCharles Keepax break; 315195fe9597SCharles Keepax 315295fe9597SCharles Keepax ret = wm_adsp_compr_attach(compr); 315395fe9597SCharles Keepax if (ret < 0) { 315495fe9597SCharles Keepax adsp_err(dsp, "Failed to link buffer and stream: %d\n", 315595fe9597SCharles Keepax ret); 315695fe9597SCharles Keepax break; 315795fe9597SCharles Keepax } 3158565ace46SCharles Keepax 3159565ace46SCharles Keepax /* Trigger the IRQ at one fragment of data */ 3160565ace46SCharles Keepax ret = wm_adsp_buffer_write(compr->buf, 3161565ace46SCharles Keepax HOST_BUFFER_FIELD(high_water_mark), 3162565ace46SCharles Keepax wm_adsp_compr_frag_words(compr)); 3163565ace46SCharles Keepax if (ret < 0) { 3164565ace46SCharles Keepax adsp_err(dsp, "Failed to set high water mark: %d\n", 3165565ace46SCharles Keepax ret); 3166565ace46SCharles Keepax break; 3167565ace46SCharles Keepax } 316895fe9597SCharles Keepax break; 316995fe9597SCharles Keepax case SNDRV_PCM_TRIGGER_STOP: 317095fe9597SCharles Keepax break; 317195fe9597SCharles Keepax default: 317295fe9597SCharles Keepax ret = -EINVAL; 317395fe9597SCharles Keepax break; 317495fe9597SCharles Keepax } 317595fe9597SCharles Keepax 317695fe9597SCharles Keepax mutex_unlock(&dsp->pwr_lock); 317795fe9597SCharles Keepax 317895fe9597SCharles Keepax return ret; 317995fe9597SCharles Keepax } 318095fe9597SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_trigger); 318195fe9597SCharles Keepax 3182565ace46SCharles Keepax static inline int wm_adsp_buffer_size(struct wm_adsp_compr_buf *buf) 3183565ace46SCharles Keepax { 3184565ace46SCharles Keepax int last_region = wm_adsp_fw[buf->dsp->fw].caps->num_regions - 1; 3185565ace46SCharles Keepax 3186565ace46SCharles Keepax return buf->regions[last_region].cumulative_size; 3187565ace46SCharles Keepax } 3188565ace46SCharles Keepax 3189565ace46SCharles Keepax static int wm_adsp_buffer_update_avail(struct wm_adsp_compr_buf *buf) 3190565ace46SCharles Keepax { 3191565ace46SCharles Keepax u32 next_read_index, next_write_index; 3192565ace46SCharles Keepax int write_index, read_index, avail; 3193565ace46SCharles Keepax int ret; 3194565ace46SCharles Keepax 3195565ace46SCharles Keepax /* Only sync read index if we haven't already read a valid index */ 3196565ace46SCharles Keepax if (buf->read_index < 0) { 3197565ace46SCharles Keepax ret = wm_adsp_buffer_read(buf, 3198565ace46SCharles Keepax HOST_BUFFER_FIELD(next_read_index), 3199565ace46SCharles Keepax &next_read_index); 3200565ace46SCharles Keepax if (ret < 0) 3201565ace46SCharles Keepax return ret; 3202565ace46SCharles Keepax 3203565ace46SCharles Keepax read_index = sign_extend32(next_read_index, 23); 3204565ace46SCharles Keepax 3205565ace46SCharles Keepax if (read_index < 0) { 3206565ace46SCharles Keepax adsp_dbg(buf->dsp, "Avail check on unstarted stream\n"); 3207565ace46SCharles Keepax return 0; 3208565ace46SCharles Keepax } 3209565ace46SCharles Keepax 3210565ace46SCharles Keepax buf->read_index = read_index; 3211565ace46SCharles Keepax } 3212565ace46SCharles Keepax 3213565ace46SCharles Keepax ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(next_write_index), 3214565ace46SCharles Keepax &next_write_index); 3215565ace46SCharles Keepax if (ret < 0) 3216565ace46SCharles Keepax return ret; 3217565ace46SCharles Keepax 3218565ace46SCharles Keepax write_index = sign_extend32(next_write_index, 23); 3219565ace46SCharles Keepax 3220565ace46SCharles Keepax avail = write_index - buf->read_index; 3221565ace46SCharles Keepax if (avail < 0) 3222565ace46SCharles Keepax avail += wm_adsp_buffer_size(buf); 3223565ace46SCharles Keepax 3224565ace46SCharles Keepax adsp_dbg(buf->dsp, "readindex=0x%x, writeindex=0x%x, avail=%d\n", 322533d740e0SCharles Keepax buf->read_index, write_index, avail * WM_ADSP_DATA_WORD_SIZE); 3226565ace46SCharles Keepax 3227565ace46SCharles Keepax buf->avail = avail; 3228565ace46SCharles Keepax 3229565ace46SCharles Keepax return 0; 3230565ace46SCharles Keepax } 3231565ace46SCharles Keepax 32329771b18aSCharles Keepax static int wm_adsp_buffer_get_error(struct wm_adsp_compr_buf *buf) 32339771b18aSCharles Keepax { 32349771b18aSCharles Keepax int ret; 32359771b18aSCharles Keepax 32369771b18aSCharles Keepax ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(error), &buf->error); 32379771b18aSCharles Keepax if (ret < 0) { 32389771b18aSCharles Keepax adsp_err(buf->dsp, "Failed to check buffer error: %d\n", ret); 32399771b18aSCharles Keepax return ret; 32409771b18aSCharles Keepax } 32419771b18aSCharles Keepax if (buf->error != 0) { 32429771b18aSCharles Keepax adsp_err(buf->dsp, "Buffer error occurred: %d\n", buf->error); 32439771b18aSCharles Keepax return -EIO; 32449771b18aSCharles Keepax } 32459771b18aSCharles Keepax 32469771b18aSCharles Keepax return 0; 32479771b18aSCharles Keepax } 32489771b18aSCharles Keepax 3249565ace46SCharles Keepax int wm_adsp_compr_handle_irq(struct wm_adsp *dsp) 3250565ace46SCharles Keepax { 3251612047f0SCharles Keepax struct wm_adsp_compr_buf *buf; 3252612047f0SCharles Keepax struct wm_adsp_compr *compr; 3253565ace46SCharles Keepax int ret = 0; 3254565ace46SCharles Keepax 3255565ace46SCharles Keepax mutex_lock(&dsp->pwr_lock); 3256565ace46SCharles Keepax 3257612047f0SCharles Keepax buf = dsp->buffer; 3258612047f0SCharles Keepax compr = dsp->compr; 3259612047f0SCharles Keepax 3260565ace46SCharles Keepax if (!buf) { 3261565ace46SCharles Keepax ret = -ENODEV; 3262565ace46SCharles Keepax goto out; 3263565ace46SCharles Keepax } 3264565ace46SCharles Keepax 3265565ace46SCharles Keepax adsp_dbg(dsp, "Handling buffer IRQ\n"); 3266565ace46SCharles Keepax 32679771b18aSCharles Keepax ret = wm_adsp_buffer_get_error(buf); 32689771b18aSCharles Keepax if (ret < 0) 32695847609eSCharles Keepax goto out_notify; /* Wake poll to report error */ 3270565ace46SCharles Keepax 3271565ace46SCharles Keepax ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(irq_count), 3272565ace46SCharles Keepax &buf->irq_count); 3273565ace46SCharles Keepax if (ret < 0) { 3274565ace46SCharles Keepax adsp_err(dsp, "Failed to get irq_count: %d\n", ret); 3275565ace46SCharles Keepax goto out; 3276565ace46SCharles Keepax } 3277565ace46SCharles Keepax 3278565ace46SCharles Keepax ret = wm_adsp_buffer_update_avail(buf); 3279565ace46SCharles Keepax if (ret < 0) { 3280565ace46SCharles Keepax adsp_err(dsp, "Error reading avail: %d\n", ret); 3281565ace46SCharles Keepax goto out; 3282565ace46SCharles Keepax } 3283565ace46SCharles Keepax 328420b7f7c5SCharles Keepax if (wm_adsp_fw[dsp->fw].voice_trigger && buf->irq_count == 2) 328520b7f7c5SCharles Keepax ret = WM_ADSP_COMPR_VOICE_TRIGGER; 328620b7f7c5SCharles Keepax 32875847609eSCharles Keepax out_notify: 3288c7dae7c4SCharles Keepax if (compr && compr->stream) 328983a40ce9SCharles Keepax snd_compr_fragment_elapsed(compr->stream); 329083a40ce9SCharles Keepax 3291565ace46SCharles Keepax out: 3292565ace46SCharles Keepax mutex_unlock(&dsp->pwr_lock); 3293565ace46SCharles Keepax 3294565ace46SCharles Keepax return ret; 3295565ace46SCharles Keepax } 3296565ace46SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_handle_irq); 3297565ace46SCharles Keepax 3298565ace46SCharles Keepax static int wm_adsp_buffer_reenable_irq(struct wm_adsp_compr_buf *buf) 3299565ace46SCharles Keepax { 3300565ace46SCharles Keepax if (buf->irq_count & 0x01) 3301565ace46SCharles Keepax return 0; 3302565ace46SCharles Keepax 3303565ace46SCharles Keepax adsp_dbg(buf->dsp, "Enable IRQ(0x%x) for next fragment\n", 3304565ace46SCharles Keepax buf->irq_count); 3305565ace46SCharles Keepax 3306565ace46SCharles Keepax buf->irq_count |= 0x01; 3307565ace46SCharles Keepax 3308565ace46SCharles Keepax return wm_adsp_buffer_write(buf, HOST_BUFFER_FIELD(irq_ack), 3309565ace46SCharles Keepax buf->irq_count); 3310565ace46SCharles Keepax } 3311565ace46SCharles Keepax 3312565ace46SCharles Keepax int wm_adsp_compr_pointer(struct snd_compr_stream *stream, 3313565ace46SCharles Keepax struct snd_compr_tstamp *tstamp) 3314565ace46SCharles Keepax { 3315565ace46SCharles Keepax struct wm_adsp_compr *compr = stream->runtime->private_data; 3316565ace46SCharles Keepax struct wm_adsp *dsp = compr->dsp; 3317612047f0SCharles Keepax struct wm_adsp_compr_buf *buf; 3318565ace46SCharles Keepax int ret = 0; 3319565ace46SCharles Keepax 3320565ace46SCharles Keepax adsp_dbg(dsp, "Pointer request\n"); 3321565ace46SCharles Keepax 3322565ace46SCharles Keepax mutex_lock(&dsp->pwr_lock); 3323565ace46SCharles Keepax 3324612047f0SCharles Keepax buf = compr->buf; 3325612047f0SCharles Keepax 332628ee3d73SCharles Keepax if (!compr->buf || compr->buf->error) { 33278d280664SCharles Keepax snd_compr_stop_error(stream, SNDRV_PCM_STATE_XRUN); 3328565ace46SCharles Keepax ret = -EIO; 3329565ace46SCharles Keepax goto out; 3330565ace46SCharles Keepax } 3331565ace46SCharles Keepax 3332565ace46SCharles Keepax if (buf->avail < wm_adsp_compr_frag_words(compr)) { 3333565ace46SCharles Keepax ret = wm_adsp_buffer_update_avail(buf); 3334565ace46SCharles Keepax if (ret < 0) { 3335565ace46SCharles Keepax adsp_err(dsp, "Error reading avail: %d\n", ret); 3336565ace46SCharles Keepax goto out; 3337565ace46SCharles Keepax } 3338565ace46SCharles Keepax 3339565ace46SCharles Keepax /* 3340565ace46SCharles Keepax * If we really have less than 1 fragment available tell the 3341565ace46SCharles Keepax * DSP to inform us once a whole fragment is available. 3342565ace46SCharles Keepax */ 3343565ace46SCharles Keepax if (buf->avail < wm_adsp_compr_frag_words(compr)) { 33445847609eSCharles Keepax ret = wm_adsp_buffer_get_error(buf); 33458d280664SCharles Keepax if (ret < 0) { 33468d280664SCharles Keepax if (compr->buf->error) 33478d280664SCharles Keepax snd_compr_stop_error(stream, 33488d280664SCharles Keepax SNDRV_PCM_STATE_XRUN); 33495847609eSCharles Keepax goto out; 33508d280664SCharles Keepax } 33515847609eSCharles Keepax 3352565ace46SCharles Keepax ret = wm_adsp_buffer_reenable_irq(buf); 3353565ace46SCharles Keepax if (ret < 0) { 3354565ace46SCharles Keepax adsp_err(dsp, 3355565ace46SCharles Keepax "Failed to re-enable buffer IRQ: %d\n", 3356565ace46SCharles Keepax ret); 3357565ace46SCharles Keepax goto out; 3358565ace46SCharles Keepax } 3359565ace46SCharles Keepax } 3360565ace46SCharles Keepax } 3361565ace46SCharles Keepax 3362565ace46SCharles Keepax tstamp->copied_total = compr->copied_total; 3363565ace46SCharles Keepax tstamp->copied_total += buf->avail * WM_ADSP_DATA_WORD_SIZE; 3364da2b3358SCharles Keepax tstamp->sampling_rate = compr->sample_rate; 3365565ace46SCharles Keepax 3366565ace46SCharles Keepax out: 3367565ace46SCharles Keepax mutex_unlock(&dsp->pwr_lock); 3368565ace46SCharles Keepax 3369565ace46SCharles Keepax return ret; 3370565ace46SCharles Keepax } 3371565ace46SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_pointer); 3372565ace46SCharles Keepax 337383a40ce9SCharles Keepax static int wm_adsp_buffer_capture_block(struct wm_adsp_compr *compr, int target) 337483a40ce9SCharles Keepax { 337583a40ce9SCharles Keepax struct wm_adsp_compr_buf *buf = compr->buf; 337683a40ce9SCharles Keepax u8 *pack_in = (u8 *)compr->raw_buf; 337783a40ce9SCharles Keepax u8 *pack_out = (u8 *)compr->raw_buf; 337883a40ce9SCharles Keepax unsigned int adsp_addr; 337983a40ce9SCharles Keepax int mem_type, nwords, max_read; 338083a40ce9SCharles Keepax int i, j, ret; 338183a40ce9SCharles Keepax 338283a40ce9SCharles Keepax /* Calculate read parameters */ 338383a40ce9SCharles Keepax for (i = 0; i < wm_adsp_fw[buf->dsp->fw].caps->num_regions; ++i) 338483a40ce9SCharles Keepax if (buf->read_index < buf->regions[i].cumulative_size) 338583a40ce9SCharles Keepax break; 338683a40ce9SCharles Keepax 338783a40ce9SCharles Keepax if (i == wm_adsp_fw[buf->dsp->fw].caps->num_regions) 338883a40ce9SCharles Keepax return -EINVAL; 338983a40ce9SCharles Keepax 339083a40ce9SCharles Keepax mem_type = buf->regions[i].mem_type; 339183a40ce9SCharles Keepax adsp_addr = buf->regions[i].base_addr + 339283a40ce9SCharles Keepax (buf->read_index - buf->regions[i].offset); 339383a40ce9SCharles Keepax 339483a40ce9SCharles Keepax max_read = wm_adsp_compr_frag_words(compr); 339583a40ce9SCharles Keepax nwords = buf->regions[i].cumulative_size - buf->read_index; 339683a40ce9SCharles Keepax 339783a40ce9SCharles Keepax if (nwords > target) 339883a40ce9SCharles Keepax nwords = target; 339983a40ce9SCharles Keepax if (nwords > buf->avail) 340083a40ce9SCharles Keepax nwords = buf->avail; 340183a40ce9SCharles Keepax if (nwords > max_read) 340283a40ce9SCharles Keepax nwords = max_read; 340383a40ce9SCharles Keepax if (!nwords) 340483a40ce9SCharles Keepax return 0; 340583a40ce9SCharles Keepax 340683a40ce9SCharles Keepax /* Read data from DSP */ 340783a40ce9SCharles Keepax ret = wm_adsp_read_data_block(buf->dsp, mem_type, adsp_addr, 340883a40ce9SCharles Keepax nwords, compr->raw_buf); 340983a40ce9SCharles Keepax if (ret < 0) 341083a40ce9SCharles Keepax return ret; 341183a40ce9SCharles Keepax 341283a40ce9SCharles Keepax /* Remove the padding bytes from the data read from the DSP */ 341383a40ce9SCharles Keepax for (i = 0; i < nwords; i++) { 341483a40ce9SCharles Keepax for (j = 0; j < WM_ADSP_DATA_WORD_SIZE; j++) 341583a40ce9SCharles Keepax *pack_out++ = *pack_in++; 341683a40ce9SCharles Keepax 341783a40ce9SCharles Keepax pack_in += sizeof(*(compr->raw_buf)) - WM_ADSP_DATA_WORD_SIZE; 341883a40ce9SCharles Keepax } 341983a40ce9SCharles Keepax 342083a40ce9SCharles Keepax /* update read index to account for words read */ 342183a40ce9SCharles Keepax buf->read_index += nwords; 342283a40ce9SCharles Keepax if (buf->read_index == wm_adsp_buffer_size(buf)) 342383a40ce9SCharles Keepax buf->read_index = 0; 342483a40ce9SCharles Keepax 342583a40ce9SCharles Keepax ret = wm_adsp_buffer_write(buf, HOST_BUFFER_FIELD(next_read_index), 342683a40ce9SCharles Keepax buf->read_index); 342783a40ce9SCharles Keepax if (ret < 0) 342883a40ce9SCharles Keepax return ret; 342983a40ce9SCharles Keepax 343083a40ce9SCharles Keepax /* update avail to account for words read */ 343183a40ce9SCharles Keepax buf->avail -= nwords; 343283a40ce9SCharles Keepax 343383a40ce9SCharles Keepax return nwords; 343483a40ce9SCharles Keepax } 343583a40ce9SCharles Keepax 343683a40ce9SCharles Keepax static int wm_adsp_compr_read(struct wm_adsp_compr *compr, 343783a40ce9SCharles Keepax char __user *buf, size_t count) 343883a40ce9SCharles Keepax { 343983a40ce9SCharles Keepax struct wm_adsp *dsp = compr->dsp; 344083a40ce9SCharles Keepax int ntotal = 0; 344183a40ce9SCharles Keepax int nwords, nbytes; 344283a40ce9SCharles Keepax 344383a40ce9SCharles Keepax adsp_dbg(dsp, "Requested read of %zu bytes\n", count); 344483a40ce9SCharles Keepax 344528ee3d73SCharles Keepax if (!compr->buf || compr->buf->error) { 34468d280664SCharles Keepax snd_compr_stop_error(compr->stream, SNDRV_PCM_STATE_XRUN); 344783a40ce9SCharles Keepax return -EIO; 34488d280664SCharles Keepax } 344983a40ce9SCharles Keepax 345083a40ce9SCharles Keepax count /= WM_ADSP_DATA_WORD_SIZE; 345183a40ce9SCharles Keepax 345283a40ce9SCharles Keepax do { 345383a40ce9SCharles Keepax nwords = wm_adsp_buffer_capture_block(compr, count); 345483a40ce9SCharles Keepax if (nwords < 0) { 345583a40ce9SCharles Keepax adsp_err(dsp, "Failed to capture block: %d\n", nwords); 345683a40ce9SCharles Keepax return nwords; 345783a40ce9SCharles Keepax } 345883a40ce9SCharles Keepax 345983a40ce9SCharles Keepax nbytes = nwords * WM_ADSP_DATA_WORD_SIZE; 346083a40ce9SCharles Keepax 346183a40ce9SCharles Keepax adsp_dbg(dsp, "Read %d bytes\n", nbytes); 346283a40ce9SCharles Keepax 346383a40ce9SCharles Keepax if (copy_to_user(buf + ntotal, compr->raw_buf, nbytes)) { 346483a40ce9SCharles Keepax adsp_err(dsp, "Failed to copy data to user: %d, %d\n", 346583a40ce9SCharles Keepax ntotal, nbytes); 346683a40ce9SCharles Keepax return -EFAULT; 346783a40ce9SCharles Keepax } 346883a40ce9SCharles Keepax 346983a40ce9SCharles Keepax count -= nwords; 347083a40ce9SCharles Keepax ntotal += nbytes; 347183a40ce9SCharles Keepax } while (nwords > 0 && count > 0); 347283a40ce9SCharles Keepax 347383a40ce9SCharles Keepax compr->copied_total += ntotal; 347483a40ce9SCharles Keepax 347583a40ce9SCharles Keepax return ntotal; 347683a40ce9SCharles Keepax } 347783a40ce9SCharles Keepax 347883a40ce9SCharles Keepax int wm_adsp_compr_copy(struct snd_compr_stream *stream, char __user *buf, 347983a40ce9SCharles Keepax size_t count) 348083a40ce9SCharles Keepax { 348183a40ce9SCharles Keepax struct wm_adsp_compr *compr = stream->runtime->private_data; 348283a40ce9SCharles Keepax struct wm_adsp *dsp = compr->dsp; 348383a40ce9SCharles Keepax int ret; 348483a40ce9SCharles Keepax 348583a40ce9SCharles Keepax mutex_lock(&dsp->pwr_lock); 348683a40ce9SCharles Keepax 348783a40ce9SCharles Keepax if (stream->direction == SND_COMPRESS_CAPTURE) 348883a40ce9SCharles Keepax ret = wm_adsp_compr_read(compr, buf, count); 348983a40ce9SCharles Keepax else 349083a40ce9SCharles Keepax ret = -ENOTSUPP; 349183a40ce9SCharles Keepax 349283a40ce9SCharles Keepax mutex_unlock(&dsp->pwr_lock); 349383a40ce9SCharles Keepax 349483a40ce9SCharles Keepax return ret; 349583a40ce9SCharles Keepax } 349683a40ce9SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_copy); 349783a40ce9SCharles Keepax 34980a37c6efSPraveen Diwakar MODULE_LICENSE("GPL v2"); 3499