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 117e1ea1879SRichard Fitzgerald #define ADSP2V2_CLOCKING 0x2 1182d30b575SMark Brown #define ADSP2_STATUS1 0x4 1192d30b575SMark Brown #define ADSP2_WDMA_CONFIG_1 0x30 1202d30b575SMark Brown #define ADSP2_WDMA_CONFIG_2 0x31 121e1ea1879SRichard Fitzgerald #define ADSP2V2_WDMA_CONFIG_2 0x32 1222d30b575SMark Brown #define ADSP2_RDMA_CONFIG_1 0x34 1232159ad93SMark Brown 12410337b07SRichard Fitzgerald #define ADSP2_SCRATCH0 0x40 12510337b07SRichard Fitzgerald #define ADSP2_SCRATCH1 0x41 12610337b07SRichard Fitzgerald #define ADSP2_SCRATCH2 0x42 12710337b07SRichard Fitzgerald #define ADSP2_SCRATCH3 0x43 12810337b07SRichard Fitzgerald 129e1ea1879SRichard Fitzgerald #define ADSP2V2_SCRATCH0_1 0x40 130e1ea1879SRichard Fitzgerald #define ADSP2V2_SCRATCH2_3 0x42 131e1ea1879SRichard Fitzgerald 1322159ad93SMark Brown /* 1332159ad93SMark Brown * ADSP2 Control 1342159ad93SMark Brown */ 1352159ad93SMark Brown 1362159ad93SMark Brown #define ADSP2_MEM_ENA 0x0010 /* DSP1_MEM_ENA */ 1372159ad93SMark Brown #define ADSP2_MEM_ENA_MASK 0x0010 /* DSP1_MEM_ENA */ 1382159ad93SMark Brown #define ADSP2_MEM_ENA_SHIFT 4 /* DSP1_MEM_ENA */ 1392159ad93SMark Brown #define ADSP2_MEM_ENA_WIDTH 1 /* DSP1_MEM_ENA */ 1402159ad93SMark Brown #define ADSP2_SYS_ENA 0x0004 /* DSP1_SYS_ENA */ 1412159ad93SMark Brown #define ADSP2_SYS_ENA_MASK 0x0004 /* DSP1_SYS_ENA */ 1422159ad93SMark Brown #define ADSP2_SYS_ENA_SHIFT 2 /* DSP1_SYS_ENA */ 1432159ad93SMark Brown #define ADSP2_SYS_ENA_WIDTH 1 /* DSP1_SYS_ENA */ 1442159ad93SMark Brown #define ADSP2_CORE_ENA 0x0002 /* DSP1_CORE_ENA */ 1452159ad93SMark Brown #define ADSP2_CORE_ENA_MASK 0x0002 /* DSP1_CORE_ENA */ 1462159ad93SMark Brown #define ADSP2_CORE_ENA_SHIFT 1 /* DSP1_CORE_ENA */ 1472159ad93SMark Brown #define ADSP2_CORE_ENA_WIDTH 1 /* DSP1_CORE_ENA */ 1482159ad93SMark Brown #define ADSP2_START 0x0001 /* DSP1_START */ 1492159ad93SMark Brown #define ADSP2_START_MASK 0x0001 /* DSP1_START */ 1502159ad93SMark Brown #define ADSP2_START_SHIFT 0 /* DSP1_START */ 1512159ad93SMark Brown #define ADSP2_START_WIDTH 1 /* DSP1_START */ 1522159ad93SMark Brown 1532159ad93SMark Brown /* 154973838a0SMark Brown * ADSP2 clocking 155973838a0SMark Brown */ 156973838a0SMark Brown #define ADSP2_CLK_SEL_MASK 0x0007 /* CLK_SEL_ENA */ 157973838a0SMark Brown #define ADSP2_CLK_SEL_SHIFT 0 /* CLK_SEL_ENA */ 158973838a0SMark Brown #define ADSP2_CLK_SEL_WIDTH 3 /* CLK_SEL_ENA */ 159973838a0SMark Brown 160973838a0SMark Brown /* 161e1ea1879SRichard Fitzgerald * ADSP2V2 clocking 162e1ea1879SRichard Fitzgerald */ 163e1ea1879SRichard Fitzgerald #define ADSP2V2_CLK_SEL_MASK 0x70000 /* CLK_SEL_ENA */ 164e1ea1879SRichard Fitzgerald #define ADSP2V2_CLK_SEL_SHIFT 16 /* CLK_SEL_ENA */ 165e1ea1879SRichard Fitzgerald #define ADSP2V2_CLK_SEL_WIDTH 3 /* CLK_SEL_ENA */ 166e1ea1879SRichard Fitzgerald 167e1ea1879SRichard Fitzgerald #define ADSP2V2_RATE_MASK 0x7800 /* DSP_RATE */ 168e1ea1879SRichard Fitzgerald #define ADSP2V2_RATE_SHIFT 11 /* DSP_RATE */ 169e1ea1879SRichard Fitzgerald #define ADSP2V2_RATE_WIDTH 4 /* DSP_RATE */ 170e1ea1879SRichard Fitzgerald 171e1ea1879SRichard Fitzgerald /* 1722159ad93SMark Brown * ADSP2 Status 1 1732159ad93SMark Brown */ 1742159ad93SMark Brown #define ADSP2_RAM_RDY 0x0001 1752159ad93SMark Brown #define ADSP2_RAM_RDY_MASK 0x0001 1762159ad93SMark Brown #define ADSP2_RAM_RDY_SHIFT 0 1772159ad93SMark Brown #define ADSP2_RAM_RDY_WIDTH 1 1782159ad93SMark Brown 17951a2c944SMayuresh Kulkarni /* 18051a2c944SMayuresh Kulkarni * ADSP2 Lock support 18151a2c944SMayuresh Kulkarni */ 18251a2c944SMayuresh Kulkarni #define ADSP2_LOCK_CODE_0 0x5555 18351a2c944SMayuresh Kulkarni #define ADSP2_LOCK_CODE_1 0xAAAA 18451a2c944SMayuresh Kulkarni 18551a2c944SMayuresh Kulkarni #define ADSP2_WATCHDOG 0x0A 18651a2c944SMayuresh Kulkarni #define ADSP2_BUS_ERR_ADDR 0x52 18751a2c944SMayuresh Kulkarni #define ADSP2_REGION_LOCK_STATUS 0x64 18851a2c944SMayuresh Kulkarni #define ADSP2_LOCK_REGION_1_LOCK_REGION_0 0x66 18951a2c944SMayuresh Kulkarni #define ADSP2_LOCK_REGION_3_LOCK_REGION_2 0x68 19051a2c944SMayuresh Kulkarni #define ADSP2_LOCK_REGION_5_LOCK_REGION_4 0x6A 19151a2c944SMayuresh Kulkarni #define ADSP2_LOCK_REGION_7_LOCK_REGION_6 0x6C 19251a2c944SMayuresh Kulkarni #define ADSP2_LOCK_REGION_9_LOCK_REGION_8 0x6E 19351a2c944SMayuresh Kulkarni #define ADSP2_LOCK_REGION_CTRL 0x7A 19451a2c944SMayuresh Kulkarni #define ADSP2_PMEM_ERR_ADDR_XMEM_ERR_ADDR 0x7C 19551a2c944SMayuresh Kulkarni 19651a2c944SMayuresh Kulkarni #define ADSP2_REGION_LOCK_ERR_MASK 0x8000 19751a2c944SMayuresh Kulkarni #define ADSP2_SLAVE_ERR_MASK 0x4000 19851a2c944SMayuresh Kulkarni #define ADSP2_WDT_TIMEOUT_STS_MASK 0x2000 19951a2c944SMayuresh Kulkarni #define ADSP2_CTRL_ERR_PAUSE_ENA 0x0002 20051a2c944SMayuresh Kulkarni #define ADSP2_CTRL_ERR_EINT 0x0001 20151a2c944SMayuresh Kulkarni 20251a2c944SMayuresh Kulkarni #define ADSP2_BUS_ERR_ADDR_MASK 0x00FFFFFF 20351a2c944SMayuresh Kulkarni #define ADSP2_XMEM_ERR_ADDR_MASK 0x0000FFFF 20451a2c944SMayuresh Kulkarni #define ADSP2_PMEM_ERR_ADDR_MASK 0x7FFF0000 20551a2c944SMayuresh Kulkarni #define ADSP2_PMEM_ERR_ADDR_SHIFT 16 20651a2c944SMayuresh Kulkarni #define ADSP2_WDT_ENA_MASK 0xFFFFFFFD 20751a2c944SMayuresh Kulkarni 20851a2c944SMayuresh Kulkarni #define ADSP2_LOCK_REGION_SHIFT 16 20951a2c944SMayuresh Kulkarni 2109ee78757SCharles Keepax #define ADSP_MAX_STD_CTRL_SIZE 512 2119ee78757SCharles Keepax 212f4f0c4c6SRichard Fitzgerald #define WM_ADSP_ACKED_CTL_TIMEOUT_MS 100 213f4f0c4c6SRichard Fitzgerald #define WM_ADSP_ACKED_CTL_N_QUICKPOLLS 10 214a23ebba8SRichard Fitzgerald #define WM_ADSP_ACKED_CTL_MIN_VALUE 0 215a23ebba8SRichard Fitzgerald #define WM_ADSP_ACKED_CTL_MAX_VALUE 0xFFFFFF 216f4f0c4c6SRichard Fitzgerald 217f4f0c4c6SRichard Fitzgerald /* 218f4f0c4c6SRichard Fitzgerald * Event control messages 219f4f0c4c6SRichard Fitzgerald */ 220f4f0c4c6SRichard Fitzgerald #define WM_ADSP_FW_EVENT_SHUTDOWN 0x000001 221f4f0c4c6SRichard Fitzgerald 222cf17c83cSMark Brown struct wm_adsp_buf { 223cf17c83cSMark Brown struct list_head list; 224cf17c83cSMark Brown void *buf; 225cf17c83cSMark Brown }; 226cf17c83cSMark Brown 227cf17c83cSMark Brown static struct wm_adsp_buf *wm_adsp_buf_alloc(const void *src, size_t len, 228cf17c83cSMark Brown struct list_head *list) 229cf17c83cSMark Brown { 230cf17c83cSMark Brown struct wm_adsp_buf *buf = kzalloc(sizeof(*buf), GFP_KERNEL); 231cf17c83cSMark Brown 232cf17c83cSMark Brown if (buf == NULL) 233cf17c83cSMark Brown return NULL; 234cf17c83cSMark Brown 235cdcd7f72SCharles Keepax buf->buf = vmalloc(len); 236cf17c83cSMark Brown if (!buf->buf) { 2374d41c74dSRichard Fitzgerald kfree(buf); 238cf17c83cSMark Brown return NULL; 239cf17c83cSMark Brown } 240cdcd7f72SCharles Keepax memcpy(buf->buf, src, len); 241cf17c83cSMark Brown 242cf17c83cSMark Brown if (list) 243cf17c83cSMark Brown list_add_tail(&buf->list, list); 244cf17c83cSMark Brown 245cf17c83cSMark Brown return buf; 246cf17c83cSMark Brown } 247cf17c83cSMark Brown 248cf17c83cSMark Brown static void wm_adsp_buf_free(struct list_head *list) 249cf17c83cSMark Brown { 250cf17c83cSMark Brown while (!list_empty(list)) { 251cf17c83cSMark Brown struct wm_adsp_buf *buf = list_first_entry(list, 252cf17c83cSMark Brown struct wm_adsp_buf, 253cf17c83cSMark Brown list); 254cf17c83cSMark Brown list_del(&buf->list); 255cdcd7f72SCharles Keepax vfree(buf->buf); 256cf17c83cSMark Brown kfree(buf); 257cf17c83cSMark Brown } 258cf17c83cSMark Brown } 259cf17c83cSMark Brown 260dd84f925SMark Brown #define WM_ADSP_FW_MBC_VSS 0 26104d1300fSCharles Keepax #define WM_ADSP_FW_HIFI 1 26204d1300fSCharles Keepax #define WM_ADSP_FW_TX 2 26304d1300fSCharles Keepax #define WM_ADSP_FW_TX_SPK 3 26404d1300fSCharles Keepax #define WM_ADSP_FW_RX 4 26504d1300fSCharles Keepax #define WM_ADSP_FW_RX_ANC 5 26604d1300fSCharles Keepax #define WM_ADSP_FW_CTRL 6 26704d1300fSCharles Keepax #define WM_ADSP_FW_ASR 7 26804d1300fSCharles Keepax #define WM_ADSP_FW_TRACE 8 26904d1300fSCharles Keepax #define WM_ADSP_FW_SPK_PROT 9 27004d1300fSCharles Keepax #define WM_ADSP_FW_MISC 10 27104d1300fSCharles Keepax 27204d1300fSCharles Keepax #define WM_ADSP_NUM_FW 11 273dd84f925SMark Brown 2741023dbd9SMark Brown static const char *wm_adsp_fw_text[WM_ADSP_NUM_FW] = { 275dd84f925SMark Brown [WM_ADSP_FW_MBC_VSS] = "MBC/VSS", 27604d1300fSCharles Keepax [WM_ADSP_FW_HIFI] = "MasterHiFi", 277dd84f925SMark Brown [WM_ADSP_FW_TX] = "Tx", 278dd84f925SMark Brown [WM_ADSP_FW_TX_SPK] = "Tx Speaker", 27904d1300fSCharles Keepax [WM_ADSP_FW_RX] = "Rx", 280dd84f925SMark Brown [WM_ADSP_FW_RX_ANC] = "Rx ANC", 28104d1300fSCharles Keepax [WM_ADSP_FW_CTRL] = "Voice Ctrl", 28204d1300fSCharles Keepax [WM_ADSP_FW_ASR] = "ASR Assist", 28304d1300fSCharles Keepax [WM_ADSP_FW_TRACE] = "Dbg Trace", 28404d1300fSCharles Keepax [WM_ADSP_FW_SPK_PROT] = "Protection", 28504d1300fSCharles Keepax [WM_ADSP_FW_MISC] = "Misc", 2861023dbd9SMark Brown }; 2871023dbd9SMark Brown 2882cd19bdbSCharles Keepax struct wm_adsp_system_config_xm_hdr { 2892cd19bdbSCharles Keepax __be32 sys_enable; 2902cd19bdbSCharles Keepax __be32 fw_id; 2912cd19bdbSCharles Keepax __be32 fw_rev; 2922cd19bdbSCharles Keepax __be32 boot_status; 2932cd19bdbSCharles Keepax __be32 watchdog; 2942cd19bdbSCharles Keepax __be32 dma_buffer_size; 2952cd19bdbSCharles Keepax __be32 rdma[6]; 2962cd19bdbSCharles Keepax __be32 wdma[8]; 2972cd19bdbSCharles Keepax __be32 build_job_name[3]; 2982cd19bdbSCharles Keepax __be32 build_job_number; 2992cd19bdbSCharles Keepax }; 3002cd19bdbSCharles Keepax 3012cd19bdbSCharles Keepax struct wm_adsp_alg_xm_struct { 3022cd19bdbSCharles Keepax __be32 magic; 3032cd19bdbSCharles Keepax __be32 smoothing; 3042cd19bdbSCharles Keepax __be32 threshold; 3052cd19bdbSCharles Keepax __be32 host_buf_ptr; 3062cd19bdbSCharles Keepax __be32 start_seq; 3072cd19bdbSCharles Keepax __be32 high_water_mark; 3082cd19bdbSCharles Keepax __be32 low_water_mark; 3092cd19bdbSCharles Keepax __be64 smoothed_power; 3102cd19bdbSCharles Keepax }; 3112cd19bdbSCharles Keepax 3122cd19bdbSCharles Keepax struct wm_adsp_buffer { 3132cd19bdbSCharles Keepax __be32 X_buf_base; /* XM base addr of first X area */ 3142cd19bdbSCharles Keepax __be32 X_buf_size; /* Size of 1st X area in words */ 3152cd19bdbSCharles Keepax __be32 X_buf_base2; /* XM base addr of 2nd X area */ 3162cd19bdbSCharles Keepax __be32 X_buf_brk; /* Total X size in words */ 3172cd19bdbSCharles Keepax __be32 Y_buf_base; /* YM base addr of Y area */ 3182cd19bdbSCharles Keepax __be32 wrap; /* Total size X and Y in words */ 3192cd19bdbSCharles Keepax __be32 high_water_mark; /* Point at which IRQ is asserted */ 3202cd19bdbSCharles Keepax __be32 irq_count; /* bits 1-31 count IRQ assertions */ 3212cd19bdbSCharles Keepax __be32 irq_ack; /* acked IRQ count, bit 0 enables IRQ */ 3222cd19bdbSCharles Keepax __be32 next_write_index; /* word index of next write */ 3232cd19bdbSCharles Keepax __be32 next_read_index; /* word index of next read */ 3242cd19bdbSCharles Keepax __be32 error; /* error if any */ 3252cd19bdbSCharles Keepax __be32 oldest_block_index; /* word index of oldest surviving */ 3262cd19bdbSCharles Keepax __be32 requested_rewind; /* how many blocks rewind was done */ 3272cd19bdbSCharles Keepax __be32 reserved_space; /* internal */ 3282cd19bdbSCharles Keepax __be32 min_free; /* min free space since stream start */ 3292cd19bdbSCharles Keepax __be32 blocks_written[2]; /* total blocks written (64 bit) */ 3302cd19bdbSCharles Keepax __be32 words_written[2]; /* total words written (64 bit) */ 3312cd19bdbSCharles Keepax }; 3322cd19bdbSCharles Keepax 333721be3beSCharles Keepax struct wm_adsp_compr; 334721be3beSCharles Keepax 3352cd19bdbSCharles Keepax struct wm_adsp_compr_buf { 3362cd19bdbSCharles Keepax struct wm_adsp *dsp; 337721be3beSCharles Keepax struct wm_adsp_compr *compr; 3382cd19bdbSCharles Keepax 3392cd19bdbSCharles Keepax struct wm_adsp_buffer_region *regions; 3402cd19bdbSCharles Keepax u32 host_buf_ptr; 341565ace46SCharles Keepax 342565ace46SCharles Keepax u32 error; 343565ace46SCharles Keepax u32 irq_count; 344565ace46SCharles Keepax int read_index; 345565ace46SCharles Keepax int avail; 3462cd19bdbSCharles Keepax }; 3472cd19bdbSCharles Keepax 348406abc95SCharles Keepax struct wm_adsp_compr { 349406abc95SCharles Keepax struct wm_adsp *dsp; 35095fe9597SCharles Keepax struct wm_adsp_compr_buf *buf; 351406abc95SCharles Keepax 352406abc95SCharles Keepax struct snd_compr_stream *stream; 353406abc95SCharles Keepax struct snd_compressed_buffer size; 354565ace46SCharles Keepax 35583a40ce9SCharles Keepax u32 *raw_buf; 356565ace46SCharles Keepax unsigned int copied_total; 357da2b3358SCharles Keepax 358da2b3358SCharles Keepax unsigned int sample_rate; 359406abc95SCharles Keepax }; 360406abc95SCharles Keepax 361406abc95SCharles Keepax #define WM_ADSP_DATA_WORD_SIZE 3 362406abc95SCharles Keepax 363406abc95SCharles Keepax #define WM_ADSP_MIN_FRAGMENTS 1 364406abc95SCharles Keepax #define WM_ADSP_MAX_FRAGMENTS 256 365406abc95SCharles Keepax #define WM_ADSP_MIN_FRAGMENT_SIZE (64 * WM_ADSP_DATA_WORD_SIZE) 366406abc95SCharles Keepax #define WM_ADSP_MAX_FRAGMENT_SIZE (4096 * WM_ADSP_DATA_WORD_SIZE) 367406abc95SCharles Keepax 3682cd19bdbSCharles Keepax #define WM_ADSP_ALG_XM_STRUCT_MAGIC 0x49aec7 3692cd19bdbSCharles Keepax 3702cd19bdbSCharles Keepax #define HOST_BUFFER_FIELD(field) \ 3712cd19bdbSCharles Keepax (offsetof(struct wm_adsp_buffer, field) / sizeof(__be32)) 3722cd19bdbSCharles Keepax 3732cd19bdbSCharles Keepax #define ALG_XM_FIELD(field) \ 3742cd19bdbSCharles Keepax (offsetof(struct wm_adsp_alg_xm_struct, field) / sizeof(__be32)) 3752cd19bdbSCharles Keepax 3762cd19bdbSCharles Keepax static int wm_adsp_buffer_init(struct wm_adsp *dsp); 3772cd19bdbSCharles Keepax static int wm_adsp_buffer_free(struct wm_adsp *dsp); 3782cd19bdbSCharles Keepax 3792cd19bdbSCharles Keepax struct wm_adsp_buffer_region { 3802cd19bdbSCharles Keepax unsigned int offset; 3812cd19bdbSCharles Keepax unsigned int cumulative_size; 3822cd19bdbSCharles Keepax unsigned int mem_type; 3832cd19bdbSCharles Keepax unsigned int base_addr; 3842cd19bdbSCharles Keepax }; 3852cd19bdbSCharles Keepax 3862cd19bdbSCharles Keepax struct wm_adsp_buffer_region_def { 3872cd19bdbSCharles Keepax unsigned int mem_type; 3882cd19bdbSCharles Keepax unsigned int base_offset; 3892cd19bdbSCharles Keepax unsigned int size_offset; 3902cd19bdbSCharles Keepax }; 3912cd19bdbSCharles Keepax 3923a9686c4SCharles Keepax static const struct wm_adsp_buffer_region_def default_regions[] = { 3932cd19bdbSCharles Keepax { 3942cd19bdbSCharles Keepax .mem_type = WMFW_ADSP2_XM, 3952cd19bdbSCharles Keepax .base_offset = HOST_BUFFER_FIELD(X_buf_base), 3962cd19bdbSCharles Keepax .size_offset = HOST_BUFFER_FIELD(X_buf_size), 3972cd19bdbSCharles Keepax }, 3982cd19bdbSCharles Keepax { 3992cd19bdbSCharles Keepax .mem_type = WMFW_ADSP2_XM, 4002cd19bdbSCharles Keepax .base_offset = HOST_BUFFER_FIELD(X_buf_base2), 4012cd19bdbSCharles Keepax .size_offset = HOST_BUFFER_FIELD(X_buf_brk), 4022cd19bdbSCharles Keepax }, 4032cd19bdbSCharles Keepax { 4042cd19bdbSCharles Keepax .mem_type = WMFW_ADSP2_YM, 4052cd19bdbSCharles Keepax .base_offset = HOST_BUFFER_FIELD(Y_buf_base), 4062cd19bdbSCharles Keepax .size_offset = HOST_BUFFER_FIELD(wrap), 4072cd19bdbSCharles Keepax }, 4082cd19bdbSCharles Keepax }; 4092cd19bdbSCharles Keepax 410406abc95SCharles Keepax struct wm_adsp_fw_caps { 411406abc95SCharles Keepax u32 id; 412406abc95SCharles Keepax struct snd_codec_desc desc; 4132cd19bdbSCharles Keepax int num_regions; 4143a9686c4SCharles Keepax const struct wm_adsp_buffer_region_def *region_defs; 415406abc95SCharles Keepax }; 416406abc95SCharles Keepax 417e6d00f34SCharles Keepax static const struct wm_adsp_fw_caps ctrl_caps[] = { 418406abc95SCharles Keepax { 419406abc95SCharles Keepax .id = SND_AUDIOCODEC_BESPOKE, 420406abc95SCharles Keepax .desc = { 421406abc95SCharles Keepax .max_ch = 1, 422406abc95SCharles Keepax .sample_rates = { 16000 }, 423406abc95SCharles Keepax .num_sample_rates = 1, 424406abc95SCharles Keepax .formats = SNDRV_PCM_FMTBIT_S16_LE, 425406abc95SCharles Keepax }, 426e6d00f34SCharles Keepax .num_regions = ARRAY_SIZE(default_regions), 427e6d00f34SCharles Keepax .region_defs = default_regions, 428406abc95SCharles Keepax }, 429406abc95SCharles Keepax }; 430406abc95SCharles Keepax 4317ce4283cSCharles Keepax static const struct wm_adsp_fw_caps trace_caps[] = { 4327ce4283cSCharles Keepax { 4337ce4283cSCharles Keepax .id = SND_AUDIOCODEC_BESPOKE, 4347ce4283cSCharles Keepax .desc = { 4357ce4283cSCharles Keepax .max_ch = 8, 4367ce4283cSCharles Keepax .sample_rates = { 4377ce4283cSCharles Keepax 4000, 8000, 11025, 12000, 16000, 22050, 4387ce4283cSCharles Keepax 24000, 32000, 44100, 48000, 64000, 88200, 4397ce4283cSCharles Keepax 96000, 176400, 192000 4407ce4283cSCharles Keepax }, 4417ce4283cSCharles Keepax .num_sample_rates = 15, 4427ce4283cSCharles Keepax .formats = SNDRV_PCM_FMTBIT_S16_LE, 4437ce4283cSCharles Keepax }, 4447ce4283cSCharles Keepax .num_regions = ARRAY_SIZE(default_regions), 4457ce4283cSCharles Keepax .region_defs = default_regions, 446406abc95SCharles Keepax }, 447406abc95SCharles Keepax }; 448406abc95SCharles Keepax 449406abc95SCharles Keepax static const struct { 4501023dbd9SMark Brown const char *file; 451406abc95SCharles Keepax int compr_direction; 452406abc95SCharles Keepax int num_caps; 453406abc95SCharles Keepax const struct wm_adsp_fw_caps *caps; 45420b7f7c5SCharles Keepax bool voice_trigger; 4551023dbd9SMark Brown } wm_adsp_fw[WM_ADSP_NUM_FW] = { 456dd84f925SMark Brown [WM_ADSP_FW_MBC_VSS] = { .file = "mbc-vss" }, 45704d1300fSCharles Keepax [WM_ADSP_FW_HIFI] = { .file = "hifi" }, 458dd84f925SMark Brown [WM_ADSP_FW_TX] = { .file = "tx" }, 459dd84f925SMark Brown [WM_ADSP_FW_TX_SPK] = { .file = "tx-spk" }, 46004d1300fSCharles Keepax [WM_ADSP_FW_RX] = { .file = "rx" }, 461dd84f925SMark Brown [WM_ADSP_FW_RX_ANC] = { .file = "rx-anc" }, 462406abc95SCharles Keepax [WM_ADSP_FW_CTRL] = { 463406abc95SCharles Keepax .file = "ctrl", 464406abc95SCharles Keepax .compr_direction = SND_COMPRESS_CAPTURE, 465e6d00f34SCharles Keepax .num_caps = ARRAY_SIZE(ctrl_caps), 466e6d00f34SCharles Keepax .caps = ctrl_caps, 46720b7f7c5SCharles Keepax .voice_trigger = true, 468406abc95SCharles Keepax }, 46904d1300fSCharles Keepax [WM_ADSP_FW_ASR] = { .file = "asr" }, 4707ce4283cSCharles Keepax [WM_ADSP_FW_TRACE] = { 4717ce4283cSCharles Keepax .file = "trace", 4727ce4283cSCharles Keepax .compr_direction = SND_COMPRESS_CAPTURE, 4737ce4283cSCharles Keepax .num_caps = ARRAY_SIZE(trace_caps), 4747ce4283cSCharles Keepax .caps = trace_caps, 4757ce4283cSCharles Keepax }, 47604d1300fSCharles Keepax [WM_ADSP_FW_SPK_PROT] = { .file = "spk-prot" }, 47704d1300fSCharles Keepax [WM_ADSP_FW_MISC] = { .file = "misc" }, 4781023dbd9SMark Brown }; 4791023dbd9SMark Brown 4806ab2b7b4SDimitris Papastamos struct wm_coeff_ctl_ops { 4816ab2b7b4SDimitris Papastamos int (*xget)(struct snd_kcontrol *kcontrol, 4826ab2b7b4SDimitris Papastamos struct snd_ctl_elem_value *ucontrol); 4836ab2b7b4SDimitris Papastamos int (*xput)(struct snd_kcontrol *kcontrol, 4846ab2b7b4SDimitris Papastamos struct snd_ctl_elem_value *ucontrol); 4856ab2b7b4SDimitris Papastamos }; 4866ab2b7b4SDimitris Papastamos 4876ab2b7b4SDimitris Papastamos struct wm_coeff_ctl { 4886ab2b7b4SDimitris Papastamos const char *name; 4892323736dSCharles Keepax const char *fw_name; 4903809f001SCharles Keepax struct wm_adsp_alg_region alg_region; 4916ab2b7b4SDimitris Papastamos struct wm_coeff_ctl_ops ops; 4923809f001SCharles Keepax struct wm_adsp *dsp; 4936ab2b7b4SDimitris Papastamos unsigned int enabled:1; 4946ab2b7b4SDimitris Papastamos struct list_head list; 4956ab2b7b4SDimitris Papastamos void *cache; 4962323736dSCharles Keepax unsigned int offset; 4976ab2b7b4SDimitris Papastamos size_t len; 4980c2e3f34SDimitris Papastamos unsigned int set:1; 4999ee78757SCharles Keepax struct soc_bytes_ext bytes_ext; 50026c22a19SCharles Keepax unsigned int flags; 5018eb084d0SStuart Henderson unsigned int type; 5026ab2b7b4SDimitris Papastamos }; 5036ab2b7b4SDimitris Papastamos 5049ce5e6e6SRichard Fitzgerald static const char *wm_adsp_mem_region_name(unsigned int type) 5059ce5e6e6SRichard Fitzgerald { 5069ce5e6e6SRichard Fitzgerald switch (type) { 5079ce5e6e6SRichard Fitzgerald case WMFW_ADSP1_PM: 5089ce5e6e6SRichard Fitzgerald return "PM"; 5099ce5e6e6SRichard Fitzgerald case WMFW_ADSP1_DM: 5109ce5e6e6SRichard Fitzgerald return "DM"; 5119ce5e6e6SRichard Fitzgerald case WMFW_ADSP2_XM: 5129ce5e6e6SRichard Fitzgerald return "XM"; 5139ce5e6e6SRichard Fitzgerald case WMFW_ADSP2_YM: 5149ce5e6e6SRichard Fitzgerald return "YM"; 5159ce5e6e6SRichard Fitzgerald case WMFW_ADSP1_ZM: 5169ce5e6e6SRichard Fitzgerald return "ZM"; 5179ce5e6e6SRichard Fitzgerald default: 5189ce5e6e6SRichard Fitzgerald return NULL; 5199ce5e6e6SRichard Fitzgerald } 5209ce5e6e6SRichard Fitzgerald } 5219ce5e6e6SRichard Fitzgerald 522f9f55e31SRichard Fitzgerald #ifdef CONFIG_DEBUG_FS 523f9f55e31SRichard Fitzgerald static void wm_adsp_debugfs_save_wmfwname(struct wm_adsp *dsp, const char *s) 524f9f55e31SRichard Fitzgerald { 525f9f55e31SRichard Fitzgerald char *tmp = kasprintf(GFP_KERNEL, "%s\n", s); 526f9f55e31SRichard Fitzgerald 527f9f55e31SRichard Fitzgerald kfree(dsp->wmfw_file_name); 528f9f55e31SRichard Fitzgerald dsp->wmfw_file_name = tmp; 529f9f55e31SRichard Fitzgerald } 530f9f55e31SRichard Fitzgerald 531f9f55e31SRichard Fitzgerald static void wm_adsp_debugfs_save_binname(struct wm_adsp *dsp, const char *s) 532f9f55e31SRichard Fitzgerald { 533f9f55e31SRichard Fitzgerald char *tmp = kasprintf(GFP_KERNEL, "%s\n", s); 534f9f55e31SRichard Fitzgerald 535f9f55e31SRichard Fitzgerald kfree(dsp->bin_file_name); 536f9f55e31SRichard Fitzgerald dsp->bin_file_name = tmp; 537f9f55e31SRichard Fitzgerald } 538f9f55e31SRichard Fitzgerald 539f9f55e31SRichard Fitzgerald static void wm_adsp_debugfs_clear(struct wm_adsp *dsp) 540f9f55e31SRichard Fitzgerald { 541f9f55e31SRichard Fitzgerald kfree(dsp->wmfw_file_name); 542f9f55e31SRichard Fitzgerald kfree(dsp->bin_file_name); 543f9f55e31SRichard Fitzgerald dsp->wmfw_file_name = NULL; 544f9f55e31SRichard Fitzgerald dsp->bin_file_name = NULL; 545f9f55e31SRichard Fitzgerald } 546f9f55e31SRichard Fitzgerald 547f9f55e31SRichard Fitzgerald static ssize_t wm_adsp_debugfs_wmfw_read(struct file *file, 548f9f55e31SRichard Fitzgerald char __user *user_buf, 549f9f55e31SRichard Fitzgerald size_t count, loff_t *ppos) 550f9f55e31SRichard Fitzgerald { 551f9f55e31SRichard Fitzgerald struct wm_adsp *dsp = file->private_data; 552f9f55e31SRichard Fitzgerald ssize_t ret; 553f9f55e31SRichard Fitzgerald 554078e7183SCharles Keepax mutex_lock(&dsp->pwr_lock); 555f9f55e31SRichard Fitzgerald 55628823ebaSCharles Keepax if (!dsp->wmfw_file_name || !dsp->booted) 557f9f55e31SRichard Fitzgerald ret = 0; 558f9f55e31SRichard Fitzgerald else 559f9f55e31SRichard Fitzgerald ret = simple_read_from_buffer(user_buf, count, ppos, 560f9f55e31SRichard Fitzgerald dsp->wmfw_file_name, 561f9f55e31SRichard Fitzgerald strlen(dsp->wmfw_file_name)); 562f9f55e31SRichard Fitzgerald 563078e7183SCharles Keepax mutex_unlock(&dsp->pwr_lock); 564f9f55e31SRichard Fitzgerald return ret; 565f9f55e31SRichard Fitzgerald } 566f9f55e31SRichard Fitzgerald 567f9f55e31SRichard Fitzgerald static ssize_t wm_adsp_debugfs_bin_read(struct file *file, 568f9f55e31SRichard Fitzgerald char __user *user_buf, 569f9f55e31SRichard Fitzgerald size_t count, loff_t *ppos) 570f9f55e31SRichard Fitzgerald { 571f9f55e31SRichard Fitzgerald struct wm_adsp *dsp = file->private_data; 572f9f55e31SRichard Fitzgerald ssize_t ret; 573f9f55e31SRichard Fitzgerald 574078e7183SCharles Keepax mutex_lock(&dsp->pwr_lock); 575f9f55e31SRichard Fitzgerald 57628823ebaSCharles Keepax if (!dsp->bin_file_name || !dsp->booted) 577f9f55e31SRichard Fitzgerald ret = 0; 578f9f55e31SRichard Fitzgerald else 579f9f55e31SRichard Fitzgerald ret = simple_read_from_buffer(user_buf, count, ppos, 580f9f55e31SRichard Fitzgerald dsp->bin_file_name, 581f9f55e31SRichard Fitzgerald strlen(dsp->bin_file_name)); 582f9f55e31SRichard Fitzgerald 583078e7183SCharles Keepax mutex_unlock(&dsp->pwr_lock); 584f9f55e31SRichard Fitzgerald return ret; 585f9f55e31SRichard Fitzgerald } 586f9f55e31SRichard Fitzgerald 587f9f55e31SRichard Fitzgerald static const struct { 588f9f55e31SRichard Fitzgerald const char *name; 589f9f55e31SRichard Fitzgerald const struct file_operations fops; 590f9f55e31SRichard Fitzgerald } wm_adsp_debugfs_fops[] = { 591f9f55e31SRichard Fitzgerald { 592f9f55e31SRichard Fitzgerald .name = "wmfw_file_name", 593f9f55e31SRichard Fitzgerald .fops = { 594f9f55e31SRichard Fitzgerald .open = simple_open, 595f9f55e31SRichard Fitzgerald .read = wm_adsp_debugfs_wmfw_read, 596f9f55e31SRichard Fitzgerald }, 597f9f55e31SRichard Fitzgerald }, 598f9f55e31SRichard Fitzgerald { 599f9f55e31SRichard Fitzgerald .name = "bin_file_name", 600f9f55e31SRichard Fitzgerald .fops = { 601f9f55e31SRichard Fitzgerald .open = simple_open, 602f9f55e31SRichard Fitzgerald .read = wm_adsp_debugfs_bin_read, 603f9f55e31SRichard Fitzgerald }, 604f9f55e31SRichard Fitzgerald }, 605f9f55e31SRichard Fitzgerald }; 606f9f55e31SRichard Fitzgerald 607f9f55e31SRichard Fitzgerald static void wm_adsp2_init_debugfs(struct wm_adsp *dsp, 608f9f55e31SRichard Fitzgerald struct snd_soc_codec *codec) 609f9f55e31SRichard Fitzgerald { 610f9f55e31SRichard Fitzgerald struct dentry *root = NULL; 611f9f55e31SRichard Fitzgerald char *root_name; 612f9f55e31SRichard Fitzgerald int i; 613f9f55e31SRichard Fitzgerald 614f9f55e31SRichard Fitzgerald if (!codec->component.debugfs_root) { 615f9f55e31SRichard Fitzgerald adsp_err(dsp, "No codec debugfs root\n"); 616f9f55e31SRichard Fitzgerald goto err; 617f9f55e31SRichard Fitzgerald } 618f9f55e31SRichard Fitzgerald 619f9f55e31SRichard Fitzgerald root_name = kmalloc(PAGE_SIZE, GFP_KERNEL); 620f9f55e31SRichard Fitzgerald if (!root_name) 621f9f55e31SRichard Fitzgerald goto err; 622f9f55e31SRichard Fitzgerald 623f9f55e31SRichard Fitzgerald snprintf(root_name, PAGE_SIZE, "dsp%d", dsp->num); 624f9f55e31SRichard Fitzgerald root = debugfs_create_dir(root_name, codec->component.debugfs_root); 625f9f55e31SRichard Fitzgerald kfree(root_name); 626f9f55e31SRichard Fitzgerald 627f9f55e31SRichard Fitzgerald if (!root) 628f9f55e31SRichard Fitzgerald goto err; 629f9f55e31SRichard Fitzgerald 63028823ebaSCharles Keepax if (!debugfs_create_bool("booted", S_IRUGO, root, &dsp->booted)) 63128823ebaSCharles Keepax goto err; 63228823ebaSCharles Keepax 633f9f55e31SRichard Fitzgerald if (!debugfs_create_bool("running", S_IRUGO, root, &dsp->running)) 634f9f55e31SRichard Fitzgerald goto err; 635f9f55e31SRichard Fitzgerald 636f9f55e31SRichard Fitzgerald if (!debugfs_create_x32("fw_id", S_IRUGO, root, &dsp->fw_id)) 637f9f55e31SRichard Fitzgerald goto err; 638f9f55e31SRichard Fitzgerald 639f9f55e31SRichard Fitzgerald if (!debugfs_create_x32("fw_version", S_IRUGO, root, 640f9f55e31SRichard Fitzgerald &dsp->fw_id_version)) 641f9f55e31SRichard Fitzgerald goto err; 642f9f55e31SRichard Fitzgerald 643f9f55e31SRichard Fitzgerald for (i = 0; i < ARRAY_SIZE(wm_adsp_debugfs_fops); ++i) { 644f9f55e31SRichard Fitzgerald if (!debugfs_create_file(wm_adsp_debugfs_fops[i].name, 645f9f55e31SRichard Fitzgerald S_IRUGO, root, dsp, 646f9f55e31SRichard Fitzgerald &wm_adsp_debugfs_fops[i].fops)) 647f9f55e31SRichard Fitzgerald goto err; 648f9f55e31SRichard Fitzgerald } 649f9f55e31SRichard Fitzgerald 650f9f55e31SRichard Fitzgerald dsp->debugfs_root = root; 651f9f55e31SRichard Fitzgerald return; 652f9f55e31SRichard Fitzgerald 653f9f55e31SRichard Fitzgerald err: 654f9f55e31SRichard Fitzgerald debugfs_remove_recursive(root); 655f9f55e31SRichard Fitzgerald adsp_err(dsp, "Failed to create debugfs\n"); 656f9f55e31SRichard Fitzgerald } 657f9f55e31SRichard Fitzgerald 658f9f55e31SRichard Fitzgerald static void wm_adsp2_cleanup_debugfs(struct wm_adsp *dsp) 659f9f55e31SRichard Fitzgerald { 660f9f55e31SRichard Fitzgerald wm_adsp_debugfs_clear(dsp); 661f9f55e31SRichard Fitzgerald debugfs_remove_recursive(dsp->debugfs_root); 662f9f55e31SRichard Fitzgerald } 663f9f55e31SRichard Fitzgerald #else 664f9f55e31SRichard Fitzgerald static inline void wm_adsp2_init_debugfs(struct wm_adsp *dsp, 665f9f55e31SRichard Fitzgerald struct snd_soc_codec *codec) 666f9f55e31SRichard Fitzgerald { 667f9f55e31SRichard Fitzgerald } 668f9f55e31SRichard Fitzgerald 669f9f55e31SRichard Fitzgerald static inline void wm_adsp2_cleanup_debugfs(struct wm_adsp *dsp) 670f9f55e31SRichard Fitzgerald { 671f9f55e31SRichard Fitzgerald } 672f9f55e31SRichard Fitzgerald 673f9f55e31SRichard Fitzgerald static inline void wm_adsp_debugfs_save_wmfwname(struct wm_adsp *dsp, 674f9f55e31SRichard Fitzgerald const char *s) 675f9f55e31SRichard Fitzgerald { 676f9f55e31SRichard Fitzgerald } 677f9f55e31SRichard Fitzgerald 678f9f55e31SRichard Fitzgerald static inline void wm_adsp_debugfs_save_binname(struct wm_adsp *dsp, 679f9f55e31SRichard Fitzgerald const char *s) 680f9f55e31SRichard Fitzgerald { 681f9f55e31SRichard Fitzgerald } 682f9f55e31SRichard Fitzgerald 683f9f55e31SRichard Fitzgerald static inline void wm_adsp_debugfs_clear(struct wm_adsp *dsp) 684f9f55e31SRichard Fitzgerald { 685f9f55e31SRichard Fitzgerald } 686f9f55e31SRichard Fitzgerald #endif 687f9f55e31SRichard Fitzgerald 6881023dbd9SMark Brown static int wm_adsp_fw_get(struct snd_kcontrol *kcontrol, 6891023dbd9SMark Brown struct snd_ctl_elem_value *ucontrol) 6901023dbd9SMark Brown { 691ea53bf77SLars-Peter Clausen struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); 6921023dbd9SMark Brown struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; 6933809f001SCharles Keepax struct wm_adsp *dsp = snd_soc_codec_get_drvdata(codec); 6941023dbd9SMark Brown 69515c66570STakashi Iwai ucontrol->value.enumerated.item[0] = dsp[e->shift_l].fw; 6961023dbd9SMark Brown 6971023dbd9SMark Brown return 0; 6981023dbd9SMark Brown } 6991023dbd9SMark Brown 7001023dbd9SMark Brown static int wm_adsp_fw_put(struct snd_kcontrol *kcontrol, 7011023dbd9SMark Brown struct snd_ctl_elem_value *ucontrol) 7021023dbd9SMark Brown { 703ea53bf77SLars-Peter Clausen struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); 7041023dbd9SMark Brown struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; 7053809f001SCharles Keepax struct wm_adsp *dsp = snd_soc_codec_get_drvdata(codec); 706d27c5e15SCharles Keepax int ret = 0; 7071023dbd9SMark Brown 70815c66570STakashi Iwai if (ucontrol->value.enumerated.item[0] == dsp[e->shift_l].fw) 7091023dbd9SMark Brown return 0; 7101023dbd9SMark Brown 71115c66570STakashi Iwai if (ucontrol->value.enumerated.item[0] >= WM_ADSP_NUM_FW) 7121023dbd9SMark Brown return -EINVAL; 7131023dbd9SMark Brown 714d27c5e15SCharles Keepax mutex_lock(&dsp[e->shift_l].pwr_lock); 7151023dbd9SMark Brown 71628823ebaSCharles Keepax if (dsp[e->shift_l].booted || dsp[e->shift_l].compr) 717d27c5e15SCharles Keepax ret = -EBUSY; 718d27c5e15SCharles Keepax else 71915c66570STakashi Iwai dsp[e->shift_l].fw = ucontrol->value.enumerated.item[0]; 7201023dbd9SMark Brown 721d27c5e15SCharles Keepax mutex_unlock(&dsp[e->shift_l].pwr_lock); 722d27c5e15SCharles Keepax 723d27c5e15SCharles Keepax return ret; 7241023dbd9SMark Brown } 7251023dbd9SMark Brown 7261023dbd9SMark Brown static const struct soc_enum wm_adsp_fw_enum[] = { 7271023dbd9SMark Brown SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 7281023dbd9SMark Brown SOC_ENUM_SINGLE(0, 1, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 7291023dbd9SMark Brown SOC_ENUM_SINGLE(0, 2, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 7301023dbd9SMark Brown SOC_ENUM_SINGLE(0, 3, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 731e1ea1879SRichard Fitzgerald SOC_ENUM_SINGLE(0, 4, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 732e1ea1879SRichard Fitzgerald SOC_ENUM_SINGLE(0, 5, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 733e1ea1879SRichard Fitzgerald SOC_ENUM_SINGLE(0, 6, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 7341023dbd9SMark Brown }; 7351023dbd9SMark Brown 736336d0442SRichard Fitzgerald const struct snd_kcontrol_new wm_adsp_fw_controls[] = { 7371023dbd9SMark Brown SOC_ENUM_EXT("DSP1 Firmware", wm_adsp_fw_enum[0], 7381023dbd9SMark Brown wm_adsp_fw_get, wm_adsp_fw_put), 7391023dbd9SMark Brown SOC_ENUM_EXT("DSP2 Firmware", wm_adsp_fw_enum[1], 7401023dbd9SMark Brown wm_adsp_fw_get, wm_adsp_fw_put), 7411023dbd9SMark Brown SOC_ENUM_EXT("DSP3 Firmware", wm_adsp_fw_enum[2], 7421023dbd9SMark Brown wm_adsp_fw_get, wm_adsp_fw_put), 7431023dbd9SMark Brown SOC_ENUM_EXT("DSP4 Firmware", wm_adsp_fw_enum[3], 7441023dbd9SMark Brown wm_adsp_fw_get, wm_adsp_fw_put), 745e1ea1879SRichard Fitzgerald SOC_ENUM_EXT("DSP5 Firmware", wm_adsp_fw_enum[4], 746e1ea1879SRichard Fitzgerald wm_adsp_fw_get, wm_adsp_fw_put), 747e1ea1879SRichard Fitzgerald SOC_ENUM_EXT("DSP6 Firmware", wm_adsp_fw_enum[5], 748e1ea1879SRichard Fitzgerald wm_adsp_fw_get, wm_adsp_fw_put), 749e1ea1879SRichard Fitzgerald SOC_ENUM_EXT("DSP7 Firmware", wm_adsp_fw_enum[6], 750e1ea1879SRichard Fitzgerald wm_adsp_fw_get, wm_adsp_fw_put), 7511023dbd9SMark Brown }; 752336d0442SRichard Fitzgerald EXPORT_SYMBOL_GPL(wm_adsp_fw_controls); 7532159ad93SMark Brown 7542159ad93SMark Brown static struct wm_adsp_region const *wm_adsp_find_region(struct wm_adsp *dsp, 7552159ad93SMark Brown int type) 7562159ad93SMark Brown { 7572159ad93SMark Brown int i; 7582159ad93SMark Brown 7592159ad93SMark Brown for (i = 0; i < dsp->num_mems; i++) 7602159ad93SMark Brown if (dsp->mem[i].type == type) 7612159ad93SMark Brown return &dsp->mem[i]; 7622159ad93SMark Brown 7632159ad93SMark Brown return NULL; 7642159ad93SMark Brown } 7652159ad93SMark Brown 7663809f001SCharles Keepax static unsigned int wm_adsp_region_to_reg(struct wm_adsp_region const *mem, 76745b9ee72SMark Brown unsigned int offset) 76845b9ee72SMark Brown { 7693809f001SCharles Keepax if (WARN_ON(!mem)) 7706c452bdaSTakashi Iwai return offset; 7713809f001SCharles Keepax switch (mem->type) { 77245b9ee72SMark Brown case WMFW_ADSP1_PM: 7733809f001SCharles Keepax return mem->base + (offset * 3); 77445b9ee72SMark Brown case WMFW_ADSP1_DM: 7753809f001SCharles Keepax return mem->base + (offset * 2); 77645b9ee72SMark Brown case WMFW_ADSP2_XM: 7773809f001SCharles Keepax return mem->base + (offset * 2); 77845b9ee72SMark Brown case WMFW_ADSP2_YM: 7793809f001SCharles Keepax return mem->base + (offset * 2); 78045b9ee72SMark Brown case WMFW_ADSP1_ZM: 7813809f001SCharles Keepax return mem->base + (offset * 2); 78245b9ee72SMark Brown default: 7836c452bdaSTakashi Iwai WARN(1, "Unknown memory region type"); 78445b9ee72SMark Brown return offset; 78545b9ee72SMark Brown } 78645b9ee72SMark Brown } 78745b9ee72SMark Brown 78810337b07SRichard Fitzgerald static void wm_adsp2_show_fw_status(struct wm_adsp *dsp) 78910337b07SRichard Fitzgerald { 79010337b07SRichard Fitzgerald u16 scratch[4]; 79110337b07SRichard Fitzgerald int ret; 79210337b07SRichard Fitzgerald 79310337b07SRichard Fitzgerald ret = regmap_raw_read(dsp->regmap, dsp->base + ADSP2_SCRATCH0, 79410337b07SRichard Fitzgerald scratch, sizeof(scratch)); 79510337b07SRichard Fitzgerald if (ret) { 79610337b07SRichard Fitzgerald adsp_err(dsp, "Failed to read SCRATCH regs: %d\n", ret); 79710337b07SRichard Fitzgerald return; 79810337b07SRichard Fitzgerald } 79910337b07SRichard Fitzgerald 80010337b07SRichard Fitzgerald adsp_dbg(dsp, "FW SCRATCH 0:0x%x 1:0x%x 2:0x%x 3:0x%x\n", 80110337b07SRichard Fitzgerald be16_to_cpu(scratch[0]), 80210337b07SRichard Fitzgerald be16_to_cpu(scratch[1]), 80310337b07SRichard Fitzgerald be16_to_cpu(scratch[2]), 80410337b07SRichard Fitzgerald be16_to_cpu(scratch[3])); 80510337b07SRichard Fitzgerald } 80610337b07SRichard Fitzgerald 807e1ea1879SRichard Fitzgerald static void wm_adsp2v2_show_fw_status(struct wm_adsp *dsp) 808e1ea1879SRichard Fitzgerald { 809e1ea1879SRichard Fitzgerald u32 scratch[2]; 810e1ea1879SRichard Fitzgerald int ret; 811e1ea1879SRichard Fitzgerald 812e1ea1879SRichard Fitzgerald ret = regmap_raw_read(dsp->regmap, dsp->base + ADSP2V2_SCRATCH0_1, 813e1ea1879SRichard Fitzgerald scratch, sizeof(scratch)); 814e1ea1879SRichard Fitzgerald 815e1ea1879SRichard Fitzgerald if (ret) { 816e1ea1879SRichard Fitzgerald adsp_err(dsp, "Failed to read SCRATCH regs: %d\n", ret); 817e1ea1879SRichard Fitzgerald return; 818e1ea1879SRichard Fitzgerald } 819e1ea1879SRichard Fitzgerald 820e1ea1879SRichard Fitzgerald scratch[0] = be32_to_cpu(scratch[0]); 821e1ea1879SRichard Fitzgerald scratch[1] = be32_to_cpu(scratch[1]); 822e1ea1879SRichard Fitzgerald 823e1ea1879SRichard Fitzgerald adsp_dbg(dsp, "FW SCRATCH 0:0x%x 1:0x%x 2:0x%x 3:0x%x\n", 824e1ea1879SRichard Fitzgerald scratch[0] & 0xFFFF, 825e1ea1879SRichard Fitzgerald scratch[0] >> 16, 826e1ea1879SRichard Fitzgerald scratch[1] & 0xFFFF, 827e1ea1879SRichard Fitzgerald scratch[1] >> 16); 828e1ea1879SRichard Fitzgerald } 829e1ea1879SRichard Fitzgerald 8309ee78757SCharles Keepax static inline struct wm_coeff_ctl *bytes_ext_to_ctl(struct soc_bytes_ext *ext) 8319ee78757SCharles Keepax { 8329ee78757SCharles Keepax return container_of(ext, struct wm_coeff_ctl, bytes_ext); 8339ee78757SCharles Keepax } 8349ee78757SCharles Keepax 835b396ebcaSRichard Fitzgerald static int wm_coeff_base_reg(struct wm_coeff_ctl *ctl, unsigned int *reg) 836b396ebcaSRichard Fitzgerald { 837b396ebcaSRichard Fitzgerald const struct wm_adsp_alg_region *alg_region = &ctl->alg_region; 838b396ebcaSRichard Fitzgerald struct wm_adsp *dsp = ctl->dsp; 839b396ebcaSRichard Fitzgerald const struct wm_adsp_region *mem; 840b396ebcaSRichard Fitzgerald 841b396ebcaSRichard Fitzgerald mem = wm_adsp_find_region(dsp, alg_region->type); 842b396ebcaSRichard Fitzgerald if (!mem) { 843b396ebcaSRichard Fitzgerald adsp_err(dsp, "No base for region %x\n", 844b396ebcaSRichard Fitzgerald alg_region->type); 845b396ebcaSRichard Fitzgerald return -EINVAL; 846b396ebcaSRichard Fitzgerald } 847b396ebcaSRichard Fitzgerald 848b396ebcaSRichard Fitzgerald *reg = wm_adsp_region_to_reg(mem, ctl->alg_region.base + ctl->offset); 849b396ebcaSRichard Fitzgerald 850b396ebcaSRichard Fitzgerald return 0; 851b396ebcaSRichard Fitzgerald } 852b396ebcaSRichard Fitzgerald 8537585a5b0SCharles Keepax static int wm_coeff_info(struct snd_kcontrol *kctl, 8546ab2b7b4SDimitris Papastamos struct snd_ctl_elem_info *uinfo) 8556ab2b7b4SDimitris Papastamos { 8569ee78757SCharles Keepax struct soc_bytes_ext *bytes_ext = 8579ee78757SCharles Keepax (struct soc_bytes_ext *)kctl->private_value; 8589ee78757SCharles Keepax struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); 8596ab2b7b4SDimitris Papastamos 860a23ebba8SRichard Fitzgerald switch (ctl->type) { 861a23ebba8SRichard Fitzgerald case WMFW_CTL_TYPE_ACKED: 862a23ebba8SRichard Fitzgerald uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 863a23ebba8SRichard Fitzgerald uinfo->value.integer.min = WM_ADSP_ACKED_CTL_MIN_VALUE; 864a23ebba8SRichard Fitzgerald uinfo->value.integer.max = WM_ADSP_ACKED_CTL_MAX_VALUE; 865a23ebba8SRichard Fitzgerald uinfo->value.integer.step = 1; 866a23ebba8SRichard Fitzgerald uinfo->count = 1; 867a23ebba8SRichard Fitzgerald break; 868a23ebba8SRichard Fitzgerald default: 8696ab2b7b4SDimitris Papastamos uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; 8706ab2b7b4SDimitris Papastamos uinfo->count = ctl->len; 871a23ebba8SRichard Fitzgerald break; 872a23ebba8SRichard Fitzgerald } 873a23ebba8SRichard Fitzgerald 8746ab2b7b4SDimitris Papastamos return 0; 8756ab2b7b4SDimitris Papastamos } 8766ab2b7b4SDimitris Papastamos 877f4f0c4c6SRichard Fitzgerald static int wm_coeff_write_acked_control(struct wm_coeff_ctl *ctl, 878f4f0c4c6SRichard Fitzgerald unsigned int event_id) 879f4f0c4c6SRichard Fitzgerald { 880f4f0c4c6SRichard Fitzgerald struct wm_adsp *dsp = ctl->dsp; 881f4f0c4c6SRichard Fitzgerald u32 val = cpu_to_be32(event_id); 882f4f0c4c6SRichard Fitzgerald unsigned int reg; 883f4f0c4c6SRichard Fitzgerald int i, ret; 884f4f0c4c6SRichard Fitzgerald 885f4f0c4c6SRichard Fitzgerald ret = wm_coeff_base_reg(ctl, ®); 886f4f0c4c6SRichard Fitzgerald if (ret) 887f4f0c4c6SRichard Fitzgerald return ret; 888f4f0c4c6SRichard Fitzgerald 889f4f0c4c6SRichard Fitzgerald adsp_dbg(dsp, "Sending 0x%x to acked control alg 0x%x %s:0x%x\n", 890f4f0c4c6SRichard Fitzgerald event_id, ctl->alg_region.alg, 891f4f0c4c6SRichard Fitzgerald wm_adsp_mem_region_name(ctl->alg_region.type), ctl->offset); 892f4f0c4c6SRichard Fitzgerald 893f4f0c4c6SRichard Fitzgerald ret = regmap_raw_write(dsp->regmap, reg, &val, sizeof(val)); 894f4f0c4c6SRichard Fitzgerald if (ret) { 895f4f0c4c6SRichard Fitzgerald adsp_err(dsp, "Failed to write %x: %d\n", reg, ret); 896f4f0c4c6SRichard Fitzgerald return ret; 897f4f0c4c6SRichard Fitzgerald } 898f4f0c4c6SRichard Fitzgerald 899f4f0c4c6SRichard Fitzgerald /* 900f4f0c4c6SRichard Fitzgerald * Poll for ack, we initially poll at ~1ms intervals for firmwares 901f4f0c4c6SRichard Fitzgerald * that respond quickly, then go to ~10ms polls. A firmware is unlikely 902f4f0c4c6SRichard Fitzgerald * to ack instantly so we do the first 1ms delay before reading the 903f4f0c4c6SRichard Fitzgerald * control to avoid a pointless bus transaction 904f4f0c4c6SRichard Fitzgerald */ 905f4f0c4c6SRichard Fitzgerald for (i = 0; i < WM_ADSP_ACKED_CTL_TIMEOUT_MS;) { 906f4f0c4c6SRichard Fitzgerald switch (i) { 907f4f0c4c6SRichard Fitzgerald case 0 ... WM_ADSP_ACKED_CTL_N_QUICKPOLLS - 1: 908f4f0c4c6SRichard Fitzgerald usleep_range(1000, 2000); 909f4f0c4c6SRichard Fitzgerald i++; 910f4f0c4c6SRichard Fitzgerald break; 911f4f0c4c6SRichard Fitzgerald default: 912f4f0c4c6SRichard Fitzgerald usleep_range(10000, 20000); 913f4f0c4c6SRichard Fitzgerald i += 10; 914f4f0c4c6SRichard Fitzgerald break; 915f4f0c4c6SRichard Fitzgerald } 916f4f0c4c6SRichard Fitzgerald 917f4f0c4c6SRichard Fitzgerald ret = regmap_raw_read(dsp->regmap, reg, &val, sizeof(val)); 918f4f0c4c6SRichard Fitzgerald if (ret) { 919f4f0c4c6SRichard Fitzgerald adsp_err(dsp, "Failed to read %x: %d\n", reg, ret); 920f4f0c4c6SRichard Fitzgerald return ret; 921f4f0c4c6SRichard Fitzgerald } 922f4f0c4c6SRichard Fitzgerald 923f4f0c4c6SRichard Fitzgerald if (val == 0) { 924f4f0c4c6SRichard Fitzgerald adsp_dbg(dsp, "Acked control ACKED at poll %u\n", i); 925f4f0c4c6SRichard Fitzgerald return 0; 926f4f0c4c6SRichard Fitzgerald } 927f4f0c4c6SRichard Fitzgerald } 928f4f0c4c6SRichard Fitzgerald 929f4f0c4c6SRichard Fitzgerald adsp_warn(dsp, "Acked control @0x%x alg:0x%x %s:0x%x timed out\n", 930f4f0c4c6SRichard Fitzgerald reg, ctl->alg_region.alg, 931f4f0c4c6SRichard Fitzgerald wm_adsp_mem_region_name(ctl->alg_region.type), 932f4f0c4c6SRichard Fitzgerald ctl->offset); 933f4f0c4c6SRichard Fitzgerald 934f4f0c4c6SRichard Fitzgerald return -ETIMEDOUT; 935f4f0c4c6SRichard Fitzgerald } 936f4f0c4c6SRichard Fitzgerald 937c9f8dd71SCharles Keepax static int wm_coeff_write_control(struct wm_coeff_ctl *ctl, 9386ab2b7b4SDimitris Papastamos const void *buf, size_t len) 9396ab2b7b4SDimitris Papastamos { 9403809f001SCharles Keepax struct wm_adsp *dsp = ctl->dsp; 9416ab2b7b4SDimitris Papastamos void *scratch; 9426ab2b7b4SDimitris Papastamos int ret; 9436ab2b7b4SDimitris Papastamos unsigned int reg; 9446ab2b7b4SDimitris Papastamos 945b396ebcaSRichard Fitzgerald ret = wm_coeff_base_reg(ctl, ®); 946b396ebcaSRichard Fitzgerald if (ret) 947b396ebcaSRichard Fitzgerald return ret; 9486ab2b7b4SDimitris Papastamos 9494f8ea6d7SCharles Keepax scratch = kmemdup(buf, len, GFP_KERNEL | GFP_DMA); 9506ab2b7b4SDimitris Papastamos if (!scratch) 9516ab2b7b4SDimitris Papastamos return -ENOMEM; 9526ab2b7b4SDimitris Papastamos 9533809f001SCharles Keepax ret = regmap_raw_write(dsp->regmap, reg, scratch, 9544f8ea6d7SCharles Keepax len); 9556ab2b7b4SDimitris Papastamos if (ret) { 9563809f001SCharles Keepax adsp_err(dsp, "Failed to write %zu bytes to %x: %d\n", 9574f8ea6d7SCharles Keepax len, reg, ret); 9586ab2b7b4SDimitris Papastamos kfree(scratch); 9596ab2b7b4SDimitris Papastamos return ret; 9606ab2b7b4SDimitris Papastamos } 9614f8ea6d7SCharles Keepax adsp_dbg(dsp, "Wrote %zu bytes to %x\n", len, reg); 9626ab2b7b4SDimitris Papastamos 9636ab2b7b4SDimitris Papastamos kfree(scratch); 9646ab2b7b4SDimitris Papastamos 9656ab2b7b4SDimitris Papastamos return 0; 9666ab2b7b4SDimitris Papastamos } 9676ab2b7b4SDimitris Papastamos 9687585a5b0SCharles Keepax static int wm_coeff_put(struct snd_kcontrol *kctl, 9696ab2b7b4SDimitris Papastamos struct snd_ctl_elem_value *ucontrol) 9706ab2b7b4SDimitris Papastamos { 9719ee78757SCharles Keepax struct soc_bytes_ext *bytes_ext = 9729ee78757SCharles Keepax (struct soc_bytes_ext *)kctl->private_value; 9739ee78757SCharles Keepax struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); 9746ab2b7b4SDimitris Papastamos char *p = ucontrol->value.bytes.data; 975168d10e7SCharles Keepax int ret = 0; 976168d10e7SCharles Keepax 977168d10e7SCharles Keepax mutex_lock(&ctl->dsp->pwr_lock); 9786ab2b7b4SDimitris Papastamos 97967430a39SCharles Keepax if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) 98067430a39SCharles Keepax ret = -EPERM; 98167430a39SCharles Keepax else 9826ab2b7b4SDimitris Papastamos memcpy(ctl->cache, p, ctl->len); 9836ab2b7b4SDimitris Papastamos 9840c2e3f34SDimitris Papastamos ctl->set = 1; 985cef45771SCharles Keepax if (ctl->enabled && ctl->dsp->running) 986168d10e7SCharles Keepax ret = wm_coeff_write_control(ctl, p, ctl->len); 9876ab2b7b4SDimitris Papastamos 988168d10e7SCharles Keepax mutex_unlock(&ctl->dsp->pwr_lock); 989168d10e7SCharles Keepax 990168d10e7SCharles Keepax return ret; 9916ab2b7b4SDimitris Papastamos } 9926ab2b7b4SDimitris Papastamos 9939ee78757SCharles Keepax static int wm_coeff_tlv_put(struct snd_kcontrol *kctl, 9949ee78757SCharles Keepax const unsigned int __user *bytes, unsigned int size) 9959ee78757SCharles Keepax { 9969ee78757SCharles Keepax struct soc_bytes_ext *bytes_ext = 9979ee78757SCharles Keepax (struct soc_bytes_ext *)kctl->private_value; 9989ee78757SCharles Keepax struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); 9999ee78757SCharles Keepax int ret = 0; 10009ee78757SCharles Keepax 10019ee78757SCharles Keepax mutex_lock(&ctl->dsp->pwr_lock); 10029ee78757SCharles Keepax 10039ee78757SCharles Keepax if (copy_from_user(ctl->cache, bytes, size)) { 10049ee78757SCharles Keepax ret = -EFAULT; 10059ee78757SCharles Keepax } else { 10069ee78757SCharles Keepax ctl->set = 1; 1007cef45771SCharles Keepax if (ctl->enabled && ctl->dsp->running) 10089ee78757SCharles Keepax ret = wm_coeff_write_control(ctl, ctl->cache, size); 100967430a39SCharles Keepax else if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) 101067430a39SCharles Keepax ret = -EPERM; 10119ee78757SCharles Keepax } 10129ee78757SCharles Keepax 10139ee78757SCharles Keepax mutex_unlock(&ctl->dsp->pwr_lock); 10149ee78757SCharles Keepax 10159ee78757SCharles Keepax return ret; 10169ee78757SCharles Keepax } 10179ee78757SCharles Keepax 1018a23ebba8SRichard Fitzgerald static int wm_coeff_put_acked(struct snd_kcontrol *kctl, 1019a23ebba8SRichard Fitzgerald struct snd_ctl_elem_value *ucontrol) 1020a23ebba8SRichard Fitzgerald { 1021a23ebba8SRichard Fitzgerald struct soc_bytes_ext *bytes_ext = 1022a23ebba8SRichard Fitzgerald (struct soc_bytes_ext *)kctl->private_value; 1023a23ebba8SRichard Fitzgerald struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); 1024a23ebba8SRichard Fitzgerald unsigned int val = ucontrol->value.integer.value[0]; 1025a23ebba8SRichard Fitzgerald int ret; 1026a23ebba8SRichard Fitzgerald 1027a23ebba8SRichard Fitzgerald if (val == 0) 1028a23ebba8SRichard Fitzgerald return 0; /* 0 means no event */ 1029a23ebba8SRichard Fitzgerald 1030a23ebba8SRichard Fitzgerald mutex_lock(&ctl->dsp->pwr_lock); 1031a23ebba8SRichard Fitzgerald 10327b4af793SCharles Keepax if (ctl->enabled && ctl->dsp->running) 1033a23ebba8SRichard Fitzgerald ret = wm_coeff_write_acked_control(ctl, val); 1034a23ebba8SRichard Fitzgerald else 1035a23ebba8SRichard Fitzgerald ret = -EPERM; 1036a23ebba8SRichard Fitzgerald 1037a23ebba8SRichard Fitzgerald mutex_unlock(&ctl->dsp->pwr_lock); 1038a23ebba8SRichard Fitzgerald 1039a23ebba8SRichard Fitzgerald return ret; 1040a23ebba8SRichard Fitzgerald } 1041a23ebba8SRichard Fitzgerald 1042c9f8dd71SCharles Keepax static int wm_coeff_read_control(struct wm_coeff_ctl *ctl, 10436ab2b7b4SDimitris Papastamos void *buf, size_t len) 10446ab2b7b4SDimitris Papastamos { 10453809f001SCharles Keepax struct wm_adsp *dsp = ctl->dsp; 10466ab2b7b4SDimitris Papastamos void *scratch; 10476ab2b7b4SDimitris Papastamos int ret; 10486ab2b7b4SDimitris Papastamos unsigned int reg; 10496ab2b7b4SDimitris Papastamos 1050b396ebcaSRichard Fitzgerald ret = wm_coeff_base_reg(ctl, ®); 1051b396ebcaSRichard Fitzgerald if (ret) 1052b396ebcaSRichard Fitzgerald return ret; 10536ab2b7b4SDimitris Papastamos 10544f8ea6d7SCharles Keepax scratch = kmalloc(len, GFP_KERNEL | GFP_DMA); 10556ab2b7b4SDimitris Papastamos if (!scratch) 10566ab2b7b4SDimitris Papastamos return -ENOMEM; 10576ab2b7b4SDimitris Papastamos 10584f8ea6d7SCharles Keepax ret = regmap_raw_read(dsp->regmap, reg, scratch, len); 10596ab2b7b4SDimitris Papastamos if (ret) { 10603809f001SCharles Keepax adsp_err(dsp, "Failed to read %zu bytes from %x: %d\n", 10615602a643SCharles Keepax len, reg, ret); 10626ab2b7b4SDimitris Papastamos kfree(scratch); 10636ab2b7b4SDimitris Papastamos return ret; 10646ab2b7b4SDimitris Papastamos } 10654f8ea6d7SCharles Keepax adsp_dbg(dsp, "Read %zu bytes from %x\n", len, reg); 10666ab2b7b4SDimitris Papastamos 10674f8ea6d7SCharles Keepax memcpy(buf, scratch, len); 10686ab2b7b4SDimitris Papastamos kfree(scratch); 10696ab2b7b4SDimitris Papastamos 10706ab2b7b4SDimitris Papastamos return 0; 10716ab2b7b4SDimitris Papastamos } 10726ab2b7b4SDimitris Papastamos 10737585a5b0SCharles Keepax static int wm_coeff_get(struct snd_kcontrol *kctl, 10746ab2b7b4SDimitris Papastamos struct snd_ctl_elem_value *ucontrol) 10756ab2b7b4SDimitris Papastamos { 10769ee78757SCharles Keepax struct soc_bytes_ext *bytes_ext = 10779ee78757SCharles Keepax (struct soc_bytes_ext *)kctl->private_value; 10789ee78757SCharles Keepax struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); 10796ab2b7b4SDimitris Papastamos char *p = ucontrol->value.bytes.data; 1080168d10e7SCharles Keepax int ret = 0; 1081168d10e7SCharles Keepax 1082168d10e7SCharles Keepax mutex_lock(&ctl->dsp->pwr_lock); 10836ab2b7b4SDimitris Papastamos 108426c22a19SCharles Keepax if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) { 1085cef45771SCharles Keepax if (ctl->enabled && ctl->dsp->running) 1086168d10e7SCharles Keepax ret = wm_coeff_read_control(ctl, p, ctl->len); 108726c22a19SCharles Keepax else 1088168d10e7SCharles Keepax ret = -EPERM; 1089168d10e7SCharles Keepax } else { 1090cef45771SCharles Keepax if (!ctl->flags && ctl->enabled && ctl->dsp->running) 1091bc1765d6SCharles Keepax ret = wm_coeff_read_control(ctl, ctl->cache, ctl->len); 1092bc1765d6SCharles Keepax 1093168d10e7SCharles Keepax memcpy(p, ctl->cache, ctl->len); 109426c22a19SCharles Keepax } 109526c22a19SCharles Keepax 1096168d10e7SCharles Keepax mutex_unlock(&ctl->dsp->pwr_lock); 109726c22a19SCharles Keepax 1098168d10e7SCharles Keepax return ret; 10996ab2b7b4SDimitris Papastamos } 11006ab2b7b4SDimitris Papastamos 11019ee78757SCharles Keepax static int wm_coeff_tlv_get(struct snd_kcontrol *kctl, 11029ee78757SCharles Keepax unsigned int __user *bytes, unsigned int size) 11039ee78757SCharles Keepax { 11049ee78757SCharles Keepax struct soc_bytes_ext *bytes_ext = 11059ee78757SCharles Keepax (struct soc_bytes_ext *)kctl->private_value; 11069ee78757SCharles Keepax struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); 11079ee78757SCharles Keepax int ret = 0; 11089ee78757SCharles Keepax 11099ee78757SCharles Keepax mutex_lock(&ctl->dsp->pwr_lock); 11109ee78757SCharles Keepax 11119ee78757SCharles Keepax if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) { 1112cef45771SCharles Keepax if (ctl->enabled && ctl->dsp->running) 11139ee78757SCharles Keepax ret = wm_coeff_read_control(ctl, ctl->cache, size); 11149ee78757SCharles Keepax else 11159ee78757SCharles Keepax ret = -EPERM; 11169ee78757SCharles Keepax } else { 1117cef45771SCharles Keepax if (!ctl->flags && ctl->enabled && ctl->dsp->running) 11189ee78757SCharles Keepax ret = wm_coeff_read_control(ctl, ctl->cache, size); 11199ee78757SCharles Keepax } 11209ee78757SCharles Keepax 11219ee78757SCharles Keepax if (!ret && copy_to_user(bytes, ctl->cache, size)) 11229ee78757SCharles Keepax ret = -EFAULT; 11239ee78757SCharles Keepax 11249ee78757SCharles Keepax mutex_unlock(&ctl->dsp->pwr_lock); 11259ee78757SCharles Keepax 11269ee78757SCharles Keepax return ret; 11279ee78757SCharles Keepax } 11289ee78757SCharles Keepax 1129a23ebba8SRichard Fitzgerald static int wm_coeff_get_acked(struct snd_kcontrol *kcontrol, 1130a23ebba8SRichard Fitzgerald struct snd_ctl_elem_value *ucontrol) 1131a23ebba8SRichard Fitzgerald { 1132a23ebba8SRichard Fitzgerald /* 1133a23ebba8SRichard Fitzgerald * Although it's not useful to read an acked control, we must satisfy 1134a23ebba8SRichard Fitzgerald * user-side assumptions that all controls are readable and that a 1135a23ebba8SRichard Fitzgerald * write of the same value should be filtered out (it's valid to send 1136a23ebba8SRichard Fitzgerald * the same event number again to the firmware). We therefore return 0, 1137a23ebba8SRichard Fitzgerald * meaning "no event" so valid event numbers will always be a change 1138a23ebba8SRichard Fitzgerald */ 1139a23ebba8SRichard Fitzgerald ucontrol->value.integer.value[0] = 0; 1140a23ebba8SRichard Fitzgerald 1141a23ebba8SRichard Fitzgerald return 0; 1142a23ebba8SRichard Fitzgerald } 1143a23ebba8SRichard Fitzgerald 11446ab2b7b4SDimitris Papastamos struct wmfw_ctl_work { 11453809f001SCharles Keepax struct wm_adsp *dsp; 11466ab2b7b4SDimitris Papastamos struct wm_coeff_ctl *ctl; 11476ab2b7b4SDimitris Papastamos struct work_struct work; 11486ab2b7b4SDimitris Papastamos }; 11496ab2b7b4SDimitris Papastamos 11509ee78757SCharles Keepax static unsigned int wmfw_convert_flags(unsigned int in, unsigned int len) 11519ee78757SCharles Keepax { 11529ee78757SCharles Keepax unsigned int out, rd, wr, vol; 11539ee78757SCharles Keepax 11549ee78757SCharles Keepax if (len > ADSP_MAX_STD_CTRL_SIZE) { 11559ee78757SCharles Keepax rd = SNDRV_CTL_ELEM_ACCESS_TLV_READ; 11569ee78757SCharles Keepax wr = SNDRV_CTL_ELEM_ACCESS_TLV_WRITE; 11579ee78757SCharles Keepax vol = SNDRV_CTL_ELEM_ACCESS_VOLATILE; 11589ee78757SCharles Keepax 11599ee78757SCharles Keepax out = SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK; 11609ee78757SCharles Keepax } else { 11619ee78757SCharles Keepax rd = SNDRV_CTL_ELEM_ACCESS_READ; 11629ee78757SCharles Keepax wr = SNDRV_CTL_ELEM_ACCESS_WRITE; 11639ee78757SCharles Keepax vol = SNDRV_CTL_ELEM_ACCESS_VOLATILE; 11649ee78757SCharles Keepax 11659ee78757SCharles Keepax out = 0; 11669ee78757SCharles Keepax } 11679ee78757SCharles Keepax 11689ee78757SCharles Keepax if (in) { 11699ee78757SCharles Keepax if (in & WMFW_CTL_FLAG_READABLE) 11709ee78757SCharles Keepax out |= rd; 11719ee78757SCharles Keepax if (in & WMFW_CTL_FLAG_WRITEABLE) 11729ee78757SCharles Keepax out |= wr; 11739ee78757SCharles Keepax if (in & WMFW_CTL_FLAG_VOLATILE) 11749ee78757SCharles Keepax out |= vol; 11759ee78757SCharles Keepax } else { 11769ee78757SCharles Keepax out |= rd | wr | vol; 11779ee78757SCharles Keepax } 11789ee78757SCharles Keepax 11799ee78757SCharles Keepax return out; 11809ee78757SCharles Keepax } 11819ee78757SCharles Keepax 11823809f001SCharles Keepax static int wmfw_add_ctl(struct wm_adsp *dsp, struct wm_coeff_ctl *ctl) 11836ab2b7b4SDimitris Papastamos { 11846ab2b7b4SDimitris Papastamos struct snd_kcontrol_new *kcontrol; 11856ab2b7b4SDimitris Papastamos int ret; 11866ab2b7b4SDimitris Papastamos 118792bb4c32SDimitris Papastamos if (!ctl || !ctl->name) 11886ab2b7b4SDimitris Papastamos return -EINVAL; 11896ab2b7b4SDimitris Papastamos 11906ab2b7b4SDimitris Papastamos kcontrol = kzalloc(sizeof(*kcontrol), GFP_KERNEL); 11916ab2b7b4SDimitris Papastamos if (!kcontrol) 11926ab2b7b4SDimitris Papastamos return -ENOMEM; 11936ab2b7b4SDimitris Papastamos 11946ab2b7b4SDimitris Papastamos kcontrol->name = ctl->name; 11956ab2b7b4SDimitris Papastamos kcontrol->info = wm_coeff_info; 11969ee78757SCharles Keepax kcontrol->iface = SNDRV_CTL_ELEM_IFACE_MIXER; 11979ee78757SCharles Keepax kcontrol->tlv.c = snd_soc_bytes_tlv_callback; 11989ee78757SCharles Keepax kcontrol->private_value = (unsigned long)&ctl->bytes_ext; 1199a23ebba8SRichard Fitzgerald kcontrol->access = wmfw_convert_flags(ctl->flags, ctl->len); 1200a23ebba8SRichard Fitzgerald 1201a23ebba8SRichard Fitzgerald switch (ctl->type) { 1202a23ebba8SRichard Fitzgerald case WMFW_CTL_TYPE_ACKED: 1203a23ebba8SRichard Fitzgerald kcontrol->get = wm_coeff_get_acked; 1204a23ebba8SRichard Fitzgerald kcontrol->put = wm_coeff_put_acked; 1205a23ebba8SRichard Fitzgerald break; 1206a23ebba8SRichard Fitzgerald default: 1207a23ebba8SRichard Fitzgerald kcontrol->get = wm_coeff_get; 1208a23ebba8SRichard Fitzgerald kcontrol->put = wm_coeff_put; 12096ab2b7b4SDimitris Papastamos 12109ee78757SCharles Keepax ctl->bytes_ext.max = ctl->len; 12119ee78757SCharles Keepax ctl->bytes_ext.get = wm_coeff_tlv_get; 12129ee78757SCharles Keepax ctl->bytes_ext.put = wm_coeff_tlv_put; 1213a23ebba8SRichard Fitzgerald break; 1214a23ebba8SRichard Fitzgerald } 121526c22a19SCharles Keepax 1216685f51a5SRichard Fitzgerald ret = snd_soc_add_codec_controls(dsp->codec, kcontrol, 1); 12176ab2b7b4SDimitris Papastamos if (ret < 0) 12186ab2b7b4SDimitris Papastamos goto err_kcontrol; 12196ab2b7b4SDimitris Papastamos 12206ab2b7b4SDimitris Papastamos kfree(kcontrol); 12216ab2b7b4SDimitris Papastamos 12226ab2b7b4SDimitris Papastamos return 0; 12236ab2b7b4SDimitris Papastamos 12246ab2b7b4SDimitris Papastamos err_kcontrol: 12256ab2b7b4SDimitris Papastamos kfree(kcontrol); 12266ab2b7b4SDimitris Papastamos return ret; 12276ab2b7b4SDimitris Papastamos } 12286ab2b7b4SDimitris Papastamos 1229b21acc1cSCharles Keepax static int wm_coeff_init_control_caches(struct wm_adsp *dsp) 1230b21acc1cSCharles Keepax { 1231b21acc1cSCharles Keepax struct wm_coeff_ctl *ctl; 1232b21acc1cSCharles Keepax int ret; 1233b21acc1cSCharles Keepax 1234b21acc1cSCharles Keepax list_for_each_entry(ctl, &dsp->ctl_list, list) { 1235b21acc1cSCharles Keepax if (!ctl->enabled || ctl->set) 1236b21acc1cSCharles Keepax continue; 123726c22a19SCharles Keepax if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) 123826c22a19SCharles Keepax continue; 123926c22a19SCharles Keepax 12407d00cd97SCharles Keepax ret = wm_coeff_read_control(ctl, ctl->cache, ctl->len); 1241b21acc1cSCharles Keepax if (ret < 0) 1242b21acc1cSCharles Keepax return ret; 1243b21acc1cSCharles Keepax } 1244b21acc1cSCharles Keepax 1245b21acc1cSCharles Keepax return 0; 1246b21acc1cSCharles Keepax } 1247b21acc1cSCharles Keepax 1248b21acc1cSCharles Keepax static int wm_coeff_sync_controls(struct wm_adsp *dsp) 1249b21acc1cSCharles Keepax { 1250b21acc1cSCharles Keepax struct wm_coeff_ctl *ctl; 1251b21acc1cSCharles Keepax int ret; 1252b21acc1cSCharles Keepax 1253b21acc1cSCharles Keepax list_for_each_entry(ctl, &dsp->ctl_list, list) { 1254b21acc1cSCharles Keepax if (!ctl->enabled) 1255b21acc1cSCharles Keepax continue; 125626c22a19SCharles Keepax if (ctl->set && !(ctl->flags & WMFW_CTL_FLAG_VOLATILE)) { 12577d00cd97SCharles Keepax ret = wm_coeff_write_control(ctl, ctl->cache, ctl->len); 1258b21acc1cSCharles Keepax if (ret < 0) 1259b21acc1cSCharles Keepax return ret; 1260b21acc1cSCharles Keepax } 1261b21acc1cSCharles Keepax } 1262b21acc1cSCharles Keepax 1263b21acc1cSCharles Keepax return 0; 1264b21acc1cSCharles Keepax } 1265b21acc1cSCharles Keepax 1266f4f0c4c6SRichard Fitzgerald static void wm_adsp_signal_event_controls(struct wm_adsp *dsp, 1267f4f0c4c6SRichard Fitzgerald unsigned int event) 1268f4f0c4c6SRichard Fitzgerald { 1269f4f0c4c6SRichard Fitzgerald struct wm_coeff_ctl *ctl; 1270f4f0c4c6SRichard Fitzgerald int ret; 1271f4f0c4c6SRichard Fitzgerald 1272f4f0c4c6SRichard Fitzgerald list_for_each_entry(ctl, &dsp->ctl_list, list) { 1273f4f0c4c6SRichard Fitzgerald if (ctl->type != WMFW_CTL_TYPE_HOSTEVENT) 1274f4f0c4c6SRichard Fitzgerald continue; 1275f4f0c4c6SRichard Fitzgerald 127687aa6374SCharles Keepax if (!ctl->enabled) 127787aa6374SCharles Keepax continue; 127887aa6374SCharles Keepax 1279f4f0c4c6SRichard Fitzgerald ret = wm_coeff_write_acked_control(ctl, event); 1280f4f0c4c6SRichard Fitzgerald if (ret) 1281f4f0c4c6SRichard Fitzgerald adsp_warn(dsp, 1282f4f0c4c6SRichard Fitzgerald "Failed to send 0x%x event to alg 0x%x (%d)\n", 1283f4f0c4c6SRichard Fitzgerald event, ctl->alg_region.alg, ret); 1284f4f0c4c6SRichard Fitzgerald } 1285f4f0c4c6SRichard Fitzgerald } 1286f4f0c4c6SRichard Fitzgerald 1287b21acc1cSCharles Keepax static void wm_adsp_ctl_work(struct work_struct *work) 1288b21acc1cSCharles Keepax { 1289b21acc1cSCharles Keepax struct wmfw_ctl_work *ctl_work = container_of(work, 1290b21acc1cSCharles Keepax struct wmfw_ctl_work, 1291b21acc1cSCharles Keepax work); 1292b21acc1cSCharles Keepax 1293b21acc1cSCharles Keepax wmfw_add_ctl(ctl_work->dsp, ctl_work->ctl); 1294b21acc1cSCharles Keepax kfree(ctl_work); 1295b21acc1cSCharles Keepax } 1296b21acc1cSCharles Keepax 129766225e98SRichard Fitzgerald static void wm_adsp_free_ctl_blk(struct wm_coeff_ctl *ctl) 129866225e98SRichard Fitzgerald { 129966225e98SRichard Fitzgerald kfree(ctl->cache); 130066225e98SRichard Fitzgerald kfree(ctl->name); 130166225e98SRichard Fitzgerald kfree(ctl); 130266225e98SRichard Fitzgerald } 130366225e98SRichard Fitzgerald 1304b21acc1cSCharles Keepax static int wm_adsp_create_control(struct wm_adsp *dsp, 1305b21acc1cSCharles Keepax const struct wm_adsp_alg_region *alg_region, 13062323736dSCharles Keepax unsigned int offset, unsigned int len, 130726c22a19SCharles Keepax const char *subname, unsigned int subname_len, 13088eb084d0SStuart Henderson unsigned int flags, unsigned int type) 1309b21acc1cSCharles Keepax { 1310b21acc1cSCharles Keepax struct wm_coeff_ctl *ctl; 1311b21acc1cSCharles Keepax struct wmfw_ctl_work *ctl_work; 1312b21acc1cSCharles Keepax char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; 13139ce5e6e6SRichard Fitzgerald const char *region_name; 1314b21acc1cSCharles Keepax int ret; 1315b21acc1cSCharles Keepax 13169ce5e6e6SRichard Fitzgerald region_name = wm_adsp_mem_region_name(alg_region->type); 13179ce5e6e6SRichard Fitzgerald if (!region_name) { 13182323736dSCharles Keepax adsp_err(dsp, "Unknown region type: %d\n", alg_region->type); 1319b21acc1cSCharles Keepax return -EINVAL; 1320b21acc1cSCharles Keepax } 1321b21acc1cSCharles Keepax 1322cb5b57a9SCharles Keepax switch (dsp->fw_ver) { 1323cb5b57a9SCharles Keepax case 0: 1324cb5b57a9SCharles Keepax case 1: 1325b21acc1cSCharles Keepax snprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "DSP%d %s %x", 1326b21acc1cSCharles Keepax dsp->num, region_name, alg_region->alg); 1327cb5b57a9SCharles Keepax break; 1328cb5b57a9SCharles Keepax default: 1329cb5b57a9SCharles Keepax ret = snprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, 1330cb5b57a9SCharles Keepax "DSP%d%c %.12s %x", dsp->num, *region_name, 1331cb5b57a9SCharles Keepax wm_adsp_fw_text[dsp->fw], alg_region->alg); 1332cb5b57a9SCharles Keepax 1333cb5b57a9SCharles Keepax /* Truncate the subname from the start if it is too long */ 1334cb5b57a9SCharles Keepax if (subname) { 1335cb5b57a9SCharles Keepax int avail = SNDRV_CTL_ELEM_ID_NAME_MAXLEN - ret - 2; 1336cb5b57a9SCharles Keepax int skip = 0; 1337cb5b57a9SCharles Keepax 1338cb5b57a9SCharles Keepax if (subname_len > avail) 1339cb5b57a9SCharles Keepax skip = subname_len - avail; 1340cb5b57a9SCharles Keepax 1341cb5b57a9SCharles Keepax snprintf(name + ret, 1342cb5b57a9SCharles Keepax SNDRV_CTL_ELEM_ID_NAME_MAXLEN - ret, " %.*s", 1343cb5b57a9SCharles Keepax subname_len - skip, subname + skip); 1344cb5b57a9SCharles Keepax } 1345cb5b57a9SCharles Keepax break; 1346cb5b57a9SCharles Keepax } 1347b21acc1cSCharles Keepax 13487585a5b0SCharles Keepax list_for_each_entry(ctl, &dsp->ctl_list, list) { 1349b21acc1cSCharles Keepax if (!strcmp(ctl->name, name)) { 1350b21acc1cSCharles Keepax if (!ctl->enabled) 1351b21acc1cSCharles Keepax ctl->enabled = 1; 1352b21acc1cSCharles Keepax return 0; 1353b21acc1cSCharles Keepax } 1354b21acc1cSCharles Keepax } 1355b21acc1cSCharles Keepax 1356b21acc1cSCharles Keepax ctl = kzalloc(sizeof(*ctl), GFP_KERNEL); 1357b21acc1cSCharles Keepax if (!ctl) 1358b21acc1cSCharles Keepax return -ENOMEM; 13592323736dSCharles Keepax ctl->fw_name = wm_adsp_fw_text[dsp->fw]; 1360b21acc1cSCharles Keepax ctl->alg_region = *alg_region; 1361b21acc1cSCharles Keepax ctl->name = kmemdup(name, strlen(name) + 1, GFP_KERNEL); 1362b21acc1cSCharles Keepax if (!ctl->name) { 1363b21acc1cSCharles Keepax ret = -ENOMEM; 1364b21acc1cSCharles Keepax goto err_ctl; 1365b21acc1cSCharles Keepax } 1366b21acc1cSCharles Keepax ctl->enabled = 1; 1367b21acc1cSCharles Keepax ctl->set = 0; 1368b21acc1cSCharles Keepax ctl->ops.xget = wm_coeff_get; 1369b21acc1cSCharles Keepax ctl->ops.xput = wm_coeff_put; 1370b21acc1cSCharles Keepax ctl->dsp = dsp; 1371b21acc1cSCharles Keepax 137226c22a19SCharles Keepax ctl->flags = flags; 13738eb084d0SStuart Henderson ctl->type = type; 13742323736dSCharles Keepax ctl->offset = offset; 1375b21acc1cSCharles Keepax ctl->len = len; 1376b21acc1cSCharles Keepax ctl->cache = kzalloc(ctl->len, GFP_KERNEL); 1377b21acc1cSCharles Keepax if (!ctl->cache) { 1378b21acc1cSCharles Keepax ret = -ENOMEM; 1379b21acc1cSCharles Keepax goto err_ctl_name; 1380b21acc1cSCharles Keepax } 1381b21acc1cSCharles Keepax 13822323736dSCharles Keepax list_add(&ctl->list, &dsp->ctl_list); 13832323736dSCharles Keepax 13848eb084d0SStuart Henderson if (flags & WMFW_CTL_FLAG_SYS) 13858eb084d0SStuart Henderson return 0; 13868eb084d0SStuart Henderson 1387b21acc1cSCharles Keepax ctl_work = kzalloc(sizeof(*ctl_work), GFP_KERNEL); 1388b21acc1cSCharles Keepax if (!ctl_work) { 1389b21acc1cSCharles Keepax ret = -ENOMEM; 1390b21acc1cSCharles Keepax goto err_ctl_cache; 1391b21acc1cSCharles Keepax } 1392b21acc1cSCharles Keepax 1393b21acc1cSCharles Keepax ctl_work->dsp = dsp; 1394b21acc1cSCharles Keepax ctl_work->ctl = ctl; 1395b21acc1cSCharles Keepax INIT_WORK(&ctl_work->work, wm_adsp_ctl_work); 1396b21acc1cSCharles Keepax schedule_work(&ctl_work->work); 1397b21acc1cSCharles Keepax 1398b21acc1cSCharles Keepax return 0; 1399b21acc1cSCharles Keepax 1400b21acc1cSCharles Keepax err_ctl_cache: 1401b21acc1cSCharles Keepax kfree(ctl->cache); 1402b21acc1cSCharles Keepax err_ctl_name: 1403b21acc1cSCharles Keepax kfree(ctl->name); 1404b21acc1cSCharles Keepax err_ctl: 1405b21acc1cSCharles Keepax kfree(ctl); 1406b21acc1cSCharles Keepax 1407b21acc1cSCharles Keepax return ret; 1408b21acc1cSCharles Keepax } 1409b21acc1cSCharles Keepax 14102323736dSCharles Keepax struct wm_coeff_parsed_alg { 14112323736dSCharles Keepax int id; 14122323736dSCharles Keepax const u8 *name; 14132323736dSCharles Keepax int name_len; 14142323736dSCharles Keepax int ncoeff; 14152323736dSCharles Keepax }; 14162323736dSCharles Keepax 14172323736dSCharles Keepax struct wm_coeff_parsed_coeff { 14182323736dSCharles Keepax int offset; 14192323736dSCharles Keepax int mem_type; 14202323736dSCharles Keepax const u8 *name; 14212323736dSCharles Keepax int name_len; 14222323736dSCharles Keepax int ctl_type; 14232323736dSCharles Keepax int flags; 14242323736dSCharles Keepax int len; 14252323736dSCharles Keepax }; 14262323736dSCharles Keepax 1427cb5b57a9SCharles Keepax static int wm_coeff_parse_string(int bytes, const u8 **pos, const u8 **str) 1428cb5b57a9SCharles Keepax { 1429cb5b57a9SCharles Keepax int length; 1430cb5b57a9SCharles Keepax 1431cb5b57a9SCharles Keepax switch (bytes) { 1432cb5b57a9SCharles Keepax case 1: 1433cb5b57a9SCharles Keepax length = **pos; 1434cb5b57a9SCharles Keepax break; 1435cb5b57a9SCharles Keepax case 2: 14368299ee81SCharles Keepax length = le16_to_cpu(*((__le16 *)*pos)); 1437cb5b57a9SCharles Keepax break; 1438cb5b57a9SCharles Keepax default: 1439cb5b57a9SCharles Keepax return 0; 1440cb5b57a9SCharles Keepax } 1441cb5b57a9SCharles Keepax 1442cb5b57a9SCharles Keepax if (str) 1443cb5b57a9SCharles Keepax *str = *pos + bytes; 1444cb5b57a9SCharles Keepax 1445cb5b57a9SCharles Keepax *pos += ((length + bytes) + 3) & ~0x03; 1446cb5b57a9SCharles Keepax 1447cb5b57a9SCharles Keepax return length; 1448cb5b57a9SCharles Keepax } 1449cb5b57a9SCharles Keepax 1450cb5b57a9SCharles Keepax static int wm_coeff_parse_int(int bytes, const u8 **pos) 1451cb5b57a9SCharles Keepax { 1452cb5b57a9SCharles Keepax int val = 0; 1453cb5b57a9SCharles Keepax 1454cb5b57a9SCharles Keepax switch (bytes) { 1455cb5b57a9SCharles Keepax case 2: 14568299ee81SCharles Keepax val = le16_to_cpu(*((__le16 *)*pos)); 1457cb5b57a9SCharles Keepax break; 1458cb5b57a9SCharles Keepax case 4: 14598299ee81SCharles Keepax val = le32_to_cpu(*((__le32 *)*pos)); 1460cb5b57a9SCharles Keepax break; 1461cb5b57a9SCharles Keepax default: 1462cb5b57a9SCharles Keepax break; 1463cb5b57a9SCharles Keepax } 1464cb5b57a9SCharles Keepax 1465cb5b57a9SCharles Keepax *pos += bytes; 1466cb5b57a9SCharles Keepax 1467cb5b57a9SCharles Keepax return val; 1468cb5b57a9SCharles Keepax } 1469cb5b57a9SCharles Keepax 14702323736dSCharles Keepax static inline void wm_coeff_parse_alg(struct wm_adsp *dsp, const u8 **data, 14712323736dSCharles Keepax struct wm_coeff_parsed_alg *blk) 14722323736dSCharles Keepax { 14732323736dSCharles Keepax const struct wmfw_adsp_alg_data *raw; 14742323736dSCharles Keepax 1475cb5b57a9SCharles Keepax switch (dsp->fw_ver) { 1476cb5b57a9SCharles Keepax case 0: 1477cb5b57a9SCharles Keepax case 1: 14782323736dSCharles Keepax raw = (const struct wmfw_adsp_alg_data *)*data; 14792323736dSCharles Keepax *data = raw->data; 14802323736dSCharles Keepax 14812323736dSCharles Keepax blk->id = le32_to_cpu(raw->id); 14822323736dSCharles Keepax blk->name = raw->name; 14832323736dSCharles Keepax blk->name_len = strlen(raw->name); 14842323736dSCharles Keepax blk->ncoeff = le32_to_cpu(raw->ncoeff); 1485cb5b57a9SCharles Keepax break; 1486cb5b57a9SCharles Keepax default: 1487cb5b57a9SCharles Keepax blk->id = wm_coeff_parse_int(sizeof(raw->id), data); 1488cb5b57a9SCharles Keepax blk->name_len = wm_coeff_parse_string(sizeof(u8), data, 1489cb5b57a9SCharles Keepax &blk->name); 1490cb5b57a9SCharles Keepax wm_coeff_parse_string(sizeof(u16), data, NULL); 1491cb5b57a9SCharles Keepax blk->ncoeff = wm_coeff_parse_int(sizeof(raw->ncoeff), data); 1492cb5b57a9SCharles Keepax break; 1493cb5b57a9SCharles Keepax } 14942323736dSCharles Keepax 14952323736dSCharles Keepax adsp_dbg(dsp, "Algorithm ID: %#x\n", blk->id); 14962323736dSCharles Keepax adsp_dbg(dsp, "Algorithm name: %.*s\n", blk->name_len, blk->name); 14972323736dSCharles Keepax adsp_dbg(dsp, "# of coefficient descriptors: %#x\n", blk->ncoeff); 14982323736dSCharles Keepax } 14992323736dSCharles Keepax 15002323736dSCharles Keepax static inline void wm_coeff_parse_coeff(struct wm_adsp *dsp, const u8 **data, 15012323736dSCharles Keepax struct wm_coeff_parsed_coeff *blk) 15022323736dSCharles Keepax { 15032323736dSCharles Keepax const struct wmfw_adsp_coeff_data *raw; 1504cb5b57a9SCharles Keepax const u8 *tmp; 1505cb5b57a9SCharles Keepax int length; 15062323736dSCharles Keepax 1507cb5b57a9SCharles Keepax switch (dsp->fw_ver) { 1508cb5b57a9SCharles Keepax case 0: 1509cb5b57a9SCharles Keepax case 1: 15102323736dSCharles Keepax raw = (const struct wmfw_adsp_coeff_data *)*data; 15112323736dSCharles Keepax *data = *data + sizeof(raw->hdr) + le32_to_cpu(raw->hdr.size); 15122323736dSCharles Keepax 15132323736dSCharles Keepax blk->offset = le16_to_cpu(raw->hdr.offset); 15142323736dSCharles Keepax blk->mem_type = le16_to_cpu(raw->hdr.type); 15152323736dSCharles Keepax blk->name = raw->name; 15162323736dSCharles Keepax blk->name_len = strlen(raw->name); 15172323736dSCharles Keepax blk->ctl_type = le16_to_cpu(raw->ctl_type); 15182323736dSCharles Keepax blk->flags = le16_to_cpu(raw->flags); 15192323736dSCharles Keepax blk->len = le32_to_cpu(raw->len); 1520cb5b57a9SCharles Keepax break; 1521cb5b57a9SCharles Keepax default: 1522cb5b57a9SCharles Keepax tmp = *data; 1523cb5b57a9SCharles Keepax blk->offset = wm_coeff_parse_int(sizeof(raw->hdr.offset), &tmp); 1524cb5b57a9SCharles Keepax blk->mem_type = wm_coeff_parse_int(sizeof(raw->hdr.type), &tmp); 1525cb5b57a9SCharles Keepax length = wm_coeff_parse_int(sizeof(raw->hdr.size), &tmp); 1526cb5b57a9SCharles Keepax blk->name_len = wm_coeff_parse_string(sizeof(u8), &tmp, 1527cb5b57a9SCharles Keepax &blk->name); 1528cb5b57a9SCharles Keepax wm_coeff_parse_string(sizeof(u8), &tmp, NULL); 1529cb5b57a9SCharles Keepax wm_coeff_parse_string(sizeof(u16), &tmp, NULL); 1530cb5b57a9SCharles Keepax blk->ctl_type = wm_coeff_parse_int(sizeof(raw->ctl_type), &tmp); 1531cb5b57a9SCharles Keepax blk->flags = wm_coeff_parse_int(sizeof(raw->flags), &tmp); 1532cb5b57a9SCharles Keepax blk->len = wm_coeff_parse_int(sizeof(raw->len), &tmp); 1533cb5b57a9SCharles Keepax 1534cb5b57a9SCharles Keepax *data = *data + sizeof(raw->hdr) + length; 1535cb5b57a9SCharles Keepax break; 1536cb5b57a9SCharles Keepax } 15372323736dSCharles Keepax 15382323736dSCharles Keepax adsp_dbg(dsp, "\tCoefficient type: %#x\n", blk->mem_type); 15392323736dSCharles Keepax adsp_dbg(dsp, "\tCoefficient offset: %#x\n", blk->offset); 15402323736dSCharles Keepax adsp_dbg(dsp, "\tCoefficient name: %.*s\n", blk->name_len, blk->name); 15412323736dSCharles Keepax adsp_dbg(dsp, "\tCoefficient flags: %#x\n", blk->flags); 15422323736dSCharles Keepax adsp_dbg(dsp, "\tALSA control type: %#x\n", blk->ctl_type); 15432323736dSCharles Keepax adsp_dbg(dsp, "\tALSA control len: %#x\n", blk->len); 15442323736dSCharles Keepax } 15452323736dSCharles Keepax 1546f4f0c4c6SRichard Fitzgerald static int wm_adsp_check_coeff_flags(struct wm_adsp *dsp, 1547f4f0c4c6SRichard Fitzgerald const struct wm_coeff_parsed_coeff *coeff_blk, 1548f4f0c4c6SRichard Fitzgerald unsigned int f_required, 1549f4f0c4c6SRichard Fitzgerald unsigned int f_illegal) 1550f4f0c4c6SRichard Fitzgerald { 1551f4f0c4c6SRichard Fitzgerald if ((coeff_blk->flags & f_illegal) || 1552f4f0c4c6SRichard Fitzgerald ((coeff_blk->flags & f_required) != f_required)) { 1553f4f0c4c6SRichard Fitzgerald adsp_err(dsp, "Illegal flags 0x%x for control type 0x%x\n", 1554f4f0c4c6SRichard Fitzgerald coeff_blk->flags, coeff_blk->ctl_type); 1555f4f0c4c6SRichard Fitzgerald return -EINVAL; 1556f4f0c4c6SRichard Fitzgerald } 1557f4f0c4c6SRichard Fitzgerald 1558f4f0c4c6SRichard Fitzgerald return 0; 1559f4f0c4c6SRichard Fitzgerald } 1560f4f0c4c6SRichard Fitzgerald 15612323736dSCharles Keepax static int wm_adsp_parse_coeff(struct wm_adsp *dsp, 15622323736dSCharles Keepax const struct wmfw_region *region) 15632323736dSCharles Keepax { 15642323736dSCharles Keepax struct wm_adsp_alg_region alg_region = {}; 15652323736dSCharles Keepax struct wm_coeff_parsed_alg alg_blk; 15662323736dSCharles Keepax struct wm_coeff_parsed_coeff coeff_blk; 15672323736dSCharles Keepax const u8 *data = region->data; 15682323736dSCharles Keepax int i, ret; 15692323736dSCharles Keepax 15702323736dSCharles Keepax wm_coeff_parse_alg(dsp, &data, &alg_blk); 15712323736dSCharles Keepax for (i = 0; i < alg_blk.ncoeff; i++) { 15722323736dSCharles Keepax wm_coeff_parse_coeff(dsp, &data, &coeff_blk); 15732323736dSCharles Keepax 15742323736dSCharles Keepax switch (coeff_blk.ctl_type) { 15752323736dSCharles Keepax case SNDRV_CTL_ELEM_TYPE_BYTES: 15762323736dSCharles Keepax break; 1577a23ebba8SRichard Fitzgerald case WMFW_CTL_TYPE_ACKED: 1578a23ebba8SRichard Fitzgerald if (coeff_blk.flags & WMFW_CTL_FLAG_SYS) 1579a23ebba8SRichard Fitzgerald continue; /* ignore */ 1580a23ebba8SRichard Fitzgerald 1581a23ebba8SRichard Fitzgerald ret = wm_adsp_check_coeff_flags(dsp, &coeff_blk, 1582a23ebba8SRichard Fitzgerald WMFW_CTL_FLAG_VOLATILE | 1583a23ebba8SRichard Fitzgerald WMFW_CTL_FLAG_WRITEABLE | 1584a23ebba8SRichard Fitzgerald WMFW_CTL_FLAG_READABLE, 1585a23ebba8SRichard Fitzgerald 0); 1586a23ebba8SRichard Fitzgerald if (ret) 1587a23ebba8SRichard Fitzgerald return -EINVAL; 1588a23ebba8SRichard Fitzgerald break; 1589f4f0c4c6SRichard Fitzgerald case WMFW_CTL_TYPE_HOSTEVENT: 1590f4f0c4c6SRichard Fitzgerald ret = wm_adsp_check_coeff_flags(dsp, &coeff_blk, 1591f4f0c4c6SRichard Fitzgerald WMFW_CTL_FLAG_SYS | 1592f4f0c4c6SRichard Fitzgerald WMFW_CTL_FLAG_VOLATILE | 1593f4f0c4c6SRichard Fitzgerald WMFW_CTL_FLAG_WRITEABLE | 1594f4f0c4c6SRichard Fitzgerald WMFW_CTL_FLAG_READABLE, 1595f4f0c4c6SRichard Fitzgerald 0); 1596f4f0c4c6SRichard Fitzgerald if (ret) 1597f4f0c4c6SRichard Fitzgerald return -EINVAL; 1598f4f0c4c6SRichard Fitzgerald break; 15992323736dSCharles Keepax default: 16002323736dSCharles Keepax adsp_err(dsp, "Unknown control type: %d\n", 16012323736dSCharles Keepax coeff_blk.ctl_type); 16022323736dSCharles Keepax return -EINVAL; 16032323736dSCharles Keepax } 16042323736dSCharles Keepax 16052323736dSCharles Keepax alg_region.type = coeff_blk.mem_type; 16062323736dSCharles Keepax alg_region.alg = alg_blk.id; 16072323736dSCharles Keepax 16082323736dSCharles Keepax ret = wm_adsp_create_control(dsp, &alg_region, 16092323736dSCharles Keepax coeff_blk.offset, 16102323736dSCharles Keepax coeff_blk.len, 16112323736dSCharles Keepax coeff_blk.name, 161226c22a19SCharles Keepax coeff_blk.name_len, 16138eb084d0SStuart Henderson coeff_blk.flags, 16148eb084d0SStuart Henderson coeff_blk.ctl_type); 16152323736dSCharles Keepax if (ret < 0) 16162323736dSCharles Keepax adsp_err(dsp, "Failed to create control: %.*s, %d\n", 16172323736dSCharles Keepax coeff_blk.name_len, coeff_blk.name, ret); 16182323736dSCharles Keepax } 16192323736dSCharles Keepax 16202323736dSCharles Keepax return 0; 16212323736dSCharles Keepax } 16222323736dSCharles Keepax 16232159ad93SMark Brown static int wm_adsp_load(struct wm_adsp *dsp) 16242159ad93SMark Brown { 1625cf17c83cSMark Brown LIST_HEAD(buf_list); 16262159ad93SMark Brown const struct firmware *firmware; 16272159ad93SMark Brown struct regmap *regmap = dsp->regmap; 16282159ad93SMark Brown unsigned int pos = 0; 16292159ad93SMark Brown const struct wmfw_header *header; 16302159ad93SMark Brown const struct wmfw_adsp1_sizes *adsp1_sizes; 16312159ad93SMark Brown const struct wmfw_adsp2_sizes *adsp2_sizes; 16322159ad93SMark Brown const struct wmfw_footer *footer; 16332159ad93SMark Brown const struct wmfw_region *region; 16342159ad93SMark Brown const struct wm_adsp_region *mem; 16352159ad93SMark Brown const char *region_name; 16361cab2a84SRichard Fitzgerald char *file, *text = NULL; 1637cf17c83cSMark Brown struct wm_adsp_buf *buf; 16382159ad93SMark Brown unsigned int reg; 16392159ad93SMark Brown int regions = 0; 16402159ad93SMark Brown int ret, offset, type, sizes; 16412159ad93SMark Brown 16422159ad93SMark Brown file = kzalloc(PAGE_SIZE, GFP_KERNEL); 16432159ad93SMark Brown if (file == NULL) 16442159ad93SMark Brown return -ENOMEM; 16452159ad93SMark Brown 16461023dbd9SMark Brown snprintf(file, PAGE_SIZE, "%s-dsp%d-%s.wmfw", dsp->part, dsp->num, 16471023dbd9SMark Brown wm_adsp_fw[dsp->fw].file); 16482159ad93SMark Brown file[PAGE_SIZE - 1] = '\0'; 16492159ad93SMark Brown 16502159ad93SMark Brown ret = request_firmware(&firmware, file, dsp->dev); 16512159ad93SMark Brown if (ret != 0) { 16522159ad93SMark Brown adsp_err(dsp, "Failed to request '%s'\n", file); 16532159ad93SMark Brown goto out; 16542159ad93SMark Brown } 16552159ad93SMark Brown ret = -EINVAL; 16562159ad93SMark Brown 16572159ad93SMark Brown pos = sizeof(*header) + sizeof(*adsp1_sizes) + sizeof(*footer); 16582159ad93SMark Brown if (pos >= firmware->size) { 16592159ad93SMark Brown adsp_err(dsp, "%s: file too short, %zu bytes\n", 16602159ad93SMark Brown file, firmware->size); 16612159ad93SMark Brown goto out_fw; 16622159ad93SMark Brown } 16632159ad93SMark Brown 16642159ad93SMark Brown header = (void *)&firmware->data[0]; 16652159ad93SMark Brown 16662159ad93SMark Brown if (memcmp(&header->magic[0], "WMFW", 4) != 0) { 16672159ad93SMark Brown adsp_err(dsp, "%s: invalid magic\n", file); 16682159ad93SMark Brown goto out_fw; 16692159ad93SMark Brown } 16702159ad93SMark Brown 16712323736dSCharles Keepax switch (header->ver) { 16722323736dSCharles Keepax case 0: 1673c61e59feSCharles Keepax adsp_warn(dsp, "%s: Depreciated file format %d\n", 1674c61e59feSCharles Keepax file, header->ver); 1675c61e59feSCharles Keepax break; 16762323736dSCharles Keepax case 1: 1677cb5b57a9SCharles Keepax case 2: 16782323736dSCharles Keepax break; 16792323736dSCharles Keepax default: 16802159ad93SMark Brown adsp_err(dsp, "%s: unknown file format %d\n", 16812159ad93SMark Brown file, header->ver); 16822159ad93SMark Brown goto out_fw; 16832159ad93SMark Brown } 16842323736dSCharles Keepax 16853626992aSDimitris Papastamos adsp_info(dsp, "Firmware version: %d\n", header->ver); 16862323736dSCharles Keepax dsp->fw_ver = header->ver; 16872159ad93SMark Brown 16882159ad93SMark Brown if (header->core != dsp->type) { 16892159ad93SMark Brown adsp_err(dsp, "%s: invalid core %d != %d\n", 16902159ad93SMark Brown file, header->core, dsp->type); 16912159ad93SMark Brown goto out_fw; 16922159ad93SMark Brown } 16932159ad93SMark Brown 16942159ad93SMark Brown switch (dsp->type) { 16952159ad93SMark Brown case WMFW_ADSP1: 16962159ad93SMark Brown pos = sizeof(*header) + sizeof(*adsp1_sizes) + sizeof(*footer); 16972159ad93SMark Brown adsp1_sizes = (void *)&(header[1]); 16982159ad93SMark Brown footer = (void *)&(adsp1_sizes[1]); 16992159ad93SMark Brown sizes = sizeof(*adsp1_sizes); 17002159ad93SMark Brown 17012159ad93SMark Brown adsp_dbg(dsp, "%s: %d DM, %d PM, %d ZM\n", 17022159ad93SMark Brown file, le32_to_cpu(adsp1_sizes->dm), 17032159ad93SMark Brown le32_to_cpu(adsp1_sizes->pm), 17042159ad93SMark Brown le32_to_cpu(adsp1_sizes->zm)); 17052159ad93SMark Brown break; 17062159ad93SMark Brown 17072159ad93SMark Brown case WMFW_ADSP2: 17082159ad93SMark Brown pos = sizeof(*header) + sizeof(*adsp2_sizes) + sizeof(*footer); 17092159ad93SMark Brown adsp2_sizes = (void *)&(header[1]); 17102159ad93SMark Brown footer = (void *)&(adsp2_sizes[1]); 17112159ad93SMark Brown sizes = sizeof(*adsp2_sizes); 17122159ad93SMark Brown 17132159ad93SMark Brown adsp_dbg(dsp, "%s: %d XM, %d YM %d PM, %d ZM\n", 17142159ad93SMark Brown file, le32_to_cpu(adsp2_sizes->xm), 17152159ad93SMark Brown le32_to_cpu(adsp2_sizes->ym), 17162159ad93SMark Brown le32_to_cpu(adsp2_sizes->pm), 17172159ad93SMark Brown le32_to_cpu(adsp2_sizes->zm)); 17182159ad93SMark Brown break; 17192159ad93SMark Brown 17202159ad93SMark Brown default: 17216c452bdaSTakashi Iwai WARN(1, "Unknown DSP type"); 17222159ad93SMark Brown goto out_fw; 17232159ad93SMark Brown } 17242159ad93SMark Brown 17252159ad93SMark Brown if (le32_to_cpu(header->len) != sizeof(*header) + 17262159ad93SMark Brown sizes + sizeof(*footer)) { 17272159ad93SMark Brown adsp_err(dsp, "%s: unexpected header length %d\n", 17282159ad93SMark Brown file, le32_to_cpu(header->len)); 17292159ad93SMark Brown goto out_fw; 17302159ad93SMark Brown } 17312159ad93SMark Brown 17322159ad93SMark Brown adsp_dbg(dsp, "%s: timestamp %llu\n", file, 17332159ad93SMark Brown le64_to_cpu(footer->timestamp)); 17342159ad93SMark Brown 17352159ad93SMark Brown while (pos < firmware->size && 173650dd2ea8SBen Hutchings sizeof(*region) < firmware->size - pos) { 17372159ad93SMark Brown region = (void *)&(firmware->data[pos]); 17382159ad93SMark Brown region_name = "Unknown"; 17392159ad93SMark Brown reg = 0; 17402159ad93SMark Brown text = NULL; 17412159ad93SMark Brown offset = le32_to_cpu(region->offset) & 0xffffff; 17422159ad93SMark Brown type = be32_to_cpu(region->type) & 0xff; 17432159ad93SMark Brown mem = wm_adsp_find_region(dsp, type); 17442159ad93SMark Brown 17452159ad93SMark Brown switch (type) { 17462159ad93SMark Brown case WMFW_NAME_TEXT: 17472159ad93SMark Brown region_name = "Firmware name"; 17482159ad93SMark Brown text = kzalloc(le32_to_cpu(region->len) + 1, 17492159ad93SMark Brown GFP_KERNEL); 17502159ad93SMark Brown break; 17512323736dSCharles Keepax case WMFW_ALGORITHM_DATA: 17522323736dSCharles Keepax region_name = "Algorithm"; 17532323736dSCharles Keepax ret = wm_adsp_parse_coeff(dsp, region); 17542323736dSCharles Keepax if (ret != 0) 17552323736dSCharles Keepax goto out_fw; 17562323736dSCharles Keepax break; 17572159ad93SMark Brown case WMFW_INFO_TEXT: 17582159ad93SMark Brown region_name = "Information"; 17592159ad93SMark Brown text = kzalloc(le32_to_cpu(region->len) + 1, 17602159ad93SMark Brown GFP_KERNEL); 17612159ad93SMark Brown break; 17622159ad93SMark Brown case WMFW_ABSOLUTE: 17632159ad93SMark Brown region_name = "Absolute"; 17642159ad93SMark Brown reg = offset; 17652159ad93SMark Brown break; 17662159ad93SMark Brown case WMFW_ADSP1_PM: 17672159ad93SMark Brown case WMFW_ADSP1_DM: 17682159ad93SMark Brown case WMFW_ADSP2_XM: 17692159ad93SMark Brown case WMFW_ADSP2_YM: 17702159ad93SMark Brown case WMFW_ADSP1_ZM: 17719ce5e6e6SRichard Fitzgerald region_name = wm_adsp_mem_region_name(type); 177245b9ee72SMark Brown reg = wm_adsp_region_to_reg(mem, offset); 17732159ad93SMark Brown break; 17742159ad93SMark Brown default: 17752159ad93SMark Brown adsp_warn(dsp, 17762159ad93SMark Brown "%s.%d: Unknown region type %x at %d(%x)\n", 17772159ad93SMark Brown file, regions, type, pos, pos); 17782159ad93SMark Brown break; 17792159ad93SMark Brown } 17802159ad93SMark Brown 17812159ad93SMark Brown adsp_dbg(dsp, "%s.%d: %d bytes at %d in %s\n", file, 17822159ad93SMark Brown regions, le32_to_cpu(region->len), offset, 17832159ad93SMark Brown region_name); 17842159ad93SMark Brown 178550dd2ea8SBen Hutchings if (le32_to_cpu(region->len) > 178650dd2ea8SBen Hutchings firmware->size - pos - sizeof(*region)) { 17871cab2a84SRichard Fitzgerald adsp_err(dsp, 17881cab2a84SRichard Fitzgerald "%s.%d: %s region len %d bytes exceeds file length %zu\n", 17891cab2a84SRichard Fitzgerald file, regions, region_name, 17901cab2a84SRichard Fitzgerald le32_to_cpu(region->len), firmware->size); 17911cab2a84SRichard Fitzgerald ret = -EINVAL; 17921cab2a84SRichard Fitzgerald goto out_fw; 17931cab2a84SRichard Fitzgerald } 17941cab2a84SRichard Fitzgerald 17952159ad93SMark Brown if (text) { 17962159ad93SMark Brown memcpy(text, region->data, le32_to_cpu(region->len)); 17972159ad93SMark Brown adsp_info(dsp, "%s: %s\n", file, text); 17982159ad93SMark Brown kfree(text); 17991cab2a84SRichard Fitzgerald text = NULL; 18002159ad93SMark Brown } 18012159ad93SMark Brown 18022159ad93SMark Brown if (reg) { 1803cdcd7f72SCharles Keepax buf = wm_adsp_buf_alloc(region->data, 1804cdcd7f72SCharles Keepax le32_to_cpu(region->len), 1805cf17c83cSMark Brown &buf_list); 1806a76fefabSMark Brown if (!buf) { 1807a76fefabSMark Brown adsp_err(dsp, "Out of memory\n"); 18087328823dSDimitris Papastamos ret = -ENOMEM; 18097328823dSDimitris Papastamos goto out_fw; 1810a76fefabSMark Brown } 1811a76fefabSMark Brown 1812cdcd7f72SCharles Keepax ret = regmap_raw_write_async(regmap, reg, buf->buf, 1813cdcd7f72SCharles Keepax le32_to_cpu(region->len)); 18142159ad93SMark Brown if (ret != 0) { 18152159ad93SMark Brown adsp_err(dsp, 1816cdcd7f72SCharles Keepax "%s.%d: Failed to write %d bytes at %d in %s: %d\n", 18172159ad93SMark Brown file, regions, 1818cdcd7f72SCharles Keepax le32_to_cpu(region->len), offset, 18192159ad93SMark Brown region_name, ret); 18202159ad93SMark Brown goto out_fw; 18212159ad93SMark Brown } 18222159ad93SMark Brown } 18232159ad93SMark Brown 18242159ad93SMark Brown pos += le32_to_cpu(region->len) + sizeof(*region); 18252159ad93SMark Brown regions++; 18262159ad93SMark Brown } 18272159ad93SMark Brown 1828cf17c83cSMark Brown ret = regmap_async_complete(regmap); 1829cf17c83cSMark Brown if (ret != 0) { 1830cf17c83cSMark Brown adsp_err(dsp, "Failed to complete async write: %d\n", ret); 1831cf17c83cSMark Brown goto out_fw; 1832cf17c83cSMark Brown } 1833cf17c83cSMark Brown 18342159ad93SMark Brown if (pos > firmware->size) 18352159ad93SMark Brown adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n", 18362159ad93SMark Brown file, regions, pos - firmware->size); 18372159ad93SMark Brown 1838f9f55e31SRichard Fitzgerald wm_adsp_debugfs_save_wmfwname(dsp, file); 1839f9f55e31SRichard Fitzgerald 18402159ad93SMark Brown out_fw: 1841cf17c83cSMark Brown regmap_async_complete(regmap); 1842cf17c83cSMark Brown wm_adsp_buf_free(&buf_list); 18432159ad93SMark Brown release_firmware(firmware); 18441cab2a84SRichard Fitzgerald kfree(text); 18452159ad93SMark Brown out: 18462159ad93SMark Brown kfree(file); 18472159ad93SMark Brown 18482159ad93SMark Brown return ret; 18492159ad93SMark Brown } 18502159ad93SMark Brown 18512323736dSCharles Keepax static void wm_adsp_ctl_fixup_base(struct wm_adsp *dsp, 18522323736dSCharles Keepax const struct wm_adsp_alg_region *alg_region) 18532323736dSCharles Keepax { 18542323736dSCharles Keepax struct wm_coeff_ctl *ctl; 18552323736dSCharles Keepax 18562323736dSCharles Keepax list_for_each_entry(ctl, &dsp->ctl_list, list) { 18572323736dSCharles Keepax if (ctl->fw_name == wm_adsp_fw_text[dsp->fw] && 18582323736dSCharles Keepax alg_region->alg == ctl->alg_region.alg && 18592323736dSCharles Keepax alg_region->type == ctl->alg_region.type) { 18602323736dSCharles Keepax ctl->alg_region.base = alg_region->base; 18612323736dSCharles Keepax } 18622323736dSCharles Keepax } 18632323736dSCharles Keepax } 18642323736dSCharles Keepax 18653809f001SCharles Keepax static void *wm_adsp_read_algs(struct wm_adsp *dsp, size_t n_algs, 1866b618a185SCharles Keepax unsigned int pos, unsigned int len) 1867db40517cSMark Brown { 1868b618a185SCharles Keepax void *alg; 1869b618a185SCharles Keepax int ret; 1870db40517cSMark Brown __be32 val; 1871db40517cSMark Brown 18723809f001SCharles Keepax if (n_algs == 0) { 1873b618a185SCharles Keepax adsp_err(dsp, "No algorithms\n"); 1874b618a185SCharles Keepax return ERR_PTR(-EINVAL); 1875db40517cSMark Brown } 1876db40517cSMark Brown 18773809f001SCharles Keepax if (n_algs > 1024) { 18783809f001SCharles Keepax adsp_err(dsp, "Algorithm count %zx excessive\n", n_algs); 1879b618a185SCharles Keepax return ERR_PTR(-EINVAL); 1880b618a185SCharles Keepax } 1881b618a185SCharles Keepax 1882b618a185SCharles Keepax /* Read the terminator first to validate the length */ 1883b618a185SCharles Keepax ret = regmap_raw_read(dsp->regmap, pos + len, &val, sizeof(val)); 1884b618a185SCharles Keepax if (ret != 0) { 1885b618a185SCharles Keepax adsp_err(dsp, "Failed to read algorithm list end: %d\n", 1886b618a185SCharles Keepax ret); 1887b618a185SCharles Keepax return ERR_PTR(ret); 1888b618a185SCharles Keepax } 1889b618a185SCharles Keepax 1890b618a185SCharles Keepax if (be32_to_cpu(val) != 0xbedead) 1891503ada8aSRichard Fitzgerald adsp_warn(dsp, "Algorithm list end %x 0x%x != 0xbedead\n", 1892b618a185SCharles Keepax pos + len, be32_to_cpu(val)); 1893b618a185SCharles Keepax 1894b618a185SCharles Keepax alg = kzalloc(len * 2, GFP_KERNEL | GFP_DMA); 1895b618a185SCharles Keepax if (!alg) 1896b618a185SCharles Keepax return ERR_PTR(-ENOMEM); 1897b618a185SCharles Keepax 1898b618a185SCharles Keepax ret = regmap_raw_read(dsp->regmap, pos, alg, len * 2); 1899b618a185SCharles Keepax if (ret != 0) { 19007d00cd97SCharles Keepax adsp_err(dsp, "Failed to read algorithm list: %d\n", ret); 1901b618a185SCharles Keepax kfree(alg); 1902b618a185SCharles Keepax return ERR_PTR(ret); 1903b618a185SCharles Keepax } 1904b618a185SCharles Keepax 1905b618a185SCharles Keepax return alg; 1906b618a185SCharles Keepax } 1907b618a185SCharles Keepax 190814197095SCharles Keepax static struct wm_adsp_alg_region * 190914197095SCharles Keepax wm_adsp_find_alg_region(struct wm_adsp *dsp, int type, unsigned int id) 191014197095SCharles Keepax { 191114197095SCharles Keepax struct wm_adsp_alg_region *alg_region; 191214197095SCharles Keepax 191314197095SCharles Keepax list_for_each_entry(alg_region, &dsp->alg_regions, list) { 191414197095SCharles Keepax if (id == alg_region->alg && type == alg_region->type) 191514197095SCharles Keepax return alg_region; 191614197095SCharles Keepax } 191714197095SCharles Keepax 191814197095SCharles Keepax return NULL; 191914197095SCharles Keepax } 192014197095SCharles Keepax 1921d9d20e17SCharles Keepax static struct wm_adsp_alg_region *wm_adsp_create_region(struct wm_adsp *dsp, 1922d9d20e17SCharles Keepax int type, __be32 id, 1923d9d20e17SCharles Keepax __be32 base) 1924d9d20e17SCharles Keepax { 1925d9d20e17SCharles Keepax struct wm_adsp_alg_region *alg_region; 1926d9d20e17SCharles Keepax 1927d9d20e17SCharles Keepax alg_region = kzalloc(sizeof(*alg_region), GFP_KERNEL); 1928d9d20e17SCharles Keepax if (!alg_region) 1929d9d20e17SCharles Keepax return ERR_PTR(-ENOMEM); 1930d9d20e17SCharles Keepax 1931d9d20e17SCharles Keepax alg_region->type = type; 1932d9d20e17SCharles Keepax alg_region->alg = be32_to_cpu(id); 1933d9d20e17SCharles Keepax alg_region->base = be32_to_cpu(base); 1934d9d20e17SCharles Keepax 1935d9d20e17SCharles Keepax list_add_tail(&alg_region->list, &dsp->alg_regions); 1936d9d20e17SCharles Keepax 19372323736dSCharles Keepax if (dsp->fw_ver > 0) 19382323736dSCharles Keepax wm_adsp_ctl_fixup_base(dsp, alg_region); 19392323736dSCharles Keepax 1940d9d20e17SCharles Keepax return alg_region; 1941d9d20e17SCharles Keepax } 1942d9d20e17SCharles Keepax 194356574d54SRichard Fitzgerald static void wm_adsp_free_alg_regions(struct wm_adsp *dsp) 194456574d54SRichard Fitzgerald { 194556574d54SRichard Fitzgerald struct wm_adsp_alg_region *alg_region; 194656574d54SRichard Fitzgerald 194756574d54SRichard Fitzgerald while (!list_empty(&dsp->alg_regions)) { 194856574d54SRichard Fitzgerald alg_region = list_first_entry(&dsp->alg_regions, 194956574d54SRichard Fitzgerald struct wm_adsp_alg_region, 195056574d54SRichard Fitzgerald list); 195156574d54SRichard Fitzgerald list_del(&alg_region->list); 195256574d54SRichard Fitzgerald kfree(alg_region); 195356574d54SRichard Fitzgerald } 195456574d54SRichard Fitzgerald } 195556574d54SRichard Fitzgerald 1956b618a185SCharles Keepax static int wm_adsp1_setup_algs(struct wm_adsp *dsp) 1957b618a185SCharles Keepax { 1958b618a185SCharles Keepax struct wmfw_adsp1_id_hdr adsp1_id; 1959b618a185SCharles Keepax struct wmfw_adsp1_alg_hdr *adsp1_alg; 19603809f001SCharles Keepax struct wm_adsp_alg_region *alg_region; 1961b618a185SCharles Keepax const struct wm_adsp_region *mem; 1962b618a185SCharles Keepax unsigned int pos, len; 19633809f001SCharles Keepax size_t n_algs; 1964b618a185SCharles Keepax int i, ret; 1965b618a185SCharles Keepax 1966b618a185SCharles Keepax mem = wm_adsp_find_region(dsp, WMFW_ADSP1_DM); 19676c452bdaSTakashi Iwai if (WARN_ON(!mem)) 1968db40517cSMark Brown return -EINVAL; 1969db40517cSMark Brown 1970b618a185SCharles Keepax ret = regmap_raw_read(dsp->regmap, mem->base, &adsp1_id, 1971db40517cSMark Brown sizeof(adsp1_id)); 1972db40517cSMark Brown if (ret != 0) { 1973db40517cSMark Brown adsp_err(dsp, "Failed to read algorithm info: %d\n", 1974db40517cSMark Brown ret); 1975db40517cSMark Brown return ret; 1976db40517cSMark Brown } 1977db40517cSMark Brown 19783809f001SCharles Keepax n_algs = be32_to_cpu(adsp1_id.n_algs); 1979f395a218SMark Brown dsp->fw_id = be32_to_cpu(adsp1_id.fw.id); 1980db40517cSMark Brown adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n", 1981f395a218SMark Brown dsp->fw_id, 1982db40517cSMark Brown (be32_to_cpu(adsp1_id.fw.ver) & 0xff0000) >> 16, 1983db40517cSMark Brown (be32_to_cpu(adsp1_id.fw.ver) & 0xff00) >> 8, 1984db40517cSMark Brown be32_to_cpu(adsp1_id.fw.ver) & 0xff, 19853809f001SCharles Keepax n_algs); 1986db40517cSMark Brown 1987d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_ZM, 1988d9d20e17SCharles Keepax adsp1_id.fw.id, adsp1_id.zm); 1989d9d20e17SCharles Keepax if (IS_ERR(alg_region)) 1990d9d20e17SCharles Keepax return PTR_ERR(alg_region); 1991ac50009fSMark Brown 1992d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_DM, 1993d9d20e17SCharles Keepax adsp1_id.fw.id, adsp1_id.dm); 1994d9d20e17SCharles Keepax if (IS_ERR(alg_region)) 1995d9d20e17SCharles Keepax return PTR_ERR(alg_region); 1996ac50009fSMark Brown 1997db40517cSMark Brown pos = sizeof(adsp1_id) / 2; 19983809f001SCharles Keepax len = (sizeof(*adsp1_alg) * n_algs) / 2; 1999db40517cSMark Brown 20003809f001SCharles Keepax adsp1_alg = wm_adsp_read_algs(dsp, n_algs, mem->base + pos, len); 2001b618a185SCharles Keepax if (IS_ERR(adsp1_alg)) 2002b618a185SCharles Keepax return PTR_ERR(adsp1_alg); 2003db40517cSMark Brown 20043809f001SCharles Keepax for (i = 0; i < n_algs; i++) { 2005471f4885SMark Brown adsp_info(dsp, "%d: ID %x v%d.%d.%d DM@%x ZM@%x\n", 2006db40517cSMark Brown i, be32_to_cpu(adsp1_alg[i].alg.id), 2007db40517cSMark Brown (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff0000) >> 16, 2008db40517cSMark Brown (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff00) >> 8, 2009471f4885SMark Brown be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff, 2010471f4885SMark Brown be32_to_cpu(adsp1_alg[i].dm), 2011471f4885SMark Brown be32_to_cpu(adsp1_alg[i].zm)); 2012471f4885SMark Brown 2013d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_DM, 2014d9d20e17SCharles Keepax adsp1_alg[i].alg.id, 2015d9d20e17SCharles Keepax adsp1_alg[i].dm); 2016d9d20e17SCharles Keepax if (IS_ERR(alg_region)) { 2017d9d20e17SCharles Keepax ret = PTR_ERR(alg_region); 2018d6d52179SJS Park goto out; 2019d6d52179SJS Park } 20202323736dSCharles Keepax if (dsp->fw_ver == 0) { 20213809f001SCharles Keepax if (i + 1 < n_algs) { 20226958eb2aSCharles Keepax len = be32_to_cpu(adsp1_alg[i + 1].dm); 20236958eb2aSCharles Keepax len -= be32_to_cpu(adsp1_alg[i].dm); 20246958eb2aSCharles Keepax len *= 4; 20252323736dSCharles Keepax wm_adsp_create_control(dsp, alg_region, 0, 20268eb084d0SStuart Henderson len, NULL, 0, 0, 20278eb084d0SStuart Henderson SNDRV_CTL_ELEM_TYPE_BYTES); 20286ab2b7b4SDimitris Papastamos } else { 20296ab2b7b4SDimitris Papastamos adsp_warn(dsp, "Missing length info for region DM with ID %x\n", 20306ab2b7b4SDimitris Papastamos be32_to_cpu(adsp1_alg[i].alg.id)); 20316ab2b7b4SDimitris Papastamos } 20322323736dSCharles Keepax } 2033471f4885SMark Brown 2034d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_ZM, 2035d9d20e17SCharles Keepax adsp1_alg[i].alg.id, 2036d9d20e17SCharles Keepax adsp1_alg[i].zm); 2037d9d20e17SCharles Keepax if (IS_ERR(alg_region)) { 2038d9d20e17SCharles Keepax ret = PTR_ERR(alg_region); 2039d6d52179SJS Park goto out; 2040d6d52179SJS Park } 20412323736dSCharles Keepax if (dsp->fw_ver == 0) { 20423809f001SCharles Keepax if (i + 1 < n_algs) { 20436958eb2aSCharles Keepax len = be32_to_cpu(adsp1_alg[i + 1].zm); 20446958eb2aSCharles Keepax len -= be32_to_cpu(adsp1_alg[i].zm); 20456958eb2aSCharles Keepax len *= 4; 20462323736dSCharles Keepax wm_adsp_create_control(dsp, alg_region, 0, 20478eb084d0SStuart Henderson len, NULL, 0, 0, 20488eb084d0SStuart Henderson SNDRV_CTL_ELEM_TYPE_BYTES); 20496ab2b7b4SDimitris Papastamos } else { 20506ab2b7b4SDimitris Papastamos adsp_warn(dsp, "Missing length info for region ZM with ID %x\n", 20516ab2b7b4SDimitris Papastamos be32_to_cpu(adsp1_alg[i].alg.id)); 20526ab2b7b4SDimitris Papastamos } 2053b618a185SCharles Keepax } 20542323736dSCharles Keepax } 2055db40517cSMark Brown 2056b618a185SCharles Keepax out: 2057b618a185SCharles Keepax kfree(adsp1_alg); 2058b618a185SCharles Keepax return ret; 2059b618a185SCharles Keepax } 2060b618a185SCharles Keepax 2061b618a185SCharles Keepax static int wm_adsp2_setup_algs(struct wm_adsp *dsp) 2062b618a185SCharles Keepax { 2063b618a185SCharles Keepax struct wmfw_adsp2_id_hdr adsp2_id; 2064b618a185SCharles Keepax struct wmfw_adsp2_alg_hdr *adsp2_alg; 20653809f001SCharles Keepax struct wm_adsp_alg_region *alg_region; 2066b618a185SCharles Keepax const struct wm_adsp_region *mem; 2067b618a185SCharles Keepax unsigned int pos, len; 20683809f001SCharles Keepax size_t n_algs; 2069b618a185SCharles Keepax int i, ret; 2070b618a185SCharles Keepax 2071b618a185SCharles Keepax mem = wm_adsp_find_region(dsp, WMFW_ADSP2_XM); 2072b618a185SCharles Keepax if (WARN_ON(!mem)) 2073b618a185SCharles Keepax return -EINVAL; 2074b618a185SCharles Keepax 2075b618a185SCharles Keepax ret = regmap_raw_read(dsp->regmap, mem->base, &adsp2_id, 2076b618a185SCharles Keepax sizeof(adsp2_id)); 2077b618a185SCharles Keepax if (ret != 0) { 2078b618a185SCharles Keepax adsp_err(dsp, "Failed to read algorithm info: %d\n", 2079b618a185SCharles Keepax ret); 2080b618a185SCharles Keepax return ret; 2081b618a185SCharles Keepax } 2082b618a185SCharles Keepax 20833809f001SCharles Keepax n_algs = be32_to_cpu(adsp2_id.n_algs); 2084b618a185SCharles Keepax dsp->fw_id = be32_to_cpu(adsp2_id.fw.id); 2085f9f55e31SRichard Fitzgerald dsp->fw_id_version = be32_to_cpu(adsp2_id.fw.ver); 2086b618a185SCharles Keepax adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n", 2087b618a185SCharles Keepax dsp->fw_id, 2088f9f55e31SRichard Fitzgerald (dsp->fw_id_version & 0xff0000) >> 16, 2089f9f55e31SRichard Fitzgerald (dsp->fw_id_version & 0xff00) >> 8, 2090f9f55e31SRichard Fitzgerald dsp->fw_id_version & 0xff, 20913809f001SCharles Keepax n_algs); 2092b618a185SCharles Keepax 2093d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_XM, 2094d9d20e17SCharles Keepax adsp2_id.fw.id, adsp2_id.xm); 2095d9d20e17SCharles Keepax if (IS_ERR(alg_region)) 2096d9d20e17SCharles Keepax return PTR_ERR(alg_region); 2097b618a185SCharles Keepax 2098d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_YM, 2099d9d20e17SCharles Keepax adsp2_id.fw.id, adsp2_id.ym); 2100d9d20e17SCharles Keepax if (IS_ERR(alg_region)) 2101d9d20e17SCharles Keepax return PTR_ERR(alg_region); 2102b618a185SCharles Keepax 2103d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_ZM, 2104d9d20e17SCharles Keepax adsp2_id.fw.id, adsp2_id.zm); 2105d9d20e17SCharles Keepax if (IS_ERR(alg_region)) 2106d9d20e17SCharles Keepax return PTR_ERR(alg_region); 2107b618a185SCharles Keepax 2108b618a185SCharles Keepax pos = sizeof(adsp2_id) / 2; 21093809f001SCharles Keepax len = (sizeof(*adsp2_alg) * n_algs) / 2; 2110b618a185SCharles Keepax 21113809f001SCharles Keepax adsp2_alg = wm_adsp_read_algs(dsp, n_algs, mem->base + pos, len); 2112b618a185SCharles Keepax if (IS_ERR(adsp2_alg)) 2113b618a185SCharles Keepax return PTR_ERR(adsp2_alg); 2114b618a185SCharles Keepax 21153809f001SCharles Keepax for (i = 0; i < n_algs; i++) { 2116471f4885SMark Brown adsp_info(dsp, 2117471f4885SMark Brown "%d: ID %x v%d.%d.%d XM@%x YM@%x ZM@%x\n", 2118db40517cSMark Brown i, be32_to_cpu(adsp2_alg[i].alg.id), 2119db40517cSMark Brown (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff0000) >> 16, 2120db40517cSMark Brown (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff00) >> 8, 2121471f4885SMark Brown be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff, 2122471f4885SMark Brown be32_to_cpu(adsp2_alg[i].xm), 2123471f4885SMark Brown be32_to_cpu(adsp2_alg[i].ym), 2124471f4885SMark Brown be32_to_cpu(adsp2_alg[i].zm)); 2125471f4885SMark Brown 2126d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_XM, 2127d9d20e17SCharles Keepax adsp2_alg[i].alg.id, 2128d9d20e17SCharles Keepax adsp2_alg[i].xm); 2129d9d20e17SCharles Keepax if (IS_ERR(alg_region)) { 2130d9d20e17SCharles Keepax ret = PTR_ERR(alg_region); 2131d6d52179SJS Park goto out; 2132d6d52179SJS Park } 21332323736dSCharles Keepax if (dsp->fw_ver == 0) { 21343809f001SCharles Keepax if (i + 1 < n_algs) { 21356958eb2aSCharles Keepax len = be32_to_cpu(adsp2_alg[i + 1].xm); 21366958eb2aSCharles Keepax len -= be32_to_cpu(adsp2_alg[i].xm); 21376958eb2aSCharles Keepax len *= 4; 21382323736dSCharles Keepax wm_adsp_create_control(dsp, alg_region, 0, 21398eb084d0SStuart Henderson len, NULL, 0, 0, 21408eb084d0SStuart Henderson SNDRV_CTL_ELEM_TYPE_BYTES); 21416ab2b7b4SDimitris Papastamos } else { 21426ab2b7b4SDimitris Papastamos adsp_warn(dsp, "Missing length info for region XM with ID %x\n", 21436ab2b7b4SDimitris Papastamos be32_to_cpu(adsp2_alg[i].alg.id)); 21446ab2b7b4SDimitris Papastamos } 21452323736dSCharles Keepax } 2146471f4885SMark Brown 2147d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_YM, 2148d9d20e17SCharles Keepax adsp2_alg[i].alg.id, 2149d9d20e17SCharles Keepax adsp2_alg[i].ym); 2150d9d20e17SCharles Keepax if (IS_ERR(alg_region)) { 2151d9d20e17SCharles Keepax ret = PTR_ERR(alg_region); 2152d6d52179SJS Park goto out; 2153d6d52179SJS Park } 21542323736dSCharles Keepax if (dsp->fw_ver == 0) { 21553809f001SCharles Keepax if (i + 1 < n_algs) { 21566958eb2aSCharles Keepax len = be32_to_cpu(adsp2_alg[i + 1].ym); 21576958eb2aSCharles Keepax len -= be32_to_cpu(adsp2_alg[i].ym); 21586958eb2aSCharles Keepax len *= 4; 21592323736dSCharles Keepax wm_adsp_create_control(dsp, alg_region, 0, 21608eb084d0SStuart Henderson len, NULL, 0, 0, 21618eb084d0SStuart Henderson SNDRV_CTL_ELEM_TYPE_BYTES); 21626ab2b7b4SDimitris Papastamos } else { 21636ab2b7b4SDimitris Papastamos adsp_warn(dsp, "Missing length info for region YM with ID %x\n", 21646ab2b7b4SDimitris Papastamos be32_to_cpu(adsp2_alg[i].alg.id)); 21656ab2b7b4SDimitris Papastamos } 21662323736dSCharles Keepax } 2167471f4885SMark Brown 2168d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_ZM, 2169d9d20e17SCharles Keepax adsp2_alg[i].alg.id, 2170d9d20e17SCharles Keepax adsp2_alg[i].zm); 2171d9d20e17SCharles Keepax if (IS_ERR(alg_region)) { 2172d9d20e17SCharles Keepax ret = PTR_ERR(alg_region); 2173d6d52179SJS Park goto out; 2174d6d52179SJS Park } 21752323736dSCharles Keepax if (dsp->fw_ver == 0) { 21763809f001SCharles Keepax if (i + 1 < n_algs) { 21776958eb2aSCharles Keepax len = be32_to_cpu(adsp2_alg[i + 1].zm); 21786958eb2aSCharles Keepax len -= be32_to_cpu(adsp2_alg[i].zm); 21796958eb2aSCharles Keepax len *= 4; 21802323736dSCharles Keepax wm_adsp_create_control(dsp, alg_region, 0, 21818eb084d0SStuart Henderson len, NULL, 0, 0, 21828eb084d0SStuart Henderson SNDRV_CTL_ELEM_TYPE_BYTES); 21836ab2b7b4SDimitris Papastamos } else { 21846ab2b7b4SDimitris Papastamos adsp_warn(dsp, "Missing length info for region ZM with ID %x\n", 21856ab2b7b4SDimitris Papastamos be32_to_cpu(adsp2_alg[i].alg.id)); 21866ab2b7b4SDimitris Papastamos } 2187db40517cSMark Brown } 21882323736dSCharles Keepax } 2189db40517cSMark Brown 2190db40517cSMark Brown out: 2191b618a185SCharles Keepax kfree(adsp2_alg); 2192db40517cSMark Brown return ret; 2193db40517cSMark Brown } 2194db40517cSMark Brown 21952159ad93SMark Brown static int wm_adsp_load_coeff(struct wm_adsp *dsp) 21962159ad93SMark Brown { 2197cf17c83cSMark Brown LIST_HEAD(buf_list); 21982159ad93SMark Brown struct regmap *regmap = dsp->regmap; 21992159ad93SMark Brown struct wmfw_coeff_hdr *hdr; 22002159ad93SMark Brown struct wmfw_coeff_item *blk; 22012159ad93SMark Brown const struct firmware *firmware; 2202471f4885SMark Brown const struct wm_adsp_region *mem; 2203471f4885SMark Brown struct wm_adsp_alg_region *alg_region; 22042159ad93SMark Brown const char *region_name; 22052159ad93SMark Brown int ret, pos, blocks, type, offset, reg; 22062159ad93SMark Brown char *file; 2207cf17c83cSMark Brown struct wm_adsp_buf *buf; 22082159ad93SMark Brown 22092159ad93SMark Brown file = kzalloc(PAGE_SIZE, GFP_KERNEL); 22102159ad93SMark Brown if (file == NULL) 22112159ad93SMark Brown return -ENOMEM; 22122159ad93SMark Brown 22131023dbd9SMark Brown snprintf(file, PAGE_SIZE, "%s-dsp%d-%s.bin", dsp->part, dsp->num, 22141023dbd9SMark Brown wm_adsp_fw[dsp->fw].file); 22152159ad93SMark Brown file[PAGE_SIZE - 1] = '\0'; 22162159ad93SMark Brown 22172159ad93SMark Brown ret = request_firmware(&firmware, file, dsp->dev); 22182159ad93SMark Brown if (ret != 0) { 22192159ad93SMark Brown adsp_warn(dsp, "Failed to request '%s'\n", file); 22202159ad93SMark Brown ret = 0; 22212159ad93SMark Brown goto out; 22222159ad93SMark Brown } 22232159ad93SMark Brown ret = -EINVAL; 22242159ad93SMark Brown 22252159ad93SMark Brown if (sizeof(*hdr) >= firmware->size) { 22262159ad93SMark Brown adsp_err(dsp, "%s: file too short, %zu bytes\n", 22272159ad93SMark Brown file, firmware->size); 22282159ad93SMark Brown goto out_fw; 22292159ad93SMark Brown } 22302159ad93SMark Brown 22312159ad93SMark Brown hdr = (void *)&firmware->data[0]; 22322159ad93SMark Brown if (memcmp(hdr->magic, "WMDR", 4) != 0) { 22332159ad93SMark Brown adsp_err(dsp, "%s: invalid magic\n", file); 2234a4cdbec7SCharles Keepax goto out_fw; 22352159ad93SMark Brown } 22362159ad93SMark Brown 2237c712326dSMark Brown switch (be32_to_cpu(hdr->rev) & 0xff) { 2238c712326dSMark Brown case 1: 2239c712326dSMark Brown break; 2240c712326dSMark Brown default: 2241c712326dSMark Brown adsp_err(dsp, "%s: Unsupported coefficient file format %d\n", 2242c712326dSMark Brown file, be32_to_cpu(hdr->rev) & 0xff); 2243c712326dSMark Brown ret = -EINVAL; 2244c712326dSMark Brown goto out_fw; 2245c712326dSMark Brown } 2246c712326dSMark Brown 22472159ad93SMark Brown adsp_dbg(dsp, "%s: v%d.%d.%d\n", file, 22482159ad93SMark Brown (le32_to_cpu(hdr->ver) >> 16) & 0xff, 22492159ad93SMark Brown (le32_to_cpu(hdr->ver) >> 8) & 0xff, 22502159ad93SMark Brown le32_to_cpu(hdr->ver) & 0xff); 22512159ad93SMark Brown 22522159ad93SMark Brown pos = le32_to_cpu(hdr->len); 22532159ad93SMark Brown 22542159ad93SMark Brown blocks = 0; 22552159ad93SMark Brown while (pos < firmware->size && 225650dd2ea8SBen Hutchings sizeof(*blk) < firmware->size - pos) { 22572159ad93SMark Brown blk = (void *)(&firmware->data[pos]); 22582159ad93SMark Brown 2259c712326dSMark Brown type = le16_to_cpu(blk->type); 2260c712326dSMark Brown offset = le16_to_cpu(blk->offset); 22612159ad93SMark Brown 22622159ad93SMark Brown adsp_dbg(dsp, "%s.%d: %x v%d.%d.%d\n", 22632159ad93SMark Brown file, blocks, le32_to_cpu(blk->id), 22642159ad93SMark Brown (le32_to_cpu(blk->ver) >> 16) & 0xff, 22652159ad93SMark Brown (le32_to_cpu(blk->ver) >> 8) & 0xff, 22662159ad93SMark Brown le32_to_cpu(blk->ver) & 0xff); 22672159ad93SMark Brown adsp_dbg(dsp, "%s.%d: %d bytes at 0x%x in %x\n", 22682159ad93SMark Brown file, blocks, le32_to_cpu(blk->len), offset, type); 22692159ad93SMark Brown 22702159ad93SMark Brown reg = 0; 22712159ad93SMark Brown region_name = "Unknown"; 22722159ad93SMark Brown switch (type) { 2273c712326dSMark Brown case (WMFW_NAME_TEXT << 8): 2274c712326dSMark Brown case (WMFW_INFO_TEXT << 8): 22752159ad93SMark Brown break; 2276c712326dSMark Brown case (WMFW_ABSOLUTE << 8): 2277f395a218SMark Brown /* 2278f395a218SMark Brown * Old files may use this for global 2279f395a218SMark Brown * coefficients. 2280f395a218SMark Brown */ 2281f395a218SMark Brown if (le32_to_cpu(blk->id) == dsp->fw_id && 2282f395a218SMark Brown offset == 0) { 2283f395a218SMark Brown region_name = "global coefficients"; 2284f395a218SMark Brown mem = wm_adsp_find_region(dsp, type); 2285f395a218SMark Brown if (!mem) { 2286f395a218SMark Brown adsp_err(dsp, "No ZM\n"); 2287f395a218SMark Brown break; 2288f395a218SMark Brown } 2289f395a218SMark Brown reg = wm_adsp_region_to_reg(mem, 0); 2290f395a218SMark Brown 2291f395a218SMark Brown } else { 22922159ad93SMark Brown region_name = "register"; 22932159ad93SMark Brown reg = offset; 2294f395a218SMark Brown } 22952159ad93SMark Brown break; 2296471f4885SMark Brown 2297471f4885SMark Brown case WMFW_ADSP1_DM: 2298471f4885SMark Brown case WMFW_ADSP1_ZM: 2299471f4885SMark Brown case WMFW_ADSP2_XM: 2300471f4885SMark Brown case WMFW_ADSP2_YM: 2301471f4885SMark Brown adsp_dbg(dsp, "%s.%d: %d bytes in %x for %x\n", 2302471f4885SMark Brown file, blocks, le32_to_cpu(blk->len), 2303471f4885SMark Brown type, le32_to_cpu(blk->id)); 2304471f4885SMark Brown 2305471f4885SMark Brown mem = wm_adsp_find_region(dsp, type); 2306471f4885SMark Brown if (!mem) { 2307471f4885SMark Brown adsp_err(dsp, "No base for region %x\n", type); 2308471f4885SMark Brown break; 2309471f4885SMark Brown } 2310471f4885SMark Brown 231114197095SCharles Keepax alg_region = wm_adsp_find_alg_region(dsp, type, 231214197095SCharles Keepax le32_to_cpu(blk->id)); 231314197095SCharles Keepax if (alg_region) { 2314338c5188SMark Brown reg = alg_region->base; 231514197095SCharles Keepax reg = wm_adsp_region_to_reg(mem, reg); 2316338c5188SMark Brown reg += offset; 231714197095SCharles Keepax } else { 2318471f4885SMark Brown adsp_err(dsp, "No %x for algorithm %x\n", 2319471f4885SMark Brown type, le32_to_cpu(blk->id)); 232014197095SCharles Keepax } 2321471f4885SMark Brown break; 2322471f4885SMark Brown 23232159ad93SMark Brown default: 232425c62f7eSMark Brown adsp_err(dsp, "%s.%d: Unknown region type %x at %d\n", 232525c62f7eSMark Brown file, blocks, type, pos); 23262159ad93SMark Brown break; 23272159ad93SMark Brown } 23282159ad93SMark Brown 23292159ad93SMark Brown if (reg) { 233050dd2ea8SBen Hutchings if (le32_to_cpu(blk->len) > 233150dd2ea8SBen Hutchings firmware->size - pos - sizeof(*blk)) { 23321cab2a84SRichard Fitzgerald adsp_err(dsp, 23331cab2a84SRichard Fitzgerald "%s.%d: %s region len %d bytes exceeds file length %zu\n", 23341cab2a84SRichard Fitzgerald file, blocks, region_name, 23351cab2a84SRichard Fitzgerald le32_to_cpu(blk->len), 23361cab2a84SRichard Fitzgerald firmware->size); 23371cab2a84SRichard Fitzgerald ret = -EINVAL; 23381cab2a84SRichard Fitzgerald goto out_fw; 23391cab2a84SRichard Fitzgerald } 23401cab2a84SRichard Fitzgerald 2341cf17c83cSMark Brown buf = wm_adsp_buf_alloc(blk->data, 2342cf17c83cSMark Brown le32_to_cpu(blk->len), 2343cf17c83cSMark Brown &buf_list); 2344a76fefabSMark Brown if (!buf) { 2345a76fefabSMark Brown adsp_err(dsp, "Out of memory\n"); 2346f4b82812SWei Yongjun ret = -ENOMEM; 2347f4b82812SWei Yongjun goto out_fw; 2348a76fefabSMark Brown } 2349a76fefabSMark Brown 235020da6d5aSMark Brown adsp_dbg(dsp, "%s.%d: Writing %d bytes at %x\n", 235120da6d5aSMark Brown file, blocks, le32_to_cpu(blk->len), 235220da6d5aSMark Brown reg); 2353cf17c83cSMark Brown ret = regmap_raw_write_async(regmap, reg, buf->buf, 23542159ad93SMark Brown le32_to_cpu(blk->len)); 23552159ad93SMark Brown if (ret != 0) { 23562159ad93SMark Brown adsp_err(dsp, 235743bc3bf6SDimitris Papastamos "%s.%d: Failed to write to %x in %s: %d\n", 235843bc3bf6SDimitris Papastamos file, blocks, reg, region_name, ret); 23592159ad93SMark Brown } 23602159ad93SMark Brown } 23612159ad93SMark Brown 2362be951017SCharles Keepax pos += (le32_to_cpu(blk->len) + sizeof(*blk) + 3) & ~0x03; 23632159ad93SMark Brown blocks++; 23642159ad93SMark Brown } 23652159ad93SMark Brown 2366cf17c83cSMark Brown ret = regmap_async_complete(regmap); 2367cf17c83cSMark Brown if (ret != 0) 2368cf17c83cSMark Brown adsp_err(dsp, "Failed to complete async write: %d\n", ret); 2369cf17c83cSMark Brown 23702159ad93SMark Brown if (pos > firmware->size) 23712159ad93SMark Brown adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n", 23722159ad93SMark Brown file, blocks, pos - firmware->size); 23732159ad93SMark Brown 2374f9f55e31SRichard Fitzgerald wm_adsp_debugfs_save_binname(dsp, file); 2375f9f55e31SRichard Fitzgerald 23762159ad93SMark Brown out_fw: 23779da7a5a9SCharles Keepax regmap_async_complete(regmap); 23782159ad93SMark Brown release_firmware(firmware); 2379cf17c83cSMark Brown wm_adsp_buf_free(&buf_list); 23802159ad93SMark Brown out: 23812159ad93SMark Brown kfree(file); 2382f4b82812SWei Yongjun return ret; 23832159ad93SMark Brown } 23842159ad93SMark Brown 23853809f001SCharles Keepax int wm_adsp1_init(struct wm_adsp *dsp) 23865e7a7a22SMark Brown { 23873809f001SCharles Keepax INIT_LIST_HEAD(&dsp->alg_regions); 23885e7a7a22SMark Brown 2389078e7183SCharles Keepax mutex_init(&dsp->pwr_lock); 2390078e7183SCharles Keepax 23915e7a7a22SMark Brown return 0; 23925e7a7a22SMark Brown } 23935e7a7a22SMark Brown EXPORT_SYMBOL_GPL(wm_adsp1_init); 23945e7a7a22SMark Brown 23952159ad93SMark Brown int wm_adsp1_event(struct snd_soc_dapm_widget *w, 23962159ad93SMark Brown struct snd_kcontrol *kcontrol, 23972159ad93SMark Brown int event) 23982159ad93SMark Brown { 239972718517SLars-Peter Clausen struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); 24002159ad93SMark Brown struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec); 24012159ad93SMark Brown struct wm_adsp *dsp = &dsps[w->shift]; 24026ab2b7b4SDimitris Papastamos struct wm_coeff_ctl *ctl; 24032159ad93SMark Brown int ret; 24047585a5b0SCharles Keepax unsigned int val; 24052159ad93SMark Brown 2406685f51a5SRichard Fitzgerald dsp->codec = codec; 240792bb4c32SDimitris Papastamos 2408078e7183SCharles Keepax mutex_lock(&dsp->pwr_lock); 2409078e7183SCharles Keepax 24102159ad93SMark Brown switch (event) { 24112159ad93SMark Brown case SND_SOC_DAPM_POST_PMU: 24122159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 24132159ad93SMark Brown ADSP1_SYS_ENA, ADSP1_SYS_ENA); 24142159ad93SMark Brown 241594e205bfSChris Rattray /* 241694e205bfSChris Rattray * For simplicity set the DSP clock rate to be the 241794e205bfSChris Rattray * SYSCLK rate rather than making it configurable. 241894e205bfSChris Rattray */ 241994e205bfSChris Rattray if (dsp->sysclk_reg) { 242094e205bfSChris Rattray ret = regmap_read(dsp->regmap, dsp->sysclk_reg, &val); 242194e205bfSChris Rattray if (ret != 0) { 242294e205bfSChris Rattray adsp_err(dsp, "Failed to read SYSCLK state: %d\n", 242394e205bfSChris Rattray ret); 2424078e7183SCharles Keepax goto err_mutex; 242594e205bfSChris Rattray } 242694e205bfSChris Rattray 24277d00cd97SCharles Keepax val = (val & dsp->sysclk_mask) >> dsp->sysclk_shift; 242894e205bfSChris Rattray 242994e205bfSChris Rattray ret = regmap_update_bits(dsp->regmap, 243094e205bfSChris Rattray dsp->base + ADSP1_CONTROL_31, 243194e205bfSChris Rattray ADSP1_CLK_SEL_MASK, val); 243294e205bfSChris Rattray if (ret != 0) { 243394e205bfSChris Rattray adsp_err(dsp, "Failed to set clock rate: %d\n", 243494e205bfSChris Rattray ret); 2435078e7183SCharles Keepax goto err_mutex; 243694e205bfSChris Rattray } 243794e205bfSChris Rattray } 243894e205bfSChris Rattray 24392159ad93SMark Brown ret = wm_adsp_load(dsp); 24402159ad93SMark Brown if (ret != 0) 2441078e7183SCharles Keepax goto err_ena; 24422159ad93SMark Brown 2443b618a185SCharles Keepax ret = wm_adsp1_setup_algs(dsp); 2444db40517cSMark Brown if (ret != 0) 2445078e7183SCharles Keepax goto err_ena; 2446db40517cSMark Brown 24472159ad93SMark Brown ret = wm_adsp_load_coeff(dsp); 24482159ad93SMark Brown if (ret != 0) 2449078e7183SCharles Keepax goto err_ena; 24502159ad93SMark Brown 24510c2e3f34SDimitris Papastamos /* Initialize caches for enabled and unset controls */ 245281ad93ecSDimitris Papastamos ret = wm_coeff_init_control_caches(dsp); 24536ab2b7b4SDimitris Papastamos if (ret != 0) 2454078e7183SCharles Keepax goto err_ena; 24556ab2b7b4SDimitris Papastamos 24560c2e3f34SDimitris Papastamos /* Sync set controls */ 245781ad93ecSDimitris Papastamos ret = wm_coeff_sync_controls(dsp); 24586ab2b7b4SDimitris Papastamos if (ret != 0) 2459078e7183SCharles Keepax goto err_ena; 24606ab2b7b4SDimitris Papastamos 246128823ebaSCharles Keepax dsp->booted = true; 246228823ebaSCharles Keepax 24632159ad93SMark Brown /* Start the core running */ 24642159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 24652159ad93SMark Brown ADSP1_CORE_ENA | ADSP1_START, 24662159ad93SMark Brown ADSP1_CORE_ENA | ADSP1_START); 246728823ebaSCharles Keepax 246828823ebaSCharles Keepax dsp->running = true; 24692159ad93SMark Brown break; 24702159ad93SMark Brown 24712159ad93SMark Brown case SND_SOC_DAPM_PRE_PMD: 247228823ebaSCharles Keepax dsp->running = false; 247328823ebaSCharles Keepax dsp->booted = false; 247428823ebaSCharles Keepax 24752159ad93SMark Brown /* Halt the core */ 24762159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 24772159ad93SMark Brown ADSP1_CORE_ENA | ADSP1_START, 0); 24782159ad93SMark Brown 24792159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_19, 24802159ad93SMark Brown ADSP1_WDMA_BUFFER_LENGTH_MASK, 0); 24812159ad93SMark Brown 24822159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 24832159ad93SMark Brown ADSP1_SYS_ENA, 0); 24846ab2b7b4SDimitris Papastamos 248581ad93ecSDimitris Papastamos list_for_each_entry(ctl, &dsp->ctl_list, list) 24866ab2b7b4SDimitris Papastamos ctl->enabled = 0; 2487b0101b4fSDimitris Papastamos 248856574d54SRichard Fitzgerald 248956574d54SRichard Fitzgerald wm_adsp_free_alg_regions(dsp); 24902159ad93SMark Brown break; 24912159ad93SMark Brown 24922159ad93SMark Brown default: 24932159ad93SMark Brown break; 24942159ad93SMark Brown } 24952159ad93SMark Brown 2496078e7183SCharles Keepax mutex_unlock(&dsp->pwr_lock); 2497078e7183SCharles Keepax 24982159ad93SMark Brown return 0; 24992159ad93SMark Brown 2500078e7183SCharles Keepax err_ena: 25012159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 25022159ad93SMark Brown ADSP1_SYS_ENA, 0); 2503078e7183SCharles Keepax err_mutex: 2504078e7183SCharles Keepax mutex_unlock(&dsp->pwr_lock); 2505078e7183SCharles Keepax 25062159ad93SMark Brown return ret; 25072159ad93SMark Brown } 25082159ad93SMark Brown EXPORT_SYMBOL_GPL(wm_adsp1_event); 25092159ad93SMark Brown 25102159ad93SMark Brown static int wm_adsp2_ena(struct wm_adsp *dsp) 25112159ad93SMark Brown { 25122159ad93SMark Brown unsigned int val; 25132159ad93SMark Brown int ret, count; 25142159ad93SMark Brown 2515e1ea1879SRichard Fitzgerald switch (dsp->rev) { 2516e1ea1879SRichard Fitzgerald case 0: 2517e1ea1879SRichard Fitzgerald ret = regmap_update_bits_async(dsp->regmap, 2518e1ea1879SRichard Fitzgerald dsp->base + ADSP2_CONTROL, 25192159ad93SMark Brown ADSP2_SYS_ENA, ADSP2_SYS_ENA); 25202159ad93SMark Brown if (ret != 0) 25212159ad93SMark Brown return ret; 2522e1ea1879SRichard Fitzgerald break; 2523e1ea1879SRichard Fitzgerald default: 2524e1ea1879SRichard Fitzgerald break; 2525e1ea1879SRichard Fitzgerald } 25262159ad93SMark Brown 25272159ad93SMark Brown /* Wait for the RAM to start, should be near instantaneous */ 2528939fd1e8SCharles Keepax for (count = 0; count < 10; ++count) { 25297d00cd97SCharles Keepax ret = regmap_read(dsp->regmap, dsp->base + ADSP2_STATUS1, &val); 25302159ad93SMark Brown if (ret != 0) 25312159ad93SMark Brown return ret; 2532939fd1e8SCharles Keepax 2533939fd1e8SCharles Keepax if (val & ADSP2_RAM_RDY) 2534939fd1e8SCharles Keepax break; 2535939fd1e8SCharles Keepax 25361fa96f3fSCharles Keepax usleep_range(250, 500); 2537939fd1e8SCharles Keepax } 25382159ad93SMark Brown 25392159ad93SMark Brown if (!(val & ADSP2_RAM_RDY)) { 25402159ad93SMark Brown adsp_err(dsp, "Failed to start DSP RAM\n"); 25412159ad93SMark Brown return -EBUSY; 25422159ad93SMark Brown } 25432159ad93SMark Brown 25442159ad93SMark Brown adsp_dbg(dsp, "RAM ready after %d polls\n", count); 25452159ad93SMark Brown 25462159ad93SMark Brown return 0; 25472159ad93SMark Brown } 25482159ad93SMark Brown 254918b1a902SCharles Keepax static void wm_adsp2_boot_work(struct work_struct *work) 25502159ad93SMark Brown { 2551d8a64d6aSCharles Keepax struct wm_adsp *dsp = container_of(work, 2552d8a64d6aSCharles Keepax struct wm_adsp, 2553d8a64d6aSCharles Keepax boot_work); 25542159ad93SMark Brown int ret; 25552159ad93SMark Brown 2556078e7183SCharles Keepax mutex_lock(&dsp->pwr_lock); 2557078e7183SCharles Keepax 255890d19ba5SCharles Keepax ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 255990d19ba5SCharles Keepax ADSP2_MEM_ENA, ADSP2_MEM_ENA); 256090d19ba5SCharles Keepax if (ret != 0) 256190d19ba5SCharles Keepax goto err_mutex; 256290d19ba5SCharles Keepax 25632159ad93SMark Brown ret = wm_adsp2_ena(dsp); 25642159ad93SMark Brown if (ret != 0) 2565d589d8b8SCharles Keepax goto err_mem; 25662159ad93SMark Brown 25672159ad93SMark Brown ret = wm_adsp_load(dsp); 25682159ad93SMark Brown if (ret != 0) 2569078e7183SCharles Keepax goto err_ena; 25702159ad93SMark Brown 2571b618a185SCharles Keepax ret = wm_adsp2_setup_algs(dsp); 2572db40517cSMark Brown if (ret != 0) 2573078e7183SCharles Keepax goto err_ena; 2574db40517cSMark Brown 25752159ad93SMark Brown ret = wm_adsp_load_coeff(dsp); 25762159ad93SMark Brown if (ret != 0) 2577078e7183SCharles Keepax goto err_ena; 25782159ad93SMark Brown 25790c2e3f34SDimitris Papastamos /* Initialize caches for enabled and unset controls */ 258081ad93ecSDimitris Papastamos ret = wm_coeff_init_control_caches(dsp); 25816ab2b7b4SDimitris Papastamos if (ret != 0) 2582078e7183SCharles Keepax goto err_ena; 25836ab2b7b4SDimitris Papastamos 2584e1ea1879SRichard Fitzgerald switch (dsp->rev) { 2585e1ea1879SRichard Fitzgerald case 0: 258690d19ba5SCharles Keepax /* Turn DSP back off until we are ready to run */ 258790d19ba5SCharles Keepax ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 258890d19ba5SCharles Keepax ADSP2_SYS_ENA, 0); 258990d19ba5SCharles Keepax if (ret != 0) 259090d19ba5SCharles Keepax goto err_ena; 2591e1ea1879SRichard Fitzgerald break; 2592e1ea1879SRichard Fitzgerald default: 2593e1ea1879SRichard Fitzgerald break; 2594e1ea1879SRichard Fitzgerald } 259590d19ba5SCharles Keepax 2596e779974bSCharles Keepax dsp->booted = true; 2597e779974bSCharles Keepax 2598078e7183SCharles Keepax mutex_unlock(&dsp->pwr_lock); 2599078e7183SCharles Keepax 2600d8a64d6aSCharles Keepax return; 2601d8a64d6aSCharles Keepax 2602078e7183SCharles Keepax err_ena: 2603d8a64d6aSCharles Keepax regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 2604d8a64d6aSCharles Keepax ADSP2_SYS_ENA | ADSP2_CORE_ENA | ADSP2_START, 0); 2605d589d8b8SCharles Keepax err_mem: 2606d589d8b8SCharles Keepax regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 2607d589d8b8SCharles Keepax ADSP2_MEM_ENA, 0); 2608078e7183SCharles Keepax err_mutex: 2609078e7183SCharles Keepax mutex_unlock(&dsp->pwr_lock); 2610d8a64d6aSCharles Keepax } 2611d8a64d6aSCharles Keepax 2612d82d767fSCharles Keepax static void wm_adsp2_set_dspclk(struct wm_adsp *dsp, unsigned int freq) 2613d82d767fSCharles Keepax { 2614d82d767fSCharles Keepax int ret; 2615d82d767fSCharles Keepax 2616e1ea1879SRichard Fitzgerald switch (dsp->rev) { 2617e1ea1879SRichard Fitzgerald case 0: 2618d82d767fSCharles Keepax ret = regmap_update_bits_async(dsp->regmap, 2619d82d767fSCharles Keepax dsp->base + ADSP2_CLOCKING, 2620d82d767fSCharles Keepax ADSP2_CLK_SEL_MASK, 2621d82d767fSCharles Keepax freq << ADSP2_CLK_SEL_SHIFT); 2622e1ea1879SRichard Fitzgerald if (ret) { 2623d82d767fSCharles Keepax adsp_err(dsp, "Failed to set clock rate: %d\n", ret); 2624e1ea1879SRichard Fitzgerald return; 2625e1ea1879SRichard Fitzgerald } 2626e1ea1879SRichard Fitzgerald break; 2627e1ea1879SRichard Fitzgerald default: 2628e1ea1879SRichard Fitzgerald /* clock is handled by parent codec driver */ 2629e1ea1879SRichard Fitzgerald break; 2630e1ea1879SRichard Fitzgerald } 2631d82d767fSCharles Keepax } 2632d82d767fSCharles Keepax 2633af813a6fSCharles Keepax int wm_adsp2_preloader_get(struct snd_kcontrol *kcontrol, 2634af813a6fSCharles Keepax struct snd_ctl_elem_value *ucontrol) 2635af813a6fSCharles Keepax { 2636af813a6fSCharles Keepax struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); 2637af813a6fSCharles Keepax struct wm_adsp *dsp = snd_soc_codec_get_drvdata(codec); 2638af813a6fSCharles Keepax 2639af813a6fSCharles Keepax ucontrol->value.integer.value[0] = dsp->preloaded; 2640af813a6fSCharles Keepax 2641af813a6fSCharles Keepax return 0; 2642af813a6fSCharles Keepax } 2643af813a6fSCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp2_preloader_get); 2644af813a6fSCharles Keepax 2645af813a6fSCharles Keepax int wm_adsp2_preloader_put(struct snd_kcontrol *kcontrol, 2646af813a6fSCharles Keepax struct snd_ctl_elem_value *ucontrol) 2647af813a6fSCharles Keepax { 2648af813a6fSCharles Keepax struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); 2649af813a6fSCharles Keepax struct wm_adsp *dsp = snd_soc_codec_get_drvdata(codec); 2650af813a6fSCharles Keepax struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); 2651af813a6fSCharles Keepax struct soc_mixer_control *mc = 2652af813a6fSCharles Keepax (struct soc_mixer_control *)kcontrol->private_value; 2653af813a6fSCharles Keepax char preload[32]; 2654af813a6fSCharles Keepax 26556298117aSRichard Fitzgerald snprintf(preload, ARRAY_SIZE(preload), "DSP%u Preload", mc->shift); 2656af813a6fSCharles Keepax 2657af813a6fSCharles Keepax dsp->preloaded = ucontrol->value.integer.value[0]; 2658af813a6fSCharles Keepax 2659af813a6fSCharles Keepax if (ucontrol->value.integer.value[0]) 2660af813a6fSCharles Keepax snd_soc_dapm_force_enable_pin(dapm, preload); 2661af813a6fSCharles Keepax else 2662af813a6fSCharles Keepax snd_soc_dapm_disable_pin(dapm, preload); 2663af813a6fSCharles Keepax 2664af813a6fSCharles Keepax snd_soc_dapm_sync(dapm); 2665af813a6fSCharles Keepax 2666af813a6fSCharles Keepax return 0; 2667af813a6fSCharles Keepax } 2668af813a6fSCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp2_preloader_put); 2669af813a6fSCharles Keepax 267051a2c944SMayuresh Kulkarni static void wm_adsp_stop_watchdog(struct wm_adsp *dsp) 267151a2c944SMayuresh Kulkarni { 267251a2c944SMayuresh Kulkarni switch (dsp->rev) { 267351a2c944SMayuresh Kulkarni case 0: 267451a2c944SMayuresh Kulkarni case 1: 267551a2c944SMayuresh Kulkarni return; 267651a2c944SMayuresh Kulkarni default: 267751a2c944SMayuresh Kulkarni regmap_update_bits(dsp->regmap, dsp->base + ADSP2_WATCHDOG, 267851a2c944SMayuresh Kulkarni ADSP2_WDT_ENA_MASK, 0); 267951a2c944SMayuresh Kulkarni } 268051a2c944SMayuresh Kulkarni } 268151a2c944SMayuresh Kulkarni 268212db5eddSCharles Keepax int wm_adsp2_early_event(struct snd_soc_dapm_widget *w, 2683d82d767fSCharles Keepax struct snd_kcontrol *kcontrol, int event, 2684d82d767fSCharles Keepax unsigned int freq) 268512db5eddSCharles Keepax { 268672718517SLars-Peter Clausen struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); 268712db5eddSCharles Keepax struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec); 268812db5eddSCharles Keepax struct wm_adsp *dsp = &dsps[w->shift]; 268957a60cc3SCharles Keepax struct wm_coeff_ctl *ctl; 269012db5eddSCharles Keepax 269112db5eddSCharles Keepax switch (event) { 269212db5eddSCharles Keepax case SND_SOC_DAPM_PRE_PMU: 2693d82d767fSCharles Keepax wm_adsp2_set_dspclk(dsp, freq); 269412db5eddSCharles Keepax queue_work(system_unbound_wq, &dsp->boot_work); 269512db5eddSCharles Keepax break; 269657a60cc3SCharles Keepax case SND_SOC_DAPM_PRE_PMD: 2697bb24ee41SCharles Keepax mutex_lock(&dsp->pwr_lock); 2698bb24ee41SCharles Keepax 269957a60cc3SCharles Keepax wm_adsp_debugfs_clear(dsp); 270057a60cc3SCharles Keepax 270157a60cc3SCharles Keepax dsp->fw_id = 0; 270257a60cc3SCharles Keepax dsp->fw_id_version = 0; 270357a60cc3SCharles Keepax 270457a60cc3SCharles Keepax dsp->booted = false; 270557a60cc3SCharles Keepax 270657a60cc3SCharles Keepax regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 270757a60cc3SCharles Keepax ADSP2_MEM_ENA, 0); 270857a60cc3SCharles Keepax 270957a60cc3SCharles Keepax list_for_each_entry(ctl, &dsp->ctl_list, list) 271057a60cc3SCharles Keepax ctl->enabled = 0; 271157a60cc3SCharles Keepax 271257a60cc3SCharles Keepax wm_adsp_free_alg_regions(dsp); 271357a60cc3SCharles Keepax 2714bb24ee41SCharles Keepax mutex_unlock(&dsp->pwr_lock); 2715bb24ee41SCharles Keepax 271657a60cc3SCharles Keepax adsp_dbg(dsp, "Shutdown complete\n"); 271757a60cc3SCharles Keepax break; 271812db5eddSCharles Keepax default: 271912db5eddSCharles Keepax break; 2720cab27258SCharles Keepax } 272112db5eddSCharles Keepax 272212db5eddSCharles Keepax return 0; 272312db5eddSCharles Keepax } 272412db5eddSCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp2_early_event); 272512db5eddSCharles Keepax 2726d8a64d6aSCharles Keepax int wm_adsp2_event(struct snd_soc_dapm_widget *w, 2727d8a64d6aSCharles Keepax struct snd_kcontrol *kcontrol, int event) 2728d8a64d6aSCharles Keepax { 272972718517SLars-Peter Clausen struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); 2730d8a64d6aSCharles Keepax struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec); 2731d8a64d6aSCharles Keepax struct wm_adsp *dsp = &dsps[w->shift]; 2732d8a64d6aSCharles Keepax int ret; 2733d8a64d6aSCharles Keepax 2734d8a64d6aSCharles Keepax switch (event) { 2735d8a64d6aSCharles Keepax case SND_SOC_DAPM_POST_PMU: 2736d8a64d6aSCharles Keepax flush_work(&dsp->boot_work); 2737d8a64d6aSCharles Keepax 2738bb24ee41SCharles Keepax mutex_lock(&dsp->pwr_lock); 2739bb24ee41SCharles Keepax 2740bb24ee41SCharles Keepax if (!dsp->booted) { 2741bb24ee41SCharles Keepax ret = -EIO; 2742bb24ee41SCharles Keepax goto err; 2743bb24ee41SCharles Keepax } 2744d8a64d6aSCharles Keepax 274590d19ba5SCharles Keepax ret = wm_adsp2_ena(dsp); 274690d19ba5SCharles Keepax if (ret != 0) 274790d19ba5SCharles Keepax goto err; 274890d19ba5SCharles Keepax 2749cef45771SCharles Keepax /* Sync set controls */ 2750cef45771SCharles Keepax ret = wm_coeff_sync_controls(dsp); 2751cef45771SCharles Keepax if (ret != 0) 2752cef45771SCharles Keepax goto err; 2753cef45771SCharles Keepax 275451a2c944SMayuresh Kulkarni wm_adsp2_lock(dsp, dsp->lock_regions); 275551a2c944SMayuresh Kulkarni 2756d8a64d6aSCharles Keepax ret = regmap_update_bits(dsp->regmap, 2757d8a64d6aSCharles Keepax dsp->base + ADSP2_CONTROL, 275800e4c3b6SCharles Keepax ADSP2_CORE_ENA | ADSP2_START, 275900e4c3b6SCharles Keepax ADSP2_CORE_ENA | ADSP2_START); 2760d8a64d6aSCharles Keepax if (ret != 0) 2761d8a64d6aSCharles Keepax goto err; 27622cd19bdbSCharles Keepax 276348c2c993SCharles Keepax if (wm_adsp_fw[dsp->fw].num_caps != 0) { 27642cd19bdbSCharles Keepax ret = wm_adsp_buffer_init(dsp); 2765bb24ee41SCharles Keepax if (ret < 0) 276648c2c993SCharles Keepax goto err; 276748c2c993SCharles Keepax } 27682cd19bdbSCharles Keepax 2769e779974bSCharles Keepax dsp->running = true; 27701023dbd9SMark Brown 2771612047f0SCharles Keepax mutex_unlock(&dsp->pwr_lock); 2772612047f0SCharles Keepax 27732159ad93SMark Brown break; 27742159ad93SMark Brown 27752159ad93SMark Brown case SND_SOC_DAPM_PRE_PMD: 2776f4f0c4c6SRichard Fitzgerald /* Tell the firmware to cleanup */ 2777f4f0c4c6SRichard Fitzgerald wm_adsp_signal_event_controls(dsp, WM_ADSP_FW_EVENT_SHUTDOWN); 2778f4f0c4c6SRichard Fitzgerald 277951a2c944SMayuresh Kulkarni wm_adsp_stop_watchdog(dsp); 278051a2c944SMayuresh Kulkarni 278110337b07SRichard Fitzgerald /* Log firmware state, it can be useful for analysis */ 2782e1ea1879SRichard Fitzgerald switch (dsp->rev) { 2783e1ea1879SRichard Fitzgerald case 0: 278410337b07SRichard Fitzgerald wm_adsp2_show_fw_status(dsp); 2785e1ea1879SRichard Fitzgerald break; 2786e1ea1879SRichard Fitzgerald default: 2787e1ea1879SRichard Fitzgerald wm_adsp2v2_show_fw_status(dsp); 2788e1ea1879SRichard Fitzgerald break; 2789e1ea1879SRichard Fitzgerald } 279010337b07SRichard Fitzgerald 2791078e7183SCharles Keepax mutex_lock(&dsp->pwr_lock); 2792078e7183SCharles Keepax 27931023dbd9SMark Brown dsp->running = false; 27941023dbd9SMark Brown 2795e1ea1879SRichard Fitzgerald regmap_update_bits(dsp->regmap, 2796e1ea1879SRichard Fitzgerald dsp->base + ADSP2_CONTROL, 279757a60cc3SCharles Keepax ADSP2_CORE_ENA | ADSP2_START, 0); 2798973838a0SMark Brown 27992d30b575SMark Brown /* Make sure DMAs are quiesced */ 2800e1ea1879SRichard Fitzgerald switch (dsp->rev) { 2801e1ea1879SRichard Fitzgerald case 0: 2802e1ea1879SRichard Fitzgerald regmap_write(dsp->regmap, 2803e1ea1879SRichard Fitzgerald dsp->base + ADSP2_RDMA_CONFIG_1, 0); 2804e1ea1879SRichard Fitzgerald regmap_write(dsp->regmap, 2805e1ea1879SRichard Fitzgerald dsp->base + ADSP2_WDMA_CONFIG_1, 0); 2806e1ea1879SRichard Fitzgerald regmap_write(dsp->regmap, 2807e1ea1879SRichard Fitzgerald dsp->base + ADSP2_WDMA_CONFIG_2, 0); 28086facd2d1SSimon Trimmer 2809e1ea1879SRichard Fitzgerald regmap_update_bits(dsp->regmap, 2810e1ea1879SRichard Fitzgerald dsp->base + ADSP2_CONTROL, 28116facd2d1SSimon Trimmer ADSP2_SYS_ENA, 0); 2812e1ea1879SRichard Fitzgerald break; 2813e1ea1879SRichard Fitzgerald default: 2814e1ea1879SRichard Fitzgerald regmap_write(dsp->regmap, 2815e1ea1879SRichard Fitzgerald dsp->base + ADSP2_RDMA_CONFIG_1, 0); 2816e1ea1879SRichard Fitzgerald regmap_write(dsp->regmap, 2817e1ea1879SRichard Fitzgerald dsp->base + ADSP2_WDMA_CONFIG_1, 0); 2818e1ea1879SRichard Fitzgerald regmap_write(dsp->regmap, 2819e1ea1879SRichard Fitzgerald dsp->base + ADSP2V2_WDMA_CONFIG_2, 0); 2820e1ea1879SRichard Fitzgerald break; 2821e1ea1879SRichard Fitzgerald } 28222d30b575SMark Brown 28232cd19bdbSCharles Keepax if (wm_adsp_fw[dsp->fw].num_caps != 0) 28242cd19bdbSCharles Keepax wm_adsp_buffer_free(dsp); 28252cd19bdbSCharles Keepax 2826078e7183SCharles Keepax mutex_unlock(&dsp->pwr_lock); 2827078e7183SCharles Keepax 282857a60cc3SCharles Keepax adsp_dbg(dsp, "Execution stopped\n"); 28292159ad93SMark Brown break; 28302159ad93SMark Brown 28312159ad93SMark Brown default: 28322159ad93SMark Brown break; 28332159ad93SMark Brown } 28342159ad93SMark Brown 28352159ad93SMark Brown return 0; 28362159ad93SMark Brown err: 28372159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 2838a7f9be7eSMark Brown ADSP2_SYS_ENA | ADSP2_CORE_ENA | ADSP2_START, 0); 2839bb24ee41SCharles Keepax mutex_unlock(&dsp->pwr_lock); 28402159ad93SMark Brown return ret; 28412159ad93SMark Brown } 28422159ad93SMark Brown EXPORT_SYMBOL_GPL(wm_adsp2_event); 2843973838a0SMark Brown 2844f5e2ce92SRichard Fitzgerald int wm_adsp2_codec_probe(struct wm_adsp *dsp, struct snd_soc_codec *codec) 2845f5e2ce92SRichard Fitzgerald { 2846af813a6fSCharles Keepax struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); 2847af813a6fSCharles Keepax char preload[32]; 2848af813a6fSCharles Keepax 2849af813a6fSCharles Keepax snprintf(preload, ARRAY_SIZE(preload), "DSP%d Preload", dsp->num); 2850af813a6fSCharles Keepax snd_soc_dapm_disable_pin(dapm, preload); 2851685f51a5SRichard Fitzgerald 2852f9f55e31SRichard Fitzgerald wm_adsp2_init_debugfs(dsp, codec); 2853f9f55e31SRichard Fitzgerald 2854af813a6fSCharles Keepax dsp->codec = codec; 2855af813a6fSCharles Keepax 2856218e5087SRichard Fitzgerald return snd_soc_add_codec_controls(codec, 2857336d0442SRichard Fitzgerald &wm_adsp_fw_controls[dsp->num - 1], 2858336d0442SRichard Fitzgerald 1); 2859f5e2ce92SRichard Fitzgerald } 2860f5e2ce92SRichard Fitzgerald EXPORT_SYMBOL_GPL(wm_adsp2_codec_probe); 2861f5e2ce92SRichard Fitzgerald 2862f5e2ce92SRichard Fitzgerald int wm_adsp2_codec_remove(struct wm_adsp *dsp, struct snd_soc_codec *codec) 2863f5e2ce92SRichard Fitzgerald { 2864f9f55e31SRichard Fitzgerald wm_adsp2_cleanup_debugfs(dsp); 2865f9f55e31SRichard Fitzgerald 2866f5e2ce92SRichard Fitzgerald return 0; 2867f5e2ce92SRichard Fitzgerald } 2868f5e2ce92SRichard Fitzgerald EXPORT_SYMBOL_GPL(wm_adsp2_codec_remove); 2869f5e2ce92SRichard Fitzgerald 287081ac58b1SRichard Fitzgerald int wm_adsp2_init(struct wm_adsp *dsp) 2871973838a0SMark Brown { 2872973838a0SMark Brown int ret; 2873973838a0SMark Brown 2874e1ea1879SRichard Fitzgerald switch (dsp->rev) { 2875e1ea1879SRichard Fitzgerald case 0: 287610a2b662SMark Brown /* 287710a2b662SMark Brown * Disable the DSP memory by default when in reset for a small 287810a2b662SMark Brown * power saving. 287910a2b662SMark Brown */ 28803809f001SCharles Keepax ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 288110a2b662SMark Brown ADSP2_MEM_ENA, 0); 2882e1ea1879SRichard Fitzgerald if (ret) { 2883e1ea1879SRichard Fitzgerald adsp_err(dsp, 2884e1ea1879SRichard Fitzgerald "Failed to clear memory retention: %d\n", ret); 288510a2b662SMark Brown return ret; 288610a2b662SMark Brown } 2887e1ea1879SRichard Fitzgerald break; 2888e1ea1879SRichard Fitzgerald default: 2889e1ea1879SRichard Fitzgerald break; 2890e1ea1879SRichard Fitzgerald } 289110a2b662SMark Brown 28923809f001SCharles Keepax INIT_LIST_HEAD(&dsp->alg_regions); 28933809f001SCharles Keepax INIT_LIST_HEAD(&dsp->ctl_list); 28943809f001SCharles Keepax INIT_WORK(&dsp->boot_work, wm_adsp2_boot_work); 28956ab2b7b4SDimitris Papastamos 2896078e7183SCharles Keepax mutex_init(&dsp->pwr_lock); 2897078e7183SCharles Keepax 2898973838a0SMark Brown return 0; 2899973838a0SMark Brown } 2900973838a0SMark Brown EXPORT_SYMBOL_GPL(wm_adsp2_init); 29010a37c6efSPraveen Diwakar 290266225e98SRichard Fitzgerald void wm_adsp2_remove(struct wm_adsp *dsp) 290366225e98SRichard Fitzgerald { 290466225e98SRichard Fitzgerald struct wm_coeff_ctl *ctl; 290566225e98SRichard Fitzgerald 290666225e98SRichard Fitzgerald while (!list_empty(&dsp->ctl_list)) { 290766225e98SRichard Fitzgerald ctl = list_first_entry(&dsp->ctl_list, struct wm_coeff_ctl, 290866225e98SRichard Fitzgerald list); 290966225e98SRichard Fitzgerald list_del(&ctl->list); 291066225e98SRichard Fitzgerald wm_adsp_free_ctl_blk(ctl); 291166225e98SRichard Fitzgerald } 291266225e98SRichard Fitzgerald } 291366225e98SRichard Fitzgerald EXPORT_SYMBOL_GPL(wm_adsp2_remove); 291466225e98SRichard Fitzgerald 2915edd71350SCharles Keepax static inline int wm_adsp_compr_attached(struct wm_adsp_compr *compr) 2916edd71350SCharles Keepax { 2917edd71350SCharles Keepax return compr->buf != NULL; 2918edd71350SCharles Keepax } 2919edd71350SCharles Keepax 2920edd71350SCharles Keepax static int wm_adsp_compr_attach(struct wm_adsp_compr *compr) 2921edd71350SCharles Keepax { 2922edd71350SCharles Keepax /* 2923edd71350SCharles Keepax * Note this will be more complex once each DSP can support multiple 2924edd71350SCharles Keepax * streams 2925edd71350SCharles Keepax */ 2926edd71350SCharles Keepax if (!compr->dsp->buffer) 2927edd71350SCharles Keepax return -EINVAL; 2928edd71350SCharles Keepax 2929edd71350SCharles Keepax compr->buf = compr->dsp->buffer; 2930721be3beSCharles Keepax compr->buf->compr = compr; 2931edd71350SCharles Keepax 2932edd71350SCharles Keepax return 0; 2933edd71350SCharles Keepax } 2934edd71350SCharles Keepax 2935721be3beSCharles Keepax static void wm_adsp_compr_detach(struct wm_adsp_compr *compr) 2936721be3beSCharles Keepax { 2937721be3beSCharles Keepax if (!compr) 2938721be3beSCharles Keepax return; 2939721be3beSCharles Keepax 2940721be3beSCharles Keepax /* Wake the poll so it can see buffer is no longer attached */ 2941721be3beSCharles Keepax if (compr->stream) 2942721be3beSCharles Keepax snd_compr_fragment_elapsed(compr->stream); 2943721be3beSCharles Keepax 2944721be3beSCharles Keepax if (wm_adsp_compr_attached(compr)) { 2945721be3beSCharles Keepax compr->buf->compr = NULL; 2946721be3beSCharles Keepax compr->buf = NULL; 2947721be3beSCharles Keepax } 2948721be3beSCharles Keepax } 2949721be3beSCharles Keepax 2950406abc95SCharles Keepax int wm_adsp_compr_open(struct wm_adsp *dsp, struct snd_compr_stream *stream) 2951406abc95SCharles Keepax { 2952406abc95SCharles Keepax struct wm_adsp_compr *compr; 2953406abc95SCharles Keepax int ret = 0; 2954406abc95SCharles Keepax 2955406abc95SCharles Keepax mutex_lock(&dsp->pwr_lock); 2956406abc95SCharles Keepax 2957406abc95SCharles Keepax if (wm_adsp_fw[dsp->fw].num_caps == 0) { 2958406abc95SCharles Keepax adsp_err(dsp, "Firmware does not support compressed API\n"); 2959406abc95SCharles Keepax ret = -ENXIO; 2960406abc95SCharles Keepax goto out; 2961406abc95SCharles Keepax } 2962406abc95SCharles Keepax 2963406abc95SCharles Keepax if (wm_adsp_fw[dsp->fw].compr_direction != stream->direction) { 2964406abc95SCharles Keepax adsp_err(dsp, "Firmware does not support stream direction\n"); 2965406abc95SCharles Keepax ret = -EINVAL; 2966406abc95SCharles Keepax goto out; 2967406abc95SCharles Keepax } 2968406abc95SCharles Keepax 296995fe9597SCharles Keepax if (dsp->compr) { 297095fe9597SCharles Keepax /* It is expect this limitation will be removed in future */ 297195fe9597SCharles Keepax adsp_err(dsp, "Only a single stream supported per DSP\n"); 297295fe9597SCharles Keepax ret = -EBUSY; 297395fe9597SCharles Keepax goto out; 297495fe9597SCharles Keepax } 297595fe9597SCharles Keepax 2976406abc95SCharles Keepax compr = kzalloc(sizeof(*compr), GFP_KERNEL); 2977406abc95SCharles Keepax if (!compr) { 2978406abc95SCharles Keepax ret = -ENOMEM; 2979406abc95SCharles Keepax goto out; 2980406abc95SCharles Keepax } 2981406abc95SCharles Keepax 2982406abc95SCharles Keepax compr->dsp = dsp; 2983406abc95SCharles Keepax compr->stream = stream; 2984406abc95SCharles Keepax 2985406abc95SCharles Keepax dsp->compr = compr; 2986406abc95SCharles Keepax 2987406abc95SCharles Keepax stream->runtime->private_data = compr; 2988406abc95SCharles Keepax 2989406abc95SCharles Keepax out: 2990406abc95SCharles Keepax mutex_unlock(&dsp->pwr_lock); 2991406abc95SCharles Keepax 2992406abc95SCharles Keepax return ret; 2993406abc95SCharles Keepax } 2994406abc95SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_open); 2995406abc95SCharles Keepax 2996406abc95SCharles Keepax int wm_adsp_compr_free(struct snd_compr_stream *stream) 2997406abc95SCharles Keepax { 2998406abc95SCharles Keepax struct wm_adsp_compr *compr = stream->runtime->private_data; 2999406abc95SCharles Keepax struct wm_adsp *dsp = compr->dsp; 3000406abc95SCharles Keepax 3001406abc95SCharles Keepax mutex_lock(&dsp->pwr_lock); 3002406abc95SCharles Keepax 3003721be3beSCharles Keepax wm_adsp_compr_detach(compr); 3004406abc95SCharles Keepax dsp->compr = NULL; 3005406abc95SCharles Keepax 300683a40ce9SCharles Keepax kfree(compr->raw_buf); 3007406abc95SCharles Keepax kfree(compr); 3008406abc95SCharles Keepax 3009406abc95SCharles Keepax mutex_unlock(&dsp->pwr_lock); 3010406abc95SCharles Keepax 3011406abc95SCharles Keepax return 0; 3012406abc95SCharles Keepax } 3013406abc95SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_free); 3014406abc95SCharles Keepax 3015406abc95SCharles Keepax static int wm_adsp_compr_check_params(struct snd_compr_stream *stream, 3016406abc95SCharles Keepax struct snd_compr_params *params) 3017406abc95SCharles Keepax { 3018406abc95SCharles Keepax struct wm_adsp_compr *compr = stream->runtime->private_data; 3019406abc95SCharles Keepax struct wm_adsp *dsp = compr->dsp; 3020406abc95SCharles Keepax const struct wm_adsp_fw_caps *caps; 3021406abc95SCharles Keepax const struct snd_codec_desc *desc; 3022406abc95SCharles Keepax int i, j; 3023406abc95SCharles Keepax 3024406abc95SCharles Keepax if (params->buffer.fragment_size < WM_ADSP_MIN_FRAGMENT_SIZE || 3025406abc95SCharles Keepax params->buffer.fragment_size > WM_ADSP_MAX_FRAGMENT_SIZE || 3026406abc95SCharles Keepax params->buffer.fragments < WM_ADSP_MIN_FRAGMENTS || 3027406abc95SCharles Keepax params->buffer.fragments > WM_ADSP_MAX_FRAGMENTS || 3028406abc95SCharles Keepax params->buffer.fragment_size % WM_ADSP_DATA_WORD_SIZE) { 3029406abc95SCharles Keepax adsp_err(dsp, "Invalid buffer fragsize=%d fragments=%d\n", 3030406abc95SCharles Keepax params->buffer.fragment_size, 3031406abc95SCharles Keepax params->buffer.fragments); 3032406abc95SCharles Keepax 3033406abc95SCharles Keepax return -EINVAL; 3034406abc95SCharles Keepax } 3035406abc95SCharles Keepax 3036406abc95SCharles Keepax for (i = 0; i < wm_adsp_fw[dsp->fw].num_caps; i++) { 3037406abc95SCharles Keepax caps = &wm_adsp_fw[dsp->fw].caps[i]; 3038406abc95SCharles Keepax desc = &caps->desc; 3039406abc95SCharles Keepax 3040406abc95SCharles Keepax if (caps->id != params->codec.id) 3041406abc95SCharles Keepax continue; 3042406abc95SCharles Keepax 3043406abc95SCharles Keepax if (stream->direction == SND_COMPRESS_PLAYBACK) { 3044406abc95SCharles Keepax if (desc->max_ch < params->codec.ch_out) 3045406abc95SCharles Keepax continue; 3046406abc95SCharles Keepax } else { 3047406abc95SCharles Keepax if (desc->max_ch < params->codec.ch_in) 3048406abc95SCharles Keepax continue; 3049406abc95SCharles Keepax } 3050406abc95SCharles Keepax 3051406abc95SCharles Keepax if (!(desc->formats & (1 << params->codec.format))) 3052406abc95SCharles Keepax continue; 3053406abc95SCharles Keepax 3054406abc95SCharles Keepax for (j = 0; j < desc->num_sample_rates; ++j) 3055406abc95SCharles Keepax if (desc->sample_rates[j] == params->codec.sample_rate) 3056406abc95SCharles Keepax return 0; 3057406abc95SCharles Keepax } 3058406abc95SCharles Keepax 3059406abc95SCharles Keepax adsp_err(dsp, "Invalid params id=%u ch=%u,%u rate=%u fmt=%u\n", 3060406abc95SCharles Keepax params->codec.id, params->codec.ch_in, params->codec.ch_out, 3061406abc95SCharles Keepax params->codec.sample_rate, params->codec.format); 3062406abc95SCharles Keepax return -EINVAL; 3063406abc95SCharles Keepax } 3064406abc95SCharles Keepax 3065565ace46SCharles Keepax static inline unsigned int wm_adsp_compr_frag_words(struct wm_adsp_compr *compr) 3066565ace46SCharles Keepax { 3067565ace46SCharles Keepax return compr->size.fragment_size / WM_ADSP_DATA_WORD_SIZE; 3068565ace46SCharles Keepax } 3069565ace46SCharles Keepax 3070406abc95SCharles Keepax int wm_adsp_compr_set_params(struct snd_compr_stream *stream, 3071406abc95SCharles Keepax struct snd_compr_params *params) 3072406abc95SCharles Keepax { 3073406abc95SCharles Keepax struct wm_adsp_compr *compr = stream->runtime->private_data; 307483a40ce9SCharles Keepax unsigned int size; 3075406abc95SCharles Keepax int ret; 3076406abc95SCharles Keepax 3077406abc95SCharles Keepax ret = wm_adsp_compr_check_params(stream, params); 3078406abc95SCharles Keepax if (ret) 3079406abc95SCharles Keepax return ret; 3080406abc95SCharles Keepax 3081406abc95SCharles Keepax compr->size = params->buffer; 3082406abc95SCharles Keepax 3083406abc95SCharles Keepax adsp_dbg(compr->dsp, "fragment_size=%d fragments=%d\n", 3084406abc95SCharles Keepax compr->size.fragment_size, compr->size.fragments); 3085406abc95SCharles Keepax 308683a40ce9SCharles Keepax size = wm_adsp_compr_frag_words(compr) * sizeof(*compr->raw_buf); 308783a40ce9SCharles Keepax compr->raw_buf = kmalloc(size, GFP_DMA | GFP_KERNEL); 308883a40ce9SCharles Keepax if (!compr->raw_buf) 308983a40ce9SCharles Keepax return -ENOMEM; 309083a40ce9SCharles Keepax 3091da2b3358SCharles Keepax compr->sample_rate = params->codec.sample_rate; 3092da2b3358SCharles Keepax 3093406abc95SCharles Keepax return 0; 3094406abc95SCharles Keepax } 3095406abc95SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_set_params); 3096406abc95SCharles Keepax 3097406abc95SCharles Keepax int wm_adsp_compr_get_caps(struct snd_compr_stream *stream, 3098406abc95SCharles Keepax struct snd_compr_caps *caps) 3099406abc95SCharles Keepax { 3100406abc95SCharles Keepax struct wm_adsp_compr *compr = stream->runtime->private_data; 3101406abc95SCharles Keepax int fw = compr->dsp->fw; 3102406abc95SCharles Keepax int i; 3103406abc95SCharles Keepax 3104406abc95SCharles Keepax if (wm_adsp_fw[fw].caps) { 3105406abc95SCharles Keepax for (i = 0; i < wm_adsp_fw[fw].num_caps; i++) 3106406abc95SCharles Keepax caps->codecs[i] = wm_adsp_fw[fw].caps[i].id; 3107406abc95SCharles Keepax 3108406abc95SCharles Keepax caps->num_codecs = i; 3109406abc95SCharles Keepax caps->direction = wm_adsp_fw[fw].compr_direction; 3110406abc95SCharles Keepax 3111406abc95SCharles Keepax caps->min_fragment_size = WM_ADSP_MIN_FRAGMENT_SIZE; 3112406abc95SCharles Keepax caps->max_fragment_size = WM_ADSP_MAX_FRAGMENT_SIZE; 3113406abc95SCharles Keepax caps->min_fragments = WM_ADSP_MIN_FRAGMENTS; 3114406abc95SCharles Keepax caps->max_fragments = WM_ADSP_MAX_FRAGMENTS; 3115406abc95SCharles Keepax } 3116406abc95SCharles Keepax 3117406abc95SCharles Keepax return 0; 3118406abc95SCharles Keepax } 3119406abc95SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_get_caps); 3120406abc95SCharles Keepax 31212cd19bdbSCharles Keepax static int wm_adsp_read_data_block(struct wm_adsp *dsp, int mem_type, 31222cd19bdbSCharles Keepax unsigned int mem_addr, 31232cd19bdbSCharles Keepax unsigned int num_words, u32 *data) 31242cd19bdbSCharles Keepax { 31252cd19bdbSCharles Keepax struct wm_adsp_region const *mem = wm_adsp_find_region(dsp, mem_type); 31262cd19bdbSCharles Keepax unsigned int i, reg; 31272cd19bdbSCharles Keepax int ret; 31282cd19bdbSCharles Keepax 31292cd19bdbSCharles Keepax if (!mem) 31302cd19bdbSCharles Keepax return -EINVAL; 31312cd19bdbSCharles Keepax 31322cd19bdbSCharles Keepax reg = wm_adsp_region_to_reg(mem, mem_addr); 31332cd19bdbSCharles Keepax 31342cd19bdbSCharles Keepax ret = regmap_raw_read(dsp->regmap, reg, data, 31352cd19bdbSCharles Keepax sizeof(*data) * num_words); 31362cd19bdbSCharles Keepax if (ret < 0) 31372cd19bdbSCharles Keepax return ret; 31382cd19bdbSCharles Keepax 31392cd19bdbSCharles Keepax for (i = 0; i < num_words; ++i) 31402cd19bdbSCharles Keepax data[i] = be32_to_cpu(data[i]) & 0x00ffffffu; 31412cd19bdbSCharles Keepax 31422cd19bdbSCharles Keepax return 0; 31432cd19bdbSCharles Keepax } 31442cd19bdbSCharles Keepax 31452cd19bdbSCharles Keepax static inline int wm_adsp_read_data_word(struct wm_adsp *dsp, int mem_type, 31462cd19bdbSCharles Keepax unsigned int mem_addr, u32 *data) 31472cd19bdbSCharles Keepax { 31482cd19bdbSCharles Keepax return wm_adsp_read_data_block(dsp, mem_type, mem_addr, 1, data); 31492cd19bdbSCharles Keepax } 31502cd19bdbSCharles Keepax 31512cd19bdbSCharles Keepax static int wm_adsp_write_data_word(struct wm_adsp *dsp, int mem_type, 31522cd19bdbSCharles Keepax unsigned int mem_addr, u32 data) 31532cd19bdbSCharles Keepax { 31542cd19bdbSCharles Keepax struct wm_adsp_region const *mem = wm_adsp_find_region(dsp, mem_type); 31552cd19bdbSCharles Keepax unsigned int reg; 31562cd19bdbSCharles Keepax 31572cd19bdbSCharles Keepax if (!mem) 31582cd19bdbSCharles Keepax return -EINVAL; 31592cd19bdbSCharles Keepax 31602cd19bdbSCharles Keepax reg = wm_adsp_region_to_reg(mem, mem_addr); 31612cd19bdbSCharles Keepax 31622cd19bdbSCharles Keepax data = cpu_to_be32(data & 0x00ffffffu); 31632cd19bdbSCharles Keepax 31642cd19bdbSCharles Keepax return regmap_raw_write(dsp->regmap, reg, &data, sizeof(data)); 31652cd19bdbSCharles Keepax } 31662cd19bdbSCharles Keepax 31672cd19bdbSCharles Keepax static inline int wm_adsp_buffer_read(struct wm_adsp_compr_buf *buf, 31682cd19bdbSCharles Keepax unsigned int field_offset, u32 *data) 31692cd19bdbSCharles Keepax { 31702cd19bdbSCharles Keepax return wm_adsp_read_data_word(buf->dsp, WMFW_ADSP2_XM, 31712cd19bdbSCharles Keepax buf->host_buf_ptr + field_offset, data); 31722cd19bdbSCharles Keepax } 31732cd19bdbSCharles Keepax 31742cd19bdbSCharles Keepax static inline int wm_adsp_buffer_write(struct wm_adsp_compr_buf *buf, 31752cd19bdbSCharles Keepax unsigned int field_offset, u32 data) 31762cd19bdbSCharles Keepax { 31772cd19bdbSCharles Keepax return wm_adsp_write_data_word(buf->dsp, WMFW_ADSP2_XM, 31782cd19bdbSCharles Keepax buf->host_buf_ptr + field_offset, data); 31792cd19bdbSCharles Keepax } 31802cd19bdbSCharles Keepax 31812cd19bdbSCharles Keepax static int wm_adsp_buffer_locate(struct wm_adsp_compr_buf *buf) 31822cd19bdbSCharles Keepax { 31832cd19bdbSCharles Keepax struct wm_adsp_alg_region *alg_region; 31842cd19bdbSCharles Keepax struct wm_adsp *dsp = buf->dsp; 31852cd19bdbSCharles Keepax u32 xmalg, addr, magic; 31862cd19bdbSCharles Keepax int i, ret; 31872cd19bdbSCharles Keepax 31882cd19bdbSCharles Keepax alg_region = wm_adsp_find_alg_region(dsp, WMFW_ADSP2_XM, dsp->fw_id); 31892cd19bdbSCharles Keepax xmalg = sizeof(struct wm_adsp_system_config_xm_hdr) / sizeof(__be32); 31902cd19bdbSCharles Keepax 31912cd19bdbSCharles Keepax addr = alg_region->base + xmalg + ALG_XM_FIELD(magic); 31922cd19bdbSCharles Keepax ret = wm_adsp_read_data_word(dsp, WMFW_ADSP2_XM, addr, &magic); 31932cd19bdbSCharles Keepax if (ret < 0) 31942cd19bdbSCharles Keepax return ret; 31952cd19bdbSCharles Keepax 31962cd19bdbSCharles Keepax if (magic != WM_ADSP_ALG_XM_STRUCT_MAGIC) 31972cd19bdbSCharles Keepax return -EINVAL; 31982cd19bdbSCharles Keepax 31992cd19bdbSCharles Keepax addr = alg_region->base + xmalg + ALG_XM_FIELD(host_buf_ptr); 32002cd19bdbSCharles Keepax for (i = 0; i < 5; ++i) { 32012cd19bdbSCharles Keepax ret = wm_adsp_read_data_word(dsp, WMFW_ADSP2_XM, addr, 32022cd19bdbSCharles Keepax &buf->host_buf_ptr); 32032cd19bdbSCharles Keepax if (ret < 0) 32042cd19bdbSCharles Keepax return ret; 32052cd19bdbSCharles Keepax 32062cd19bdbSCharles Keepax if (buf->host_buf_ptr) 32072cd19bdbSCharles Keepax break; 32082cd19bdbSCharles Keepax 32092cd19bdbSCharles Keepax usleep_range(1000, 2000); 32102cd19bdbSCharles Keepax } 32112cd19bdbSCharles Keepax 32122cd19bdbSCharles Keepax if (!buf->host_buf_ptr) 32132cd19bdbSCharles Keepax return -EIO; 32142cd19bdbSCharles Keepax 32152cd19bdbSCharles Keepax adsp_dbg(dsp, "host_buf_ptr=%x\n", buf->host_buf_ptr); 32162cd19bdbSCharles Keepax 32172cd19bdbSCharles Keepax return 0; 32182cd19bdbSCharles Keepax } 32192cd19bdbSCharles Keepax 32202cd19bdbSCharles Keepax static int wm_adsp_buffer_populate(struct wm_adsp_compr_buf *buf) 32212cd19bdbSCharles Keepax { 32222cd19bdbSCharles Keepax const struct wm_adsp_fw_caps *caps = wm_adsp_fw[buf->dsp->fw].caps; 32232cd19bdbSCharles Keepax struct wm_adsp_buffer_region *region; 32242cd19bdbSCharles Keepax u32 offset = 0; 32252cd19bdbSCharles Keepax int i, ret; 32262cd19bdbSCharles Keepax 32272cd19bdbSCharles Keepax for (i = 0; i < caps->num_regions; ++i) { 32282cd19bdbSCharles Keepax region = &buf->regions[i]; 32292cd19bdbSCharles Keepax 32302cd19bdbSCharles Keepax region->offset = offset; 32312cd19bdbSCharles Keepax region->mem_type = caps->region_defs[i].mem_type; 32322cd19bdbSCharles Keepax 32332cd19bdbSCharles Keepax ret = wm_adsp_buffer_read(buf, caps->region_defs[i].base_offset, 32342cd19bdbSCharles Keepax ®ion->base_addr); 32352cd19bdbSCharles Keepax if (ret < 0) 32362cd19bdbSCharles Keepax return ret; 32372cd19bdbSCharles Keepax 32382cd19bdbSCharles Keepax ret = wm_adsp_buffer_read(buf, caps->region_defs[i].size_offset, 32392cd19bdbSCharles Keepax &offset); 32402cd19bdbSCharles Keepax if (ret < 0) 32412cd19bdbSCharles Keepax return ret; 32422cd19bdbSCharles Keepax 32432cd19bdbSCharles Keepax region->cumulative_size = offset; 32442cd19bdbSCharles Keepax 32452cd19bdbSCharles Keepax adsp_dbg(buf->dsp, 32462cd19bdbSCharles Keepax "region=%d type=%d base=%04x off=%04x size=%04x\n", 32472cd19bdbSCharles Keepax i, region->mem_type, region->base_addr, 32482cd19bdbSCharles Keepax region->offset, region->cumulative_size); 32492cd19bdbSCharles Keepax } 32502cd19bdbSCharles Keepax 32512cd19bdbSCharles Keepax return 0; 32522cd19bdbSCharles Keepax } 32532cd19bdbSCharles Keepax 32542cd19bdbSCharles Keepax static int wm_adsp_buffer_init(struct wm_adsp *dsp) 32552cd19bdbSCharles Keepax { 32562cd19bdbSCharles Keepax struct wm_adsp_compr_buf *buf; 32572cd19bdbSCharles Keepax int ret; 32582cd19bdbSCharles Keepax 32592cd19bdbSCharles Keepax buf = kzalloc(sizeof(*buf), GFP_KERNEL); 32602cd19bdbSCharles Keepax if (!buf) 32612cd19bdbSCharles Keepax return -ENOMEM; 32622cd19bdbSCharles Keepax 32632cd19bdbSCharles Keepax buf->dsp = dsp; 3264565ace46SCharles Keepax buf->read_index = -1; 3265565ace46SCharles Keepax buf->irq_count = 0xFFFFFFFF; 32662cd19bdbSCharles Keepax 32672cd19bdbSCharles Keepax ret = wm_adsp_buffer_locate(buf); 32682cd19bdbSCharles Keepax if (ret < 0) { 32692cd19bdbSCharles Keepax adsp_err(dsp, "Failed to acquire host buffer: %d\n", ret); 32702cd19bdbSCharles Keepax goto err_buffer; 32712cd19bdbSCharles Keepax } 32722cd19bdbSCharles Keepax 32732cd19bdbSCharles Keepax buf->regions = kcalloc(wm_adsp_fw[dsp->fw].caps->num_regions, 32742cd19bdbSCharles Keepax sizeof(*buf->regions), GFP_KERNEL); 32752cd19bdbSCharles Keepax if (!buf->regions) { 32762cd19bdbSCharles Keepax ret = -ENOMEM; 32772cd19bdbSCharles Keepax goto err_buffer; 32782cd19bdbSCharles Keepax } 32792cd19bdbSCharles Keepax 32802cd19bdbSCharles Keepax ret = wm_adsp_buffer_populate(buf); 32812cd19bdbSCharles Keepax if (ret < 0) { 32822cd19bdbSCharles Keepax adsp_err(dsp, "Failed to populate host buffer: %d\n", ret); 32832cd19bdbSCharles Keepax goto err_regions; 32842cd19bdbSCharles Keepax } 32852cd19bdbSCharles Keepax 32862cd19bdbSCharles Keepax dsp->buffer = buf; 32872cd19bdbSCharles Keepax 32882cd19bdbSCharles Keepax return 0; 32892cd19bdbSCharles Keepax 32902cd19bdbSCharles Keepax err_regions: 32912cd19bdbSCharles Keepax kfree(buf->regions); 32922cd19bdbSCharles Keepax err_buffer: 32932cd19bdbSCharles Keepax kfree(buf); 32942cd19bdbSCharles Keepax return ret; 32952cd19bdbSCharles Keepax } 32962cd19bdbSCharles Keepax 32972cd19bdbSCharles Keepax static int wm_adsp_buffer_free(struct wm_adsp *dsp) 32982cd19bdbSCharles Keepax { 32992cd19bdbSCharles Keepax if (dsp->buffer) { 3300721be3beSCharles Keepax wm_adsp_compr_detach(dsp->buffer->compr); 3301721be3beSCharles Keepax 33022cd19bdbSCharles Keepax kfree(dsp->buffer->regions); 33032cd19bdbSCharles Keepax kfree(dsp->buffer); 33042cd19bdbSCharles Keepax 33052cd19bdbSCharles Keepax dsp->buffer = NULL; 33062cd19bdbSCharles Keepax } 33072cd19bdbSCharles Keepax 33082cd19bdbSCharles Keepax return 0; 33092cd19bdbSCharles Keepax } 33102cd19bdbSCharles Keepax 331195fe9597SCharles Keepax int wm_adsp_compr_trigger(struct snd_compr_stream *stream, int cmd) 331295fe9597SCharles Keepax { 331395fe9597SCharles Keepax struct wm_adsp_compr *compr = stream->runtime->private_data; 331495fe9597SCharles Keepax struct wm_adsp *dsp = compr->dsp; 331595fe9597SCharles Keepax int ret = 0; 331695fe9597SCharles Keepax 331795fe9597SCharles Keepax adsp_dbg(dsp, "Trigger: %d\n", cmd); 331895fe9597SCharles Keepax 331995fe9597SCharles Keepax mutex_lock(&dsp->pwr_lock); 332095fe9597SCharles Keepax 332195fe9597SCharles Keepax switch (cmd) { 332295fe9597SCharles Keepax case SNDRV_PCM_TRIGGER_START: 332395fe9597SCharles Keepax if (wm_adsp_compr_attached(compr)) 332495fe9597SCharles Keepax break; 332595fe9597SCharles Keepax 332695fe9597SCharles Keepax ret = wm_adsp_compr_attach(compr); 332795fe9597SCharles Keepax if (ret < 0) { 332895fe9597SCharles Keepax adsp_err(dsp, "Failed to link buffer and stream: %d\n", 332995fe9597SCharles Keepax ret); 333095fe9597SCharles Keepax break; 333195fe9597SCharles Keepax } 3332565ace46SCharles Keepax 3333565ace46SCharles Keepax /* Trigger the IRQ at one fragment of data */ 3334565ace46SCharles Keepax ret = wm_adsp_buffer_write(compr->buf, 3335565ace46SCharles Keepax HOST_BUFFER_FIELD(high_water_mark), 3336565ace46SCharles Keepax wm_adsp_compr_frag_words(compr)); 3337565ace46SCharles Keepax if (ret < 0) { 3338565ace46SCharles Keepax adsp_err(dsp, "Failed to set high water mark: %d\n", 3339565ace46SCharles Keepax ret); 3340565ace46SCharles Keepax break; 3341565ace46SCharles Keepax } 334295fe9597SCharles Keepax break; 334395fe9597SCharles Keepax case SNDRV_PCM_TRIGGER_STOP: 334495fe9597SCharles Keepax break; 334595fe9597SCharles Keepax default: 334695fe9597SCharles Keepax ret = -EINVAL; 334795fe9597SCharles Keepax break; 334895fe9597SCharles Keepax } 334995fe9597SCharles Keepax 335095fe9597SCharles Keepax mutex_unlock(&dsp->pwr_lock); 335195fe9597SCharles Keepax 335295fe9597SCharles Keepax return ret; 335395fe9597SCharles Keepax } 335495fe9597SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_trigger); 335595fe9597SCharles Keepax 3356565ace46SCharles Keepax static inline int wm_adsp_buffer_size(struct wm_adsp_compr_buf *buf) 3357565ace46SCharles Keepax { 3358565ace46SCharles Keepax int last_region = wm_adsp_fw[buf->dsp->fw].caps->num_regions - 1; 3359565ace46SCharles Keepax 3360565ace46SCharles Keepax return buf->regions[last_region].cumulative_size; 3361565ace46SCharles Keepax } 3362565ace46SCharles Keepax 3363565ace46SCharles Keepax static int wm_adsp_buffer_update_avail(struct wm_adsp_compr_buf *buf) 3364565ace46SCharles Keepax { 3365565ace46SCharles Keepax u32 next_read_index, next_write_index; 3366565ace46SCharles Keepax int write_index, read_index, avail; 3367565ace46SCharles Keepax int ret; 3368565ace46SCharles Keepax 3369565ace46SCharles Keepax /* Only sync read index if we haven't already read a valid index */ 3370565ace46SCharles Keepax if (buf->read_index < 0) { 3371565ace46SCharles Keepax ret = wm_adsp_buffer_read(buf, 3372565ace46SCharles Keepax HOST_BUFFER_FIELD(next_read_index), 3373565ace46SCharles Keepax &next_read_index); 3374565ace46SCharles Keepax if (ret < 0) 3375565ace46SCharles Keepax return ret; 3376565ace46SCharles Keepax 3377565ace46SCharles Keepax read_index = sign_extend32(next_read_index, 23); 3378565ace46SCharles Keepax 3379565ace46SCharles Keepax if (read_index < 0) { 3380565ace46SCharles Keepax adsp_dbg(buf->dsp, "Avail check on unstarted stream\n"); 3381565ace46SCharles Keepax return 0; 3382565ace46SCharles Keepax } 3383565ace46SCharles Keepax 3384565ace46SCharles Keepax buf->read_index = read_index; 3385565ace46SCharles Keepax } 3386565ace46SCharles Keepax 3387565ace46SCharles Keepax ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(next_write_index), 3388565ace46SCharles Keepax &next_write_index); 3389565ace46SCharles Keepax if (ret < 0) 3390565ace46SCharles Keepax return ret; 3391565ace46SCharles Keepax 3392565ace46SCharles Keepax write_index = sign_extend32(next_write_index, 23); 3393565ace46SCharles Keepax 3394565ace46SCharles Keepax avail = write_index - buf->read_index; 3395565ace46SCharles Keepax if (avail < 0) 3396565ace46SCharles Keepax avail += wm_adsp_buffer_size(buf); 3397565ace46SCharles Keepax 3398565ace46SCharles Keepax adsp_dbg(buf->dsp, "readindex=0x%x, writeindex=0x%x, avail=%d\n", 339933d740e0SCharles Keepax buf->read_index, write_index, avail * WM_ADSP_DATA_WORD_SIZE); 3400565ace46SCharles Keepax 3401565ace46SCharles Keepax buf->avail = avail; 3402565ace46SCharles Keepax 3403565ace46SCharles Keepax return 0; 3404565ace46SCharles Keepax } 3405565ace46SCharles Keepax 34069771b18aSCharles Keepax static int wm_adsp_buffer_get_error(struct wm_adsp_compr_buf *buf) 34079771b18aSCharles Keepax { 34089771b18aSCharles Keepax int ret; 34099771b18aSCharles Keepax 34109771b18aSCharles Keepax ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(error), &buf->error); 34119771b18aSCharles Keepax if (ret < 0) { 34129771b18aSCharles Keepax adsp_err(buf->dsp, "Failed to check buffer error: %d\n", ret); 34139771b18aSCharles Keepax return ret; 34149771b18aSCharles Keepax } 34159771b18aSCharles Keepax if (buf->error != 0) { 34169771b18aSCharles Keepax adsp_err(buf->dsp, "Buffer error occurred: %d\n", buf->error); 34179771b18aSCharles Keepax return -EIO; 34189771b18aSCharles Keepax } 34199771b18aSCharles Keepax 34209771b18aSCharles Keepax return 0; 34219771b18aSCharles Keepax } 34229771b18aSCharles Keepax 3423565ace46SCharles Keepax int wm_adsp_compr_handle_irq(struct wm_adsp *dsp) 3424565ace46SCharles Keepax { 3425612047f0SCharles Keepax struct wm_adsp_compr_buf *buf; 3426612047f0SCharles Keepax struct wm_adsp_compr *compr; 3427565ace46SCharles Keepax int ret = 0; 3428565ace46SCharles Keepax 3429565ace46SCharles Keepax mutex_lock(&dsp->pwr_lock); 3430565ace46SCharles Keepax 3431612047f0SCharles Keepax buf = dsp->buffer; 3432612047f0SCharles Keepax compr = dsp->compr; 3433612047f0SCharles Keepax 3434565ace46SCharles Keepax if (!buf) { 3435565ace46SCharles Keepax ret = -ENODEV; 3436565ace46SCharles Keepax goto out; 3437565ace46SCharles Keepax } 3438565ace46SCharles Keepax 3439565ace46SCharles Keepax adsp_dbg(dsp, "Handling buffer IRQ\n"); 3440565ace46SCharles Keepax 34419771b18aSCharles Keepax ret = wm_adsp_buffer_get_error(buf); 34429771b18aSCharles Keepax if (ret < 0) 34435847609eSCharles Keepax goto out_notify; /* Wake poll to report error */ 3444565ace46SCharles Keepax 3445565ace46SCharles Keepax ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(irq_count), 3446565ace46SCharles Keepax &buf->irq_count); 3447565ace46SCharles Keepax if (ret < 0) { 3448565ace46SCharles Keepax adsp_err(dsp, "Failed to get irq_count: %d\n", ret); 3449565ace46SCharles Keepax goto out; 3450565ace46SCharles Keepax } 3451565ace46SCharles Keepax 3452565ace46SCharles Keepax ret = wm_adsp_buffer_update_avail(buf); 3453565ace46SCharles Keepax if (ret < 0) { 3454565ace46SCharles Keepax adsp_err(dsp, "Error reading avail: %d\n", ret); 3455565ace46SCharles Keepax goto out; 3456565ace46SCharles Keepax } 3457565ace46SCharles Keepax 345820b7f7c5SCharles Keepax if (wm_adsp_fw[dsp->fw].voice_trigger && buf->irq_count == 2) 345920b7f7c5SCharles Keepax ret = WM_ADSP_COMPR_VOICE_TRIGGER; 346020b7f7c5SCharles Keepax 34615847609eSCharles Keepax out_notify: 3462c7dae7c4SCharles Keepax if (compr && compr->stream) 346383a40ce9SCharles Keepax snd_compr_fragment_elapsed(compr->stream); 346483a40ce9SCharles Keepax 3465565ace46SCharles Keepax out: 3466565ace46SCharles Keepax mutex_unlock(&dsp->pwr_lock); 3467565ace46SCharles Keepax 3468565ace46SCharles Keepax return ret; 3469565ace46SCharles Keepax } 3470565ace46SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_handle_irq); 3471565ace46SCharles Keepax 3472565ace46SCharles Keepax static int wm_adsp_buffer_reenable_irq(struct wm_adsp_compr_buf *buf) 3473565ace46SCharles Keepax { 3474565ace46SCharles Keepax if (buf->irq_count & 0x01) 3475565ace46SCharles Keepax return 0; 3476565ace46SCharles Keepax 3477565ace46SCharles Keepax adsp_dbg(buf->dsp, "Enable IRQ(0x%x) for next fragment\n", 3478565ace46SCharles Keepax buf->irq_count); 3479565ace46SCharles Keepax 3480565ace46SCharles Keepax buf->irq_count |= 0x01; 3481565ace46SCharles Keepax 3482565ace46SCharles Keepax return wm_adsp_buffer_write(buf, HOST_BUFFER_FIELD(irq_ack), 3483565ace46SCharles Keepax buf->irq_count); 3484565ace46SCharles Keepax } 3485565ace46SCharles Keepax 3486565ace46SCharles Keepax int wm_adsp_compr_pointer(struct snd_compr_stream *stream, 3487565ace46SCharles Keepax struct snd_compr_tstamp *tstamp) 3488565ace46SCharles Keepax { 3489565ace46SCharles Keepax struct wm_adsp_compr *compr = stream->runtime->private_data; 3490565ace46SCharles Keepax struct wm_adsp *dsp = compr->dsp; 3491612047f0SCharles Keepax struct wm_adsp_compr_buf *buf; 3492565ace46SCharles Keepax int ret = 0; 3493565ace46SCharles Keepax 3494565ace46SCharles Keepax adsp_dbg(dsp, "Pointer request\n"); 3495565ace46SCharles Keepax 3496565ace46SCharles Keepax mutex_lock(&dsp->pwr_lock); 3497565ace46SCharles Keepax 3498612047f0SCharles Keepax buf = compr->buf; 3499612047f0SCharles Keepax 350028ee3d73SCharles Keepax if (!compr->buf || compr->buf->error) { 35018d280664SCharles Keepax snd_compr_stop_error(stream, SNDRV_PCM_STATE_XRUN); 3502565ace46SCharles Keepax ret = -EIO; 3503565ace46SCharles Keepax goto out; 3504565ace46SCharles Keepax } 3505565ace46SCharles Keepax 3506565ace46SCharles Keepax if (buf->avail < wm_adsp_compr_frag_words(compr)) { 3507565ace46SCharles Keepax ret = wm_adsp_buffer_update_avail(buf); 3508565ace46SCharles Keepax if (ret < 0) { 3509565ace46SCharles Keepax adsp_err(dsp, "Error reading avail: %d\n", ret); 3510565ace46SCharles Keepax goto out; 3511565ace46SCharles Keepax } 3512565ace46SCharles Keepax 3513565ace46SCharles Keepax /* 3514565ace46SCharles Keepax * If we really have less than 1 fragment available tell the 3515565ace46SCharles Keepax * DSP to inform us once a whole fragment is available. 3516565ace46SCharles Keepax */ 3517565ace46SCharles Keepax if (buf->avail < wm_adsp_compr_frag_words(compr)) { 35185847609eSCharles Keepax ret = wm_adsp_buffer_get_error(buf); 35198d280664SCharles Keepax if (ret < 0) { 35208d280664SCharles Keepax if (compr->buf->error) 35218d280664SCharles Keepax snd_compr_stop_error(stream, 35228d280664SCharles Keepax SNDRV_PCM_STATE_XRUN); 35235847609eSCharles Keepax goto out; 35248d280664SCharles Keepax } 35255847609eSCharles Keepax 3526565ace46SCharles Keepax ret = wm_adsp_buffer_reenable_irq(buf); 3527565ace46SCharles Keepax if (ret < 0) { 3528565ace46SCharles Keepax adsp_err(dsp, 3529565ace46SCharles Keepax "Failed to re-enable buffer IRQ: %d\n", 3530565ace46SCharles Keepax ret); 3531565ace46SCharles Keepax goto out; 3532565ace46SCharles Keepax } 3533565ace46SCharles Keepax } 3534565ace46SCharles Keepax } 3535565ace46SCharles Keepax 3536565ace46SCharles Keepax tstamp->copied_total = compr->copied_total; 3537565ace46SCharles Keepax tstamp->copied_total += buf->avail * WM_ADSP_DATA_WORD_SIZE; 3538da2b3358SCharles Keepax tstamp->sampling_rate = compr->sample_rate; 3539565ace46SCharles Keepax 3540565ace46SCharles Keepax out: 3541565ace46SCharles Keepax mutex_unlock(&dsp->pwr_lock); 3542565ace46SCharles Keepax 3543565ace46SCharles Keepax return ret; 3544565ace46SCharles Keepax } 3545565ace46SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_pointer); 3546565ace46SCharles Keepax 354783a40ce9SCharles Keepax static int wm_adsp_buffer_capture_block(struct wm_adsp_compr *compr, int target) 354883a40ce9SCharles Keepax { 354983a40ce9SCharles Keepax struct wm_adsp_compr_buf *buf = compr->buf; 355083a40ce9SCharles Keepax u8 *pack_in = (u8 *)compr->raw_buf; 355183a40ce9SCharles Keepax u8 *pack_out = (u8 *)compr->raw_buf; 355283a40ce9SCharles Keepax unsigned int adsp_addr; 355383a40ce9SCharles Keepax int mem_type, nwords, max_read; 355483a40ce9SCharles Keepax int i, j, ret; 355583a40ce9SCharles Keepax 355683a40ce9SCharles Keepax /* Calculate read parameters */ 355783a40ce9SCharles Keepax for (i = 0; i < wm_adsp_fw[buf->dsp->fw].caps->num_regions; ++i) 355883a40ce9SCharles Keepax if (buf->read_index < buf->regions[i].cumulative_size) 355983a40ce9SCharles Keepax break; 356083a40ce9SCharles Keepax 356183a40ce9SCharles Keepax if (i == wm_adsp_fw[buf->dsp->fw].caps->num_regions) 356283a40ce9SCharles Keepax return -EINVAL; 356383a40ce9SCharles Keepax 356483a40ce9SCharles Keepax mem_type = buf->regions[i].mem_type; 356583a40ce9SCharles Keepax adsp_addr = buf->regions[i].base_addr + 356683a40ce9SCharles Keepax (buf->read_index - buf->regions[i].offset); 356783a40ce9SCharles Keepax 356883a40ce9SCharles Keepax max_read = wm_adsp_compr_frag_words(compr); 356983a40ce9SCharles Keepax nwords = buf->regions[i].cumulative_size - buf->read_index; 357083a40ce9SCharles Keepax 357183a40ce9SCharles Keepax if (nwords > target) 357283a40ce9SCharles Keepax nwords = target; 357383a40ce9SCharles Keepax if (nwords > buf->avail) 357483a40ce9SCharles Keepax nwords = buf->avail; 357583a40ce9SCharles Keepax if (nwords > max_read) 357683a40ce9SCharles Keepax nwords = max_read; 357783a40ce9SCharles Keepax if (!nwords) 357883a40ce9SCharles Keepax return 0; 357983a40ce9SCharles Keepax 358083a40ce9SCharles Keepax /* Read data from DSP */ 358183a40ce9SCharles Keepax ret = wm_adsp_read_data_block(buf->dsp, mem_type, adsp_addr, 358283a40ce9SCharles Keepax nwords, compr->raw_buf); 358383a40ce9SCharles Keepax if (ret < 0) 358483a40ce9SCharles Keepax return ret; 358583a40ce9SCharles Keepax 358683a40ce9SCharles Keepax /* Remove the padding bytes from the data read from the DSP */ 358783a40ce9SCharles Keepax for (i = 0; i < nwords; i++) { 358883a40ce9SCharles Keepax for (j = 0; j < WM_ADSP_DATA_WORD_SIZE; j++) 358983a40ce9SCharles Keepax *pack_out++ = *pack_in++; 359083a40ce9SCharles Keepax 359183a40ce9SCharles Keepax pack_in += sizeof(*(compr->raw_buf)) - WM_ADSP_DATA_WORD_SIZE; 359283a40ce9SCharles Keepax } 359383a40ce9SCharles Keepax 359483a40ce9SCharles Keepax /* update read index to account for words read */ 359583a40ce9SCharles Keepax buf->read_index += nwords; 359683a40ce9SCharles Keepax if (buf->read_index == wm_adsp_buffer_size(buf)) 359783a40ce9SCharles Keepax buf->read_index = 0; 359883a40ce9SCharles Keepax 359983a40ce9SCharles Keepax ret = wm_adsp_buffer_write(buf, HOST_BUFFER_FIELD(next_read_index), 360083a40ce9SCharles Keepax buf->read_index); 360183a40ce9SCharles Keepax if (ret < 0) 360283a40ce9SCharles Keepax return ret; 360383a40ce9SCharles Keepax 360483a40ce9SCharles Keepax /* update avail to account for words read */ 360583a40ce9SCharles Keepax buf->avail -= nwords; 360683a40ce9SCharles Keepax 360783a40ce9SCharles Keepax return nwords; 360883a40ce9SCharles Keepax } 360983a40ce9SCharles Keepax 361083a40ce9SCharles Keepax static int wm_adsp_compr_read(struct wm_adsp_compr *compr, 361183a40ce9SCharles Keepax char __user *buf, size_t count) 361283a40ce9SCharles Keepax { 361383a40ce9SCharles Keepax struct wm_adsp *dsp = compr->dsp; 361483a40ce9SCharles Keepax int ntotal = 0; 361583a40ce9SCharles Keepax int nwords, nbytes; 361683a40ce9SCharles Keepax 361783a40ce9SCharles Keepax adsp_dbg(dsp, "Requested read of %zu bytes\n", count); 361883a40ce9SCharles Keepax 361928ee3d73SCharles Keepax if (!compr->buf || compr->buf->error) { 36208d280664SCharles Keepax snd_compr_stop_error(compr->stream, SNDRV_PCM_STATE_XRUN); 362183a40ce9SCharles Keepax return -EIO; 36228d280664SCharles Keepax } 362383a40ce9SCharles Keepax 362483a40ce9SCharles Keepax count /= WM_ADSP_DATA_WORD_SIZE; 362583a40ce9SCharles Keepax 362683a40ce9SCharles Keepax do { 362783a40ce9SCharles Keepax nwords = wm_adsp_buffer_capture_block(compr, count); 362883a40ce9SCharles Keepax if (nwords < 0) { 362983a40ce9SCharles Keepax adsp_err(dsp, "Failed to capture block: %d\n", nwords); 363083a40ce9SCharles Keepax return nwords; 363183a40ce9SCharles Keepax } 363283a40ce9SCharles Keepax 363383a40ce9SCharles Keepax nbytes = nwords * WM_ADSP_DATA_WORD_SIZE; 363483a40ce9SCharles Keepax 363583a40ce9SCharles Keepax adsp_dbg(dsp, "Read %d bytes\n", nbytes); 363683a40ce9SCharles Keepax 363783a40ce9SCharles Keepax if (copy_to_user(buf + ntotal, compr->raw_buf, nbytes)) { 363883a40ce9SCharles Keepax adsp_err(dsp, "Failed to copy data to user: %d, %d\n", 363983a40ce9SCharles Keepax ntotal, nbytes); 364083a40ce9SCharles Keepax return -EFAULT; 364183a40ce9SCharles Keepax } 364283a40ce9SCharles Keepax 364383a40ce9SCharles Keepax count -= nwords; 364483a40ce9SCharles Keepax ntotal += nbytes; 364583a40ce9SCharles Keepax } while (nwords > 0 && count > 0); 364683a40ce9SCharles Keepax 364783a40ce9SCharles Keepax compr->copied_total += ntotal; 364883a40ce9SCharles Keepax 364983a40ce9SCharles Keepax return ntotal; 365083a40ce9SCharles Keepax } 365183a40ce9SCharles Keepax 365283a40ce9SCharles Keepax int wm_adsp_compr_copy(struct snd_compr_stream *stream, char __user *buf, 365383a40ce9SCharles Keepax size_t count) 365483a40ce9SCharles Keepax { 365583a40ce9SCharles Keepax struct wm_adsp_compr *compr = stream->runtime->private_data; 365683a40ce9SCharles Keepax struct wm_adsp *dsp = compr->dsp; 365783a40ce9SCharles Keepax int ret; 365883a40ce9SCharles Keepax 365983a40ce9SCharles Keepax mutex_lock(&dsp->pwr_lock); 366083a40ce9SCharles Keepax 366183a40ce9SCharles Keepax if (stream->direction == SND_COMPRESS_CAPTURE) 366283a40ce9SCharles Keepax ret = wm_adsp_compr_read(compr, buf, count); 366383a40ce9SCharles Keepax else 366483a40ce9SCharles Keepax ret = -ENOTSUPP; 366583a40ce9SCharles Keepax 366683a40ce9SCharles Keepax mutex_unlock(&dsp->pwr_lock); 366783a40ce9SCharles Keepax 366883a40ce9SCharles Keepax return ret; 366983a40ce9SCharles Keepax } 367083a40ce9SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_copy); 367183a40ce9SCharles Keepax 367251a2c944SMayuresh Kulkarni int wm_adsp2_lock(struct wm_adsp *dsp, unsigned int lock_regions) 367351a2c944SMayuresh Kulkarni { 367451a2c944SMayuresh Kulkarni struct regmap *regmap = dsp->regmap; 367551a2c944SMayuresh Kulkarni unsigned int code0, code1, lock_reg; 367651a2c944SMayuresh Kulkarni 367751a2c944SMayuresh Kulkarni if (!(lock_regions & WM_ADSP2_REGION_ALL)) 367851a2c944SMayuresh Kulkarni return 0; 367951a2c944SMayuresh Kulkarni 368051a2c944SMayuresh Kulkarni lock_regions &= WM_ADSP2_REGION_ALL; 368151a2c944SMayuresh Kulkarni lock_reg = dsp->base + ADSP2_LOCK_REGION_1_LOCK_REGION_0; 368251a2c944SMayuresh Kulkarni 368351a2c944SMayuresh Kulkarni while (lock_regions) { 368451a2c944SMayuresh Kulkarni code0 = code1 = 0; 368551a2c944SMayuresh Kulkarni if (lock_regions & BIT(0)) { 368651a2c944SMayuresh Kulkarni code0 = ADSP2_LOCK_CODE_0; 368751a2c944SMayuresh Kulkarni code1 = ADSP2_LOCK_CODE_1; 368851a2c944SMayuresh Kulkarni } 368951a2c944SMayuresh Kulkarni if (lock_regions & BIT(1)) { 369051a2c944SMayuresh Kulkarni code0 |= ADSP2_LOCK_CODE_0 << ADSP2_LOCK_REGION_SHIFT; 369151a2c944SMayuresh Kulkarni code1 |= ADSP2_LOCK_CODE_1 << ADSP2_LOCK_REGION_SHIFT; 369251a2c944SMayuresh Kulkarni } 369351a2c944SMayuresh Kulkarni regmap_write(regmap, lock_reg, code0); 369451a2c944SMayuresh Kulkarni regmap_write(regmap, lock_reg, code1); 369551a2c944SMayuresh Kulkarni lock_regions >>= 2; 369651a2c944SMayuresh Kulkarni lock_reg += 2; 369751a2c944SMayuresh Kulkarni } 369851a2c944SMayuresh Kulkarni 369951a2c944SMayuresh Kulkarni return 0; 370051a2c944SMayuresh Kulkarni } 370151a2c944SMayuresh Kulkarni EXPORT_SYMBOL_GPL(wm_adsp2_lock); 370251a2c944SMayuresh Kulkarni 370351a2c944SMayuresh Kulkarni irqreturn_t wm_adsp2_bus_error(struct wm_adsp *dsp) 370451a2c944SMayuresh Kulkarni { 370551a2c944SMayuresh Kulkarni unsigned int val; 370651a2c944SMayuresh Kulkarni struct regmap *regmap = dsp->regmap; 370751a2c944SMayuresh Kulkarni int ret = 0; 370851a2c944SMayuresh Kulkarni 370951a2c944SMayuresh Kulkarni ret = regmap_read(regmap, dsp->base + ADSP2_LOCK_REGION_CTRL, &val); 371051a2c944SMayuresh Kulkarni if (ret) { 371151a2c944SMayuresh Kulkarni adsp_err(dsp, 371251a2c944SMayuresh Kulkarni "Failed to read Region Lock Ctrl register: %d\n", ret); 371351a2c944SMayuresh Kulkarni return IRQ_HANDLED; 371451a2c944SMayuresh Kulkarni } 371551a2c944SMayuresh Kulkarni 371651a2c944SMayuresh Kulkarni if (val & ADSP2_WDT_TIMEOUT_STS_MASK) { 371751a2c944SMayuresh Kulkarni adsp_err(dsp, "watchdog timeout error\n"); 371851a2c944SMayuresh Kulkarni wm_adsp_stop_watchdog(dsp); 371951a2c944SMayuresh Kulkarni } 372051a2c944SMayuresh Kulkarni 372151a2c944SMayuresh Kulkarni if (val & (ADSP2_SLAVE_ERR_MASK | ADSP2_REGION_LOCK_ERR_MASK)) { 372251a2c944SMayuresh Kulkarni if (val & ADSP2_SLAVE_ERR_MASK) 372351a2c944SMayuresh Kulkarni adsp_err(dsp, "bus error: slave error\n"); 372451a2c944SMayuresh Kulkarni else 372551a2c944SMayuresh Kulkarni adsp_err(dsp, "bus error: region lock error\n"); 372651a2c944SMayuresh Kulkarni 372751a2c944SMayuresh Kulkarni ret = regmap_read(regmap, dsp->base + ADSP2_BUS_ERR_ADDR, &val); 372851a2c944SMayuresh Kulkarni if (ret) { 372951a2c944SMayuresh Kulkarni adsp_err(dsp, 373051a2c944SMayuresh Kulkarni "Failed to read Bus Err Addr register: %d\n", 373151a2c944SMayuresh Kulkarni ret); 373251a2c944SMayuresh Kulkarni return IRQ_HANDLED; 373351a2c944SMayuresh Kulkarni } 373451a2c944SMayuresh Kulkarni 373551a2c944SMayuresh Kulkarni adsp_err(dsp, "bus error address = 0x%x\n", 373651a2c944SMayuresh Kulkarni val & ADSP2_BUS_ERR_ADDR_MASK); 373751a2c944SMayuresh Kulkarni 373851a2c944SMayuresh Kulkarni ret = regmap_read(regmap, 373951a2c944SMayuresh Kulkarni dsp->base + ADSP2_PMEM_ERR_ADDR_XMEM_ERR_ADDR, 374051a2c944SMayuresh Kulkarni &val); 374151a2c944SMayuresh Kulkarni if (ret) { 374251a2c944SMayuresh Kulkarni adsp_err(dsp, 374351a2c944SMayuresh Kulkarni "Failed to read Pmem Xmem Err Addr register: %d\n", 374451a2c944SMayuresh Kulkarni ret); 374551a2c944SMayuresh Kulkarni return IRQ_HANDLED; 374651a2c944SMayuresh Kulkarni } 374751a2c944SMayuresh Kulkarni 374851a2c944SMayuresh Kulkarni adsp_err(dsp, "xmem error address = 0x%x\n", 374951a2c944SMayuresh Kulkarni val & ADSP2_XMEM_ERR_ADDR_MASK); 375051a2c944SMayuresh Kulkarni adsp_err(dsp, "pmem error address = 0x%x\n", 375151a2c944SMayuresh Kulkarni (val & ADSP2_PMEM_ERR_ADDR_MASK) >> 375251a2c944SMayuresh Kulkarni ADSP2_PMEM_ERR_ADDR_SHIFT); 375351a2c944SMayuresh Kulkarni } 375451a2c944SMayuresh Kulkarni 375551a2c944SMayuresh Kulkarni regmap_update_bits(regmap, dsp->base + ADSP2_LOCK_REGION_CTRL, 375651a2c944SMayuresh Kulkarni ADSP2_CTRL_ERR_EINT, ADSP2_CTRL_ERR_EINT); 375751a2c944SMayuresh Kulkarni 375851a2c944SMayuresh Kulkarni return IRQ_HANDLED; 375951a2c944SMayuresh Kulkarni } 376051a2c944SMayuresh Kulkarni EXPORT_SYMBOL_GPL(wm_adsp2_bus_error); 376151a2c944SMayuresh Kulkarni 37620a37c6efSPraveen Diwakar MODULE_LICENSE("GPL v2"); 3763