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 13605391d0SRichard Fitzgerald #include <linux/ctype.h> 142159ad93SMark Brown #include <linux/module.h> 152159ad93SMark Brown #include <linux/moduleparam.h> 162159ad93SMark Brown #include <linux/init.h> 172159ad93SMark Brown #include <linux/delay.h> 182159ad93SMark Brown #include <linux/firmware.h> 19cf17c83cSMark Brown #include <linux/list.h> 202159ad93SMark Brown #include <linux/pm.h> 212159ad93SMark Brown #include <linux/pm_runtime.h> 222159ad93SMark Brown #include <linux/regmap.h> 23973838a0SMark Brown #include <linux/regulator/consumer.h> 242159ad93SMark Brown #include <linux/slab.h> 25cdcd7f72SCharles Keepax #include <linux/vmalloc.h> 266ab2b7b4SDimitris Papastamos #include <linux/workqueue.h> 27f9f55e31SRichard Fitzgerald #include <linux/debugfs.h> 282159ad93SMark Brown #include <sound/core.h> 292159ad93SMark Brown #include <sound/pcm.h> 302159ad93SMark Brown #include <sound/pcm_params.h> 312159ad93SMark Brown #include <sound/soc.h> 322159ad93SMark Brown #include <sound/jack.h> 332159ad93SMark Brown #include <sound/initval.h> 342159ad93SMark Brown #include <sound/tlv.h> 352159ad93SMark Brown 362159ad93SMark Brown #include "wm_adsp.h" 372159ad93SMark Brown 382159ad93SMark Brown #define adsp_crit(_dsp, fmt, ...) \ 39605391d0SRichard Fitzgerald dev_crit(_dsp->dev, "%s: " fmt, _dsp->name, ##__VA_ARGS__) 402159ad93SMark Brown #define adsp_err(_dsp, fmt, ...) \ 41605391d0SRichard Fitzgerald dev_err(_dsp->dev, "%s: " fmt, _dsp->name, ##__VA_ARGS__) 422159ad93SMark Brown #define adsp_warn(_dsp, fmt, ...) \ 43605391d0SRichard Fitzgerald dev_warn(_dsp->dev, "%s: " fmt, _dsp->name, ##__VA_ARGS__) 442159ad93SMark Brown #define adsp_info(_dsp, fmt, ...) \ 45605391d0SRichard Fitzgerald dev_info(_dsp->dev, "%s: " fmt, _dsp->name, ##__VA_ARGS__) 462159ad93SMark Brown #define adsp_dbg(_dsp, fmt, ...) \ 47605391d0SRichard Fitzgerald dev_dbg(_dsp->dev, "%s: " fmt, _dsp->name, ##__VA_ARGS__) 482159ad93SMark Brown 490d3fba3eSCharles Keepax #define compr_err(_obj, fmt, ...) \ 500d3fba3eSCharles Keepax adsp_err(_obj->dsp, "%s: " fmt, _obj->name ? _obj->name : "legacy", \ 510d3fba3eSCharles Keepax ##__VA_ARGS__) 520d3fba3eSCharles Keepax #define compr_dbg(_obj, fmt, ...) \ 530d3fba3eSCharles Keepax adsp_dbg(_obj->dsp, "%s: " fmt, _obj->name ? _obj->name : "legacy", \ 540d3fba3eSCharles Keepax ##__VA_ARGS__) 550d3fba3eSCharles Keepax 562159ad93SMark Brown #define ADSP1_CONTROL_1 0x00 572159ad93SMark Brown #define ADSP1_CONTROL_2 0x02 582159ad93SMark Brown #define ADSP1_CONTROL_3 0x03 592159ad93SMark Brown #define ADSP1_CONTROL_4 0x04 602159ad93SMark Brown #define ADSP1_CONTROL_5 0x06 612159ad93SMark Brown #define ADSP1_CONTROL_6 0x07 622159ad93SMark Brown #define ADSP1_CONTROL_7 0x08 632159ad93SMark Brown #define ADSP1_CONTROL_8 0x09 642159ad93SMark Brown #define ADSP1_CONTROL_9 0x0A 652159ad93SMark Brown #define ADSP1_CONTROL_10 0x0B 662159ad93SMark Brown #define ADSP1_CONTROL_11 0x0C 672159ad93SMark Brown #define ADSP1_CONTROL_12 0x0D 682159ad93SMark Brown #define ADSP1_CONTROL_13 0x0F 692159ad93SMark Brown #define ADSP1_CONTROL_14 0x10 702159ad93SMark Brown #define ADSP1_CONTROL_15 0x11 712159ad93SMark Brown #define ADSP1_CONTROL_16 0x12 722159ad93SMark Brown #define ADSP1_CONTROL_17 0x13 732159ad93SMark Brown #define ADSP1_CONTROL_18 0x14 742159ad93SMark Brown #define ADSP1_CONTROL_19 0x16 752159ad93SMark Brown #define ADSP1_CONTROL_20 0x17 762159ad93SMark Brown #define ADSP1_CONTROL_21 0x18 772159ad93SMark Brown #define ADSP1_CONTROL_22 0x1A 782159ad93SMark Brown #define ADSP1_CONTROL_23 0x1B 792159ad93SMark Brown #define ADSP1_CONTROL_24 0x1C 802159ad93SMark Brown #define ADSP1_CONTROL_25 0x1E 812159ad93SMark Brown #define ADSP1_CONTROL_26 0x20 822159ad93SMark Brown #define ADSP1_CONTROL_27 0x21 832159ad93SMark Brown #define ADSP1_CONTROL_28 0x22 842159ad93SMark Brown #define ADSP1_CONTROL_29 0x23 852159ad93SMark Brown #define ADSP1_CONTROL_30 0x24 862159ad93SMark Brown #define ADSP1_CONTROL_31 0x26 872159ad93SMark Brown 882159ad93SMark Brown /* 892159ad93SMark Brown * ADSP1 Control 19 902159ad93SMark Brown */ 912159ad93SMark Brown #define ADSP1_WDMA_BUFFER_LENGTH_MASK 0x00FF /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */ 922159ad93SMark Brown #define ADSP1_WDMA_BUFFER_LENGTH_SHIFT 0 /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */ 932159ad93SMark Brown #define ADSP1_WDMA_BUFFER_LENGTH_WIDTH 8 /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */ 942159ad93SMark Brown 952159ad93SMark Brown 962159ad93SMark Brown /* 972159ad93SMark Brown * ADSP1 Control 30 982159ad93SMark Brown */ 992159ad93SMark Brown #define ADSP1_DBG_CLK_ENA 0x0008 /* DSP1_DBG_CLK_ENA */ 1002159ad93SMark Brown #define ADSP1_DBG_CLK_ENA_MASK 0x0008 /* DSP1_DBG_CLK_ENA */ 1012159ad93SMark Brown #define ADSP1_DBG_CLK_ENA_SHIFT 3 /* DSP1_DBG_CLK_ENA */ 1022159ad93SMark Brown #define ADSP1_DBG_CLK_ENA_WIDTH 1 /* DSP1_DBG_CLK_ENA */ 1032159ad93SMark Brown #define ADSP1_SYS_ENA 0x0004 /* DSP1_SYS_ENA */ 1042159ad93SMark Brown #define ADSP1_SYS_ENA_MASK 0x0004 /* DSP1_SYS_ENA */ 1052159ad93SMark Brown #define ADSP1_SYS_ENA_SHIFT 2 /* DSP1_SYS_ENA */ 1062159ad93SMark Brown #define ADSP1_SYS_ENA_WIDTH 1 /* DSP1_SYS_ENA */ 1072159ad93SMark Brown #define ADSP1_CORE_ENA 0x0002 /* DSP1_CORE_ENA */ 1082159ad93SMark Brown #define ADSP1_CORE_ENA_MASK 0x0002 /* DSP1_CORE_ENA */ 1092159ad93SMark Brown #define ADSP1_CORE_ENA_SHIFT 1 /* DSP1_CORE_ENA */ 1102159ad93SMark Brown #define ADSP1_CORE_ENA_WIDTH 1 /* DSP1_CORE_ENA */ 1112159ad93SMark Brown #define ADSP1_START 0x0001 /* DSP1_START */ 1122159ad93SMark Brown #define ADSP1_START_MASK 0x0001 /* DSP1_START */ 1132159ad93SMark Brown #define ADSP1_START_SHIFT 0 /* DSP1_START */ 1142159ad93SMark Brown #define ADSP1_START_WIDTH 1 /* DSP1_START */ 1152159ad93SMark Brown 11694e205bfSChris Rattray /* 11794e205bfSChris Rattray * ADSP1 Control 31 11894e205bfSChris Rattray */ 11994e205bfSChris Rattray #define ADSP1_CLK_SEL_MASK 0x0007 /* CLK_SEL_ENA */ 12094e205bfSChris Rattray #define ADSP1_CLK_SEL_SHIFT 0 /* CLK_SEL_ENA */ 12194e205bfSChris Rattray #define ADSP1_CLK_SEL_WIDTH 3 /* CLK_SEL_ENA */ 12294e205bfSChris Rattray 1232d30b575SMark Brown #define ADSP2_CONTROL 0x0 1242d30b575SMark Brown #define ADSP2_CLOCKING 0x1 125e1ea1879SRichard Fitzgerald #define ADSP2V2_CLOCKING 0x2 1262d30b575SMark Brown #define ADSP2_STATUS1 0x4 1272d30b575SMark Brown #define ADSP2_WDMA_CONFIG_1 0x30 1282d30b575SMark Brown #define ADSP2_WDMA_CONFIG_2 0x31 129e1ea1879SRichard Fitzgerald #define ADSP2V2_WDMA_CONFIG_2 0x32 1302d30b575SMark Brown #define ADSP2_RDMA_CONFIG_1 0x34 1312159ad93SMark Brown 13210337b07SRichard Fitzgerald #define ADSP2_SCRATCH0 0x40 13310337b07SRichard Fitzgerald #define ADSP2_SCRATCH1 0x41 13410337b07SRichard Fitzgerald #define ADSP2_SCRATCH2 0x42 13510337b07SRichard Fitzgerald #define ADSP2_SCRATCH3 0x43 13610337b07SRichard Fitzgerald 137e1ea1879SRichard Fitzgerald #define ADSP2V2_SCRATCH0_1 0x40 138e1ea1879SRichard Fitzgerald #define ADSP2V2_SCRATCH2_3 0x42 139e1ea1879SRichard Fitzgerald 1402159ad93SMark Brown /* 1412159ad93SMark Brown * ADSP2 Control 1422159ad93SMark Brown */ 1432159ad93SMark Brown 1442159ad93SMark Brown #define ADSP2_MEM_ENA 0x0010 /* DSP1_MEM_ENA */ 1452159ad93SMark Brown #define ADSP2_MEM_ENA_MASK 0x0010 /* DSP1_MEM_ENA */ 1462159ad93SMark Brown #define ADSP2_MEM_ENA_SHIFT 4 /* DSP1_MEM_ENA */ 1472159ad93SMark Brown #define ADSP2_MEM_ENA_WIDTH 1 /* DSP1_MEM_ENA */ 1482159ad93SMark Brown #define ADSP2_SYS_ENA 0x0004 /* DSP1_SYS_ENA */ 1492159ad93SMark Brown #define ADSP2_SYS_ENA_MASK 0x0004 /* DSP1_SYS_ENA */ 1502159ad93SMark Brown #define ADSP2_SYS_ENA_SHIFT 2 /* DSP1_SYS_ENA */ 1512159ad93SMark Brown #define ADSP2_SYS_ENA_WIDTH 1 /* DSP1_SYS_ENA */ 1522159ad93SMark Brown #define ADSP2_CORE_ENA 0x0002 /* DSP1_CORE_ENA */ 1532159ad93SMark Brown #define ADSP2_CORE_ENA_MASK 0x0002 /* DSP1_CORE_ENA */ 1542159ad93SMark Brown #define ADSP2_CORE_ENA_SHIFT 1 /* DSP1_CORE_ENA */ 1552159ad93SMark Brown #define ADSP2_CORE_ENA_WIDTH 1 /* DSP1_CORE_ENA */ 1562159ad93SMark Brown #define ADSP2_START 0x0001 /* DSP1_START */ 1572159ad93SMark Brown #define ADSP2_START_MASK 0x0001 /* DSP1_START */ 1582159ad93SMark Brown #define ADSP2_START_SHIFT 0 /* DSP1_START */ 1592159ad93SMark Brown #define ADSP2_START_WIDTH 1 /* DSP1_START */ 1602159ad93SMark Brown 1612159ad93SMark Brown /* 162973838a0SMark Brown * ADSP2 clocking 163973838a0SMark Brown */ 164973838a0SMark Brown #define ADSP2_CLK_SEL_MASK 0x0007 /* CLK_SEL_ENA */ 165973838a0SMark Brown #define ADSP2_CLK_SEL_SHIFT 0 /* CLK_SEL_ENA */ 166973838a0SMark Brown #define ADSP2_CLK_SEL_WIDTH 3 /* CLK_SEL_ENA */ 167973838a0SMark Brown 168973838a0SMark Brown /* 169e1ea1879SRichard Fitzgerald * ADSP2V2 clocking 170e1ea1879SRichard Fitzgerald */ 171e1ea1879SRichard Fitzgerald #define ADSP2V2_CLK_SEL_MASK 0x70000 /* CLK_SEL_ENA */ 172e1ea1879SRichard Fitzgerald #define ADSP2V2_CLK_SEL_SHIFT 16 /* CLK_SEL_ENA */ 173e1ea1879SRichard Fitzgerald #define ADSP2V2_CLK_SEL_WIDTH 3 /* CLK_SEL_ENA */ 174e1ea1879SRichard Fitzgerald 175e1ea1879SRichard Fitzgerald #define ADSP2V2_RATE_MASK 0x7800 /* DSP_RATE */ 176e1ea1879SRichard Fitzgerald #define ADSP2V2_RATE_SHIFT 11 /* DSP_RATE */ 177e1ea1879SRichard Fitzgerald #define ADSP2V2_RATE_WIDTH 4 /* DSP_RATE */ 178e1ea1879SRichard Fitzgerald 179e1ea1879SRichard Fitzgerald /* 1802159ad93SMark Brown * ADSP2 Status 1 1812159ad93SMark Brown */ 1822159ad93SMark Brown #define ADSP2_RAM_RDY 0x0001 1832159ad93SMark Brown #define ADSP2_RAM_RDY_MASK 0x0001 1842159ad93SMark Brown #define ADSP2_RAM_RDY_SHIFT 0 1852159ad93SMark Brown #define ADSP2_RAM_RDY_WIDTH 1 1862159ad93SMark Brown 18751a2c944SMayuresh Kulkarni /* 18851a2c944SMayuresh Kulkarni * ADSP2 Lock support 18951a2c944SMayuresh Kulkarni */ 19051a2c944SMayuresh Kulkarni #define ADSP2_LOCK_CODE_0 0x5555 19151a2c944SMayuresh Kulkarni #define ADSP2_LOCK_CODE_1 0xAAAA 19251a2c944SMayuresh Kulkarni 19351a2c944SMayuresh Kulkarni #define ADSP2_WATCHDOG 0x0A 19451a2c944SMayuresh Kulkarni #define ADSP2_BUS_ERR_ADDR 0x52 19551a2c944SMayuresh Kulkarni #define ADSP2_REGION_LOCK_STATUS 0x64 19651a2c944SMayuresh Kulkarni #define ADSP2_LOCK_REGION_1_LOCK_REGION_0 0x66 19751a2c944SMayuresh Kulkarni #define ADSP2_LOCK_REGION_3_LOCK_REGION_2 0x68 19851a2c944SMayuresh Kulkarni #define ADSP2_LOCK_REGION_5_LOCK_REGION_4 0x6A 19951a2c944SMayuresh Kulkarni #define ADSP2_LOCK_REGION_7_LOCK_REGION_6 0x6C 20051a2c944SMayuresh Kulkarni #define ADSP2_LOCK_REGION_9_LOCK_REGION_8 0x6E 20151a2c944SMayuresh Kulkarni #define ADSP2_LOCK_REGION_CTRL 0x7A 20251a2c944SMayuresh Kulkarni #define ADSP2_PMEM_ERR_ADDR_XMEM_ERR_ADDR 0x7C 20351a2c944SMayuresh Kulkarni 20451a2c944SMayuresh Kulkarni #define ADSP2_REGION_LOCK_ERR_MASK 0x8000 20551a2c944SMayuresh Kulkarni #define ADSP2_SLAVE_ERR_MASK 0x4000 20651a2c944SMayuresh Kulkarni #define ADSP2_WDT_TIMEOUT_STS_MASK 0x2000 20751a2c944SMayuresh Kulkarni #define ADSP2_CTRL_ERR_PAUSE_ENA 0x0002 20851a2c944SMayuresh Kulkarni #define ADSP2_CTRL_ERR_EINT 0x0001 20951a2c944SMayuresh Kulkarni 21051a2c944SMayuresh Kulkarni #define ADSP2_BUS_ERR_ADDR_MASK 0x00FFFFFF 21151a2c944SMayuresh Kulkarni #define ADSP2_XMEM_ERR_ADDR_MASK 0x0000FFFF 21251a2c944SMayuresh Kulkarni #define ADSP2_PMEM_ERR_ADDR_MASK 0x7FFF0000 21351a2c944SMayuresh Kulkarni #define ADSP2_PMEM_ERR_ADDR_SHIFT 16 21451a2c944SMayuresh Kulkarni #define ADSP2_WDT_ENA_MASK 0xFFFFFFFD 21551a2c944SMayuresh Kulkarni 21651a2c944SMayuresh Kulkarni #define ADSP2_LOCK_REGION_SHIFT 16 21751a2c944SMayuresh Kulkarni 2189ee78757SCharles Keepax #define ADSP_MAX_STD_CTRL_SIZE 512 2199ee78757SCharles Keepax 220f4f0c4c6SRichard Fitzgerald #define WM_ADSP_ACKED_CTL_TIMEOUT_MS 100 221f4f0c4c6SRichard Fitzgerald #define WM_ADSP_ACKED_CTL_N_QUICKPOLLS 10 222a23ebba8SRichard Fitzgerald #define WM_ADSP_ACKED_CTL_MIN_VALUE 0 223a23ebba8SRichard Fitzgerald #define WM_ADSP_ACKED_CTL_MAX_VALUE 0xFFFFFF 224f4f0c4c6SRichard Fitzgerald 225f4f0c4c6SRichard Fitzgerald /* 226f4f0c4c6SRichard Fitzgerald * Event control messages 227f4f0c4c6SRichard Fitzgerald */ 228f4f0c4c6SRichard Fitzgerald #define WM_ADSP_FW_EVENT_SHUTDOWN 0x000001 229f4f0c4c6SRichard Fitzgerald 230cf17c83cSMark Brown struct wm_adsp_buf { 231cf17c83cSMark Brown struct list_head list; 232cf17c83cSMark Brown void *buf; 233cf17c83cSMark Brown }; 234cf17c83cSMark Brown 235cf17c83cSMark Brown static struct wm_adsp_buf *wm_adsp_buf_alloc(const void *src, size_t len, 236cf17c83cSMark Brown struct list_head *list) 237cf17c83cSMark Brown { 238cf17c83cSMark Brown struct wm_adsp_buf *buf = kzalloc(sizeof(*buf), GFP_KERNEL); 239cf17c83cSMark Brown 240cf17c83cSMark Brown if (buf == NULL) 241cf17c83cSMark Brown return NULL; 242cf17c83cSMark Brown 243cdcd7f72SCharles Keepax buf->buf = vmalloc(len); 244cf17c83cSMark Brown if (!buf->buf) { 2454d41c74dSRichard Fitzgerald kfree(buf); 246cf17c83cSMark Brown return NULL; 247cf17c83cSMark Brown } 248cdcd7f72SCharles Keepax memcpy(buf->buf, src, len); 249cf17c83cSMark Brown 250cf17c83cSMark Brown if (list) 251cf17c83cSMark Brown list_add_tail(&buf->list, list); 252cf17c83cSMark Brown 253cf17c83cSMark Brown return buf; 254cf17c83cSMark Brown } 255cf17c83cSMark Brown 256cf17c83cSMark Brown static void wm_adsp_buf_free(struct list_head *list) 257cf17c83cSMark Brown { 258cf17c83cSMark Brown while (!list_empty(list)) { 259cf17c83cSMark Brown struct wm_adsp_buf *buf = list_first_entry(list, 260cf17c83cSMark Brown struct wm_adsp_buf, 261cf17c83cSMark Brown list); 262cf17c83cSMark Brown list_del(&buf->list); 263cdcd7f72SCharles Keepax vfree(buf->buf); 264cf17c83cSMark Brown kfree(buf); 265cf17c83cSMark Brown } 266cf17c83cSMark Brown } 267cf17c83cSMark Brown 268dd84f925SMark Brown #define WM_ADSP_FW_MBC_VSS 0 26904d1300fSCharles Keepax #define WM_ADSP_FW_HIFI 1 27004d1300fSCharles Keepax #define WM_ADSP_FW_TX 2 27104d1300fSCharles Keepax #define WM_ADSP_FW_TX_SPK 3 27204d1300fSCharles Keepax #define WM_ADSP_FW_RX 4 27304d1300fSCharles Keepax #define WM_ADSP_FW_RX_ANC 5 27404d1300fSCharles Keepax #define WM_ADSP_FW_CTRL 6 27504d1300fSCharles Keepax #define WM_ADSP_FW_ASR 7 27604d1300fSCharles Keepax #define WM_ADSP_FW_TRACE 8 27704d1300fSCharles Keepax #define WM_ADSP_FW_SPK_PROT 9 27804d1300fSCharles Keepax #define WM_ADSP_FW_MISC 10 27904d1300fSCharles Keepax 28004d1300fSCharles Keepax #define WM_ADSP_NUM_FW 11 281dd84f925SMark Brown 2821023dbd9SMark Brown static const char *wm_adsp_fw_text[WM_ADSP_NUM_FW] = { 283dd84f925SMark Brown [WM_ADSP_FW_MBC_VSS] = "MBC/VSS", 28404d1300fSCharles Keepax [WM_ADSP_FW_HIFI] = "MasterHiFi", 285dd84f925SMark Brown [WM_ADSP_FW_TX] = "Tx", 286dd84f925SMark Brown [WM_ADSP_FW_TX_SPK] = "Tx Speaker", 28704d1300fSCharles Keepax [WM_ADSP_FW_RX] = "Rx", 288dd84f925SMark Brown [WM_ADSP_FW_RX_ANC] = "Rx ANC", 28904d1300fSCharles Keepax [WM_ADSP_FW_CTRL] = "Voice Ctrl", 29004d1300fSCharles Keepax [WM_ADSP_FW_ASR] = "ASR Assist", 29104d1300fSCharles Keepax [WM_ADSP_FW_TRACE] = "Dbg Trace", 29204d1300fSCharles Keepax [WM_ADSP_FW_SPK_PROT] = "Protection", 29304d1300fSCharles Keepax [WM_ADSP_FW_MISC] = "Misc", 2941023dbd9SMark Brown }; 2951023dbd9SMark Brown 2962cd19bdbSCharles Keepax struct wm_adsp_system_config_xm_hdr { 2972cd19bdbSCharles Keepax __be32 sys_enable; 2982cd19bdbSCharles Keepax __be32 fw_id; 2992cd19bdbSCharles Keepax __be32 fw_rev; 3002cd19bdbSCharles Keepax __be32 boot_status; 3012cd19bdbSCharles Keepax __be32 watchdog; 3022cd19bdbSCharles Keepax __be32 dma_buffer_size; 3032cd19bdbSCharles Keepax __be32 rdma[6]; 3042cd19bdbSCharles Keepax __be32 wdma[8]; 3052cd19bdbSCharles Keepax __be32 build_job_name[3]; 3062cd19bdbSCharles Keepax __be32 build_job_number; 3072cd19bdbSCharles Keepax }; 3082cd19bdbSCharles Keepax 3092cd19bdbSCharles Keepax struct wm_adsp_alg_xm_struct { 3102cd19bdbSCharles Keepax __be32 magic; 3112cd19bdbSCharles Keepax __be32 smoothing; 3122cd19bdbSCharles Keepax __be32 threshold; 3132cd19bdbSCharles Keepax __be32 host_buf_ptr; 3142cd19bdbSCharles Keepax __be32 start_seq; 3152cd19bdbSCharles Keepax __be32 high_water_mark; 3162cd19bdbSCharles Keepax __be32 low_water_mark; 3172cd19bdbSCharles Keepax __be64 smoothed_power; 3182cd19bdbSCharles Keepax }; 3192cd19bdbSCharles Keepax 3204f2d4eabSStuart Henderson struct wm_adsp_host_buf_coeff_v1 { 3214f2d4eabSStuart Henderson __be32 host_buf_ptr; /* Host buffer pointer */ 3224f2d4eabSStuart Henderson __be32 versions; /* Version numbers */ 3234f2d4eabSStuart Henderson __be32 name[4]; /* The buffer name */ 3244f2d4eabSStuart Henderson }; 3254f2d4eabSStuart Henderson 3262cd19bdbSCharles Keepax struct wm_adsp_buffer { 3272a2aefa4SRichard Fitzgerald __be32 buf1_base; /* Base addr of first buffer area */ 3282a2aefa4SRichard Fitzgerald __be32 buf1_size; /* Size of buf1 area in DSP words */ 3292a2aefa4SRichard Fitzgerald __be32 buf2_base; /* Base addr of 2nd buffer area */ 3302a2aefa4SRichard Fitzgerald __be32 buf1_buf2_size; /* Size of buf1+buf2 in DSP words */ 3312a2aefa4SRichard Fitzgerald __be32 buf3_base; /* Base addr of buf3 area */ 3322a2aefa4SRichard Fitzgerald __be32 buf_total_size; /* Size of buf1+buf2+buf3 in DSP words */ 3332cd19bdbSCharles Keepax __be32 high_water_mark; /* Point at which IRQ is asserted */ 3342cd19bdbSCharles Keepax __be32 irq_count; /* bits 1-31 count IRQ assertions */ 3352cd19bdbSCharles Keepax __be32 irq_ack; /* acked IRQ count, bit 0 enables IRQ */ 3362cd19bdbSCharles Keepax __be32 next_write_index; /* word index of next write */ 3372cd19bdbSCharles Keepax __be32 next_read_index; /* word index of next read */ 3382cd19bdbSCharles Keepax __be32 error; /* error if any */ 3392cd19bdbSCharles Keepax __be32 oldest_block_index; /* word index of oldest surviving */ 3402cd19bdbSCharles Keepax __be32 requested_rewind; /* how many blocks rewind was done */ 3412cd19bdbSCharles Keepax __be32 reserved_space; /* internal */ 3422cd19bdbSCharles Keepax __be32 min_free; /* min free space since stream start */ 3432cd19bdbSCharles Keepax __be32 blocks_written[2]; /* total blocks written (64 bit) */ 3442cd19bdbSCharles Keepax __be32 words_written[2]; /* total words written (64 bit) */ 3452cd19bdbSCharles Keepax }; 3462cd19bdbSCharles Keepax 347721be3beSCharles Keepax struct wm_adsp_compr; 348721be3beSCharles Keepax 3492cd19bdbSCharles Keepax struct wm_adsp_compr_buf { 3504f2d4eabSStuart Henderson struct list_head list; 3512cd19bdbSCharles Keepax struct wm_adsp *dsp; 352721be3beSCharles Keepax struct wm_adsp_compr *compr; 3532cd19bdbSCharles Keepax 3542cd19bdbSCharles Keepax struct wm_adsp_buffer_region *regions; 3552cd19bdbSCharles Keepax u32 host_buf_ptr; 356565ace46SCharles Keepax 357565ace46SCharles Keepax u32 error; 358565ace46SCharles Keepax u32 irq_count; 359565ace46SCharles Keepax int read_index; 360565ace46SCharles Keepax int avail; 361fb13f19dSAndrew Ford int host_buf_mem_type; 3624f2d4eabSStuart Henderson 3634f2d4eabSStuart Henderson char *name; 3642cd19bdbSCharles Keepax }; 3652cd19bdbSCharles Keepax 366406abc95SCharles Keepax struct wm_adsp_compr { 3674f2d4eabSStuart Henderson struct list_head list; 368406abc95SCharles Keepax struct wm_adsp *dsp; 36995fe9597SCharles Keepax struct wm_adsp_compr_buf *buf; 370406abc95SCharles Keepax 371406abc95SCharles Keepax struct snd_compr_stream *stream; 372406abc95SCharles Keepax struct snd_compressed_buffer size; 373565ace46SCharles Keepax 37483a40ce9SCharles Keepax u32 *raw_buf; 375565ace46SCharles Keepax unsigned int copied_total; 376da2b3358SCharles Keepax 377da2b3358SCharles Keepax unsigned int sample_rate; 3784f2d4eabSStuart Henderson 3794f2d4eabSStuart Henderson const char *name; 380406abc95SCharles Keepax }; 381406abc95SCharles Keepax 382406abc95SCharles Keepax #define WM_ADSP_DATA_WORD_SIZE 3 383406abc95SCharles Keepax 384406abc95SCharles Keepax #define WM_ADSP_MIN_FRAGMENTS 1 385406abc95SCharles Keepax #define WM_ADSP_MAX_FRAGMENTS 256 386406abc95SCharles Keepax #define WM_ADSP_MIN_FRAGMENT_SIZE (64 * WM_ADSP_DATA_WORD_SIZE) 387406abc95SCharles Keepax #define WM_ADSP_MAX_FRAGMENT_SIZE (4096 * WM_ADSP_DATA_WORD_SIZE) 388406abc95SCharles Keepax 3892cd19bdbSCharles Keepax #define WM_ADSP_ALG_XM_STRUCT_MAGIC 0x49aec7 3902cd19bdbSCharles Keepax 3912cd19bdbSCharles Keepax #define HOST_BUFFER_FIELD(field) \ 3922cd19bdbSCharles Keepax (offsetof(struct wm_adsp_buffer, field) / sizeof(__be32)) 3932cd19bdbSCharles Keepax 3942cd19bdbSCharles Keepax #define ALG_XM_FIELD(field) \ 3952cd19bdbSCharles Keepax (offsetof(struct wm_adsp_alg_xm_struct, field) / sizeof(__be32)) 3962cd19bdbSCharles Keepax 3974f2d4eabSStuart Henderson #define HOST_BUF_COEFF_SUPPORTED_COMPAT_VER 1 3984f2d4eabSStuart Henderson 3994f2d4eabSStuart Henderson #define HOST_BUF_COEFF_COMPAT_VER_MASK 0xFF00 4004f2d4eabSStuart Henderson #define HOST_BUF_COEFF_COMPAT_VER_SHIFT 8 4014f2d4eabSStuart Henderson 4022cd19bdbSCharles Keepax static int wm_adsp_buffer_init(struct wm_adsp *dsp); 4032cd19bdbSCharles Keepax static int wm_adsp_buffer_free(struct wm_adsp *dsp); 4042cd19bdbSCharles Keepax 4052cd19bdbSCharles Keepax struct wm_adsp_buffer_region { 4062cd19bdbSCharles Keepax unsigned int offset; 4072cd19bdbSCharles Keepax unsigned int cumulative_size; 4082cd19bdbSCharles Keepax unsigned int mem_type; 4092cd19bdbSCharles Keepax unsigned int base_addr; 4102cd19bdbSCharles Keepax }; 4112cd19bdbSCharles Keepax 4122cd19bdbSCharles Keepax struct wm_adsp_buffer_region_def { 4132cd19bdbSCharles Keepax unsigned int mem_type; 4142cd19bdbSCharles Keepax unsigned int base_offset; 4152cd19bdbSCharles Keepax unsigned int size_offset; 4162cd19bdbSCharles Keepax }; 4172cd19bdbSCharles Keepax 4183a9686c4SCharles Keepax static const struct wm_adsp_buffer_region_def default_regions[] = { 4192cd19bdbSCharles Keepax { 4202cd19bdbSCharles Keepax .mem_type = WMFW_ADSP2_XM, 4212a2aefa4SRichard Fitzgerald .base_offset = HOST_BUFFER_FIELD(buf1_base), 4222a2aefa4SRichard Fitzgerald .size_offset = HOST_BUFFER_FIELD(buf1_size), 4232cd19bdbSCharles Keepax }, 4242cd19bdbSCharles Keepax { 4252cd19bdbSCharles Keepax .mem_type = WMFW_ADSP2_XM, 4262a2aefa4SRichard Fitzgerald .base_offset = HOST_BUFFER_FIELD(buf2_base), 4272a2aefa4SRichard Fitzgerald .size_offset = HOST_BUFFER_FIELD(buf1_buf2_size), 4282cd19bdbSCharles Keepax }, 4292cd19bdbSCharles Keepax { 4302cd19bdbSCharles Keepax .mem_type = WMFW_ADSP2_YM, 4312a2aefa4SRichard Fitzgerald .base_offset = HOST_BUFFER_FIELD(buf3_base), 4322a2aefa4SRichard Fitzgerald .size_offset = HOST_BUFFER_FIELD(buf_total_size), 4332cd19bdbSCharles Keepax }, 4342cd19bdbSCharles Keepax }; 4352cd19bdbSCharles Keepax 436406abc95SCharles Keepax struct wm_adsp_fw_caps { 437406abc95SCharles Keepax u32 id; 438406abc95SCharles Keepax struct snd_codec_desc desc; 4392cd19bdbSCharles Keepax int num_regions; 4403a9686c4SCharles Keepax const struct wm_adsp_buffer_region_def *region_defs; 441406abc95SCharles Keepax }; 442406abc95SCharles Keepax 443e6d00f34SCharles Keepax static const struct wm_adsp_fw_caps ctrl_caps[] = { 444406abc95SCharles Keepax { 445406abc95SCharles Keepax .id = SND_AUDIOCODEC_BESPOKE, 446406abc95SCharles Keepax .desc = { 4473bbc2705SRichard Fitzgerald .max_ch = 8, 448406abc95SCharles Keepax .sample_rates = { 16000 }, 449406abc95SCharles Keepax .num_sample_rates = 1, 450406abc95SCharles Keepax .formats = SNDRV_PCM_FMTBIT_S16_LE, 451406abc95SCharles Keepax }, 452e6d00f34SCharles Keepax .num_regions = ARRAY_SIZE(default_regions), 453e6d00f34SCharles Keepax .region_defs = default_regions, 454406abc95SCharles Keepax }, 455406abc95SCharles Keepax }; 456406abc95SCharles Keepax 4577ce4283cSCharles Keepax static const struct wm_adsp_fw_caps trace_caps[] = { 4587ce4283cSCharles Keepax { 4597ce4283cSCharles Keepax .id = SND_AUDIOCODEC_BESPOKE, 4607ce4283cSCharles Keepax .desc = { 4617ce4283cSCharles Keepax .max_ch = 8, 4627ce4283cSCharles Keepax .sample_rates = { 4637ce4283cSCharles Keepax 4000, 8000, 11025, 12000, 16000, 22050, 4647ce4283cSCharles Keepax 24000, 32000, 44100, 48000, 64000, 88200, 4657ce4283cSCharles Keepax 96000, 176400, 192000 4667ce4283cSCharles Keepax }, 4677ce4283cSCharles Keepax .num_sample_rates = 15, 4687ce4283cSCharles Keepax .formats = SNDRV_PCM_FMTBIT_S16_LE, 4697ce4283cSCharles Keepax }, 4707ce4283cSCharles Keepax .num_regions = ARRAY_SIZE(default_regions), 4717ce4283cSCharles Keepax .region_defs = default_regions, 472406abc95SCharles Keepax }, 473406abc95SCharles Keepax }; 474406abc95SCharles Keepax 475406abc95SCharles Keepax static const struct { 4761023dbd9SMark Brown const char *file; 477406abc95SCharles Keepax int compr_direction; 478406abc95SCharles Keepax int num_caps; 479406abc95SCharles Keepax const struct wm_adsp_fw_caps *caps; 48020b7f7c5SCharles Keepax bool voice_trigger; 4811023dbd9SMark Brown } wm_adsp_fw[WM_ADSP_NUM_FW] = { 482dd84f925SMark Brown [WM_ADSP_FW_MBC_VSS] = { .file = "mbc-vss" }, 48304d1300fSCharles Keepax [WM_ADSP_FW_HIFI] = { .file = "hifi" }, 484dd84f925SMark Brown [WM_ADSP_FW_TX] = { .file = "tx" }, 485dd84f925SMark Brown [WM_ADSP_FW_TX_SPK] = { .file = "tx-spk" }, 48604d1300fSCharles Keepax [WM_ADSP_FW_RX] = { .file = "rx" }, 487dd84f925SMark Brown [WM_ADSP_FW_RX_ANC] = { .file = "rx-anc" }, 488406abc95SCharles Keepax [WM_ADSP_FW_CTRL] = { 489406abc95SCharles Keepax .file = "ctrl", 490406abc95SCharles Keepax .compr_direction = SND_COMPRESS_CAPTURE, 491e6d00f34SCharles Keepax .num_caps = ARRAY_SIZE(ctrl_caps), 492e6d00f34SCharles Keepax .caps = ctrl_caps, 49320b7f7c5SCharles Keepax .voice_trigger = true, 494406abc95SCharles Keepax }, 49504d1300fSCharles Keepax [WM_ADSP_FW_ASR] = { .file = "asr" }, 4967ce4283cSCharles Keepax [WM_ADSP_FW_TRACE] = { 4977ce4283cSCharles Keepax .file = "trace", 4987ce4283cSCharles Keepax .compr_direction = SND_COMPRESS_CAPTURE, 4997ce4283cSCharles Keepax .num_caps = ARRAY_SIZE(trace_caps), 5007ce4283cSCharles Keepax .caps = trace_caps, 5017ce4283cSCharles Keepax }, 50204d1300fSCharles Keepax [WM_ADSP_FW_SPK_PROT] = { .file = "spk-prot" }, 50304d1300fSCharles Keepax [WM_ADSP_FW_MISC] = { .file = "misc" }, 5041023dbd9SMark Brown }; 5051023dbd9SMark Brown 5066ab2b7b4SDimitris Papastamos struct wm_coeff_ctl_ops { 5076ab2b7b4SDimitris Papastamos int (*xget)(struct snd_kcontrol *kcontrol, 5086ab2b7b4SDimitris Papastamos struct snd_ctl_elem_value *ucontrol); 5096ab2b7b4SDimitris Papastamos int (*xput)(struct snd_kcontrol *kcontrol, 5106ab2b7b4SDimitris Papastamos struct snd_ctl_elem_value *ucontrol); 5116ab2b7b4SDimitris Papastamos }; 5126ab2b7b4SDimitris Papastamos 5136ab2b7b4SDimitris Papastamos struct wm_coeff_ctl { 5146ab2b7b4SDimitris Papastamos const char *name; 5152323736dSCharles Keepax const char *fw_name; 5163809f001SCharles Keepax struct wm_adsp_alg_region alg_region; 5176ab2b7b4SDimitris Papastamos struct wm_coeff_ctl_ops ops; 5183809f001SCharles Keepax struct wm_adsp *dsp; 5196ab2b7b4SDimitris Papastamos unsigned int enabled:1; 5206ab2b7b4SDimitris Papastamos struct list_head list; 5216ab2b7b4SDimitris Papastamos void *cache; 5222323736dSCharles Keepax unsigned int offset; 5236ab2b7b4SDimitris Papastamos size_t len; 5240c2e3f34SDimitris Papastamos unsigned int set:1; 5259ee78757SCharles Keepax struct soc_bytes_ext bytes_ext; 52626c22a19SCharles Keepax unsigned int flags; 5278eb084d0SStuart Henderson unsigned int type; 5286ab2b7b4SDimitris Papastamos }; 5296ab2b7b4SDimitris Papastamos 5309ce5e6e6SRichard Fitzgerald static const char *wm_adsp_mem_region_name(unsigned int type) 5319ce5e6e6SRichard Fitzgerald { 5329ce5e6e6SRichard Fitzgerald switch (type) { 5339ce5e6e6SRichard Fitzgerald case WMFW_ADSP1_PM: 5349ce5e6e6SRichard Fitzgerald return "PM"; 5359ce5e6e6SRichard Fitzgerald case WMFW_ADSP1_DM: 5369ce5e6e6SRichard Fitzgerald return "DM"; 5379ce5e6e6SRichard Fitzgerald case WMFW_ADSP2_XM: 5389ce5e6e6SRichard Fitzgerald return "XM"; 5399ce5e6e6SRichard Fitzgerald case WMFW_ADSP2_YM: 5409ce5e6e6SRichard Fitzgerald return "YM"; 5419ce5e6e6SRichard Fitzgerald case WMFW_ADSP1_ZM: 5429ce5e6e6SRichard Fitzgerald return "ZM"; 5439ce5e6e6SRichard Fitzgerald default: 5449ce5e6e6SRichard Fitzgerald return NULL; 5459ce5e6e6SRichard Fitzgerald } 5469ce5e6e6SRichard Fitzgerald } 5479ce5e6e6SRichard Fitzgerald 548f9f55e31SRichard Fitzgerald #ifdef CONFIG_DEBUG_FS 549f9f55e31SRichard Fitzgerald static void wm_adsp_debugfs_save_wmfwname(struct wm_adsp *dsp, const char *s) 550f9f55e31SRichard Fitzgerald { 551f9f55e31SRichard Fitzgerald char *tmp = kasprintf(GFP_KERNEL, "%s\n", s); 552f9f55e31SRichard Fitzgerald 553f9f55e31SRichard Fitzgerald kfree(dsp->wmfw_file_name); 554f9f55e31SRichard Fitzgerald dsp->wmfw_file_name = tmp; 555f9f55e31SRichard Fitzgerald } 556f9f55e31SRichard Fitzgerald 557f9f55e31SRichard Fitzgerald static void wm_adsp_debugfs_save_binname(struct wm_adsp *dsp, const char *s) 558f9f55e31SRichard Fitzgerald { 559f9f55e31SRichard Fitzgerald char *tmp = kasprintf(GFP_KERNEL, "%s\n", s); 560f9f55e31SRichard Fitzgerald 561f9f55e31SRichard Fitzgerald kfree(dsp->bin_file_name); 562f9f55e31SRichard Fitzgerald dsp->bin_file_name = tmp; 563f9f55e31SRichard Fitzgerald } 564f9f55e31SRichard Fitzgerald 565f9f55e31SRichard Fitzgerald static void wm_adsp_debugfs_clear(struct wm_adsp *dsp) 566f9f55e31SRichard Fitzgerald { 567f9f55e31SRichard Fitzgerald kfree(dsp->wmfw_file_name); 568f9f55e31SRichard Fitzgerald kfree(dsp->bin_file_name); 569f9f55e31SRichard Fitzgerald dsp->wmfw_file_name = NULL; 570f9f55e31SRichard Fitzgerald dsp->bin_file_name = NULL; 571f9f55e31SRichard Fitzgerald } 572f9f55e31SRichard Fitzgerald 573f9f55e31SRichard Fitzgerald static ssize_t wm_adsp_debugfs_wmfw_read(struct file *file, 574f9f55e31SRichard Fitzgerald char __user *user_buf, 575f9f55e31SRichard Fitzgerald size_t count, loff_t *ppos) 576f9f55e31SRichard Fitzgerald { 577f9f55e31SRichard Fitzgerald struct wm_adsp *dsp = file->private_data; 578f9f55e31SRichard Fitzgerald ssize_t ret; 579f9f55e31SRichard Fitzgerald 580078e7183SCharles Keepax mutex_lock(&dsp->pwr_lock); 581f9f55e31SRichard Fitzgerald 58228823ebaSCharles Keepax if (!dsp->wmfw_file_name || !dsp->booted) 583f9f55e31SRichard Fitzgerald ret = 0; 584f9f55e31SRichard Fitzgerald else 585f9f55e31SRichard Fitzgerald ret = simple_read_from_buffer(user_buf, count, ppos, 586f9f55e31SRichard Fitzgerald dsp->wmfw_file_name, 587f9f55e31SRichard Fitzgerald strlen(dsp->wmfw_file_name)); 588f9f55e31SRichard Fitzgerald 589078e7183SCharles Keepax mutex_unlock(&dsp->pwr_lock); 590f9f55e31SRichard Fitzgerald return ret; 591f9f55e31SRichard Fitzgerald } 592f9f55e31SRichard Fitzgerald 593f9f55e31SRichard Fitzgerald static ssize_t wm_adsp_debugfs_bin_read(struct file *file, 594f9f55e31SRichard Fitzgerald char __user *user_buf, 595f9f55e31SRichard Fitzgerald size_t count, loff_t *ppos) 596f9f55e31SRichard Fitzgerald { 597f9f55e31SRichard Fitzgerald struct wm_adsp *dsp = file->private_data; 598f9f55e31SRichard Fitzgerald ssize_t ret; 599f9f55e31SRichard Fitzgerald 600078e7183SCharles Keepax mutex_lock(&dsp->pwr_lock); 601f9f55e31SRichard Fitzgerald 60228823ebaSCharles Keepax if (!dsp->bin_file_name || !dsp->booted) 603f9f55e31SRichard Fitzgerald ret = 0; 604f9f55e31SRichard Fitzgerald else 605f9f55e31SRichard Fitzgerald ret = simple_read_from_buffer(user_buf, count, ppos, 606f9f55e31SRichard Fitzgerald dsp->bin_file_name, 607f9f55e31SRichard Fitzgerald strlen(dsp->bin_file_name)); 608f9f55e31SRichard Fitzgerald 609078e7183SCharles Keepax mutex_unlock(&dsp->pwr_lock); 610f9f55e31SRichard Fitzgerald return ret; 611f9f55e31SRichard Fitzgerald } 612f9f55e31SRichard Fitzgerald 613f9f55e31SRichard Fitzgerald static const struct { 614f9f55e31SRichard Fitzgerald const char *name; 615f9f55e31SRichard Fitzgerald const struct file_operations fops; 616f9f55e31SRichard Fitzgerald } wm_adsp_debugfs_fops[] = { 617f9f55e31SRichard Fitzgerald { 618f9f55e31SRichard Fitzgerald .name = "wmfw_file_name", 619f9f55e31SRichard Fitzgerald .fops = { 620f9f55e31SRichard Fitzgerald .open = simple_open, 621f9f55e31SRichard Fitzgerald .read = wm_adsp_debugfs_wmfw_read, 622f9f55e31SRichard Fitzgerald }, 623f9f55e31SRichard Fitzgerald }, 624f9f55e31SRichard Fitzgerald { 625f9f55e31SRichard Fitzgerald .name = "bin_file_name", 626f9f55e31SRichard Fitzgerald .fops = { 627f9f55e31SRichard Fitzgerald .open = simple_open, 628f9f55e31SRichard Fitzgerald .read = wm_adsp_debugfs_bin_read, 629f9f55e31SRichard Fitzgerald }, 630f9f55e31SRichard Fitzgerald }, 631f9f55e31SRichard Fitzgerald }; 632f9f55e31SRichard Fitzgerald 633f9f55e31SRichard Fitzgerald static void wm_adsp2_init_debugfs(struct wm_adsp *dsp, 6340fe1daa6SKuninori Morimoto struct snd_soc_component *component) 635f9f55e31SRichard Fitzgerald { 636f9f55e31SRichard Fitzgerald struct dentry *root = NULL; 637f9f55e31SRichard Fitzgerald int i; 638f9f55e31SRichard Fitzgerald 6390fe1daa6SKuninori Morimoto if (!component->debugfs_root) { 640f9f55e31SRichard Fitzgerald adsp_err(dsp, "No codec debugfs root\n"); 641f9f55e31SRichard Fitzgerald goto err; 642f9f55e31SRichard Fitzgerald } 643f9f55e31SRichard Fitzgerald 644605391d0SRichard Fitzgerald root = debugfs_create_dir(dsp->name, component->debugfs_root); 645f9f55e31SRichard Fitzgerald 646f9f55e31SRichard Fitzgerald if (!root) 647f9f55e31SRichard Fitzgerald goto err; 648f9f55e31SRichard Fitzgerald 6496a73cf46SJoe Perches if (!debugfs_create_bool("booted", 0444, root, &dsp->booted)) 65028823ebaSCharles Keepax goto err; 65128823ebaSCharles Keepax 6526a73cf46SJoe Perches if (!debugfs_create_bool("running", 0444, root, &dsp->running)) 653f9f55e31SRichard Fitzgerald goto err; 654f9f55e31SRichard Fitzgerald 6556a73cf46SJoe Perches if (!debugfs_create_x32("fw_id", 0444, root, &dsp->fw_id)) 656f9f55e31SRichard Fitzgerald goto err; 657f9f55e31SRichard Fitzgerald 6586a73cf46SJoe Perches if (!debugfs_create_x32("fw_version", 0444, root, &dsp->fw_id_version)) 659f9f55e31SRichard Fitzgerald goto err; 660f9f55e31SRichard Fitzgerald 661f9f55e31SRichard Fitzgerald for (i = 0; i < ARRAY_SIZE(wm_adsp_debugfs_fops); ++i) { 662f9f55e31SRichard Fitzgerald if (!debugfs_create_file(wm_adsp_debugfs_fops[i].name, 6636a73cf46SJoe Perches 0444, root, dsp, 664f9f55e31SRichard Fitzgerald &wm_adsp_debugfs_fops[i].fops)) 665f9f55e31SRichard Fitzgerald goto err; 666f9f55e31SRichard Fitzgerald } 667f9f55e31SRichard Fitzgerald 668f9f55e31SRichard Fitzgerald dsp->debugfs_root = root; 669f9f55e31SRichard Fitzgerald return; 670f9f55e31SRichard Fitzgerald 671f9f55e31SRichard Fitzgerald err: 672f9f55e31SRichard Fitzgerald debugfs_remove_recursive(root); 673f9f55e31SRichard Fitzgerald adsp_err(dsp, "Failed to create debugfs\n"); 674f9f55e31SRichard Fitzgerald } 675f9f55e31SRichard Fitzgerald 676f9f55e31SRichard Fitzgerald static void wm_adsp2_cleanup_debugfs(struct wm_adsp *dsp) 677f9f55e31SRichard Fitzgerald { 678f9f55e31SRichard Fitzgerald wm_adsp_debugfs_clear(dsp); 679f9f55e31SRichard Fitzgerald debugfs_remove_recursive(dsp->debugfs_root); 680f9f55e31SRichard Fitzgerald } 681f9f55e31SRichard Fitzgerald #else 682f9f55e31SRichard Fitzgerald static inline void wm_adsp2_init_debugfs(struct wm_adsp *dsp, 6830fe1daa6SKuninori Morimoto struct snd_soc_component *component) 684f9f55e31SRichard Fitzgerald { 685f9f55e31SRichard Fitzgerald } 686f9f55e31SRichard Fitzgerald 687f9f55e31SRichard Fitzgerald static inline void wm_adsp2_cleanup_debugfs(struct wm_adsp *dsp) 688f9f55e31SRichard Fitzgerald { 689f9f55e31SRichard Fitzgerald } 690f9f55e31SRichard Fitzgerald 691f9f55e31SRichard Fitzgerald static inline void wm_adsp_debugfs_save_wmfwname(struct wm_adsp *dsp, 692f9f55e31SRichard Fitzgerald const char *s) 693f9f55e31SRichard Fitzgerald { 694f9f55e31SRichard Fitzgerald } 695f9f55e31SRichard Fitzgerald 696f9f55e31SRichard Fitzgerald static inline void wm_adsp_debugfs_save_binname(struct wm_adsp *dsp, 697f9f55e31SRichard Fitzgerald const char *s) 698f9f55e31SRichard Fitzgerald { 699f9f55e31SRichard Fitzgerald } 700f9f55e31SRichard Fitzgerald 701f9f55e31SRichard Fitzgerald static inline void wm_adsp_debugfs_clear(struct wm_adsp *dsp) 702f9f55e31SRichard Fitzgerald { 703f9f55e31SRichard Fitzgerald } 704f9f55e31SRichard Fitzgerald #endif 705f9f55e31SRichard Fitzgerald 7060a047f07SRichard Fitzgerald int wm_adsp_fw_get(struct snd_kcontrol *kcontrol, 7071023dbd9SMark Brown struct snd_ctl_elem_value *ucontrol) 7081023dbd9SMark Brown { 7090fe1daa6SKuninori Morimoto struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); 7101023dbd9SMark Brown struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; 7110fe1daa6SKuninori Morimoto struct wm_adsp *dsp = snd_soc_component_get_drvdata(component); 7121023dbd9SMark Brown 71315c66570STakashi Iwai ucontrol->value.enumerated.item[0] = dsp[e->shift_l].fw; 7141023dbd9SMark Brown 7151023dbd9SMark Brown return 0; 7161023dbd9SMark Brown } 7170a047f07SRichard Fitzgerald EXPORT_SYMBOL_GPL(wm_adsp_fw_get); 7181023dbd9SMark Brown 7190a047f07SRichard Fitzgerald int wm_adsp_fw_put(struct snd_kcontrol *kcontrol, 7201023dbd9SMark Brown struct snd_ctl_elem_value *ucontrol) 7211023dbd9SMark Brown { 7220fe1daa6SKuninori Morimoto struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); 7231023dbd9SMark Brown struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; 7240fe1daa6SKuninori Morimoto struct wm_adsp *dsp = snd_soc_component_get_drvdata(component); 725d27c5e15SCharles Keepax int ret = 0; 7261023dbd9SMark Brown 72715c66570STakashi Iwai if (ucontrol->value.enumerated.item[0] == dsp[e->shift_l].fw) 7281023dbd9SMark Brown return 0; 7291023dbd9SMark Brown 73015c66570STakashi Iwai if (ucontrol->value.enumerated.item[0] >= WM_ADSP_NUM_FW) 7311023dbd9SMark Brown return -EINVAL; 7321023dbd9SMark Brown 733d27c5e15SCharles Keepax mutex_lock(&dsp[e->shift_l].pwr_lock); 7341023dbd9SMark Brown 7354f2d4eabSStuart Henderson if (dsp[e->shift_l].booted || !list_empty(&dsp[e->shift_l].compr_list)) 736d27c5e15SCharles Keepax ret = -EBUSY; 737d27c5e15SCharles Keepax else 73815c66570STakashi Iwai dsp[e->shift_l].fw = ucontrol->value.enumerated.item[0]; 7391023dbd9SMark Brown 740d27c5e15SCharles Keepax mutex_unlock(&dsp[e->shift_l].pwr_lock); 741d27c5e15SCharles Keepax 742d27c5e15SCharles Keepax return ret; 7431023dbd9SMark Brown } 7440a047f07SRichard Fitzgerald EXPORT_SYMBOL_GPL(wm_adsp_fw_put); 7451023dbd9SMark Brown 7460a047f07SRichard Fitzgerald const struct soc_enum wm_adsp_fw_enum[] = { 7471023dbd9SMark Brown SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 7481023dbd9SMark Brown SOC_ENUM_SINGLE(0, 1, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 7491023dbd9SMark Brown SOC_ENUM_SINGLE(0, 2, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 7501023dbd9SMark Brown SOC_ENUM_SINGLE(0, 3, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 751e1ea1879SRichard Fitzgerald SOC_ENUM_SINGLE(0, 4, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 752e1ea1879SRichard Fitzgerald SOC_ENUM_SINGLE(0, 5, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 753e1ea1879SRichard Fitzgerald SOC_ENUM_SINGLE(0, 6, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 7541023dbd9SMark Brown }; 7550a047f07SRichard Fitzgerald EXPORT_SYMBOL_GPL(wm_adsp_fw_enum); 7562159ad93SMark Brown 7572159ad93SMark Brown static struct wm_adsp_region const *wm_adsp_find_region(struct wm_adsp *dsp, 7582159ad93SMark Brown int type) 7592159ad93SMark Brown { 7602159ad93SMark Brown int i; 7612159ad93SMark Brown 7622159ad93SMark Brown for (i = 0; i < dsp->num_mems; i++) 7632159ad93SMark Brown if (dsp->mem[i].type == type) 7642159ad93SMark Brown return &dsp->mem[i]; 7652159ad93SMark Brown 7662159ad93SMark Brown return NULL; 7672159ad93SMark Brown } 7682159ad93SMark Brown 7693809f001SCharles Keepax static unsigned int wm_adsp_region_to_reg(struct wm_adsp_region const *mem, 77045b9ee72SMark Brown unsigned int offset) 77145b9ee72SMark Brown { 7723809f001SCharles Keepax if (WARN_ON(!mem)) 7736c452bdaSTakashi Iwai return offset; 7743809f001SCharles Keepax switch (mem->type) { 77545b9ee72SMark Brown case WMFW_ADSP1_PM: 7763809f001SCharles Keepax return mem->base + (offset * 3); 77745b9ee72SMark Brown case WMFW_ADSP1_DM: 7783809f001SCharles Keepax return mem->base + (offset * 2); 77945b9ee72SMark Brown case WMFW_ADSP2_XM: 7803809f001SCharles Keepax return mem->base + (offset * 2); 78145b9ee72SMark Brown case WMFW_ADSP2_YM: 7823809f001SCharles Keepax return mem->base + (offset * 2); 78345b9ee72SMark Brown case WMFW_ADSP1_ZM: 7843809f001SCharles Keepax return mem->base + (offset * 2); 78545b9ee72SMark Brown default: 7866c452bdaSTakashi Iwai WARN(1, "Unknown memory region type"); 78745b9ee72SMark Brown return offset; 78845b9ee72SMark Brown } 78945b9ee72SMark Brown } 79045b9ee72SMark Brown 7914049ce86SCharles Keepax static void wm_adsp_read_fw_status(struct wm_adsp *dsp, 7924049ce86SCharles Keepax int noffs, unsigned int *offs) 79310337b07SRichard Fitzgerald { 79420e00db2SRichard Fitzgerald unsigned int i; 79510337b07SRichard Fitzgerald int ret; 79610337b07SRichard Fitzgerald 7974049ce86SCharles Keepax for (i = 0; i < noffs; ++i) { 7984049ce86SCharles Keepax ret = regmap_read(dsp->regmap, dsp->base + offs[i], &offs[i]); 79910337b07SRichard Fitzgerald if (ret) { 80020e00db2SRichard Fitzgerald adsp_err(dsp, "Failed to read SCRATCH%u: %d\n", i, ret); 80110337b07SRichard Fitzgerald return; 80210337b07SRichard Fitzgerald } 80320e00db2SRichard Fitzgerald } 8044049ce86SCharles Keepax } 8054049ce86SCharles Keepax 8064049ce86SCharles Keepax static void wm_adsp2_show_fw_status(struct wm_adsp *dsp) 8074049ce86SCharles Keepax { 8084049ce86SCharles Keepax unsigned int offs[] = { 8094049ce86SCharles Keepax ADSP2_SCRATCH0, ADSP2_SCRATCH1, ADSP2_SCRATCH2, ADSP2_SCRATCH3, 8104049ce86SCharles Keepax }; 8114049ce86SCharles Keepax 8124049ce86SCharles Keepax wm_adsp_read_fw_status(dsp, ARRAY_SIZE(offs), offs); 81310337b07SRichard Fitzgerald 81410337b07SRichard Fitzgerald adsp_dbg(dsp, "FW SCRATCH 0:0x%x 1:0x%x 2:0x%x 3:0x%x\n", 8154049ce86SCharles Keepax offs[0], offs[1], offs[2], offs[3]); 81610337b07SRichard Fitzgerald } 81710337b07SRichard Fitzgerald 818e1ea1879SRichard Fitzgerald static void wm_adsp2v2_show_fw_status(struct wm_adsp *dsp) 819e1ea1879SRichard Fitzgerald { 8204049ce86SCharles Keepax unsigned int offs[] = { ADSP2V2_SCRATCH0_1, ADSP2V2_SCRATCH2_3 }; 821e1ea1879SRichard Fitzgerald 8224049ce86SCharles Keepax wm_adsp_read_fw_status(dsp, ARRAY_SIZE(offs), offs); 823e1ea1879SRichard Fitzgerald 824e1ea1879SRichard Fitzgerald adsp_dbg(dsp, "FW SCRATCH 0:0x%x 1:0x%x 2:0x%x 3:0x%x\n", 8254049ce86SCharles Keepax offs[0] & 0xFFFF, offs[0] >> 16, 8264049ce86SCharles Keepax offs[1] & 0xFFFF, offs[1] >> 16); 827e1ea1879SRichard Fitzgerald } 828e1ea1879SRichard Fitzgerald 8299ee78757SCharles Keepax static inline struct wm_coeff_ctl *bytes_ext_to_ctl(struct soc_bytes_ext *ext) 8309ee78757SCharles Keepax { 8319ee78757SCharles Keepax return container_of(ext, struct wm_coeff_ctl, bytes_ext); 8329ee78757SCharles Keepax } 8339ee78757SCharles Keepax 834b396ebcaSRichard Fitzgerald static int wm_coeff_base_reg(struct wm_coeff_ctl *ctl, unsigned int *reg) 835b396ebcaSRichard Fitzgerald { 836b396ebcaSRichard Fitzgerald const struct wm_adsp_alg_region *alg_region = &ctl->alg_region; 837b396ebcaSRichard Fitzgerald struct wm_adsp *dsp = ctl->dsp; 838b396ebcaSRichard Fitzgerald const struct wm_adsp_region *mem; 839b396ebcaSRichard Fitzgerald 840b396ebcaSRichard Fitzgerald mem = wm_adsp_find_region(dsp, alg_region->type); 841b396ebcaSRichard Fitzgerald if (!mem) { 842b396ebcaSRichard Fitzgerald adsp_err(dsp, "No base for region %x\n", 843b396ebcaSRichard Fitzgerald alg_region->type); 844b396ebcaSRichard Fitzgerald return -EINVAL; 845b396ebcaSRichard Fitzgerald } 846b396ebcaSRichard Fitzgerald 847b396ebcaSRichard Fitzgerald *reg = wm_adsp_region_to_reg(mem, ctl->alg_region.base + ctl->offset); 848b396ebcaSRichard Fitzgerald 849b396ebcaSRichard Fitzgerald return 0; 850b396ebcaSRichard Fitzgerald } 851b396ebcaSRichard Fitzgerald 8527585a5b0SCharles Keepax static int wm_coeff_info(struct snd_kcontrol *kctl, 8536ab2b7b4SDimitris Papastamos struct snd_ctl_elem_info *uinfo) 8546ab2b7b4SDimitris Papastamos { 8559ee78757SCharles Keepax struct soc_bytes_ext *bytes_ext = 8569ee78757SCharles Keepax (struct soc_bytes_ext *)kctl->private_value; 8579ee78757SCharles Keepax struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); 8586ab2b7b4SDimitris Papastamos 859a23ebba8SRichard Fitzgerald switch (ctl->type) { 860a23ebba8SRichard Fitzgerald case WMFW_CTL_TYPE_ACKED: 861a23ebba8SRichard Fitzgerald uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 862a23ebba8SRichard Fitzgerald uinfo->value.integer.min = WM_ADSP_ACKED_CTL_MIN_VALUE; 863a23ebba8SRichard Fitzgerald uinfo->value.integer.max = WM_ADSP_ACKED_CTL_MAX_VALUE; 864a23ebba8SRichard Fitzgerald uinfo->value.integer.step = 1; 865a23ebba8SRichard Fitzgerald uinfo->count = 1; 866a23ebba8SRichard Fitzgerald break; 867a23ebba8SRichard Fitzgerald default: 8686ab2b7b4SDimitris Papastamos uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; 8696ab2b7b4SDimitris Papastamos uinfo->count = ctl->len; 870a23ebba8SRichard Fitzgerald break; 871a23ebba8SRichard Fitzgerald } 872a23ebba8SRichard Fitzgerald 8736ab2b7b4SDimitris Papastamos return 0; 8746ab2b7b4SDimitris Papastamos } 8756ab2b7b4SDimitris Papastamos 876f4f0c4c6SRichard Fitzgerald static int wm_coeff_write_acked_control(struct wm_coeff_ctl *ctl, 877f4f0c4c6SRichard Fitzgerald unsigned int event_id) 878f4f0c4c6SRichard Fitzgerald { 879f4f0c4c6SRichard Fitzgerald struct wm_adsp *dsp = ctl->dsp; 880f4f0c4c6SRichard Fitzgerald u32 val = cpu_to_be32(event_id); 881f4f0c4c6SRichard Fitzgerald unsigned int reg; 882f4f0c4c6SRichard Fitzgerald int i, ret; 883f4f0c4c6SRichard Fitzgerald 884f4f0c4c6SRichard Fitzgerald ret = wm_coeff_base_reg(ctl, ®); 885f4f0c4c6SRichard Fitzgerald if (ret) 886f4f0c4c6SRichard Fitzgerald return ret; 887f4f0c4c6SRichard Fitzgerald 888f4f0c4c6SRichard Fitzgerald adsp_dbg(dsp, "Sending 0x%x to acked control alg 0x%x %s:0x%x\n", 889f4f0c4c6SRichard Fitzgerald event_id, ctl->alg_region.alg, 890f4f0c4c6SRichard Fitzgerald wm_adsp_mem_region_name(ctl->alg_region.type), ctl->offset); 891f4f0c4c6SRichard Fitzgerald 892f4f0c4c6SRichard Fitzgerald ret = regmap_raw_write(dsp->regmap, reg, &val, sizeof(val)); 893f4f0c4c6SRichard Fitzgerald if (ret) { 894f4f0c4c6SRichard Fitzgerald adsp_err(dsp, "Failed to write %x: %d\n", reg, ret); 895f4f0c4c6SRichard Fitzgerald return ret; 896f4f0c4c6SRichard Fitzgerald } 897f4f0c4c6SRichard Fitzgerald 898f4f0c4c6SRichard Fitzgerald /* 899f4f0c4c6SRichard Fitzgerald * Poll for ack, we initially poll at ~1ms intervals for firmwares 900f4f0c4c6SRichard Fitzgerald * that respond quickly, then go to ~10ms polls. A firmware is unlikely 901f4f0c4c6SRichard Fitzgerald * to ack instantly so we do the first 1ms delay before reading the 902f4f0c4c6SRichard Fitzgerald * control to avoid a pointless bus transaction 903f4f0c4c6SRichard Fitzgerald */ 904f4f0c4c6SRichard Fitzgerald for (i = 0; i < WM_ADSP_ACKED_CTL_TIMEOUT_MS;) { 905f4f0c4c6SRichard Fitzgerald switch (i) { 906f4f0c4c6SRichard Fitzgerald case 0 ... WM_ADSP_ACKED_CTL_N_QUICKPOLLS - 1: 907f4f0c4c6SRichard Fitzgerald usleep_range(1000, 2000); 908f4f0c4c6SRichard Fitzgerald i++; 909f4f0c4c6SRichard Fitzgerald break; 910f4f0c4c6SRichard Fitzgerald default: 911f4f0c4c6SRichard Fitzgerald usleep_range(10000, 20000); 912f4f0c4c6SRichard Fitzgerald i += 10; 913f4f0c4c6SRichard Fitzgerald break; 914f4f0c4c6SRichard Fitzgerald } 915f4f0c4c6SRichard Fitzgerald 916f4f0c4c6SRichard Fitzgerald ret = regmap_raw_read(dsp->regmap, reg, &val, sizeof(val)); 917f4f0c4c6SRichard Fitzgerald if (ret) { 918f4f0c4c6SRichard Fitzgerald adsp_err(dsp, "Failed to read %x: %d\n", reg, ret); 919f4f0c4c6SRichard Fitzgerald return ret; 920f4f0c4c6SRichard Fitzgerald } 921f4f0c4c6SRichard Fitzgerald 922f4f0c4c6SRichard Fitzgerald if (val == 0) { 923f4f0c4c6SRichard Fitzgerald adsp_dbg(dsp, "Acked control ACKED at poll %u\n", i); 924f4f0c4c6SRichard Fitzgerald return 0; 925f4f0c4c6SRichard Fitzgerald } 926f4f0c4c6SRichard Fitzgerald } 927f4f0c4c6SRichard Fitzgerald 928f4f0c4c6SRichard Fitzgerald adsp_warn(dsp, "Acked control @0x%x alg:0x%x %s:0x%x timed out\n", 929f4f0c4c6SRichard Fitzgerald reg, ctl->alg_region.alg, 930f4f0c4c6SRichard Fitzgerald wm_adsp_mem_region_name(ctl->alg_region.type), 931f4f0c4c6SRichard Fitzgerald ctl->offset); 932f4f0c4c6SRichard Fitzgerald 933f4f0c4c6SRichard Fitzgerald return -ETIMEDOUT; 934f4f0c4c6SRichard Fitzgerald } 935f4f0c4c6SRichard Fitzgerald 936c9f8dd71SCharles Keepax static int wm_coeff_write_control(struct wm_coeff_ctl *ctl, 9376ab2b7b4SDimitris Papastamos const void *buf, size_t len) 9386ab2b7b4SDimitris Papastamos { 9393809f001SCharles Keepax struct wm_adsp *dsp = ctl->dsp; 9406ab2b7b4SDimitris Papastamos void *scratch; 9416ab2b7b4SDimitris Papastamos int ret; 9426ab2b7b4SDimitris Papastamos unsigned int reg; 9436ab2b7b4SDimitris Papastamos 944b396ebcaSRichard Fitzgerald ret = wm_coeff_base_reg(ctl, ®); 945b396ebcaSRichard Fitzgerald if (ret) 946b396ebcaSRichard Fitzgerald return ret; 9476ab2b7b4SDimitris Papastamos 9484f8ea6d7SCharles Keepax scratch = kmemdup(buf, len, GFP_KERNEL | GFP_DMA); 9496ab2b7b4SDimitris Papastamos if (!scratch) 9506ab2b7b4SDimitris Papastamos return -ENOMEM; 9516ab2b7b4SDimitris Papastamos 9523809f001SCharles Keepax ret = regmap_raw_write(dsp->regmap, reg, scratch, 9534f8ea6d7SCharles Keepax len); 9546ab2b7b4SDimitris Papastamos if (ret) { 9553809f001SCharles Keepax adsp_err(dsp, "Failed to write %zu bytes to %x: %d\n", 9564f8ea6d7SCharles Keepax len, reg, ret); 9576ab2b7b4SDimitris Papastamos kfree(scratch); 9586ab2b7b4SDimitris Papastamos return ret; 9596ab2b7b4SDimitris Papastamos } 9604f8ea6d7SCharles Keepax adsp_dbg(dsp, "Wrote %zu bytes to %x\n", len, reg); 9616ab2b7b4SDimitris Papastamos 9626ab2b7b4SDimitris Papastamos kfree(scratch); 9636ab2b7b4SDimitris Papastamos 9646ab2b7b4SDimitris Papastamos return 0; 9656ab2b7b4SDimitris Papastamos } 9666ab2b7b4SDimitris Papastamos 9677585a5b0SCharles Keepax static int wm_coeff_put(struct snd_kcontrol *kctl, 9686ab2b7b4SDimitris Papastamos struct snd_ctl_elem_value *ucontrol) 9696ab2b7b4SDimitris Papastamos { 9709ee78757SCharles Keepax struct soc_bytes_ext *bytes_ext = 9719ee78757SCharles Keepax (struct soc_bytes_ext *)kctl->private_value; 9729ee78757SCharles Keepax struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); 9736ab2b7b4SDimitris Papastamos char *p = ucontrol->value.bytes.data; 974168d10e7SCharles Keepax int ret = 0; 975168d10e7SCharles Keepax 976168d10e7SCharles Keepax mutex_lock(&ctl->dsp->pwr_lock); 9776ab2b7b4SDimitris Papastamos 97867430a39SCharles Keepax if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) 97967430a39SCharles Keepax ret = -EPERM; 98067430a39SCharles Keepax else 9816ab2b7b4SDimitris Papastamos memcpy(ctl->cache, p, ctl->len); 9826ab2b7b4SDimitris Papastamos 9830c2e3f34SDimitris Papastamos ctl->set = 1; 984cef45771SCharles Keepax if (ctl->enabled && ctl->dsp->running) 985168d10e7SCharles Keepax ret = wm_coeff_write_control(ctl, p, ctl->len); 9866ab2b7b4SDimitris Papastamos 987168d10e7SCharles Keepax mutex_unlock(&ctl->dsp->pwr_lock); 988168d10e7SCharles Keepax 989168d10e7SCharles Keepax return ret; 9906ab2b7b4SDimitris Papastamos } 9916ab2b7b4SDimitris Papastamos 9929ee78757SCharles Keepax static int wm_coeff_tlv_put(struct snd_kcontrol *kctl, 9939ee78757SCharles Keepax const unsigned int __user *bytes, unsigned int size) 9949ee78757SCharles Keepax { 9959ee78757SCharles Keepax struct soc_bytes_ext *bytes_ext = 9969ee78757SCharles Keepax (struct soc_bytes_ext *)kctl->private_value; 9979ee78757SCharles Keepax struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); 9989ee78757SCharles Keepax int ret = 0; 9999ee78757SCharles Keepax 10009ee78757SCharles Keepax mutex_lock(&ctl->dsp->pwr_lock); 10019ee78757SCharles Keepax 10029ee78757SCharles Keepax if (copy_from_user(ctl->cache, bytes, size)) { 10039ee78757SCharles Keepax ret = -EFAULT; 10049ee78757SCharles Keepax } else { 10059ee78757SCharles Keepax ctl->set = 1; 1006cef45771SCharles Keepax if (ctl->enabled && ctl->dsp->running) 10079ee78757SCharles Keepax ret = wm_coeff_write_control(ctl, ctl->cache, size); 100867430a39SCharles Keepax else if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) 100967430a39SCharles Keepax ret = -EPERM; 10109ee78757SCharles Keepax } 10119ee78757SCharles Keepax 10129ee78757SCharles Keepax mutex_unlock(&ctl->dsp->pwr_lock); 10139ee78757SCharles Keepax 10149ee78757SCharles Keepax return ret; 10159ee78757SCharles Keepax } 10169ee78757SCharles Keepax 1017a23ebba8SRichard Fitzgerald static int wm_coeff_put_acked(struct snd_kcontrol *kctl, 1018a23ebba8SRichard Fitzgerald struct snd_ctl_elem_value *ucontrol) 1019a23ebba8SRichard Fitzgerald { 1020a23ebba8SRichard Fitzgerald struct soc_bytes_ext *bytes_ext = 1021a23ebba8SRichard Fitzgerald (struct soc_bytes_ext *)kctl->private_value; 1022a23ebba8SRichard Fitzgerald struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); 1023a23ebba8SRichard Fitzgerald unsigned int val = ucontrol->value.integer.value[0]; 1024a23ebba8SRichard Fitzgerald int ret; 1025a23ebba8SRichard Fitzgerald 1026a23ebba8SRichard Fitzgerald if (val == 0) 1027a23ebba8SRichard Fitzgerald return 0; /* 0 means no event */ 1028a23ebba8SRichard Fitzgerald 1029a23ebba8SRichard Fitzgerald mutex_lock(&ctl->dsp->pwr_lock); 1030a23ebba8SRichard Fitzgerald 10317b4af793SCharles Keepax if (ctl->enabled && ctl->dsp->running) 1032a23ebba8SRichard Fitzgerald ret = wm_coeff_write_acked_control(ctl, val); 1033a23ebba8SRichard Fitzgerald else 1034a23ebba8SRichard Fitzgerald ret = -EPERM; 1035a23ebba8SRichard Fitzgerald 1036a23ebba8SRichard Fitzgerald mutex_unlock(&ctl->dsp->pwr_lock); 1037a23ebba8SRichard Fitzgerald 1038a23ebba8SRichard Fitzgerald return ret; 1039a23ebba8SRichard Fitzgerald } 1040a23ebba8SRichard Fitzgerald 1041c9f8dd71SCharles Keepax static int wm_coeff_read_control(struct wm_coeff_ctl *ctl, 10426ab2b7b4SDimitris Papastamos void *buf, size_t len) 10436ab2b7b4SDimitris Papastamos { 10443809f001SCharles Keepax struct wm_adsp *dsp = ctl->dsp; 10456ab2b7b4SDimitris Papastamos void *scratch; 10466ab2b7b4SDimitris Papastamos int ret; 10476ab2b7b4SDimitris Papastamos unsigned int reg; 10486ab2b7b4SDimitris Papastamos 1049b396ebcaSRichard Fitzgerald ret = wm_coeff_base_reg(ctl, ®); 1050b396ebcaSRichard Fitzgerald if (ret) 1051b396ebcaSRichard Fitzgerald return ret; 10526ab2b7b4SDimitris Papastamos 10534f8ea6d7SCharles Keepax scratch = kmalloc(len, GFP_KERNEL | GFP_DMA); 10546ab2b7b4SDimitris Papastamos if (!scratch) 10556ab2b7b4SDimitris Papastamos return -ENOMEM; 10566ab2b7b4SDimitris Papastamos 10574f8ea6d7SCharles Keepax ret = regmap_raw_read(dsp->regmap, reg, scratch, len); 10586ab2b7b4SDimitris Papastamos if (ret) { 10593809f001SCharles Keepax adsp_err(dsp, "Failed to read %zu bytes from %x: %d\n", 10605602a643SCharles Keepax len, reg, ret); 10616ab2b7b4SDimitris Papastamos kfree(scratch); 10626ab2b7b4SDimitris Papastamos return ret; 10636ab2b7b4SDimitris Papastamos } 10644f8ea6d7SCharles Keepax adsp_dbg(dsp, "Read %zu bytes from %x\n", len, reg); 10656ab2b7b4SDimitris Papastamos 10664f8ea6d7SCharles Keepax memcpy(buf, scratch, len); 10676ab2b7b4SDimitris Papastamos kfree(scratch); 10686ab2b7b4SDimitris Papastamos 10696ab2b7b4SDimitris Papastamos return 0; 10706ab2b7b4SDimitris Papastamos } 10716ab2b7b4SDimitris Papastamos 10727585a5b0SCharles Keepax static int wm_coeff_get(struct snd_kcontrol *kctl, 10736ab2b7b4SDimitris Papastamos struct snd_ctl_elem_value *ucontrol) 10746ab2b7b4SDimitris Papastamos { 10759ee78757SCharles Keepax struct soc_bytes_ext *bytes_ext = 10769ee78757SCharles Keepax (struct soc_bytes_ext *)kctl->private_value; 10779ee78757SCharles Keepax struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); 10786ab2b7b4SDimitris Papastamos char *p = ucontrol->value.bytes.data; 1079168d10e7SCharles Keepax int ret = 0; 1080168d10e7SCharles Keepax 1081168d10e7SCharles Keepax mutex_lock(&ctl->dsp->pwr_lock); 10826ab2b7b4SDimitris Papastamos 108326c22a19SCharles Keepax if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) { 1084cef45771SCharles Keepax if (ctl->enabled && ctl->dsp->running) 1085168d10e7SCharles Keepax ret = wm_coeff_read_control(ctl, p, ctl->len); 108626c22a19SCharles Keepax else 1087168d10e7SCharles Keepax ret = -EPERM; 1088168d10e7SCharles Keepax } else { 1089cef45771SCharles Keepax if (!ctl->flags && ctl->enabled && ctl->dsp->running) 1090bc1765d6SCharles Keepax ret = wm_coeff_read_control(ctl, ctl->cache, ctl->len); 1091bc1765d6SCharles Keepax 1092168d10e7SCharles Keepax memcpy(p, ctl->cache, ctl->len); 109326c22a19SCharles Keepax } 109426c22a19SCharles Keepax 1095168d10e7SCharles Keepax mutex_unlock(&ctl->dsp->pwr_lock); 109626c22a19SCharles Keepax 1097168d10e7SCharles Keepax return ret; 10986ab2b7b4SDimitris Papastamos } 10996ab2b7b4SDimitris Papastamos 11009ee78757SCharles Keepax static int wm_coeff_tlv_get(struct snd_kcontrol *kctl, 11019ee78757SCharles Keepax unsigned int __user *bytes, unsigned int size) 11029ee78757SCharles Keepax { 11039ee78757SCharles Keepax struct soc_bytes_ext *bytes_ext = 11049ee78757SCharles Keepax (struct soc_bytes_ext *)kctl->private_value; 11059ee78757SCharles Keepax struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); 11069ee78757SCharles Keepax int ret = 0; 11079ee78757SCharles Keepax 11089ee78757SCharles Keepax mutex_lock(&ctl->dsp->pwr_lock); 11099ee78757SCharles Keepax 11109ee78757SCharles Keepax if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) { 1111cef45771SCharles Keepax if (ctl->enabled && ctl->dsp->running) 11129ee78757SCharles Keepax ret = wm_coeff_read_control(ctl, ctl->cache, size); 11139ee78757SCharles Keepax else 11149ee78757SCharles Keepax ret = -EPERM; 11159ee78757SCharles Keepax } else { 1116cef45771SCharles Keepax if (!ctl->flags && ctl->enabled && ctl->dsp->running) 11179ee78757SCharles Keepax ret = wm_coeff_read_control(ctl, ctl->cache, size); 11189ee78757SCharles Keepax } 11199ee78757SCharles Keepax 11209ee78757SCharles Keepax if (!ret && copy_to_user(bytes, ctl->cache, size)) 11219ee78757SCharles Keepax ret = -EFAULT; 11229ee78757SCharles Keepax 11239ee78757SCharles Keepax mutex_unlock(&ctl->dsp->pwr_lock); 11249ee78757SCharles Keepax 11259ee78757SCharles Keepax return ret; 11269ee78757SCharles Keepax } 11279ee78757SCharles Keepax 1128a23ebba8SRichard Fitzgerald static int wm_coeff_get_acked(struct snd_kcontrol *kcontrol, 1129a23ebba8SRichard Fitzgerald struct snd_ctl_elem_value *ucontrol) 1130a23ebba8SRichard Fitzgerald { 1131a23ebba8SRichard Fitzgerald /* 1132a23ebba8SRichard Fitzgerald * Although it's not useful to read an acked control, we must satisfy 1133a23ebba8SRichard Fitzgerald * user-side assumptions that all controls are readable and that a 1134a23ebba8SRichard Fitzgerald * write of the same value should be filtered out (it's valid to send 1135a23ebba8SRichard Fitzgerald * the same event number again to the firmware). We therefore return 0, 1136a23ebba8SRichard Fitzgerald * meaning "no event" so valid event numbers will always be a change 1137a23ebba8SRichard Fitzgerald */ 1138a23ebba8SRichard Fitzgerald ucontrol->value.integer.value[0] = 0; 1139a23ebba8SRichard Fitzgerald 1140a23ebba8SRichard Fitzgerald return 0; 1141a23ebba8SRichard Fitzgerald } 1142a23ebba8SRichard Fitzgerald 11436ab2b7b4SDimitris Papastamos struct wmfw_ctl_work { 11443809f001SCharles Keepax struct wm_adsp *dsp; 11456ab2b7b4SDimitris Papastamos struct wm_coeff_ctl *ctl; 11466ab2b7b4SDimitris Papastamos struct work_struct work; 11476ab2b7b4SDimitris Papastamos }; 11486ab2b7b4SDimitris Papastamos 11499ee78757SCharles Keepax static unsigned int wmfw_convert_flags(unsigned int in, unsigned int len) 11509ee78757SCharles Keepax { 11519ee78757SCharles Keepax unsigned int out, rd, wr, vol; 11529ee78757SCharles Keepax 11539ee78757SCharles Keepax if (len > ADSP_MAX_STD_CTRL_SIZE) { 11549ee78757SCharles Keepax rd = SNDRV_CTL_ELEM_ACCESS_TLV_READ; 11559ee78757SCharles Keepax wr = SNDRV_CTL_ELEM_ACCESS_TLV_WRITE; 11569ee78757SCharles Keepax vol = SNDRV_CTL_ELEM_ACCESS_VOLATILE; 11579ee78757SCharles Keepax 11589ee78757SCharles Keepax out = SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK; 11599ee78757SCharles Keepax } else { 11609ee78757SCharles Keepax rd = SNDRV_CTL_ELEM_ACCESS_READ; 11619ee78757SCharles Keepax wr = SNDRV_CTL_ELEM_ACCESS_WRITE; 11629ee78757SCharles Keepax vol = SNDRV_CTL_ELEM_ACCESS_VOLATILE; 11639ee78757SCharles Keepax 11649ee78757SCharles Keepax out = 0; 11659ee78757SCharles Keepax } 11669ee78757SCharles Keepax 11679ee78757SCharles Keepax if (in) { 11689ee78757SCharles Keepax if (in & WMFW_CTL_FLAG_READABLE) 11699ee78757SCharles Keepax out |= rd; 11709ee78757SCharles Keepax if (in & WMFW_CTL_FLAG_WRITEABLE) 11719ee78757SCharles Keepax out |= wr; 11729ee78757SCharles Keepax if (in & WMFW_CTL_FLAG_VOLATILE) 11739ee78757SCharles Keepax out |= vol; 11749ee78757SCharles Keepax } else { 11759ee78757SCharles Keepax out |= rd | wr | vol; 11769ee78757SCharles Keepax } 11779ee78757SCharles Keepax 11789ee78757SCharles Keepax return out; 11799ee78757SCharles Keepax } 11809ee78757SCharles Keepax 11813809f001SCharles Keepax static int wmfw_add_ctl(struct wm_adsp *dsp, struct wm_coeff_ctl *ctl) 11826ab2b7b4SDimitris Papastamos { 11836ab2b7b4SDimitris Papastamos struct snd_kcontrol_new *kcontrol; 11846ab2b7b4SDimitris Papastamos int ret; 11856ab2b7b4SDimitris Papastamos 118692bb4c32SDimitris Papastamos if (!ctl || !ctl->name) 11876ab2b7b4SDimitris Papastamos return -EINVAL; 11886ab2b7b4SDimitris Papastamos 11896ab2b7b4SDimitris Papastamos kcontrol = kzalloc(sizeof(*kcontrol), GFP_KERNEL); 11906ab2b7b4SDimitris Papastamos if (!kcontrol) 11916ab2b7b4SDimitris Papastamos return -ENOMEM; 11926ab2b7b4SDimitris Papastamos 11936ab2b7b4SDimitris Papastamos kcontrol->name = ctl->name; 11946ab2b7b4SDimitris Papastamos kcontrol->info = wm_coeff_info; 11959ee78757SCharles Keepax kcontrol->iface = SNDRV_CTL_ELEM_IFACE_MIXER; 11969ee78757SCharles Keepax kcontrol->tlv.c = snd_soc_bytes_tlv_callback; 11979ee78757SCharles Keepax kcontrol->private_value = (unsigned long)&ctl->bytes_ext; 1198a23ebba8SRichard Fitzgerald kcontrol->access = wmfw_convert_flags(ctl->flags, ctl->len); 1199a23ebba8SRichard Fitzgerald 1200a23ebba8SRichard Fitzgerald switch (ctl->type) { 1201a23ebba8SRichard Fitzgerald case WMFW_CTL_TYPE_ACKED: 1202a23ebba8SRichard Fitzgerald kcontrol->get = wm_coeff_get_acked; 1203a23ebba8SRichard Fitzgerald kcontrol->put = wm_coeff_put_acked; 1204a23ebba8SRichard Fitzgerald break; 1205a23ebba8SRichard Fitzgerald default: 1206d7789f5bSRichard Fitzgerald if (kcontrol->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) { 12079ee78757SCharles Keepax ctl->bytes_ext.max = ctl->len; 12089ee78757SCharles Keepax ctl->bytes_ext.get = wm_coeff_tlv_get; 12099ee78757SCharles Keepax ctl->bytes_ext.put = wm_coeff_tlv_put; 1210d7789f5bSRichard Fitzgerald } else { 1211d7789f5bSRichard Fitzgerald kcontrol->get = wm_coeff_get; 1212d7789f5bSRichard Fitzgerald kcontrol->put = wm_coeff_put; 1213d7789f5bSRichard Fitzgerald } 1214a23ebba8SRichard Fitzgerald break; 1215a23ebba8SRichard Fitzgerald } 121626c22a19SCharles Keepax 12170fe1daa6SKuninori Morimoto ret = snd_soc_add_component_controls(dsp->component, kcontrol, 1); 12186ab2b7b4SDimitris Papastamos if (ret < 0) 12196ab2b7b4SDimitris Papastamos goto err_kcontrol; 12206ab2b7b4SDimitris Papastamos 12216ab2b7b4SDimitris Papastamos kfree(kcontrol); 12226ab2b7b4SDimitris Papastamos 12236ab2b7b4SDimitris Papastamos return 0; 12246ab2b7b4SDimitris Papastamos 12256ab2b7b4SDimitris Papastamos err_kcontrol: 12266ab2b7b4SDimitris Papastamos kfree(kcontrol); 12276ab2b7b4SDimitris Papastamos return ret; 12286ab2b7b4SDimitris Papastamos } 12296ab2b7b4SDimitris Papastamos 1230b21acc1cSCharles Keepax static int wm_coeff_init_control_caches(struct wm_adsp *dsp) 1231b21acc1cSCharles Keepax { 1232b21acc1cSCharles Keepax struct wm_coeff_ctl *ctl; 1233b21acc1cSCharles Keepax int ret; 1234b21acc1cSCharles Keepax 1235b21acc1cSCharles Keepax list_for_each_entry(ctl, &dsp->ctl_list, list) { 1236b21acc1cSCharles Keepax if (!ctl->enabled || ctl->set) 1237b21acc1cSCharles Keepax continue; 123826c22a19SCharles Keepax if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) 123926c22a19SCharles Keepax continue; 124026c22a19SCharles Keepax 124104ff40a9SRichard Fitzgerald /* 124204ff40a9SRichard Fitzgerald * For readable controls populate the cache from the DSP memory. 124304ff40a9SRichard Fitzgerald * For non-readable controls the cache was zero-filled when 124404ff40a9SRichard Fitzgerald * created so we don't need to do anything. 124504ff40a9SRichard Fitzgerald */ 124604ff40a9SRichard Fitzgerald if (!ctl->flags || (ctl->flags & WMFW_CTL_FLAG_READABLE)) { 12477d00cd97SCharles Keepax ret = wm_coeff_read_control(ctl, ctl->cache, ctl->len); 1248b21acc1cSCharles Keepax if (ret < 0) 1249b21acc1cSCharles Keepax return ret; 1250b21acc1cSCharles Keepax } 125104ff40a9SRichard Fitzgerald } 1252b21acc1cSCharles Keepax 1253b21acc1cSCharles Keepax return 0; 1254b21acc1cSCharles Keepax } 1255b21acc1cSCharles Keepax 1256b21acc1cSCharles Keepax static int wm_coeff_sync_controls(struct wm_adsp *dsp) 1257b21acc1cSCharles Keepax { 1258b21acc1cSCharles Keepax struct wm_coeff_ctl *ctl; 1259b21acc1cSCharles Keepax int ret; 1260b21acc1cSCharles Keepax 1261b21acc1cSCharles Keepax list_for_each_entry(ctl, &dsp->ctl_list, list) { 1262b21acc1cSCharles Keepax if (!ctl->enabled) 1263b21acc1cSCharles Keepax continue; 126426c22a19SCharles Keepax if (ctl->set && !(ctl->flags & WMFW_CTL_FLAG_VOLATILE)) { 12657d00cd97SCharles Keepax ret = wm_coeff_write_control(ctl, ctl->cache, ctl->len); 1266b21acc1cSCharles Keepax if (ret < 0) 1267b21acc1cSCharles Keepax return ret; 1268b21acc1cSCharles Keepax } 1269b21acc1cSCharles Keepax } 1270b21acc1cSCharles Keepax 1271b21acc1cSCharles Keepax return 0; 1272b21acc1cSCharles Keepax } 1273b21acc1cSCharles Keepax 1274f4f0c4c6SRichard Fitzgerald static void wm_adsp_signal_event_controls(struct wm_adsp *dsp, 1275f4f0c4c6SRichard Fitzgerald unsigned int event) 1276f4f0c4c6SRichard Fitzgerald { 1277f4f0c4c6SRichard Fitzgerald struct wm_coeff_ctl *ctl; 1278f4f0c4c6SRichard Fitzgerald int ret; 1279f4f0c4c6SRichard Fitzgerald 1280f4f0c4c6SRichard Fitzgerald list_for_each_entry(ctl, &dsp->ctl_list, list) { 1281f4f0c4c6SRichard Fitzgerald if (ctl->type != WMFW_CTL_TYPE_HOSTEVENT) 1282f4f0c4c6SRichard Fitzgerald continue; 1283f4f0c4c6SRichard Fitzgerald 128487aa6374SCharles Keepax if (!ctl->enabled) 128587aa6374SCharles Keepax continue; 128687aa6374SCharles Keepax 1287f4f0c4c6SRichard Fitzgerald ret = wm_coeff_write_acked_control(ctl, event); 1288f4f0c4c6SRichard Fitzgerald if (ret) 1289f4f0c4c6SRichard Fitzgerald adsp_warn(dsp, 1290f4f0c4c6SRichard Fitzgerald "Failed to send 0x%x event to alg 0x%x (%d)\n", 1291f4f0c4c6SRichard Fitzgerald event, ctl->alg_region.alg, ret); 1292f4f0c4c6SRichard Fitzgerald } 1293f4f0c4c6SRichard Fitzgerald } 1294f4f0c4c6SRichard Fitzgerald 1295b21acc1cSCharles Keepax static void wm_adsp_ctl_work(struct work_struct *work) 1296b21acc1cSCharles Keepax { 1297b21acc1cSCharles Keepax struct wmfw_ctl_work *ctl_work = container_of(work, 1298b21acc1cSCharles Keepax struct wmfw_ctl_work, 1299b21acc1cSCharles Keepax work); 1300b21acc1cSCharles Keepax 1301b21acc1cSCharles Keepax wmfw_add_ctl(ctl_work->dsp, ctl_work->ctl); 1302b21acc1cSCharles Keepax kfree(ctl_work); 1303b21acc1cSCharles Keepax } 1304b21acc1cSCharles Keepax 130566225e98SRichard Fitzgerald static void wm_adsp_free_ctl_blk(struct wm_coeff_ctl *ctl) 130666225e98SRichard Fitzgerald { 130766225e98SRichard Fitzgerald kfree(ctl->cache); 130866225e98SRichard Fitzgerald kfree(ctl->name); 130966225e98SRichard Fitzgerald kfree(ctl); 131066225e98SRichard Fitzgerald } 131166225e98SRichard Fitzgerald 1312b21acc1cSCharles Keepax static int wm_adsp_create_control(struct wm_adsp *dsp, 1313b21acc1cSCharles Keepax const struct wm_adsp_alg_region *alg_region, 13142323736dSCharles Keepax unsigned int offset, unsigned int len, 131526c22a19SCharles Keepax const char *subname, unsigned int subname_len, 13168eb084d0SStuart Henderson unsigned int flags, unsigned int type) 1317b21acc1cSCharles Keepax { 1318b21acc1cSCharles Keepax struct wm_coeff_ctl *ctl; 1319b21acc1cSCharles Keepax struct wmfw_ctl_work *ctl_work; 1320b21acc1cSCharles Keepax char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; 13219ce5e6e6SRichard Fitzgerald const char *region_name; 1322b21acc1cSCharles Keepax int ret; 1323b21acc1cSCharles Keepax 13249ce5e6e6SRichard Fitzgerald region_name = wm_adsp_mem_region_name(alg_region->type); 13259ce5e6e6SRichard Fitzgerald if (!region_name) { 13262323736dSCharles Keepax adsp_err(dsp, "Unknown region type: %d\n", alg_region->type); 1327b21acc1cSCharles Keepax return -EINVAL; 1328b21acc1cSCharles Keepax } 1329b21acc1cSCharles Keepax 1330cb5b57a9SCharles Keepax switch (dsp->fw_ver) { 1331cb5b57a9SCharles Keepax case 0: 1332cb5b57a9SCharles Keepax case 1: 1333605391d0SRichard Fitzgerald snprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "%s %s %x", 1334605391d0SRichard Fitzgerald dsp->name, region_name, alg_region->alg); 1335cb5b57a9SCharles Keepax break; 1336cb5b57a9SCharles Keepax default: 1337cb5b57a9SCharles Keepax ret = snprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, 1338605391d0SRichard Fitzgerald "%s%c %.12s %x", dsp->name, *region_name, 1339cb5b57a9SCharles Keepax wm_adsp_fw_text[dsp->fw], alg_region->alg); 1340cb5b57a9SCharles Keepax 1341cb5b57a9SCharles Keepax /* Truncate the subname from the start if it is too long */ 1342cb5b57a9SCharles Keepax if (subname) { 1343cb5b57a9SCharles Keepax int avail = SNDRV_CTL_ELEM_ID_NAME_MAXLEN - ret - 2; 1344cb5b57a9SCharles Keepax int skip = 0; 1345cb5b57a9SCharles Keepax 1346b7ede5afSCharles Keepax if (dsp->component->name_prefix) 1347b7ede5afSCharles Keepax avail -= strlen(dsp->component->name_prefix) + 1; 1348b7ede5afSCharles Keepax 1349cb5b57a9SCharles Keepax if (subname_len > avail) 1350cb5b57a9SCharles Keepax skip = subname_len - avail; 1351cb5b57a9SCharles Keepax 1352cb5b57a9SCharles Keepax snprintf(name + ret, 1353cb5b57a9SCharles Keepax SNDRV_CTL_ELEM_ID_NAME_MAXLEN - ret, " %.*s", 1354cb5b57a9SCharles Keepax subname_len - skip, subname + skip); 1355cb5b57a9SCharles Keepax } 1356cb5b57a9SCharles Keepax break; 1357cb5b57a9SCharles Keepax } 1358b21acc1cSCharles Keepax 13597585a5b0SCharles Keepax list_for_each_entry(ctl, &dsp->ctl_list, list) { 1360b21acc1cSCharles Keepax if (!strcmp(ctl->name, name)) { 1361b21acc1cSCharles Keepax if (!ctl->enabled) 1362b21acc1cSCharles Keepax ctl->enabled = 1; 1363b21acc1cSCharles Keepax return 0; 1364b21acc1cSCharles Keepax } 1365b21acc1cSCharles Keepax } 1366b21acc1cSCharles Keepax 1367b21acc1cSCharles Keepax ctl = kzalloc(sizeof(*ctl), GFP_KERNEL); 1368b21acc1cSCharles Keepax if (!ctl) 1369b21acc1cSCharles Keepax return -ENOMEM; 13702323736dSCharles Keepax ctl->fw_name = wm_adsp_fw_text[dsp->fw]; 1371b21acc1cSCharles Keepax ctl->alg_region = *alg_region; 1372b21acc1cSCharles Keepax ctl->name = kmemdup(name, strlen(name) + 1, GFP_KERNEL); 1373b21acc1cSCharles Keepax if (!ctl->name) { 1374b21acc1cSCharles Keepax ret = -ENOMEM; 1375b21acc1cSCharles Keepax goto err_ctl; 1376b21acc1cSCharles Keepax } 1377b21acc1cSCharles Keepax ctl->enabled = 1; 1378b21acc1cSCharles Keepax ctl->set = 0; 1379b21acc1cSCharles Keepax ctl->ops.xget = wm_coeff_get; 1380b21acc1cSCharles Keepax ctl->ops.xput = wm_coeff_put; 1381b21acc1cSCharles Keepax ctl->dsp = dsp; 1382b21acc1cSCharles Keepax 138326c22a19SCharles Keepax ctl->flags = flags; 13848eb084d0SStuart Henderson ctl->type = type; 13852323736dSCharles Keepax ctl->offset = offset; 1386b21acc1cSCharles Keepax ctl->len = len; 1387b21acc1cSCharles Keepax ctl->cache = kzalloc(ctl->len, GFP_KERNEL); 1388b21acc1cSCharles Keepax if (!ctl->cache) { 1389b21acc1cSCharles Keepax ret = -ENOMEM; 1390b21acc1cSCharles Keepax goto err_ctl_name; 1391b21acc1cSCharles Keepax } 1392b21acc1cSCharles Keepax 13932323736dSCharles Keepax list_add(&ctl->list, &dsp->ctl_list); 13942323736dSCharles Keepax 13958eb084d0SStuart Henderson if (flags & WMFW_CTL_FLAG_SYS) 13968eb084d0SStuart Henderson return 0; 13978eb084d0SStuart Henderson 1398b21acc1cSCharles Keepax ctl_work = kzalloc(sizeof(*ctl_work), GFP_KERNEL); 1399b21acc1cSCharles Keepax if (!ctl_work) { 1400b21acc1cSCharles Keepax ret = -ENOMEM; 1401b21acc1cSCharles Keepax goto err_ctl_cache; 1402b21acc1cSCharles Keepax } 1403b21acc1cSCharles Keepax 1404b21acc1cSCharles Keepax ctl_work->dsp = dsp; 1405b21acc1cSCharles Keepax ctl_work->ctl = ctl; 1406b21acc1cSCharles Keepax INIT_WORK(&ctl_work->work, wm_adsp_ctl_work); 1407b21acc1cSCharles Keepax schedule_work(&ctl_work->work); 1408b21acc1cSCharles Keepax 1409b21acc1cSCharles Keepax return 0; 1410b21acc1cSCharles Keepax 1411b21acc1cSCharles Keepax err_ctl_cache: 1412b21acc1cSCharles Keepax kfree(ctl->cache); 1413b21acc1cSCharles Keepax err_ctl_name: 1414b21acc1cSCharles Keepax kfree(ctl->name); 1415b21acc1cSCharles Keepax err_ctl: 1416b21acc1cSCharles Keepax kfree(ctl); 1417b21acc1cSCharles Keepax 1418b21acc1cSCharles Keepax return ret; 1419b21acc1cSCharles Keepax } 1420b21acc1cSCharles Keepax 14212323736dSCharles Keepax struct wm_coeff_parsed_alg { 14222323736dSCharles Keepax int id; 14232323736dSCharles Keepax const u8 *name; 14242323736dSCharles Keepax int name_len; 14252323736dSCharles Keepax int ncoeff; 14262323736dSCharles Keepax }; 14272323736dSCharles Keepax 14282323736dSCharles Keepax struct wm_coeff_parsed_coeff { 14292323736dSCharles Keepax int offset; 14302323736dSCharles Keepax int mem_type; 14312323736dSCharles Keepax const u8 *name; 14322323736dSCharles Keepax int name_len; 14332323736dSCharles Keepax int ctl_type; 14342323736dSCharles Keepax int flags; 14352323736dSCharles Keepax int len; 14362323736dSCharles Keepax }; 14372323736dSCharles Keepax 1438cb5b57a9SCharles Keepax static int wm_coeff_parse_string(int bytes, const u8 **pos, const u8 **str) 1439cb5b57a9SCharles Keepax { 1440cb5b57a9SCharles Keepax int length; 1441cb5b57a9SCharles Keepax 1442cb5b57a9SCharles Keepax switch (bytes) { 1443cb5b57a9SCharles Keepax case 1: 1444cb5b57a9SCharles Keepax length = **pos; 1445cb5b57a9SCharles Keepax break; 1446cb5b57a9SCharles Keepax case 2: 14478299ee81SCharles Keepax length = le16_to_cpu(*((__le16 *)*pos)); 1448cb5b57a9SCharles Keepax break; 1449cb5b57a9SCharles Keepax default: 1450cb5b57a9SCharles Keepax return 0; 1451cb5b57a9SCharles Keepax } 1452cb5b57a9SCharles Keepax 1453cb5b57a9SCharles Keepax if (str) 1454cb5b57a9SCharles Keepax *str = *pos + bytes; 1455cb5b57a9SCharles Keepax 1456cb5b57a9SCharles Keepax *pos += ((length + bytes) + 3) & ~0x03; 1457cb5b57a9SCharles Keepax 1458cb5b57a9SCharles Keepax return length; 1459cb5b57a9SCharles Keepax } 1460cb5b57a9SCharles Keepax 1461cb5b57a9SCharles Keepax static int wm_coeff_parse_int(int bytes, const u8 **pos) 1462cb5b57a9SCharles Keepax { 1463cb5b57a9SCharles Keepax int val = 0; 1464cb5b57a9SCharles Keepax 1465cb5b57a9SCharles Keepax switch (bytes) { 1466cb5b57a9SCharles Keepax case 2: 14678299ee81SCharles Keepax val = le16_to_cpu(*((__le16 *)*pos)); 1468cb5b57a9SCharles Keepax break; 1469cb5b57a9SCharles Keepax case 4: 14708299ee81SCharles Keepax val = le32_to_cpu(*((__le32 *)*pos)); 1471cb5b57a9SCharles Keepax break; 1472cb5b57a9SCharles Keepax default: 1473cb5b57a9SCharles Keepax break; 1474cb5b57a9SCharles Keepax } 1475cb5b57a9SCharles Keepax 1476cb5b57a9SCharles Keepax *pos += bytes; 1477cb5b57a9SCharles Keepax 1478cb5b57a9SCharles Keepax return val; 1479cb5b57a9SCharles Keepax } 1480cb5b57a9SCharles Keepax 14812323736dSCharles Keepax static inline void wm_coeff_parse_alg(struct wm_adsp *dsp, const u8 **data, 14822323736dSCharles Keepax struct wm_coeff_parsed_alg *blk) 14832323736dSCharles Keepax { 14842323736dSCharles Keepax const struct wmfw_adsp_alg_data *raw; 14852323736dSCharles Keepax 1486cb5b57a9SCharles Keepax switch (dsp->fw_ver) { 1487cb5b57a9SCharles Keepax case 0: 1488cb5b57a9SCharles Keepax case 1: 14892323736dSCharles Keepax raw = (const struct wmfw_adsp_alg_data *)*data; 14902323736dSCharles Keepax *data = raw->data; 14912323736dSCharles Keepax 14922323736dSCharles Keepax blk->id = le32_to_cpu(raw->id); 14932323736dSCharles Keepax blk->name = raw->name; 14942323736dSCharles Keepax blk->name_len = strlen(raw->name); 14952323736dSCharles Keepax blk->ncoeff = le32_to_cpu(raw->ncoeff); 1496cb5b57a9SCharles Keepax break; 1497cb5b57a9SCharles Keepax default: 1498cb5b57a9SCharles Keepax blk->id = wm_coeff_parse_int(sizeof(raw->id), data); 1499cb5b57a9SCharles Keepax blk->name_len = wm_coeff_parse_string(sizeof(u8), data, 1500cb5b57a9SCharles Keepax &blk->name); 1501cb5b57a9SCharles Keepax wm_coeff_parse_string(sizeof(u16), data, NULL); 1502cb5b57a9SCharles Keepax blk->ncoeff = wm_coeff_parse_int(sizeof(raw->ncoeff), data); 1503cb5b57a9SCharles Keepax break; 1504cb5b57a9SCharles Keepax } 15052323736dSCharles Keepax 15062323736dSCharles Keepax adsp_dbg(dsp, "Algorithm ID: %#x\n", blk->id); 15072323736dSCharles Keepax adsp_dbg(dsp, "Algorithm name: %.*s\n", blk->name_len, blk->name); 15082323736dSCharles Keepax adsp_dbg(dsp, "# of coefficient descriptors: %#x\n", blk->ncoeff); 15092323736dSCharles Keepax } 15102323736dSCharles Keepax 15112323736dSCharles Keepax static inline void wm_coeff_parse_coeff(struct wm_adsp *dsp, const u8 **data, 15122323736dSCharles Keepax struct wm_coeff_parsed_coeff *blk) 15132323736dSCharles Keepax { 15142323736dSCharles Keepax const struct wmfw_adsp_coeff_data *raw; 1515cb5b57a9SCharles Keepax const u8 *tmp; 1516cb5b57a9SCharles Keepax int length; 15172323736dSCharles Keepax 1518cb5b57a9SCharles Keepax switch (dsp->fw_ver) { 1519cb5b57a9SCharles Keepax case 0: 1520cb5b57a9SCharles Keepax case 1: 15212323736dSCharles Keepax raw = (const struct wmfw_adsp_coeff_data *)*data; 15222323736dSCharles Keepax *data = *data + sizeof(raw->hdr) + le32_to_cpu(raw->hdr.size); 15232323736dSCharles Keepax 15242323736dSCharles Keepax blk->offset = le16_to_cpu(raw->hdr.offset); 15252323736dSCharles Keepax blk->mem_type = le16_to_cpu(raw->hdr.type); 15262323736dSCharles Keepax blk->name = raw->name; 15272323736dSCharles Keepax blk->name_len = strlen(raw->name); 15282323736dSCharles Keepax blk->ctl_type = le16_to_cpu(raw->ctl_type); 15292323736dSCharles Keepax blk->flags = le16_to_cpu(raw->flags); 15302323736dSCharles Keepax blk->len = le32_to_cpu(raw->len); 1531cb5b57a9SCharles Keepax break; 1532cb5b57a9SCharles Keepax default: 1533cb5b57a9SCharles Keepax tmp = *data; 1534cb5b57a9SCharles Keepax blk->offset = wm_coeff_parse_int(sizeof(raw->hdr.offset), &tmp); 1535cb5b57a9SCharles Keepax blk->mem_type = wm_coeff_parse_int(sizeof(raw->hdr.type), &tmp); 1536cb5b57a9SCharles Keepax length = wm_coeff_parse_int(sizeof(raw->hdr.size), &tmp); 1537cb5b57a9SCharles Keepax blk->name_len = wm_coeff_parse_string(sizeof(u8), &tmp, 1538cb5b57a9SCharles Keepax &blk->name); 1539cb5b57a9SCharles Keepax wm_coeff_parse_string(sizeof(u8), &tmp, NULL); 1540cb5b57a9SCharles Keepax wm_coeff_parse_string(sizeof(u16), &tmp, NULL); 1541cb5b57a9SCharles Keepax blk->ctl_type = wm_coeff_parse_int(sizeof(raw->ctl_type), &tmp); 1542cb5b57a9SCharles Keepax blk->flags = wm_coeff_parse_int(sizeof(raw->flags), &tmp); 1543cb5b57a9SCharles Keepax blk->len = wm_coeff_parse_int(sizeof(raw->len), &tmp); 1544cb5b57a9SCharles Keepax 1545cb5b57a9SCharles Keepax *data = *data + sizeof(raw->hdr) + length; 1546cb5b57a9SCharles Keepax break; 1547cb5b57a9SCharles Keepax } 15482323736dSCharles Keepax 15492323736dSCharles Keepax adsp_dbg(dsp, "\tCoefficient type: %#x\n", blk->mem_type); 15502323736dSCharles Keepax adsp_dbg(dsp, "\tCoefficient offset: %#x\n", blk->offset); 15512323736dSCharles Keepax adsp_dbg(dsp, "\tCoefficient name: %.*s\n", blk->name_len, blk->name); 15522323736dSCharles Keepax adsp_dbg(dsp, "\tCoefficient flags: %#x\n", blk->flags); 15532323736dSCharles Keepax adsp_dbg(dsp, "\tALSA control type: %#x\n", blk->ctl_type); 15542323736dSCharles Keepax adsp_dbg(dsp, "\tALSA control len: %#x\n", blk->len); 15552323736dSCharles Keepax } 15562323736dSCharles Keepax 1557f4f0c4c6SRichard Fitzgerald static int wm_adsp_check_coeff_flags(struct wm_adsp *dsp, 1558f4f0c4c6SRichard Fitzgerald const struct wm_coeff_parsed_coeff *coeff_blk, 1559f4f0c4c6SRichard Fitzgerald unsigned int f_required, 1560f4f0c4c6SRichard Fitzgerald unsigned int f_illegal) 1561f4f0c4c6SRichard Fitzgerald { 1562f4f0c4c6SRichard Fitzgerald if ((coeff_blk->flags & f_illegal) || 1563f4f0c4c6SRichard Fitzgerald ((coeff_blk->flags & f_required) != f_required)) { 1564f4f0c4c6SRichard Fitzgerald adsp_err(dsp, "Illegal flags 0x%x for control type 0x%x\n", 1565f4f0c4c6SRichard Fitzgerald coeff_blk->flags, coeff_blk->ctl_type); 1566f4f0c4c6SRichard Fitzgerald return -EINVAL; 1567f4f0c4c6SRichard Fitzgerald } 1568f4f0c4c6SRichard Fitzgerald 1569f4f0c4c6SRichard Fitzgerald return 0; 1570f4f0c4c6SRichard Fitzgerald } 1571f4f0c4c6SRichard Fitzgerald 15722323736dSCharles Keepax static int wm_adsp_parse_coeff(struct wm_adsp *dsp, 15732323736dSCharles Keepax const struct wmfw_region *region) 15742323736dSCharles Keepax { 15752323736dSCharles Keepax struct wm_adsp_alg_region alg_region = {}; 15762323736dSCharles Keepax struct wm_coeff_parsed_alg alg_blk; 15772323736dSCharles Keepax struct wm_coeff_parsed_coeff coeff_blk; 15782323736dSCharles Keepax const u8 *data = region->data; 15792323736dSCharles Keepax int i, ret; 15802323736dSCharles Keepax 15812323736dSCharles Keepax wm_coeff_parse_alg(dsp, &data, &alg_blk); 15822323736dSCharles Keepax for (i = 0; i < alg_blk.ncoeff; i++) { 15832323736dSCharles Keepax wm_coeff_parse_coeff(dsp, &data, &coeff_blk); 15842323736dSCharles Keepax 15852323736dSCharles Keepax switch (coeff_blk.ctl_type) { 15862323736dSCharles Keepax case SNDRV_CTL_ELEM_TYPE_BYTES: 15872323736dSCharles Keepax break; 1588a23ebba8SRichard Fitzgerald case WMFW_CTL_TYPE_ACKED: 1589a23ebba8SRichard Fitzgerald if (coeff_blk.flags & WMFW_CTL_FLAG_SYS) 1590a23ebba8SRichard Fitzgerald continue; /* ignore */ 1591a23ebba8SRichard Fitzgerald 1592a23ebba8SRichard Fitzgerald ret = wm_adsp_check_coeff_flags(dsp, &coeff_blk, 1593a23ebba8SRichard Fitzgerald WMFW_CTL_FLAG_VOLATILE | 1594a23ebba8SRichard Fitzgerald WMFW_CTL_FLAG_WRITEABLE | 1595a23ebba8SRichard Fitzgerald WMFW_CTL_FLAG_READABLE, 1596a23ebba8SRichard Fitzgerald 0); 1597a23ebba8SRichard Fitzgerald if (ret) 1598a23ebba8SRichard Fitzgerald return -EINVAL; 1599a23ebba8SRichard Fitzgerald break; 1600f4f0c4c6SRichard Fitzgerald case WMFW_CTL_TYPE_HOSTEVENT: 1601f4f0c4c6SRichard Fitzgerald ret = wm_adsp_check_coeff_flags(dsp, &coeff_blk, 1602f4f0c4c6SRichard Fitzgerald WMFW_CTL_FLAG_SYS | 1603f4f0c4c6SRichard Fitzgerald WMFW_CTL_FLAG_VOLATILE | 1604f4f0c4c6SRichard Fitzgerald WMFW_CTL_FLAG_WRITEABLE | 1605f4f0c4c6SRichard Fitzgerald WMFW_CTL_FLAG_READABLE, 1606f4f0c4c6SRichard Fitzgerald 0); 1607f4f0c4c6SRichard Fitzgerald if (ret) 1608f4f0c4c6SRichard Fitzgerald return -EINVAL; 1609f4f0c4c6SRichard Fitzgerald break; 1610d52ed4b0SRichard Fitzgerald case WMFW_CTL_TYPE_HOST_BUFFER: 1611d52ed4b0SRichard Fitzgerald ret = wm_adsp_check_coeff_flags(dsp, &coeff_blk, 1612d52ed4b0SRichard Fitzgerald WMFW_CTL_FLAG_SYS | 1613d52ed4b0SRichard Fitzgerald WMFW_CTL_FLAG_VOLATILE | 1614d52ed4b0SRichard Fitzgerald WMFW_CTL_FLAG_READABLE, 1615d52ed4b0SRichard Fitzgerald 0); 1616d52ed4b0SRichard Fitzgerald if (ret) 1617d52ed4b0SRichard Fitzgerald return -EINVAL; 1618d52ed4b0SRichard Fitzgerald break; 16192323736dSCharles Keepax default: 16202323736dSCharles Keepax adsp_err(dsp, "Unknown control type: %d\n", 16212323736dSCharles Keepax coeff_blk.ctl_type); 16222323736dSCharles Keepax return -EINVAL; 16232323736dSCharles Keepax } 16242323736dSCharles Keepax 16252323736dSCharles Keepax alg_region.type = coeff_blk.mem_type; 16262323736dSCharles Keepax alg_region.alg = alg_blk.id; 16272323736dSCharles Keepax 16282323736dSCharles Keepax ret = wm_adsp_create_control(dsp, &alg_region, 16292323736dSCharles Keepax coeff_blk.offset, 16302323736dSCharles Keepax coeff_blk.len, 16312323736dSCharles Keepax coeff_blk.name, 163226c22a19SCharles Keepax coeff_blk.name_len, 16338eb084d0SStuart Henderson coeff_blk.flags, 16348eb084d0SStuart Henderson coeff_blk.ctl_type); 16352323736dSCharles Keepax if (ret < 0) 16362323736dSCharles Keepax adsp_err(dsp, "Failed to create control: %.*s, %d\n", 16372323736dSCharles Keepax coeff_blk.name_len, coeff_blk.name, ret); 16382323736dSCharles Keepax } 16392323736dSCharles Keepax 16402323736dSCharles Keepax return 0; 16412323736dSCharles Keepax } 16422323736dSCharles Keepax 16432159ad93SMark Brown static int wm_adsp_load(struct wm_adsp *dsp) 16442159ad93SMark Brown { 1645cf17c83cSMark Brown LIST_HEAD(buf_list); 16462159ad93SMark Brown const struct firmware *firmware; 16472159ad93SMark Brown struct regmap *regmap = dsp->regmap; 16482159ad93SMark Brown unsigned int pos = 0; 16492159ad93SMark Brown const struct wmfw_header *header; 16502159ad93SMark Brown const struct wmfw_adsp1_sizes *adsp1_sizes; 16512159ad93SMark Brown const struct wmfw_adsp2_sizes *adsp2_sizes; 16522159ad93SMark Brown const struct wmfw_footer *footer; 16532159ad93SMark Brown const struct wmfw_region *region; 16542159ad93SMark Brown const struct wm_adsp_region *mem; 16552159ad93SMark Brown const char *region_name; 16561cab2a84SRichard Fitzgerald char *file, *text = NULL; 1657cf17c83cSMark Brown struct wm_adsp_buf *buf; 16582159ad93SMark Brown unsigned int reg; 16592159ad93SMark Brown int regions = 0; 16602159ad93SMark Brown int ret, offset, type, sizes; 16612159ad93SMark Brown 16622159ad93SMark Brown file = kzalloc(PAGE_SIZE, GFP_KERNEL); 16632159ad93SMark Brown if (file == NULL) 16642159ad93SMark Brown return -ENOMEM; 16652159ad93SMark Brown 1666605391d0SRichard Fitzgerald snprintf(file, PAGE_SIZE, "%s-%s-%s.wmfw", dsp->part, dsp->fwf_name, 16671023dbd9SMark Brown wm_adsp_fw[dsp->fw].file); 16682159ad93SMark Brown file[PAGE_SIZE - 1] = '\0'; 16692159ad93SMark Brown 16702159ad93SMark Brown ret = request_firmware(&firmware, file, dsp->dev); 16712159ad93SMark Brown if (ret != 0) { 16722159ad93SMark Brown adsp_err(dsp, "Failed to request '%s'\n", file); 16732159ad93SMark Brown goto out; 16742159ad93SMark Brown } 16752159ad93SMark Brown ret = -EINVAL; 16762159ad93SMark Brown 16772159ad93SMark Brown pos = sizeof(*header) + sizeof(*adsp1_sizes) + sizeof(*footer); 16782159ad93SMark Brown if (pos >= firmware->size) { 16792159ad93SMark Brown adsp_err(dsp, "%s: file too short, %zu bytes\n", 16802159ad93SMark Brown file, firmware->size); 16812159ad93SMark Brown goto out_fw; 16822159ad93SMark Brown } 16832159ad93SMark Brown 16842159ad93SMark Brown header = (void *)&firmware->data[0]; 16852159ad93SMark Brown 16862159ad93SMark Brown if (memcmp(&header->magic[0], "WMFW", 4) != 0) { 16872159ad93SMark Brown adsp_err(dsp, "%s: invalid magic\n", file); 16882159ad93SMark Brown goto out_fw; 16892159ad93SMark Brown } 16902159ad93SMark Brown 16912323736dSCharles Keepax switch (header->ver) { 16922323736dSCharles Keepax case 0: 1693c61e59feSCharles Keepax adsp_warn(dsp, "%s: Depreciated file format %d\n", 1694c61e59feSCharles Keepax file, header->ver); 1695c61e59feSCharles Keepax break; 16962323736dSCharles Keepax case 1: 1697cb5b57a9SCharles Keepax case 2: 16982323736dSCharles Keepax break; 16992323736dSCharles Keepax default: 17002159ad93SMark Brown adsp_err(dsp, "%s: unknown file format %d\n", 17012159ad93SMark Brown file, header->ver); 17022159ad93SMark Brown goto out_fw; 17032159ad93SMark Brown } 17042323736dSCharles Keepax 17053626992aSDimitris Papastamos adsp_info(dsp, "Firmware version: %d\n", header->ver); 17062323736dSCharles Keepax dsp->fw_ver = header->ver; 17072159ad93SMark Brown 17082159ad93SMark Brown if (header->core != dsp->type) { 17092159ad93SMark Brown adsp_err(dsp, "%s: invalid core %d != %d\n", 17102159ad93SMark Brown file, header->core, dsp->type); 17112159ad93SMark Brown goto out_fw; 17122159ad93SMark Brown } 17132159ad93SMark Brown 17142159ad93SMark Brown switch (dsp->type) { 17152159ad93SMark Brown case WMFW_ADSP1: 17162159ad93SMark Brown pos = sizeof(*header) + sizeof(*adsp1_sizes) + sizeof(*footer); 17172159ad93SMark Brown adsp1_sizes = (void *)&(header[1]); 17182159ad93SMark Brown footer = (void *)&(adsp1_sizes[1]); 17192159ad93SMark Brown sizes = sizeof(*adsp1_sizes); 17202159ad93SMark Brown 17212159ad93SMark Brown adsp_dbg(dsp, "%s: %d DM, %d PM, %d ZM\n", 17222159ad93SMark Brown file, le32_to_cpu(adsp1_sizes->dm), 17232159ad93SMark Brown le32_to_cpu(adsp1_sizes->pm), 17242159ad93SMark Brown le32_to_cpu(adsp1_sizes->zm)); 17252159ad93SMark Brown break; 17262159ad93SMark Brown 17272159ad93SMark Brown case WMFW_ADSP2: 17282159ad93SMark Brown pos = sizeof(*header) + sizeof(*adsp2_sizes) + sizeof(*footer); 17292159ad93SMark Brown adsp2_sizes = (void *)&(header[1]); 17302159ad93SMark Brown footer = (void *)&(adsp2_sizes[1]); 17312159ad93SMark Brown sizes = sizeof(*adsp2_sizes); 17322159ad93SMark Brown 17332159ad93SMark Brown adsp_dbg(dsp, "%s: %d XM, %d YM %d PM, %d ZM\n", 17342159ad93SMark Brown file, le32_to_cpu(adsp2_sizes->xm), 17352159ad93SMark Brown le32_to_cpu(adsp2_sizes->ym), 17362159ad93SMark Brown le32_to_cpu(adsp2_sizes->pm), 17372159ad93SMark Brown le32_to_cpu(adsp2_sizes->zm)); 17382159ad93SMark Brown break; 17392159ad93SMark Brown 17402159ad93SMark Brown default: 17416c452bdaSTakashi Iwai WARN(1, "Unknown DSP type"); 17422159ad93SMark Brown goto out_fw; 17432159ad93SMark Brown } 17442159ad93SMark Brown 17452159ad93SMark Brown if (le32_to_cpu(header->len) != sizeof(*header) + 17462159ad93SMark Brown sizes + sizeof(*footer)) { 17472159ad93SMark Brown adsp_err(dsp, "%s: unexpected header length %d\n", 17482159ad93SMark Brown file, le32_to_cpu(header->len)); 17492159ad93SMark Brown goto out_fw; 17502159ad93SMark Brown } 17512159ad93SMark Brown 17522159ad93SMark Brown adsp_dbg(dsp, "%s: timestamp %llu\n", file, 17532159ad93SMark Brown le64_to_cpu(footer->timestamp)); 17542159ad93SMark Brown 17552159ad93SMark Brown while (pos < firmware->size && 175650dd2ea8SBen Hutchings sizeof(*region) < firmware->size - pos) { 17572159ad93SMark Brown region = (void *)&(firmware->data[pos]); 17582159ad93SMark Brown region_name = "Unknown"; 17592159ad93SMark Brown reg = 0; 17602159ad93SMark Brown text = NULL; 17612159ad93SMark Brown offset = le32_to_cpu(region->offset) & 0xffffff; 17622159ad93SMark Brown type = be32_to_cpu(region->type) & 0xff; 17632159ad93SMark Brown mem = wm_adsp_find_region(dsp, type); 17642159ad93SMark Brown 17652159ad93SMark Brown switch (type) { 17662159ad93SMark Brown case WMFW_NAME_TEXT: 17672159ad93SMark Brown region_name = "Firmware name"; 17682159ad93SMark Brown text = kzalloc(le32_to_cpu(region->len) + 1, 17692159ad93SMark Brown GFP_KERNEL); 17702159ad93SMark Brown break; 17712323736dSCharles Keepax case WMFW_ALGORITHM_DATA: 17722323736dSCharles Keepax region_name = "Algorithm"; 17732323736dSCharles Keepax ret = wm_adsp_parse_coeff(dsp, region); 17742323736dSCharles Keepax if (ret != 0) 17752323736dSCharles Keepax goto out_fw; 17762323736dSCharles Keepax break; 17772159ad93SMark Brown case WMFW_INFO_TEXT: 17782159ad93SMark Brown region_name = "Information"; 17792159ad93SMark Brown text = kzalloc(le32_to_cpu(region->len) + 1, 17802159ad93SMark Brown GFP_KERNEL); 17812159ad93SMark Brown break; 17822159ad93SMark Brown case WMFW_ABSOLUTE: 17832159ad93SMark Brown region_name = "Absolute"; 17842159ad93SMark Brown reg = offset; 17852159ad93SMark Brown break; 17862159ad93SMark Brown case WMFW_ADSP1_PM: 17872159ad93SMark Brown case WMFW_ADSP1_DM: 17882159ad93SMark Brown case WMFW_ADSP2_XM: 17892159ad93SMark Brown case WMFW_ADSP2_YM: 17902159ad93SMark Brown case WMFW_ADSP1_ZM: 17919ce5e6e6SRichard Fitzgerald region_name = wm_adsp_mem_region_name(type); 179245b9ee72SMark Brown reg = wm_adsp_region_to_reg(mem, offset); 17932159ad93SMark Brown break; 17942159ad93SMark Brown default: 17952159ad93SMark Brown adsp_warn(dsp, 17962159ad93SMark Brown "%s.%d: Unknown region type %x at %d(%x)\n", 17972159ad93SMark Brown file, regions, type, pos, pos); 17982159ad93SMark Brown break; 17992159ad93SMark Brown } 18002159ad93SMark Brown 18012159ad93SMark Brown adsp_dbg(dsp, "%s.%d: %d bytes at %d in %s\n", file, 18022159ad93SMark Brown regions, le32_to_cpu(region->len), offset, 18032159ad93SMark Brown region_name); 18042159ad93SMark Brown 180550dd2ea8SBen Hutchings if (le32_to_cpu(region->len) > 180650dd2ea8SBen Hutchings firmware->size - pos - sizeof(*region)) { 18071cab2a84SRichard Fitzgerald adsp_err(dsp, 18081cab2a84SRichard Fitzgerald "%s.%d: %s region len %d bytes exceeds file length %zu\n", 18091cab2a84SRichard Fitzgerald file, regions, region_name, 18101cab2a84SRichard Fitzgerald le32_to_cpu(region->len), firmware->size); 18111cab2a84SRichard Fitzgerald ret = -EINVAL; 18121cab2a84SRichard Fitzgerald goto out_fw; 18131cab2a84SRichard Fitzgerald } 18141cab2a84SRichard Fitzgerald 18152159ad93SMark Brown if (text) { 18162159ad93SMark Brown memcpy(text, region->data, le32_to_cpu(region->len)); 18172159ad93SMark Brown adsp_info(dsp, "%s: %s\n", file, text); 18182159ad93SMark Brown kfree(text); 18191cab2a84SRichard Fitzgerald text = NULL; 18202159ad93SMark Brown } 18212159ad93SMark Brown 18222159ad93SMark Brown if (reg) { 1823cdcd7f72SCharles Keepax buf = wm_adsp_buf_alloc(region->data, 1824cdcd7f72SCharles Keepax le32_to_cpu(region->len), 1825cf17c83cSMark Brown &buf_list); 1826a76fefabSMark Brown if (!buf) { 1827a76fefabSMark Brown adsp_err(dsp, "Out of memory\n"); 18287328823dSDimitris Papastamos ret = -ENOMEM; 18297328823dSDimitris Papastamos goto out_fw; 1830a76fefabSMark Brown } 1831a76fefabSMark Brown 1832cdcd7f72SCharles Keepax ret = regmap_raw_write_async(regmap, reg, buf->buf, 1833cdcd7f72SCharles Keepax le32_to_cpu(region->len)); 18342159ad93SMark Brown if (ret != 0) { 18352159ad93SMark Brown adsp_err(dsp, 1836cdcd7f72SCharles Keepax "%s.%d: Failed to write %d bytes at %d in %s: %d\n", 18372159ad93SMark Brown file, regions, 1838cdcd7f72SCharles Keepax le32_to_cpu(region->len), offset, 18392159ad93SMark Brown region_name, ret); 18402159ad93SMark Brown goto out_fw; 18412159ad93SMark Brown } 18422159ad93SMark Brown } 18432159ad93SMark Brown 18442159ad93SMark Brown pos += le32_to_cpu(region->len) + sizeof(*region); 18452159ad93SMark Brown regions++; 18462159ad93SMark Brown } 18472159ad93SMark Brown 1848cf17c83cSMark Brown ret = regmap_async_complete(regmap); 1849cf17c83cSMark Brown if (ret != 0) { 1850cf17c83cSMark Brown adsp_err(dsp, "Failed to complete async write: %d\n", ret); 1851cf17c83cSMark Brown goto out_fw; 1852cf17c83cSMark Brown } 1853cf17c83cSMark Brown 18542159ad93SMark Brown if (pos > firmware->size) 18552159ad93SMark Brown adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n", 18562159ad93SMark Brown file, regions, pos - firmware->size); 18572159ad93SMark Brown 1858f9f55e31SRichard Fitzgerald wm_adsp_debugfs_save_wmfwname(dsp, file); 1859f9f55e31SRichard Fitzgerald 18602159ad93SMark Brown out_fw: 1861cf17c83cSMark Brown regmap_async_complete(regmap); 1862cf17c83cSMark Brown wm_adsp_buf_free(&buf_list); 18632159ad93SMark Brown release_firmware(firmware); 18641cab2a84SRichard Fitzgerald kfree(text); 18652159ad93SMark Brown out: 18662159ad93SMark Brown kfree(file); 18672159ad93SMark Brown 18682159ad93SMark Brown return ret; 18692159ad93SMark Brown } 18702159ad93SMark Brown 18712323736dSCharles Keepax static void wm_adsp_ctl_fixup_base(struct wm_adsp *dsp, 18722323736dSCharles Keepax const struct wm_adsp_alg_region *alg_region) 18732323736dSCharles Keepax { 18742323736dSCharles Keepax struct wm_coeff_ctl *ctl; 18752323736dSCharles Keepax 18762323736dSCharles Keepax list_for_each_entry(ctl, &dsp->ctl_list, list) { 18772323736dSCharles Keepax if (ctl->fw_name == wm_adsp_fw_text[dsp->fw] && 18782323736dSCharles Keepax alg_region->alg == ctl->alg_region.alg && 18792323736dSCharles Keepax alg_region->type == ctl->alg_region.type) { 18802323736dSCharles Keepax ctl->alg_region.base = alg_region->base; 18812323736dSCharles Keepax } 18822323736dSCharles Keepax } 18832323736dSCharles Keepax } 18842323736dSCharles Keepax 18853809f001SCharles Keepax static void *wm_adsp_read_algs(struct wm_adsp *dsp, size_t n_algs, 18867f7cca08SCharles Keepax const struct wm_adsp_region *mem, 1887b618a185SCharles Keepax unsigned int pos, unsigned int len) 1888db40517cSMark Brown { 1889b618a185SCharles Keepax void *alg; 18907f7cca08SCharles Keepax unsigned int reg; 1891b618a185SCharles Keepax int ret; 1892db40517cSMark Brown __be32 val; 1893db40517cSMark Brown 18943809f001SCharles Keepax if (n_algs == 0) { 1895b618a185SCharles Keepax adsp_err(dsp, "No algorithms\n"); 1896b618a185SCharles Keepax return ERR_PTR(-EINVAL); 1897db40517cSMark Brown } 1898db40517cSMark Brown 18993809f001SCharles Keepax if (n_algs > 1024) { 19003809f001SCharles Keepax adsp_err(dsp, "Algorithm count %zx excessive\n", n_algs); 1901b618a185SCharles Keepax return ERR_PTR(-EINVAL); 1902b618a185SCharles Keepax } 1903b618a185SCharles Keepax 1904b618a185SCharles Keepax /* Read the terminator first to validate the length */ 19057f7cca08SCharles Keepax reg = wm_adsp_region_to_reg(mem, pos + len); 19067f7cca08SCharles Keepax 19077f7cca08SCharles Keepax ret = regmap_raw_read(dsp->regmap, reg, &val, sizeof(val)); 1908b618a185SCharles Keepax if (ret != 0) { 1909b618a185SCharles Keepax adsp_err(dsp, "Failed to read algorithm list end: %d\n", 1910b618a185SCharles Keepax ret); 1911b618a185SCharles Keepax return ERR_PTR(ret); 1912b618a185SCharles Keepax } 1913b618a185SCharles Keepax 1914b618a185SCharles Keepax if (be32_to_cpu(val) != 0xbedead) 1915503ada8aSRichard Fitzgerald adsp_warn(dsp, "Algorithm list end %x 0x%x != 0xbedead\n", 19167f7cca08SCharles Keepax reg, be32_to_cpu(val)); 19177f7cca08SCharles Keepax 19187f7cca08SCharles Keepax /* Convert length from DSP words to bytes */ 19197f7cca08SCharles Keepax len *= sizeof(u32); 1920b618a185SCharles Keepax 1921517ee74eSCharles Keepax alg = kzalloc(len, GFP_KERNEL | GFP_DMA); 1922b618a185SCharles Keepax if (!alg) 1923b618a185SCharles Keepax return ERR_PTR(-ENOMEM); 1924b618a185SCharles Keepax 19257f7cca08SCharles Keepax reg = wm_adsp_region_to_reg(mem, pos); 19267f7cca08SCharles Keepax 19277f7cca08SCharles Keepax ret = regmap_raw_read(dsp->regmap, reg, alg, len); 1928b618a185SCharles Keepax if (ret != 0) { 19297d00cd97SCharles Keepax adsp_err(dsp, "Failed to read algorithm list: %d\n", ret); 1930b618a185SCharles Keepax kfree(alg); 1931b618a185SCharles Keepax return ERR_PTR(ret); 1932b618a185SCharles Keepax } 1933b618a185SCharles Keepax 1934b618a185SCharles Keepax return alg; 1935b618a185SCharles Keepax } 1936b618a185SCharles Keepax 193714197095SCharles Keepax static struct wm_adsp_alg_region * 193814197095SCharles Keepax wm_adsp_find_alg_region(struct wm_adsp *dsp, int type, unsigned int id) 193914197095SCharles Keepax { 194014197095SCharles Keepax struct wm_adsp_alg_region *alg_region; 194114197095SCharles Keepax 194214197095SCharles Keepax list_for_each_entry(alg_region, &dsp->alg_regions, list) { 194314197095SCharles Keepax if (id == alg_region->alg && type == alg_region->type) 194414197095SCharles Keepax return alg_region; 194514197095SCharles Keepax } 194614197095SCharles Keepax 194714197095SCharles Keepax return NULL; 194814197095SCharles Keepax } 194914197095SCharles Keepax 1950d9d20e17SCharles Keepax static struct wm_adsp_alg_region *wm_adsp_create_region(struct wm_adsp *dsp, 1951d9d20e17SCharles Keepax int type, __be32 id, 1952d9d20e17SCharles Keepax __be32 base) 1953d9d20e17SCharles Keepax { 1954d9d20e17SCharles Keepax struct wm_adsp_alg_region *alg_region; 1955d9d20e17SCharles Keepax 1956d9d20e17SCharles Keepax alg_region = kzalloc(sizeof(*alg_region), GFP_KERNEL); 1957d9d20e17SCharles Keepax if (!alg_region) 1958d9d20e17SCharles Keepax return ERR_PTR(-ENOMEM); 1959d9d20e17SCharles Keepax 1960d9d20e17SCharles Keepax alg_region->type = type; 1961d9d20e17SCharles Keepax alg_region->alg = be32_to_cpu(id); 1962d9d20e17SCharles Keepax alg_region->base = be32_to_cpu(base); 1963d9d20e17SCharles Keepax 1964d9d20e17SCharles Keepax list_add_tail(&alg_region->list, &dsp->alg_regions); 1965d9d20e17SCharles Keepax 19662323736dSCharles Keepax if (dsp->fw_ver > 0) 19672323736dSCharles Keepax wm_adsp_ctl_fixup_base(dsp, alg_region); 19682323736dSCharles Keepax 1969d9d20e17SCharles Keepax return alg_region; 1970d9d20e17SCharles Keepax } 1971d9d20e17SCharles Keepax 197256574d54SRichard Fitzgerald static void wm_adsp_free_alg_regions(struct wm_adsp *dsp) 197356574d54SRichard Fitzgerald { 197456574d54SRichard Fitzgerald struct wm_adsp_alg_region *alg_region; 197556574d54SRichard Fitzgerald 197656574d54SRichard Fitzgerald while (!list_empty(&dsp->alg_regions)) { 197756574d54SRichard Fitzgerald alg_region = list_first_entry(&dsp->alg_regions, 197856574d54SRichard Fitzgerald struct wm_adsp_alg_region, 197956574d54SRichard Fitzgerald list); 198056574d54SRichard Fitzgerald list_del(&alg_region->list); 198156574d54SRichard Fitzgerald kfree(alg_region); 198256574d54SRichard Fitzgerald } 198356574d54SRichard Fitzgerald } 198456574d54SRichard Fitzgerald 1985a5dcb24dSCharles Keepax static void wmfw_parse_id_header(struct wm_adsp *dsp, 1986a5dcb24dSCharles Keepax struct wmfw_id_hdr *fw, int nalgs) 1987a5dcb24dSCharles Keepax { 1988a5dcb24dSCharles Keepax dsp->fw_id = be32_to_cpu(fw->id); 1989a5dcb24dSCharles Keepax dsp->fw_id_version = be32_to_cpu(fw->ver); 1990a5dcb24dSCharles Keepax 1991a5dcb24dSCharles Keepax adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n", 1992a5dcb24dSCharles Keepax dsp->fw_id, (dsp->fw_id_version & 0xff0000) >> 16, 1993a5dcb24dSCharles Keepax (dsp->fw_id_version & 0xff00) >> 8, dsp->fw_id_version & 0xff, 1994a5dcb24dSCharles Keepax nalgs); 1995a5dcb24dSCharles Keepax } 1996a5dcb24dSCharles Keepax 1997b618a185SCharles Keepax static int wm_adsp1_setup_algs(struct wm_adsp *dsp) 1998b618a185SCharles Keepax { 1999b618a185SCharles Keepax struct wmfw_adsp1_id_hdr adsp1_id; 2000b618a185SCharles Keepax struct wmfw_adsp1_alg_hdr *adsp1_alg; 20013809f001SCharles Keepax struct wm_adsp_alg_region *alg_region; 2002b618a185SCharles Keepax const struct wm_adsp_region *mem; 2003b618a185SCharles Keepax unsigned int pos, len; 20043809f001SCharles Keepax size_t n_algs; 2005b618a185SCharles Keepax int i, ret; 2006b618a185SCharles Keepax 2007b618a185SCharles Keepax mem = wm_adsp_find_region(dsp, WMFW_ADSP1_DM); 20086c452bdaSTakashi Iwai if (WARN_ON(!mem)) 2009db40517cSMark Brown return -EINVAL; 2010db40517cSMark Brown 2011b618a185SCharles Keepax ret = regmap_raw_read(dsp->regmap, mem->base, &adsp1_id, 2012db40517cSMark Brown sizeof(adsp1_id)); 2013db40517cSMark Brown if (ret != 0) { 2014db40517cSMark Brown adsp_err(dsp, "Failed to read algorithm info: %d\n", 2015db40517cSMark Brown ret); 2016db40517cSMark Brown return ret; 2017db40517cSMark Brown } 2018db40517cSMark Brown 20193809f001SCharles Keepax n_algs = be32_to_cpu(adsp1_id.n_algs); 2020a5dcb24dSCharles Keepax 2021a5dcb24dSCharles Keepax wmfw_parse_id_header(dsp, &adsp1_id.fw, n_algs); 2022db40517cSMark Brown 2023d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_ZM, 2024d9d20e17SCharles Keepax adsp1_id.fw.id, adsp1_id.zm); 2025d9d20e17SCharles Keepax if (IS_ERR(alg_region)) 2026d9d20e17SCharles Keepax return PTR_ERR(alg_region); 2027ac50009fSMark Brown 2028d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_DM, 2029d9d20e17SCharles Keepax adsp1_id.fw.id, adsp1_id.dm); 2030d9d20e17SCharles Keepax if (IS_ERR(alg_region)) 2031d9d20e17SCharles Keepax return PTR_ERR(alg_region); 2032ac50009fSMark Brown 20337f7cca08SCharles Keepax /* Calculate offset and length in DSP words */ 20347f7cca08SCharles Keepax pos = sizeof(adsp1_id) / sizeof(u32); 20357f7cca08SCharles Keepax len = (sizeof(*adsp1_alg) * n_algs) / sizeof(u32); 2036db40517cSMark Brown 20377f7cca08SCharles Keepax adsp1_alg = wm_adsp_read_algs(dsp, n_algs, mem, pos, len); 2038b618a185SCharles Keepax if (IS_ERR(adsp1_alg)) 2039b618a185SCharles Keepax return PTR_ERR(adsp1_alg); 2040db40517cSMark Brown 20413809f001SCharles Keepax for (i = 0; i < n_algs; i++) { 2042471f4885SMark Brown adsp_info(dsp, "%d: ID %x v%d.%d.%d DM@%x ZM@%x\n", 2043db40517cSMark Brown i, be32_to_cpu(adsp1_alg[i].alg.id), 2044db40517cSMark Brown (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff0000) >> 16, 2045db40517cSMark Brown (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff00) >> 8, 2046471f4885SMark Brown be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff, 2047471f4885SMark Brown be32_to_cpu(adsp1_alg[i].dm), 2048471f4885SMark Brown be32_to_cpu(adsp1_alg[i].zm)); 2049471f4885SMark Brown 2050d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_DM, 2051d9d20e17SCharles Keepax adsp1_alg[i].alg.id, 2052d9d20e17SCharles Keepax adsp1_alg[i].dm); 2053d9d20e17SCharles Keepax if (IS_ERR(alg_region)) { 2054d9d20e17SCharles Keepax ret = PTR_ERR(alg_region); 2055d6d52179SJS Park goto out; 2056d6d52179SJS Park } 20572323736dSCharles Keepax if (dsp->fw_ver == 0) { 20583809f001SCharles Keepax if (i + 1 < n_algs) { 20596958eb2aSCharles Keepax len = be32_to_cpu(adsp1_alg[i + 1].dm); 20606958eb2aSCharles Keepax len -= be32_to_cpu(adsp1_alg[i].dm); 20616958eb2aSCharles Keepax len *= 4; 20622323736dSCharles Keepax wm_adsp_create_control(dsp, alg_region, 0, 20638eb084d0SStuart Henderson len, NULL, 0, 0, 20648eb084d0SStuart Henderson SNDRV_CTL_ELEM_TYPE_BYTES); 20656ab2b7b4SDimitris Papastamos } else { 20666ab2b7b4SDimitris Papastamos adsp_warn(dsp, "Missing length info for region DM with ID %x\n", 20676ab2b7b4SDimitris Papastamos be32_to_cpu(adsp1_alg[i].alg.id)); 20686ab2b7b4SDimitris Papastamos } 20692323736dSCharles Keepax } 2070471f4885SMark Brown 2071d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_ZM, 2072d9d20e17SCharles Keepax adsp1_alg[i].alg.id, 2073d9d20e17SCharles Keepax adsp1_alg[i].zm); 2074d9d20e17SCharles Keepax if (IS_ERR(alg_region)) { 2075d9d20e17SCharles Keepax ret = PTR_ERR(alg_region); 2076d6d52179SJS Park goto out; 2077d6d52179SJS Park } 20782323736dSCharles Keepax if (dsp->fw_ver == 0) { 20793809f001SCharles Keepax if (i + 1 < n_algs) { 20806958eb2aSCharles Keepax len = be32_to_cpu(adsp1_alg[i + 1].zm); 20816958eb2aSCharles Keepax len -= be32_to_cpu(adsp1_alg[i].zm); 20826958eb2aSCharles Keepax len *= 4; 20832323736dSCharles Keepax wm_adsp_create_control(dsp, alg_region, 0, 20848eb084d0SStuart Henderson len, NULL, 0, 0, 20858eb084d0SStuart Henderson SNDRV_CTL_ELEM_TYPE_BYTES); 20866ab2b7b4SDimitris Papastamos } else { 20876ab2b7b4SDimitris Papastamos adsp_warn(dsp, "Missing length info for region ZM with ID %x\n", 20886ab2b7b4SDimitris Papastamos be32_to_cpu(adsp1_alg[i].alg.id)); 20896ab2b7b4SDimitris Papastamos } 2090b618a185SCharles Keepax } 20912323736dSCharles Keepax } 2092db40517cSMark Brown 2093b618a185SCharles Keepax out: 2094b618a185SCharles Keepax kfree(adsp1_alg); 2095b618a185SCharles Keepax return ret; 2096b618a185SCharles Keepax } 2097b618a185SCharles Keepax 2098b618a185SCharles Keepax static int wm_adsp2_setup_algs(struct wm_adsp *dsp) 2099b618a185SCharles Keepax { 2100b618a185SCharles Keepax struct wmfw_adsp2_id_hdr adsp2_id; 2101b618a185SCharles Keepax struct wmfw_adsp2_alg_hdr *adsp2_alg; 21023809f001SCharles Keepax struct wm_adsp_alg_region *alg_region; 2103b618a185SCharles Keepax const struct wm_adsp_region *mem; 2104b618a185SCharles Keepax unsigned int pos, len; 21053809f001SCharles Keepax size_t n_algs; 2106b618a185SCharles Keepax int i, ret; 2107b618a185SCharles Keepax 2108b618a185SCharles Keepax mem = wm_adsp_find_region(dsp, WMFW_ADSP2_XM); 2109b618a185SCharles Keepax if (WARN_ON(!mem)) 2110b618a185SCharles Keepax return -EINVAL; 2111b618a185SCharles Keepax 2112b618a185SCharles Keepax ret = regmap_raw_read(dsp->regmap, mem->base, &adsp2_id, 2113b618a185SCharles Keepax sizeof(adsp2_id)); 2114b618a185SCharles Keepax if (ret != 0) { 2115b618a185SCharles Keepax adsp_err(dsp, "Failed to read algorithm info: %d\n", 2116b618a185SCharles Keepax ret); 2117b618a185SCharles Keepax return ret; 2118b618a185SCharles Keepax } 2119b618a185SCharles Keepax 21203809f001SCharles Keepax n_algs = be32_to_cpu(adsp2_id.n_algs); 2121a5dcb24dSCharles Keepax 2122a5dcb24dSCharles Keepax wmfw_parse_id_header(dsp, &adsp2_id.fw, n_algs); 2123b618a185SCharles Keepax 2124d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_XM, 2125d9d20e17SCharles Keepax adsp2_id.fw.id, adsp2_id.xm); 2126d9d20e17SCharles Keepax if (IS_ERR(alg_region)) 2127d9d20e17SCharles Keepax return PTR_ERR(alg_region); 2128b618a185SCharles Keepax 2129d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_YM, 2130d9d20e17SCharles Keepax adsp2_id.fw.id, adsp2_id.ym); 2131d9d20e17SCharles Keepax if (IS_ERR(alg_region)) 2132d9d20e17SCharles Keepax return PTR_ERR(alg_region); 2133b618a185SCharles Keepax 2134d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_ZM, 2135d9d20e17SCharles Keepax adsp2_id.fw.id, adsp2_id.zm); 2136d9d20e17SCharles Keepax if (IS_ERR(alg_region)) 2137d9d20e17SCharles Keepax return PTR_ERR(alg_region); 2138b618a185SCharles Keepax 21397f7cca08SCharles Keepax /* Calculate offset and length in DSP words */ 21407f7cca08SCharles Keepax pos = sizeof(adsp2_id) / sizeof(u32); 21417f7cca08SCharles Keepax len = (sizeof(*adsp2_alg) * n_algs) / sizeof(u32); 2142b618a185SCharles Keepax 21437f7cca08SCharles Keepax adsp2_alg = wm_adsp_read_algs(dsp, n_algs, mem, pos, len); 2144b618a185SCharles Keepax if (IS_ERR(adsp2_alg)) 2145b618a185SCharles Keepax return PTR_ERR(adsp2_alg); 2146b618a185SCharles Keepax 21473809f001SCharles Keepax for (i = 0; i < n_algs; i++) { 2148471f4885SMark Brown adsp_info(dsp, 2149471f4885SMark Brown "%d: ID %x v%d.%d.%d XM@%x YM@%x ZM@%x\n", 2150db40517cSMark Brown i, be32_to_cpu(adsp2_alg[i].alg.id), 2151db40517cSMark Brown (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff0000) >> 16, 2152db40517cSMark Brown (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff00) >> 8, 2153471f4885SMark Brown be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff, 2154471f4885SMark Brown be32_to_cpu(adsp2_alg[i].xm), 2155471f4885SMark Brown be32_to_cpu(adsp2_alg[i].ym), 2156471f4885SMark Brown be32_to_cpu(adsp2_alg[i].zm)); 2157471f4885SMark Brown 2158d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_XM, 2159d9d20e17SCharles Keepax adsp2_alg[i].alg.id, 2160d9d20e17SCharles Keepax adsp2_alg[i].xm); 2161d9d20e17SCharles Keepax if (IS_ERR(alg_region)) { 2162d9d20e17SCharles Keepax ret = PTR_ERR(alg_region); 2163d6d52179SJS Park goto out; 2164d6d52179SJS Park } 21652323736dSCharles Keepax if (dsp->fw_ver == 0) { 21663809f001SCharles Keepax if (i + 1 < n_algs) { 21676958eb2aSCharles Keepax len = be32_to_cpu(adsp2_alg[i + 1].xm); 21686958eb2aSCharles Keepax len -= be32_to_cpu(adsp2_alg[i].xm); 21696958eb2aSCharles Keepax len *= 4; 21702323736dSCharles Keepax wm_adsp_create_control(dsp, alg_region, 0, 21718eb084d0SStuart Henderson len, NULL, 0, 0, 21728eb084d0SStuart Henderson SNDRV_CTL_ELEM_TYPE_BYTES); 21736ab2b7b4SDimitris Papastamos } else { 21746ab2b7b4SDimitris Papastamos adsp_warn(dsp, "Missing length info for region XM with ID %x\n", 21756ab2b7b4SDimitris Papastamos be32_to_cpu(adsp2_alg[i].alg.id)); 21766ab2b7b4SDimitris Papastamos } 21772323736dSCharles Keepax } 2178471f4885SMark Brown 2179d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_YM, 2180d9d20e17SCharles Keepax adsp2_alg[i].alg.id, 2181d9d20e17SCharles Keepax adsp2_alg[i].ym); 2182d9d20e17SCharles Keepax if (IS_ERR(alg_region)) { 2183d9d20e17SCharles Keepax ret = PTR_ERR(alg_region); 2184d6d52179SJS Park goto out; 2185d6d52179SJS Park } 21862323736dSCharles Keepax if (dsp->fw_ver == 0) { 21873809f001SCharles Keepax if (i + 1 < n_algs) { 21886958eb2aSCharles Keepax len = be32_to_cpu(adsp2_alg[i + 1].ym); 21896958eb2aSCharles Keepax len -= be32_to_cpu(adsp2_alg[i].ym); 21906958eb2aSCharles Keepax len *= 4; 21912323736dSCharles Keepax wm_adsp_create_control(dsp, alg_region, 0, 21928eb084d0SStuart Henderson len, NULL, 0, 0, 21938eb084d0SStuart Henderson SNDRV_CTL_ELEM_TYPE_BYTES); 21946ab2b7b4SDimitris Papastamos } else { 21956ab2b7b4SDimitris Papastamos adsp_warn(dsp, "Missing length info for region YM with ID %x\n", 21966ab2b7b4SDimitris Papastamos be32_to_cpu(adsp2_alg[i].alg.id)); 21976ab2b7b4SDimitris Papastamos } 21982323736dSCharles Keepax } 2199471f4885SMark Brown 2200d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_ZM, 2201d9d20e17SCharles Keepax adsp2_alg[i].alg.id, 2202d9d20e17SCharles Keepax adsp2_alg[i].zm); 2203d9d20e17SCharles Keepax if (IS_ERR(alg_region)) { 2204d9d20e17SCharles Keepax ret = PTR_ERR(alg_region); 2205d6d52179SJS Park goto out; 2206d6d52179SJS Park } 22072323736dSCharles Keepax if (dsp->fw_ver == 0) { 22083809f001SCharles Keepax if (i + 1 < n_algs) { 22096958eb2aSCharles Keepax len = be32_to_cpu(adsp2_alg[i + 1].zm); 22106958eb2aSCharles Keepax len -= be32_to_cpu(adsp2_alg[i].zm); 22116958eb2aSCharles Keepax len *= 4; 22122323736dSCharles Keepax wm_adsp_create_control(dsp, alg_region, 0, 22138eb084d0SStuart Henderson len, NULL, 0, 0, 22148eb084d0SStuart Henderson SNDRV_CTL_ELEM_TYPE_BYTES); 22156ab2b7b4SDimitris Papastamos } else { 22166ab2b7b4SDimitris Papastamos adsp_warn(dsp, "Missing length info for region ZM with ID %x\n", 22176ab2b7b4SDimitris Papastamos be32_to_cpu(adsp2_alg[i].alg.id)); 22186ab2b7b4SDimitris Papastamos } 2219db40517cSMark Brown } 22202323736dSCharles Keepax } 2221db40517cSMark Brown 2222db40517cSMark Brown out: 2223b618a185SCharles Keepax kfree(adsp2_alg); 2224db40517cSMark Brown return ret; 2225db40517cSMark Brown } 2226db40517cSMark Brown 22272159ad93SMark Brown static int wm_adsp_load_coeff(struct wm_adsp *dsp) 22282159ad93SMark Brown { 2229cf17c83cSMark Brown LIST_HEAD(buf_list); 22302159ad93SMark Brown struct regmap *regmap = dsp->regmap; 22312159ad93SMark Brown struct wmfw_coeff_hdr *hdr; 22322159ad93SMark Brown struct wmfw_coeff_item *blk; 22332159ad93SMark Brown const struct firmware *firmware; 2234471f4885SMark Brown const struct wm_adsp_region *mem; 2235471f4885SMark Brown struct wm_adsp_alg_region *alg_region; 22362159ad93SMark Brown const char *region_name; 22372159ad93SMark Brown int ret, pos, blocks, type, offset, reg; 22382159ad93SMark Brown char *file; 2239cf17c83cSMark Brown struct wm_adsp_buf *buf; 22402159ad93SMark Brown 22412159ad93SMark Brown file = kzalloc(PAGE_SIZE, GFP_KERNEL); 22422159ad93SMark Brown if (file == NULL) 22432159ad93SMark Brown return -ENOMEM; 22442159ad93SMark Brown 2245605391d0SRichard Fitzgerald snprintf(file, PAGE_SIZE, "%s-%s-%s.bin", dsp->part, dsp->fwf_name, 22461023dbd9SMark Brown wm_adsp_fw[dsp->fw].file); 22472159ad93SMark Brown file[PAGE_SIZE - 1] = '\0'; 22482159ad93SMark Brown 22492159ad93SMark Brown ret = request_firmware(&firmware, file, dsp->dev); 22502159ad93SMark Brown if (ret != 0) { 22512159ad93SMark Brown adsp_warn(dsp, "Failed to request '%s'\n", file); 22522159ad93SMark Brown ret = 0; 22532159ad93SMark Brown goto out; 22542159ad93SMark Brown } 22552159ad93SMark Brown ret = -EINVAL; 22562159ad93SMark Brown 22572159ad93SMark Brown if (sizeof(*hdr) >= firmware->size) { 22582159ad93SMark Brown adsp_err(dsp, "%s: file too short, %zu bytes\n", 22592159ad93SMark Brown file, firmware->size); 22602159ad93SMark Brown goto out_fw; 22612159ad93SMark Brown } 22622159ad93SMark Brown 22632159ad93SMark Brown hdr = (void *)&firmware->data[0]; 22642159ad93SMark Brown if (memcmp(hdr->magic, "WMDR", 4) != 0) { 22652159ad93SMark Brown adsp_err(dsp, "%s: invalid magic\n", file); 2266a4cdbec7SCharles Keepax goto out_fw; 22672159ad93SMark Brown } 22682159ad93SMark Brown 2269c712326dSMark Brown switch (be32_to_cpu(hdr->rev) & 0xff) { 2270c712326dSMark Brown case 1: 2271c712326dSMark Brown break; 2272c712326dSMark Brown default: 2273c712326dSMark Brown adsp_err(dsp, "%s: Unsupported coefficient file format %d\n", 2274c712326dSMark Brown file, be32_to_cpu(hdr->rev) & 0xff); 2275c712326dSMark Brown ret = -EINVAL; 2276c712326dSMark Brown goto out_fw; 2277c712326dSMark Brown } 2278c712326dSMark Brown 22792159ad93SMark Brown adsp_dbg(dsp, "%s: v%d.%d.%d\n", file, 22802159ad93SMark Brown (le32_to_cpu(hdr->ver) >> 16) & 0xff, 22812159ad93SMark Brown (le32_to_cpu(hdr->ver) >> 8) & 0xff, 22822159ad93SMark Brown le32_to_cpu(hdr->ver) & 0xff); 22832159ad93SMark Brown 22842159ad93SMark Brown pos = le32_to_cpu(hdr->len); 22852159ad93SMark Brown 22862159ad93SMark Brown blocks = 0; 22872159ad93SMark Brown while (pos < firmware->size && 228850dd2ea8SBen Hutchings sizeof(*blk) < firmware->size - pos) { 22892159ad93SMark Brown blk = (void *)(&firmware->data[pos]); 22902159ad93SMark Brown 2291c712326dSMark Brown type = le16_to_cpu(blk->type); 2292c712326dSMark Brown offset = le16_to_cpu(blk->offset); 22932159ad93SMark Brown 22942159ad93SMark Brown adsp_dbg(dsp, "%s.%d: %x v%d.%d.%d\n", 22952159ad93SMark Brown file, blocks, le32_to_cpu(blk->id), 22962159ad93SMark Brown (le32_to_cpu(blk->ver) >> 16) & 0xff, 22972159ad93SMark Brown (le32_to_cpu(blk->ver) >> 8) & 0xff, 22982159ad93SMark Brown le32_to_cpu(blk->ver) & 0xff); 22992159ad93SMark Brown adsp_dbg(dsp, "%s.%d: %d bytes at 0x%x in %x\n", 23002159ad93SMark Brown file, blocks, le32_to_cpu(blk->len), offset, type); 23012159ad93SMark Brown 23022159ad93SMark Brown reg = 0; 23032159ad93SMark Brown region_name = "Unknown"; 23042159ad93SMark Brown switch (type) { 2305c712326dSMark Brown case (WMFW_NAME_TEXT << 8): 2306c712326dSMark Brown case (WMFW_INFO_TEXT << 8): 23072159ad93SMark Brown break; 2308c712326dSMark Brown case (WMFW_ABSOLUTE << 8): 2309f395a218SMark Brown /* 2310f395a218SMark Brown * Old files may use this for global 2311f395a218SMark Brown * coefficients. 2312f395a218SMark Brown */ 2313f395a218SMark Brown if (le32_to_cpu(blk->id) == dsp->fw_id && 2314f395a218SMark Brown offset == 0) { 2315f395a218SMark Brown region_name = "global coefficients"; 2316f395a218SMark Brown mem = wm_adsp_find_region(dsp, type); 2317f395a218SMark Brown if (!mem) { 2318f395a218SMark Brown adsp_err(dsp, "No ZM\n"); 2319f395a218SMark Brown break; 2320f395a218SMark Brown } 2321f395a218SMark Brown reg = wm_adsp_region_to_reg(mem, 0); 2322f395a218SMark Brown 2323f395a218SMark Brown } else { 23242159ad93SMark Brown region_name = "register"; 23252159ad93SMark Brown reg = offset; 2326f395a218SMark Brown } 23272159ad93SMark Brown break; 2328471f4885SMark Brown 2329471f4885SMark Brown case WMFW_ADSP1_DM: 2330471f4885SMark Brown case WMFW_ADSP1_ZM: 2331471f4885SMark Brown case WMFW_ADSP2_XM: 2332471f4885SMark Brown case WMFW_ADSP2_YM: 2333471f4885SMark Brown adsp_dbg(dsp, "%s.%d: %d bytes in %x for %x\n", 2334471f4885SMark Brown file, blocks, le32_to_cpu(blk->len), 2335471f4885SMark Brown type, le32_to_cpu(blk->id)); 2336471f4885SMark Brown 2337471f4885SMark Brown mem = wm_adsp_find_region(dsp, type); 2338471f4885SMark Brown if (!mem) { 2339471f4885SMark Brown adsp_err(dsp, "No base for region %x\n", type); 2340471f4885SMark Brown break; 2341471f4885SMark Brown } 2342471f4885SMark Brown 234314197095SCharles Keepax alg_region = wm_adsp_find_alg_region(dsp, type, 234414197095SCharles Keepax le32_to_cpu(blk->id)); 234514197095SCharles Keepax if (alg_region) { 2346338c5188SMark Brown reg = alg_region->base; 234714197095SCharles Keepax reg = wm_adsp_region_to_reg(mem, reg); 2348338c5188SMark Brown reg += offset; 234914197095SCharles Keepax } else { 2350471f4885SMark Brown adsp_err(dsp, "No %x for algorithm %x\n", 2351471f4885SMark Brown type, le32_to_cpu(blk->id)); 235214197095SCharles Keepax } 2353471f4885SMark Brown break; 2354471f4885SMark Brown 23552159ad93SMark Brown default: 235625c62f7eSMark Brown adsp_err(dsp, "%s.%d: Unknown region type %x at %d\n", 235725c62f7eSMark Brown file, blocks, type, pos); 23582159ad93SMark Brown break; 23592159ad93SMark Brown } 23602159ad93SMark Brown 23612159ad93SMark Brown if (reg) { 236250dd2ea8SBen Hutchings if (le32_to_cpu(blk->len) > 236350dd2ea8SBen Hutchings firmware->size - pos - sizeof(*blk)) { 23641cab2a84SRichard Fitzgerald adsp_err(dsp, 23651cab2a84SRichard Fitzgerald "%s.%d: %s region len %d bytes exceeds file length %zu\n", 23661cab2a84SRichard Fitzgerald file, blocks, region_name, 23671cab2a84SRichard Fitzgerald le32_to_cpu(blk->len), 23681cab2a84SRichard Fitzgerald firmware->size); 23691cab2a84SRichard Fitzgerald ret = -EINVAL; 23701cab2a84SRichard Fitzgerald goto out_fw; 23711cab2a84SRichard Fitzgerald } 23721cab2a84SRichard Fitzgerald 2373cf17c83cSMark Brown buf = wm_adsp_buf_alloc(blk->data, 2374cf17c83cSMark Brown le32_to_cpu(blk->len), 2375cf17c83cSMark Brown &buf_list); 2376a76fefabSMark Brown if (!buf) { 2377a76fefabSMark Brown adsp_err(dsp, "Out of memory\n"); 2378f4b82812SWei Yongjun ret = -ENOMEM; 2379f4b82812SWei Yongjun goto out_fw; 2380a76fefabSMark Brown } 2381a76fefabSMark Brown 238220da6d5aSMark Brown adsp_dbg(dsp, "%s.%d: Writing %d bytes at %x\n", 238320da6d5aSMark Brown file, blocks, le32_to_cpu(blk->len), 238420da6d5aSMark Brown reg); 2385cf17c83cSMark Brown ret = regmap_raw_write_async(regmap, reg, buf->buf, 23862159ad93SMark Brown le32_to_cpu(blk->len)); 23872159ad93SMark Brown if (ret != 0) { 23882159ad93SMark Brown adsp_err(dsp, 238943bc3bf6SDimitris Papastamos "%s.%d: Failed to write to %x in %s: %d\n", 239043bc3bf6SDimitris Papastamos file, blocks, reg, region_name, ret); 23912159ad93SMark Brown } 23922159ad93SMark Brown } 23932159ad93SMark Brown 2394be951017SCharles Keepax pos += (le32_to_cpu(blk->len) + sizeof(*blk) + 3) & ~0x03; 23952159ad93SMark Brown blocks++; 23962159ad93SMark Brown } 23972159ad93SMark Brown 2398cf17c83cSMark Brown ret = regmap_async_complete(regmap); 2399cf17c83cSMark Brown if (ret != 0) 2400cf17c83cSMark Brown adsp_err(dsp, "Failed to complete async write: %d\n", ret); 2401cf17c83cSMark Brown 24022159ad93SMark Brown if (pos > firmware->size) 24032159ad93SMark Brown adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n", 24042159ad93SMark Brown file, blocks, pos - firmware->size); 24052159ad93SMark Brown 2406f9f55e31SRichard Fitzgerald wm_adsp_debugfs_save_binname(dsp, file); 2407f9f55e31SRichard Fitzgerald 24082159ad93SMark Brown out_fw: 24099da7a5a9SCharles Keepax regmap_async_complete(regmap); 24102159ad93SMark Brown release_firmware(firmware); 2411cf17c83cSMark Brown wm_adsp_buf_free(&buf_list); 24122159ad93SMark Brown out: 24132159ad93SMark Brown kfree(file); 2414f4b82812SWei Yongjun return ret; 24152159ad93SMark Brown } 24162159ad93SMark Brown 2417605391d0SRichard Fitzgerald static int wm_adsp_create_name(struct wm_adsp *dsp) 2418605391d0SRichard Fitzgerald { 2419605391d0SRichard Fitzgerald char *p; 2420605391d0SRichard Fitzgerald 2421605391d0SRichard Fitzgerald if (!dsp->name) { 2422605391d0SRichard Fitzgerald dsp->name = devm_kasprintf(dsp->dev, GFP_KERNEL, "DSP%d", 2423605391d0SRichard Fitzgerald dsp->num); 2424605391d0SRichard Fitzgerald if (!dsp->name) 2425605391d0SRichard Fitzgerald return -ENOMEM; 2426605391d0SRichard Fitzgerald } 2427605391d0SRichard Fitzgerald 2428605391d0SRichard Fitzgerald if (!dsp->fwf_name) { 2429605391d0SRichard Fitzgerald p = devm_kstrdup(dsp->dev, dsp->name, GFP_KERNEL); 2430605391d0SRichard Fitzgerald if (!p) 2431605391d0SRichard Fitzgerald return -ENOMEM; 2432605391d0SRichard Fitzgerald 2433605391d0SRichard Fitzgerald dsp->fwf_name = p; 2434605391d0SRichard Fitzgerald for (; *p != 0; ++p) 2435605391d0SRichard Fitzgerald *p = tolower(*p); 2436605391d0SRichard Fitzgerald } 2437605391d0SRichard Fitzgerald 2438605391d0SRichard Fitzgerald return 0; 2439605391d0SRichard Fitzgerald } 2440605391d0SRichard Fitzgerald 2441dcad34f8SRichard Fitzgerald static int wm_adsp_common_init(struct wm_adsp *dsp) 24425e7a7a22SMark Brown { 2443605391d0SRichard Fitzgerald int ret; 2444605391d0SRichard Fitzgerald 2445605391d0SRichard Fitzgerald ret = wm_adsp_create_name(dsp); 2446605391d0SRichard Fitzgerald if (ret) 2447605391d0SRichard Fitzgerald return ret; 2448605391d0SRichard Fitzgerald 24493809f001SCharles Keepax INIT_LIST_HEAD(&dsp->alg_regions); 2450dcad34f8SRichard Fitzgerald INIT_LIST_HEAD(&dsp->ctl_list); 24514f2d4eabSStuart Henderson INIT_LIST_HEAD(&dsp->compr_list); 24524f2d4eabSStuart Henderson INIT_LIST_HEAD(&dsp->buffer_list); 24535e7a7a22SMark Brown 2454078e7183SCharles Keepax mutex_init(&dsp->pwr_lock); 2455078e7183SCharles Keepax 24565e7a7a22SMark Brown return 0; 24575e7a7a22SMark Brown } 2458dcad34f8SRichard Fitzgerald 2459dcad34f8SRichard Fitzgerald int wm_adsp1_init(struct wm_adsp *dsp) 2460dcad34f8SRichard Fitzgerald { 2461dcad34f8SRichard Fitzgerald return wm_adsp_common_init(dsp); 2462dcad34f8SRichard Fitzgerald } 24635e7a7a22SMark Brown EXPORT_SYMBOL_GPL(wm_adsp1_init); 24645e7a7a22SMark Brown 24652159ad93SMark Brown int wm_adsp1_event(struct snd_soc_dapm_widget *w, 24662159ad93SMark Brown struct snd_kcontrol *kcontrol, 24672159ad93SMark Brown int event) 24682159ad93SMark Brown { 24690fe1daa6SKuninori Morimoto struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); 24700fe1daa6SKuninori Morimoto struct wm_adsp *dsps = snd_soc_component_get_drvdata(component); 24712159ad93SMark Brown struct wm_adsp *dsp = &dsps[w->shift]; 24726ab2b7b4SDimitris Papastamos struct wm_coeff_ctl *ctl; 24732159ad93SMark Brown int ret; 24747585a5b0SCharles Keepax unsigned int val; 24752159ad93SMark Brown 24760fe1daa6SKuninori Morimoto dsp->component = component; 247792bb4c32SDimitris Papastamos 2478078e7183SCharles Keepax mutex_lock(&dsp->pwr_lock); 2479078e7183SCharles Keepax 24802159ad93SMark Brown switch (event) { 24812159ad93SMark Brown case SND_SOC_DAPM_POST_PMU: 24822159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 24832159ad93SMark Brown ADSP1_SYS_ENA, ADSP1_SYS_ENA); 24842159ad93SMark Brown 248594e205bfSChris Rattray /* 248694e205bfSChris Rattray * For simplicity set the DSP clock rate to be the 248794e205bfSChris Rattray * SYSCLK rate rather than making it configurable. 248894e205bfSChris Rattray */ 248994e205bfSChris Rattray if (dsp->sysclk_reg) { 249094e205bfSChris Rattray ret = regmap_read(dsp->regmap, dsp->sysclk_reg, &val); 249194e205bfSChris Rattray if (ret != 0) { 249294e205bfSChris Rattray adsp_err(dsp, "Failed to read SYSCLK state: %d\n", 249394e205bfSChris Rattray ret); 2494078e7183SCharles Keepax goto err_mutex; 249594e205bfSChris Rattray } 249694e205bfSChris Rattray 24977d00cd97SCharles Keepax val = (val & dsp->sysclk_mask) >> dsp->sysclk_shift; 249894e205bfSChris Rattray 249994e205bfSChris Rattray ret = regmap_update_bits(dsp->regmap, 250094e205bfSChris Rattray dsp->base + ADSP1_CONTROL_31, 250194e205bfSChris Rattray ADSP1_CLK_SEL_MASK, val); 250294e205bfSChris Rattray if (ret != 0) { 250394e205bfSChris Rattray adsp_err(dsp, "Failed to set clock rate: %d\n", 250494e205bfSChris Rattray ret); 2505078e7183SCharles Keepax goto err_mutex; 250694e205bfSChris Rattray } 250794e205bfSChris Rattray } 250894e205bfSChris Rattray 25092159ad93SMark Brown ret = wm_adsp_load(dsp); 25102159ad93SMark Brown if (ret != 0) 2511078e7183SCharles Keepax goto err_ena; 25122159ad93SMark Brown 2513b618a185SCharles Keepax ret = wm_adsp1_setup_algs(dsp); 2514db40517cSMark Brown if (ret != 0) 2515078e7183SCharles Keepax goto err_ena; 2516db40517cSMark Brown 25172159ad93SMark Brown ret = wm_adsp_load_coeff(dsp); 25182159ad93SMark Brown if (ret != 0) 2519078e7183SCharles Keepax goto err_ena; 25202159ad93SMark Brown 25210c2e3f34SDimitris Papastamos /* Initialize caches for enabled and unset controls */ 252281ad93ecSDimitris Papastamos ret = wm_coeff_init_control_caches(dsp); 25236ab2b7b4SDimitris Papastamos if (ret != 0) 2524078e7183SCharles Keepax goto err_ena; 25256ab2b7b4SDimitris Papastamos 25260c2e3f34SDimitris Papastamos /* Sync set controls */ 252781ad93ecSDimitris Papastamos ret = wm_coeff_sync_controls(dsp); 25286ab2b7b4SDimitris Papastamos if (ret != 0) 2529078e7183SCharles Keepax goto err_ena; 25306ab2b7b4SDimitris Papastamos 253128823ebaSCharles Keepax dsp->booted = true; 253228823ebaSCharles Keepax 25332159ad93SMark Brown /* Start the core running */ 25342159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 25352159ad93SMark Brown ADSP1_CORE_ENA | ADSP1_START, 25362159ad93SMark Brown ADSP1_CORE_ENA | ADSP1_START); 253728823ebaSCharles Keepax 253828823ebaSCharles Keepax dsp->running = true; 25392159ad93SMark Brown break; 25402159ad93SMark Brown 25412159ad93SMark Brown case SND_SOC_DAPM_PRE_PMD: 254228823ebaSCharles Keepax dsp->running = false; 254328823ebaSCharles Keepax dsp->booted = false; 254428823ebaSCharles Keepax 25452159ad93SMark Brown /* Halt the core */ 25462159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 25472159ad93SMark Brown ADSP1_CORE_ENA | ADSP1_START, 0); 25482159ad93SMark Brown 25492159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_19, 25502159ad93SMark Brown ADSP1_WDMA_BUFFER_LENGTH_MASK, 0); 25512159ad93SMark Brown 25522159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 25532159ad93SMark Brown ADSP1_SYS_ENA, 0); 25546ab2b7b4SDimitris Papastamos 255581ad93ecSDimitris Papastamos list_for_each_entry(ctl, &dsp->ctl_list, list) 25566ab2b7b4SDimitris Papastamos ctl->enabled = 0; 2557b0101b4fSDimitris Papastamos 255856574d54SRichard Fitzgerald 255956574d54SRichard Fitzgerald wm_adsp_free_alg_regions(dsp); 25602159ad93SMark Brown break; 25612159ad93SMark Brown 25622159ad93SMark Brown default: 25632159ad93SMark Brown break; 25642159ad93SMark Brown } 25652159ad93SMark Brown 2566078e7183SCharles Keepax mutex_unlock(&dsp->pwr_lock); 2567078e7183SCharles Keepax 25682159ad93SMark Brown return 0; 25692159ad93SMark Brown 2570078e7183SCharles Keepax err_ena: 25712159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 25722159ad93SMark Brown ADSP1_SYS_ENA, 0); 2573078e7183SCharles Keepax err_mutex: 2574078e7183SCharles Keepax mutex_unlock(&dsp->pwr_lock); 2575078e7183SCharles Keepax 25762159ad93SMark Brown return ret; 25772159ad93SMark Brown } 25782159ad93SMark Brown EXPORT_SYMBOL_GPL(wm_adsp1_event); 25792159ad93SMark Brown 25802159ad93SMark Brown static int wm_adsp2_ena(struct wm_adsp *dsp) 25812159ad93SMark Brown { 25822159ad93SMark Brown unsigned int val; 25832159ad93SMark Brown int ret, count; 25842159ad93SMark Brown 2585e1ea1879SRichard Fitzgerald switch (dsp->rev) { 2586e1ea1879SRichard Fitzgerald case 0: 2587e1ea1879SRichard Fitzgerald ret = regmap_update_bits_async(dsp->regmap, 2588e1ea1879SRichard Fitzgerald dsp->base + ADSP2_CONTROL, 25892159ad93SMark Brown ADSP2_SYS_ENA, ADSP2_SYS_ENA); 25902159ad93SMark Brown if (ret != 0) 25912159ad93SMark Brown return ret; 2592e1ea1879SRichard Fitzgerald break; 2593e1ea1879SRichard Fitzgerald default: 2594e1ea1879SRichard Fitzgerald break; 2595e1ea1879SRichard Fitzgerald } 25962159ad93SMark Brown 25972159ad93SMark Brown /* Wait for the RAM to start, should be near instantaneous */ 2598939fd1e8SCharles Keepax for (count = 0; count < 10; ++count) { 25997d00cd97SCharles Keepax ret = regmap_read(dsp->regmap, dsp->base + ADSP2_STATUS1, &val); 26002159ad93SMark Brown if (ret != 0) 26012159ad93SMark Brown return ret; 2602939fd1e8SCharles Keepax 2603939fd1e8SCharles Keepax if (val & ADSP2_RAM_RDY) 2604939fd1e8SCharles Keepax break; 2605939fd1e8SCharles Keepax 26061fa96f3fSCharles Keepax usleep_range(250, 500); 2607939fd1e8SCharles Keepax } 26082159ad93SMark Brown 26092159ad93SMark Brown if (!(val & ADSP2_RAM_RDY)) { 26102159ad93SMark Brown adsp_err(dsp, "Failed to start DSP RAM\n"); 26112159ad93SMark Brown return -EBUSY; 26122159ad93SMark Brown } 26132159ad93SMark Brown 26142159ad93SMark Brown adsp_dbg(dsp, "RAM ready after %d polls\n", count); 26152159ad93SMark Brown 26162159ad93SMark Brown return 0; 26172159ad93SMark Brown } 26182159ad93SMark Brown 26192b0ee49fSCharles Keepax static int wm_adsp2_lock(struct wm_adsp *dsp, unsigned int lock_regions) 26202b0ee49fSCharles Keepax { 26212b0ee49fSCharles Keepax struct regmap *regmap = dsp->regmap; 26222b0ee49fSCharles Keepax unsigned int code0, code1, lock_reg; 26232b0ee49fSCharles Keepax 26242b0ee49fSCharles Keepax if (!(lock_regions & WM_ADSP2_REGION_ALL)) 26252b0ee49fSCharles Keepax return 0; 26262b0ee49fSCharles Keepax 26272b0ee49fSCharles Keepax lock_regions &= WM_ADSP2_REGION_ALL; 26282b0ee49fSCharles Keepax lock_reg = dsp->base + ADSP2_LOCK_REGION_1_LOCK_REGION_0; 26292b0ee49fSCharles Keepax 26302b0ee49fSCharles Keepax while (lock_regions) { 26312b0ee49fSCharles Keepax code0 = code1 = 0; 26322b0ee49fSCharles Keepax if (lock_regions & BIT(0)) { 26332b0ee49fSCharles Keepax code0 = ADSP2_LOCK_CODE_0; 26342b0ee49fSCharles Keepax code1 = ADSP2_LOCK_CODE_1; 26352b0ee49fSCharles Keepax } 26362b0ee49fSCharles Keepax if (lock_regions & BIT(1)) { 26372b0ee49fSCharles Keepax code0 |= ADSP2_LOCK_CODE_0 << ADSP2_LOCK_REGION_SHIFT; 26382b0ee49fSCharles Keepax code1 |= ADSP2_LOCK_CODE_1 << ADSP2_LOCK_REGION_SHIFT; 26392b0ee49fSCharles Keepax } 26402b0ee49fSCharles Keepax regmap_write(regmap, lock_reg, code0); 26412b0ee49fSCharles Keepax regmap_write(regmap, lock_reg, code1); 26422b0ee49fSCharles Keepax lock_regions >>= 2; 26432b0ee49fSCharles Keepax lock_reg += 2; 26442b0ee49fSCharles Keepax } 26452b0ee49fSCharles Keepax 26462b0ee49fSCharles Keepax return 0; 26472b0ee49fSCharles Keepax } 26482b0ee49fSCharles Keepax 264918b1a902SCharles Keepax static void wm_adsp2_boot_work(struct work_struct *work) 26502159ad93SMark Brown { 2651d8a64d6aSCharles Keepax struct wm_adsp *dsp = container_of(work, 2652d8a64d6aSCharles Keepax struct wm_adsp, 2653d8a64d6aSCharles Keepax boot_work); 26542159ad93SMark Brown int ret; 26552159ad93SMark Brown 2656078e7183SCharles Keepax mutex_lock(&dsp->pwr_lock); 2657078e7183SCharles Keepax 265890d19ba5SCharles Keepax ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 265990d19ba5SCharles Keepax ADSP2_MEM_ENA, ADSP2_MEM_ENA); 266090d19ba5SCharles Keepax if (ret != 0) 266190d19ba5SCharles Keepax goto err_mutex; 266290d19ba5SCharles Keepax 26632159ad93SMark Brown ret = wm_adsp2_ena(dsp); 26642159ad93SMark Brown if (ret != 0) 2665d589d8b8SCharles Keepax goto err_mem; 26662159ad93SMark Brown 26672159ad93SMark Brown ret = wm_adsp_load(dsp); 26682159ad93SMark Brown if (ret != 0) 2669078e7183SCharles Keepax goto err_ena; 26702159ad93SMark Brown 2671b618a185SCharles Keepax ret = wm_adsp2_setup_algs(dsp); 2672db40517cSMark Brown if (ret != 0) 2673078e7183SCharles Keepax goto err_ena; 2674db40517cSMark Brown 26752159ad93SMark Brown ret = wm_adsp_load_coeff(dsp); 26762159ad93SMark Brown if (ret != 0) 2677078e7183SCharles Keepax goto err_ena; 26782159ad93SMark Brown 26790c2e3f34SDimitris Papastamos /* Initialize caches for enabled and unset controls */ 268081ad93ecSDimitris Papastamos ret = wm_coeff_init_control_caches(dsp); 26816ab2b7b4SDimitris Papastamos if (ret != 0) 2682078e7183SCharles Keepax goto err_ena; 26836ab2b7b4SDimitris Papastamos 2684e1ea1879SRichard Fitzgerald switch (dsp->rev) { 2685e1ea1879SRichard Fitzgerald case 0: 268690d19ba5SCharles Keepax /* Turn DSP back off until we are ready to run */ 268790d19ba5SCharles Keepax ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 268890d19ba5SCharles Keepax ADSP2_SYS_ENA, 0); 268990d19ba5SCharles Keepax if (ret != 0) 269090d19ba5SCharles Keepax goto err_ena; 2691e1ea1879SRichard Fitzgerald break; 2692e1ea1879SRichard Fitzgerald default: 2693e1ea1879SRichard Fitzgerald break; 2694e1ea1879SRichard Fitzgerald } 269590d19ba5SCharles Keepax 2696e779974bSCharles Keepax dsp->booted = true; 2697e779974bSCharles Keepax 2698078e7183SCharles Keepax mutex_unlock(&dsp->pwr_lock); 2699078e7183SCharles Keepax 2700d8a64d6aSCharles Keepax return; 2701d8a64d6aSCharles Keepax 2702078e7183SCharles Keepax err_ena: 2703d8a64d6aSCharles Keepax regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 2704d8a64d6aSCharles Keepax ADSP2_SYS_ENA | ADSP2_CORE_ENA | ADSP2_START, 0); 2705d589d8b8SCharles Keepax err_mem: 2706d589d8b8SCharles Keepax regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 2707d589d8b8SCharles Keepax ADSP2_MEM_ENA, 0); 2708078e7183SCharles Keepax err_mutex: 2709078e7183SCharles Keepax mutex_unlock(&dsp->pwr_lock); 2710d8a64d6aSCharles Keepax } 2711d8a64d6aSCharles Keepax 2712b9070df4SRichard Fitzgerald int wm_adsp2_set_dspclk(struct snd_soc_dapm_widget *w, unsigned int freq) 2713d82d767fSCharles Keepax { 2714b9070df4SRichard Fitzgerald struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); 2715b9070df4SRichard Fitzgerald struct wm_adsp *dsps = snd_soc_component_get_drvdata(component); 2716b9070df4SRichard Fitzgerald struct wm_adsp *dsp = &dsps[w->shift]; 2717d82d767fSCharles Keepax int ret; 2718d82d767fSCharles Keepax 2719b9070df4SRichard Fitzgerald ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CLOCKING, 2720d82d767fSCharles Keepax ADSP2_CLK_SEL_MASK, 2721d82d767fSCharles Keepax freq << ADSP2_CLK_SEL_SHIFT); 2722b9070df4SRichard Fitzgerald if (ret) 2723d82d767fSCharles Keepax adsp_err(dsp, "Failed to set clock rate: %d\n", ret); 2724b9070df4SRichard Fitzgerald 2725b9070df4SRichard Fitzgerald return ret; 2726e1ea1879SRichard Fitzgerald } 2727b9070df4SRichard Fitzgerald EXPORT_SYMBOL_GPL(wm_adsp2_set_dspclk); 2728d82d767fSCharles Keepax 2729af813a6fSCharles Keepax int wm_adsp2_preloader_get(struct snd_kcontrol *kcontrol, 2730af813a6fSCharles Keepax struct snd_ctl_elem_value *ucontrol) 2731af813a6fSCharles Keepax { 27320fe1daa6SKuninori Morimoto struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); 2733b1470d4cSAjit Pandey struct wm_adsp *dsps = snd_soc_component_get_drvdata(component); 2734b1470d4cSAjit Pandey struct soc_mixer_control *mc = 2735b1470d4cSAjit Pandey (struct soc_mixer_control *)kcontrol->private_value; 2736b1470d4cSAjit Pandey struct wm_adsp *dsp = &dsps[mc->shift - 1]; 2737af813a6fSCharles Keepax 2738af813a6fSCharles Keepax ucontrol->value.integer.value[0] = dsp->preloaded; 2739af813a6fSCharles Keepax 2740af813a6fSCharles Keepax return 0; 2741af813a6fSCharles Keepax } 2742af813a6fSCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp2_preloader_get); 2743af813a6fSCharles Keepax 2744af813a6fSCharles Keepax int wm_adsp2_preloader_put(struct snd_kcontrol *kcontrol, 2745af813a6fSCharles Keepax struct snd_ctl_elem_value *ucontrol) 2746af813a6fSCharles Keepax { 27470fe1daa6SKuninori Morimoto struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); 2748b1470d4cSAjit Pandey struct wm_adsp *dsps = snd_soc_component_get_drvdata(component); 27490fe1daa6SKuninori Morimoto struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); 2750af813a6fSCharles Keepax struct soc_mixer_control *mc = 2751af813a6fSCharles Keepax (struct soc_mixer_control *)kcontrol->private_value; 2752b1470d4cSAjit Pandey struct wm_adsp *dsp = &dsps[mc->shift - 1]; 2753af813a6fSCharles Keepax char preload[32]; 2754af813a6fSCharles Keepax 2755605391d0SRichard Fitzgerald snprintf(preload, ARRAY_SIZE(preload), "%s Preload", dsp->name); 2756af813a6fSCharles Keepax 2757af813a6fSCharles Keepax dsp->preloaded = ucontrol->value.integer.value[0]; 2758af813a6fSCharles Keepax 2759af813a6fSCharles Keepax if (ucontrol->value.integer.value[0]) 276095a594d0SCharles Keepax snd_soc_component_force_enable_pin(component, preload); 2761af813a6fSCharles Keepax else 276295a594d0SCharles Keepax snd_soc_component_disable_pin(component, preload); 2763af813a6fSCharles Keepax 2764af813a6fSCharles Keepax snd_soc_dapm_sync(dapm); 2765af813a6fSCharles Keepax 2766868e49a4SStuart Henderson flush_work(&dsp->boot_work); 2767868e49a4SStuart Henderson 2768af813a6fSCharles Keepax return 0; 2769af813a6fSCharles Keepax } 2770af813a6fSCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp2_preloader_put); 2771af813a6fSCharles Keepax 277251a2c944SMayuresh Kulkarni static void wm_adsp_stop_watchdog(struct wm_adsp *dsp) 277351a2c944SMayuresh Kulkarni { 277451a2c944SMayuresh Kulkarni switch (dsp->rev) { 277551a2c944SMayuresh Kulkarni case 0: 277651a2c944SMayuresh Kulkarni case 1: 277751a2c944SMayuresh Kulkarni return; 277851a2c944SMayuresh Kulkarni default: 277951a2c944SMayuresh Kulkarni regmap_update_bits(dsp->regmap, dsp->base + ADSP2_WATCHDOG, 278051a2c944SMayuresh Kulkarni ADSP2_WDT_ENA_MASK, 0); 278151a2c944SMayuresh Kulkarni } 278251a2c944SMayuresh Kulkarni } 278351a2c944SMayuresh Kulkarni 278412db5eddSCharles Keepax int wm_adsp2_early_event(struct snd_soc_dapm_widget *w, 2785b9070df4SRichard Fitzgerald struct snd_kcontrol *kcontrol, int event) 278612db5eddSCharles Keepax { 27870fe1daa6SKuninori Morimoto struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); 27880fe1daa6SKuninori Morimoto struct wm_adsp *dsps = snd_soc_component_get_drvdata(component); 278912db5eddSCharles Keepax struct wm_adsp *dsp = &dsps[w->shift]; 279057a60cc3SCharles Keepax struct wm_coeff_ctl *ctl; 279112db5eddSCharles Keepax 279212db5eddSCharles Keepax switch (event) { 279312db5eddSCharles Keepax case SND_SOC_DAPM_PRE_PMU: 279412db5eddSCharles Keepax queue_work(system_unbound_wq, &dsp->boot_work); 279512db5eddSCharles Keepax break; 279657a60cc3SCharles Keepax case SND_SOC_DAPM_PRE_PMD: 2797bb24ee41SCharles Keepax mutex_lock(&dsp->pwr_lock); 2798bb24ee41SCharles Keepax 279957a60cc3SCharles Keepax wm_adsp_debugfs_clear(dsp); 280057a60cc3SCharles Keepax 280157a60cc3SCharles Keepax dsp->fw_id = 0; 280257a60cc3SCharles Keepax dsp->fw_id_version = 0; 280357a60cc3SCharles Keepax 280457a60cc3SCharles Keepax dsp->booted = false; 280557a60cc3SCharles Keepax 280657a60cc3SCharles Keepax regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 280757a60cc3SCharles Keepax ADSP2_MEM_ENA, 0); 280857a60cc3SCharles Keepax 280957a60cc3SCharles Keepax list_for_each_entry(ctl, &dsp->ctl_list, list) 281057a60cc3SCharles Keepax ctl->enabled = 0; 281157a60cc3SCharles Keepax 281257a60cc3SCharles Keepax wm_adsp_free_alg_regions(dsp); 281357a60cc3SCharles Keepax 2814bb24ee41SCharles Keepax mutex_unlock(&dsp->pwr_lock); 2815bb24ee41SCharles Keepax 281657a60cc3SCharles Keepax adsp_dbg(dsp, "Shutdown complete\n"); 281757a60cc3SCharles Keepax break; 281812db5eddSCharles Keepax default: 281912db5eddSCharles Keepax break; 2820cab27258SCharles Keepax } 282112db5eddSCharles Keepax 282212db5eddSCharles Keepax return 0; 282312db5eddSCharles Keepax } 282412db5eddSCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp2_early_event); 282512db5eddSCharles Keepax 2826d8a64d6aSCharles Keepax int wm_adsp2_event(struct snd_soc_dapm_widget *w, 2827d8a64d6aSCharles Keepax struct snd_kcontrol *kcontrol, int event) 2828d8a64d6aSCharles Keepax { 28290fe1daa6SKuninori Morimoto struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); 28300fe1daa6SKuninori Morimoto struct wm_adsp *dsps = snd_soc_component_get_drvdata(component); 2831d8a64d6aSCharles Keepax struct wm_adsp *dsp = &dsps[w->shift]; 2832d8a64d6aSCharles Keepax int ret; 2833d8a64d6aSCharles Keepax 2834d8a64d6aSCharles Keepax switch (event) { 2835d8a64d6aSCharles Keepax case SND_SOC_DAPM_POST_PMU: 2836d8a64d6aSCharles Keepax flush_work(&dsp->boot_work); 2837d8a64d6aSCharles Keepax 2838bb24ee41SCharles Keepax mutex_lock(&dsp->pwr_lock); 2839bb24ee41SCharles Keepax 2840bb24ee41SCharles Keepax if (!dsp->booted) { 2841bb24ee41SCharles Keepax ret = -EIO; 2842bb24ee41SCharles Keepax goto err; 2843bb24ee41SCharles Keepax } 2844d8a64d6aSCharles Keepax 284590d19ba5SCharles Keepax ret = wm_adsp2_ena(dsp); 284690d19ba5SCharles Keepax if (ret != 0) 284790d19ba5SCharles Keepax goto err; 284890d19ba5SCharles Keepax 2849cef45771SCharles Keepax /* Sync set controls */ 2850cef45771SCharles Keepax ret = wm_coeff_sync_controls(dsp); 2851cef45771SCharles Keepax if (ret != 0) 2852cef45771SCharles Keepax goto err; 2853cef45771SCharles Keepax 285451a2c944SMayuresh Kulkarni wm_adsp2_lock(dsp, dsp->lock_regions); 285551a2c944SMayuresh Kulkarni 2856d8a64d6aSCharles Keepax ret = regmap_update_bits(dsp->regmap, 2857d8a64d6aSCharles Keepax dsp->base + ADSP2_CONTROL, 285800e4c3b6SCharles Keepax ADSP2_CORE_ENA | ADSP2_START, 285900e4c3b6SCharles Keepax ADSP2_CORE_ENA | ADSP2_START); 2860d8a64d6aSCharles Keepax if (ret != 0) 2861d8a64d6aSCharles Keepax goto err; 28622cd19bdbSCharles Keepax 286348c2c993SCharles Keepax if (wm_adsp_fw[dsp->fw].num_caps != 0) { 28642cd19bdbSCharles Keepax ret = wm_adsp_buffer_init(dsp); 2865bb24ee41SCharles Keepax if (ret < 0) 286648c2c993SCharles Keepax goto err; 286748c2c993SCharles Keepax } 28682cd19bdbSCharles Keepax 2869e779974bSCharles Keepax dsp->running = true; 28701023dbd9SMark Brown 2871612047f0SCharles Keepax mutex_unlock(&dsp->pwr_lock); 2872612047f0SCharles Keepax 28732159ad93SMark Brown break; 28742159ad93SMark Brown 28752159ad93SMark Brown case SND_SOC_DAPM_PRE_PMD: 2876f4f0c4c6SRichard Fitzgerald /* Tell the firmware to cleanup */ 2877f4f0c4c6SRichard Fitzgerald wm_adsp_signal_event_controls(dsp, WM_ADSP_FW_EVENT_SHUTDOWN); 2878f4f0c4c6SRichard Fitzgerald 287951a2c944SMayuresh Kulkarni wm_adsp_stop_watchdog(dsp); 288051a2c944SMayuresh Kulkarni 288110337b07SRichard Fitzgerald /* Log firmware state, it can be useful for analysis */ 2882e1ea1879SRichard Fitzgerald switch (dsp->rev) { 2883e1ea1879SRichard Fitzgerald case 0: 288410337b07SRichard Fitzgerald wm_adsp2_show_fw_status(dsp); 2885e1ea1879SRichard Fitzgerald break; 2886e1ea1879SRichard Fitzgerald default: 2887e1ea1879SRichard Fitzgerald wm_adsp2v2_show_fw_status(dsp); 2888e1ea1879SRichard Fitzgerald break; 2889e1ea1879SRichard Fitzgerald } 289010337b07SRichard Fitzgerald 2891078e7183SCharles Keepax mutex_lock(&dsp->pwr_lock); 2892078e7183SCharles Keepax 28931023dbd9SMark Brown dsp->running = false; 28941023dbd9SMark Brown 2895e1ea1879SRichard Fitzgerald regmap_update_bits(dsp->regmap, 2896e1ea1879SRichard Fitzgerald dsp->base + ADSP2_CONTROL, 289757a60cc3SCharles Keepax ADSP2_CORE_ENA | ADSP2_START, 0); 2898973838a0SMark Brown 28992d30b575SMark Brown /* Make sure DMAs are quiesced */ 2900e1ea1879SRichard Fitzgerald switch (dsp->rev) { 2901e1ea1879SRichard Fitzgerald case 0: 2902e1ea1879SRichard Fitzgerald regmap_write(dsp->regmap, 2903e1ea1879SRichard Fitzgerald dsp->base + ADSP2_RDMA_CONFIG_1, 0); 2904e1ea1879SRichard Fitzgerald regmap_write(dsp->regmap, 2905e1ea1879SRichard Fitzgerald dsp->base + ADSP2_WDMA_CONFIG_1, 0); 2906e1ea1879SRichard Fitzgerald regmap_write(dsp->regmap, 2907e1ea1879SRichard Fitzgerald dsp->base + ADSP2_WDMA_CONFIG_2, 0); 29086facd2d1SSimon Trimmer 2909e1ea1879SRichard Fitzgerald regmap_update_bits(dsp->regmap, 2910e1ea1879SRichard Fitzgerald dsp->base + ADSP2_CONTROL, 29116facd2d1SSimon Trimmer ADSP2_SYS_ENA, 0); 2912e1ea1879SRichard Fitzgerald break; 2913e1ea1879SRichard Fitzgerald default: 2914e1ea1879SRichard Fitzgerald regmap_write(dsp->regmap, 2915e1ea1879SRichard Fitzgerald dsp->base + ADSP2_RDMA_CONFIG_1, 0); 2916e1ea1879SRichard Fitzgerald regmap_write(dsp->regmap, 2917e1ea1879SRichard Fitzgerald dsp->base + ADSP2_WDMA_CONFIG_1, 0); 2918e1ea1879SRichard Fitzgerald regmap_write(dsp->regmap, 2919e1ea1879SRichard Fitzgerald dsp->base + ADSP2V2_WDMA_CONFIG_2, 0); 2920e1ea1879SRichard Fitzgerald break; 2921e1ea1879SRichard Fitzgerald } 29222d30b575SMark Brown 29232cd19bdbSCharles Keepax if (wm_adsp_fw[dsp->fw].num_caps != 0) 29242cd19bdbSCharles Keepax wm_adsp_buffer_free(dsp); 29252cd19bdbSCharles Keepax 2926a2bcbc1bSCharles Keepax dsp->fatal_error = false; 2927a2bcbc1bSCharles Keepax 2928078e7183SCharles Keepax mutex_unlock(&dsp->pwr_lock); 2929078e7183SCharles Keepax 293057a60cc3SCharles Keepax adsp_dbg(dsp, "Execution stopped\n"); 29312159ad93SMark Brown break; 29322159ad93SMark Brown 29332159ad93SMark Brown default: 29342159ad93SMark Brown break; 29352159ad93SMark Brown } 29362159ad93SMark Brown 29372159ad93SMark Brown return 0; 29382159ad93SMark Brown err: 29392159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 2940a7f9be7eSMark Brown ADSP2_SYS_ENA | ADSP2_CORE_ENA | ADSP2_START, 0); 2941bb24ee41SCharles Keepax mutex_unlock(&dsp->pwr_lock); 29422159ad93SMark Brown return ret; 29432159ad93SMark Brown } 29442159ad93SMark Brown EXPORT_SYMBOL_GPL(wm_adsp2_event); 2945973838a0SMark Brown 29460fe1daa6SKuninori Morimoto int wm_adsp2_component_probe(struct wm_adsp *dsp, struct snd_soc_component *component) 2947f5e2ce92SRichard Fitzgerald { 2948af813a6fSCharles Keepax char preload[32]; 2949af813a6fSCharles Keepax 2950605391d0SRichard Fitzgerald snprintf(preload, ARRAY_SIZE(preload), "%s Preload", dsp->name); 295195a594d0SCharles Keepax snd_soc_component_disable_pin(component, preload); 2952685f51a5SRichard Fitzgerald 29530fe1daa6SKuninori Morimoto wm_adsp2_init_debugfs(dsp, component); 2954f9f55e31SRichard Fitzgerald 29550fe1daa6SKuninori Morimoto dsp->component = component; 2956af813a6fSCharles Keepax 29570a047f07SRichard Fitzgerald return 0; 2958f5e2ce92SRichard Fitzgerald } 29590fe1daa6SKuninori Morimoto EXPORT_SYMBOL_GPL(wm_adsp2_component_probe); 2960f5e2ce92SRichard Fitzgerald 29610fe1daa6SKuninori Morimoto int wm_adsp2_component_remove(struct wm_adsp *dsp, struct snd_soc_component *component) 2962f5e2ce92SRichard Fitzgerald { 2963f9f55e31SRichard Fitzgerald wm_adsp2_cleanup_debugfs(dsp); 2964f9f55e31SRichard Fitzgerald 2965f5e2ce92SRichard Fitzgerald return 0; 2966f5e2ce92SRichard Fitzgerald } 29670fe1daa6SKuninori Morimoto EXPORT_SYMBOL_GPL(wm_adsp2_component_remove); 2968f5e2ce92SRichard Fitzgerald 296981ac58b1SRichard Fitzgerald int wm_adsp2_init(struct wm_adsp *dsp) 2970973838a0SMark Brown { 2971973838a0SMark Brown int ret; 2972973838a0SMark Brown 2973dcad34f8SRichard Fitzgerald ret = wm_adsp_common_init(dsp); 2974605391d0SRichard Fitzgerald if (ret) 2975605391d0SRichard Fitzgerald return ret; 2976605391d0SRichard Fitzgerald 2977e1ea1879SRichard Fitzgerald switch (dsp->rev) { 2978e1ea1879SRichard Fitzgerald case 0: 297910a2b662SMark Brown /* 298010a2b662SMark Brown * Disable the DSP memory by default when in reset for a small 298110a2b662SMark Brown * power saving. 298210a2b662SMark Brown */ 29833809f001SCharles Keepax ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 298410a2b662SMark Brown ADSP2_MEM_ENA, 0); 2985e1ea1879SRichard Fitzgerald if (ret) { 2986e1ea1879SRichard Fitzgerald adsp_err(dsp, 2987e1ea1879SRichard Fitzgerald "Failed to clear memory retention: %d\n", ret); 298810a2b662SMark Brown return ret; 298910a2b662SMark Brown } 2990e1ea1879SRichard Fitzgerald break; 2991e1ea1879SRichard Fitzgerald default: 2992e1ea1879SRichard Fitzgerald break; 2993e1ea1879SRichard Fitzgerald } 299410a2b662SMark Brown 29953809f001SCharles Keepax INIT_WORK(&dsp->boot_work, wm_adsp2_boot_work); 29966ab2b7b4SDimitris Papastamos 2997973838a0SMark Brown return 0; 2998973838a0SMark Brown } 2999973838a0SMark Brown EXPORT_SYMBOL_GPL(wm_adsp2_init); 30000a37c6efSPraveen Diwakar 300166225e98SRichard Fitzgerald void wm_adsp2_remove(struct wm_adsp *dsp) 300266225e98SRichard Fitzgerald { 300366225e98SRichard Fitzgerald struct wm_coeff_ctl *ctl; 300466225e98SRichard Fitzgerald 300566225e98SRichard Fitzgerald while (!list_empty(&dsp->ctl_list)) { 300666225e98SRichard Fitzgerald ctl = list_first_entry(&dsp->ctl_list, struct wm_coeff_ctl, 300766225e98SRichard Fitzgerald list); 300866225e98SRichard Fitzgerald list_del(&ctl->list); 300966225e98SRichard Fitzgerald wm_adsp_free_ctl_blk(ctl); 301066225e98SRichard Fitzgerald } 301166225e98SRichard Fitzgerald } 301266225e98SRichard Fitzgerald EXPORT_SYMBOL_GPL(wm_adsp2_remove); 301366225e98SRichard Fitzgerald 3014edd71350SCharles Keepax static inline int wm_adsp_compr_attached(struct wm_adsp_compr *compr) 3015edd71350SCharles Keepax { 3016edd71350SCharles Keepax return compr->buf != NULL; 3017edd71350SCharles Keepax } 3018edd71350SCharles Keepax 3019edd71350SCharles Keepax static int wm_adsp_compr_attach(struct wm_adsp_compr *compr) 3020edd71350SCharles Keepax { 30214f2d4eabSStuart Henderson struct wm_adsp_compr_buf *buf = NULL, *tmp; 30224f2d4eabSStuart Henderson 3023a2bcbc1bSCharles Keepax if (compr->dsp->fatal_error) 3024a2bcbc1bSCharles Keepax return -EINVAL; 3025a2bcbc1bSCharles Keepax 30264f2d4eabSStuart Henderson list_for_each_entry(tmp, &compr->dsp->buffer_list, list) { 30274f2d4eabSStuart Henderson if (!tmp->name || !strcmp(compr->name, tmp->name)) { 30284f2d4eabSStuart Henderson buf = tmp; 30294f2d4eabSStuart Henderson break; 30304f2d4eabSStuart Henderson } 30314f2d4eabSStuart Henderson } 30324f2d4eabSStuart Henderson 30334f2d4eabSStuart Henderson if (!buf) 3034edd71350SCharles Keepax return -EINVAL; 3035edd71350SCharles Keepax 30364f2d4eabSStuart Henderson compr->buf = buf; 3037721be3beSCharles Keepax compr->buf->compr = compr; 3038edd71350SCharles Keepax 3039edd71350SCharles Keepax return 0; 3040edd71350SCharles Keepax } 3041edd71350SCharles Keepax 3042721be3beSCharles Keepax static void wm_adsp_compr_detach(struct wm_adsp_compr *compr) 3043721be3beSCharles Keepax { 3044721be3beSCharles Keepax if (!compr) 3045721be3beSCharles Keepax return; 3046721be3beSCharles Keepax 3047721be3beSCharles Keepax /* Wake the poll so it can see buffer is no longer attached */ 3048721be3beSCharles Keepax if (compr->stream) 3049721be3beSCharles Keepax snd_compr_fragment_elapsed(compr->stream); 3050721be3beSCharles Keepax 3051721be3beSCharles Keepax if (wm_adsp_compr_attached(compr)) { 3052721be3beSCharles Keepax compr->buf->compr = NULL; 3053721be3beSCharles Keepax compr->buf = NULL; 3054721be3beSCharles Keepax } 3055721be3beSCharles Keepax } 3056721be3beSCharles Keepax 3057406abc95SCharles Keepax int wm_adsp_compr_open(struct wm_adsp *dsp, struct snd_compr_stream *stream) 3058406abc95SCharles Keepax { 30594f2d4eabSStuart Henderson struct wm_adsp_compr *compr, *tmp; 30604f2d4eabSStuart Henderson struct snd_soc_pcm_runtime *rtd = stream->private_data; 3061406abc95SCharles Keepax int ret = 0; 3062406abc95SCharles Keepax 3063406abc95SCharles Keepax mutex_lock(&dsp->pwr_lock); 3064406abc95SCharles Keepax 3065406abc95SCharles Keepax if (wm_adsp_fw[dsp->fw].num_caps == 0) { 30660d3fba3eSCharles Keepax adsp_err(dsp, "%s: Firmware does not support compressed API\n", 30670d3fba3eSCharles Keepax rtd->codec_dai->name); 3068406abc95SCharles Keepax ret = -ENXIO; 3069406abc95SCharles Keepax goto out; 3070406abc95SCharles Keepax } 3071406abc95SCharles Keepax 3072406abc95SCharles Keepax if (wm_adsp_fw[dsp->fw].compr_direction != stream->direction) { 30730d3fba3eSCharles Keepax adsp_err(dsp, "%s: Firmware does not support stream direction\n", 30740d3fba3eSCharles Keepax rtd->codec_dai->name); 3075406abc95SCharles Keepax ret = -EINVAL; 3076406abc95SCharles Keepax goto out; 3077406abc95SCharles Keepax } 3078406abc95SCharles Keepax 30794f2d4eabSStuart Henderson list_for_each_entry(tmp, &dsp->compr_list, list) { 30804f2d4eabSStuart Henderson if (!strcmp(tmp->name, rtd->codec_dai->name)) { 30810d3fba3eSCharles Keepax adsp_err(dsp, "%s: Only a single stream supported per dai\n", 30820d3fba3eSCharles Keepax rtd->codec_dai->name); 308395fe9597SCharles Keepax ret = -EBUSY; 308495fe9597SCharles Keepax goto out; 308595fe9597SCharles Keepax } 30864f2d4eabSStuart Henderson } 308795fe9597SCharles Keepax 3088406abc95SCharles Keepax compr = kzalloc(sizeof(*compr), GFP_KERNEL); 3089406abc95SCharles Keepax if (!compr) { 3090406abc95SCharles Keepax ret = -ENOMEM; 3091406abc95SCharles Keepax goto out; 3092406abc95SCharles Keepax } 3093406abc95SCharles Keepax 3094406abc95SCharles Keepax compr->dsp = dsp; 3095406abc95SCharles Keepax compr->stream = stream; 30964f2d4eabSStuart Henderson compr->name = rtd->codec_dai->name; 3097406abc95SCharles Keepax 30984f2d4eabSStuart Henderson list_add_tail(&compr->list, &dsp->compr_list); 3099406abc95SCharles Keepax 3100406abc95SCharles Keepax stream->runtime->private_data = compr; 3101406abc95SCharles Keepax 3102406abc95SCharles Keepax out: 3103406abc95SCharles Keepax mutex_unlock(&dsp->pwr_lock); 3104406abc95SCharles Keepax 3105406abc95SCharles Keepax return ret; 3106406abc95SCharles Keepax } 3107406abc95SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_open); 3108406abc95SCharles Keepax 3109406abc95SCharles Keepax int wm_adsp_compr_free(struct snd_compr_stream *stream) 3110406abc95SCharles Keepax { 3111406abc95SCharles Keepax struct wm_adsp_compr *compr = stream->runtime->private_data; 3112406abc95SCharles Keepax struct wm_adsp *dsp = compr->dsp; 3113406abc95SCharles Keepax 3114406abc95SCharles Keepax mutex_lock(&dsp->pwr_lock); 3115406abc95SCharles Keepax 3116721be3beSCharles Keepax wm_adsp_compr_detach(compr); 31174f2d4eabSStuart Henderson list_del(&compr->list); 3118406abc95SCharles Keepax 311983a40ce9SCharles Keepax kfree(compr->raw_buf); 3120406abc95SCharles Keepax kfree(compr); 3121406abc95SCharles Keepax 3122406abc95SCharles Keepax mutex_unlock(&dsp->pwr_lock); 3123406abc95SCharles Keepax 3124406abc95SCharles Keepax return 0; 3125406abc95SCharles Keepax } 3126406abc95SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_free); 3127406abc95SCharles Keepax 3128406abc95SCharles Keepax static int wm_adsp_compr_check_params(struct snd_compr_stream *stream, 3129406abc95SCharles Keepax struct snd_compr_params *params) 3130406abc95SCharles Keepax { 3131406abc95SCharles Keepax struct wm_adsp_compr *compr = stream->runtime->private_data; 3132406abc95SCharles Keepax struct wm_adsp *dsp = compr->dsp; 3133406abc95SCharles Keepax const struct wm_adsp_fw_caps *caps; 3134406abc95SCharles Keepax const struct snd_codec_desc *desc; 3135406abc95SCharles Keepax int i, j; 3136406abc95SCharles Keepax 3137406abc95SCharles Keepax if (params->buffer.fragment_size < WM_ADSP_MIN_FRAGMENT_SIZE || 3138406abc95SCharles Keepax params->buffer.fragment_size > WM_ADSP_MAX_FRAGMENT_SIZE || 3139406abc95SCharles Keepax params->buffer.fragments < WM_ADSP_MIN_FRAGMENTS || 3140406abc95SCharles Keepax params->buffer.fragments > WM_ADSP_MAX_FRAGMENTS || 3141406abc95SCharles Keepax params->buffer.fragment_size % WM_ADSP_DATA_WORD_SIZE) { 31420d3fba3eSCharles Keepax compr_err(compr, "Invalid buffer fragsize=%d fragments=%d\n", 3143406abc95SCharles Keepax params->buffer.fragment_size, 3144406abc95SCharles Keepax params->buffer.fragments); 3145406abc95SCharles Keepax 3146406abc95SCharles Keepax return -EINVAL; 3147406abc95SCharles Keepax } 3148406abc95SCharles Keepax 3149406abc95SCharles Keepax for (i = 0; i < wm_adsp_fw[dsp->fw].num_caps; i++) { 3150406abc95SCharles Keepax caps = &wm_adsp_fw[dsp->fw].caps[i]; 3151406abc95SCharles Keepax desc = &caps->desc; 3152406abc95SCharles Keepax 3153406abc95SCharles Keepax if (caps->id != params->codec.id) 3154406abc95SCharles Keepax continue; 3155406abc95SCharles Keepax 3156406abc95SCharles Keepax if (stream->direction == SND_COMPRESS_PLAYBACK) { 3157406abc95SCharles Keepax if (desc->max_ch < params->codec.ch_out) 3158406abc95SCharles Keepax continue; 3159406abc95SCharles Keepax } else { 3160406abc95SCharles Keepax if (desc->max_ch < params->codec.ch_in) 3161406abc95SCharles Keepax continue; 3162406abc95SCharles Keepax } 3163406abc95SCharles Keepax 3164406abc95SCharles Keepax if (!(desc->formats & (1 << params->codec.format))) 3165406abc95SCharles Keepax continue; 3166406abc95SCharles Keepax 3167406abc95SCharles Keepax for (j = 0; j < desc->num_sample_rates; ++j) 3168406abc95SCharles Keepax if (desc->sample_rates[j] == params->codec.sample_rate) 3169406abc95SCharles Keepax return 0; 3170406abc95SCharles Keepax } 3171406abc95SCharles Keepax 31720d3fba3eSCharles Keepax compr_err(compr, "Invalid params id=%u ch=%u,%u rate=%u fmt=%u\n", 3173406abc95SCharles Keepax params->codec.id, params->codec.ch_in, params->codec.ch_out, 3174406abc95SCharles Keepax params->codec.sample_rate, params->codec.format); 3175406abc95SCharles Keepax return -EINVAL; 3176406abc95SCharles Keepax } 3177406abc95SCharles Keepax 3178565ace46SCharles Keepax static inline unsigned int wm_adsp_compr_frag_words(struct wm_adsp_compr *compr) 3179565ace46SCharles Keepax { 3180565ace46SCharles Keepax return compr->size.fragment_size / WM_ADSP_DATA_WORD_SIZE; 3181565ace46SCharles Keepax } 3182565ace46SCharles Keepax 3183406abc95SCharles Keepax int wm_adsp_compr_set_params(struct snd_compr_stream *stream, 3184406abc95SCharles Keepax struct snd_compr_params *params) 3185406abc95SCharles Keepax { 3186406abc95SCharles Keepax struct wm_adsp_compr *compr = stream->runtime->private_data; 318783a40ce9SCharles Keepax unsigned int size; 3188406abc95SCharles Keepax int ret; 3189406abc95SCharles Keepax 3190406abc95SCharles Keepax ret = wm_adsp_compr_check_params(stream, params); 3191406abc95SCharles Keepax if (ret) 3192406abc95SCharles Keepax return ret; 3193406abc95SCharles Keepax 3194406abc95SCharles Keepax compr->size = params->buffer; 3195406abc95SCharles Keepax 31960d3fba3eSCharles Keepax compr_dbg(compr, "fragment_size=%d fragments=%d\n", 3197406abc95SCharles Keepax compr->size.fragment_size, compr->size.fragments); 3198406abc95SCharles Keepax 319983a40ce9SCharles Keepax size = wm_adsp_compr_frag_words(compr) * sizeof(*compr->raw_buf); 320083a40ce9SCharles Keepax compr->raw_buf = kmalloc(size, GFP_DMA | GFP_KERNEL); 320183a40ce9SCharles Keepax if (!compr->raw_buf) 320283a40ce9SCharles Keepax return -ENOMEM; 320383a40ce9SCharles Keepax 3204da2b3358SCharles Keepax compr->sample_rate = params->codec.sample_rate; 3205da2b3358SCharles Keepax 3206406abc95SCharles Keepax return 0; 3207406abc95SCharles Keepax } 3208406abc95SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_set_params); 3209406abc95SCharles Keepax 3210406abc95SCharles Keepax int wm_adsp_compr_get_caps(struct snd_compr_stream *stream, 3211406abc95SCharles Keepax struct snd_compr_caps *caps) 3212406abc95SCharles Keepax { 3213406abc95SCharles Keepax struct wm_adsp_compr *compr = stream->runtime->private_data; 3214406abc95SCharles Keepax int fw = compr->dsp->fw; 3215406abc95SCharles Keepax int i; 3216406abc95SCharles Keepax 3217406abc95SCharles Keepax if (wm_adsp_fw[fw].caps) { 3218406abc95SCharles Keepax for (i = 0; i < wm_adsp_fw[fw].num_caps; i++) 3219406abc95SCharles Keepax caps->codecs[i] = wm_adsp_fw[fw].caps[i].id; 3220406abc95SCharles Keepax 3221406abc95SCharles Keepax caps->num_codecs = i; 3222406abc95SCharles Keepax caps->direction = wm_adsp_fw[fw].compr_direction; 3223406abc95SCharles Keepax 3224406abc95SCharles Keepax caps->min_fragment_size = WM_ADSP_MIN_FRAGMENT_SIZE; 3225406abc95SCharles Keepax caps->max_fragment_size = WM_ADSP_MAX_FRAGMENT_SIZE; 3226406abc95SCharles Keepax caps->min_fragments = WM_ADSP_MIN_FRAGMENTS; 3227406abc95SCharles Keepax caps->max_fragments = WM_ADSP_MAX_FRAGMENTS; 3228406abc95SCharles Keepax } 3229406abc95SCharles Keepax 3230406abc95SCharles Keepax return 0; 3231406abc95SCharles Keepax } 3232406abc95SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_get_caps); 3233406abc95SCharles Keepax 32342cd19bdbSCharles Keepax static int wm_adsp_read_data_block(struct wm_adsp *dsp, int mem_type, 32352cd19bdbSCharles Keepax unsigned int mem_addr, 32362cd19bdbSCharles Keepax unsigned int num_words, u32 *data) 32372cd19bdbSCharles Keepax { 32382cd19bdbSCharles Keepax struct wm_adsp_region const *mem = wm_adsp_find_region(dsp, mem_type); 32392cd19bdbSCharles Keepax unsigned int i, reg; 32402cd19bdbSCharles Keepax int ret; 32412cd19bdbSCharles Keepax 32422cd19bdbSCharles Keepax if (!mem) 32432cd19bdbSCharles Keepax return -EINVAL; 32442cd19bdbSCharles Keepax 32452cd19bdbSCharles Keepax reg = wm_adsp_region_to_reg(mem, mem_addr); 32462cd19bdbSCharles Keepax 32472cd19bdbSCharles Keepax ret = regmap_raw_read(dsp->regmap, reg, data, 32482cd19bdbSCharles Keepax sizeof(*data) * num_words); 32492cd19bdbSCharles Keepax if (ret < 0) 32502cd19bdbSCharles Keepax return ret; 32512cd19bdbSCharles Keepax 32522cd19bdbSCharles Keepax for (i = 0; i < num_words; ++i) 32532cd19bdbSCharles Keepax data[i] = be32_to_cpu(data[i]) & 0x00ffffffu; 32542cd19bdbSCharles Keepax 32552cd19bdbSCharles Keepax return 0; 32562cd19bdbSCharles Keepax } 32572cd19bdbSCharles Keepax 32582cd19bdbSCharles Keepax static inline int wm_adsp_read_data_word(struct wm_adsp *dsp, int mem_type, 32592cd19bdbSCharles Keepax unsigned int mem_addr, u32 *data) 32602cd19bdbSCharles Keepax { 32612cd19bdbSCharles Keepax return wm_adsp_read_data_block(dsp, mem_type, mem_addr, 1, data); 32622cd19bdbSCharles Keepax } 32632cd19bdbSCharles Keepax 32642cd19bdbSCharles Keepax static int wm_adsp_write_data_word(struct wm_adsp *dsp, int mem_type, 32652cd19bdbSCharles Keepax unsigned int mem_addr, u32 data) 32662cd19bdbSCharles Keepax { 32672cd19bdbSCharles Keepax struct wm_adsp_region const *mem = wm_adsp_find_region(dsp, mem_type); 32682cd19bdbSCharles Keepax unsigned int reg; 32692cd19bdbSCharles Keepax 32702cd19bdbSCharles Keepax if (!mem) 32712cd19bdbSCharles Keepax return -EINVAL; 32722cd19bdbSCharles Keepax 32732cd19bdbSCharles Keepax reg = wm_adsp_region_to_reg(mem, mem_addr); 32742cd19bdbSCharles Keepax 32752cd19bdbSCharles Keepax data = cpu_to_be32(data & 0x00ffffffu); 32762cd19bdbSCharles Keepax 32772cd19bdbSCharles Keepax return regmap_raw_write(dsp->regmap, reg, &data, sizeof(data)); 32782cd19bdbSCharles Keepax } 32792cd19bdbSCharles Keepax 32802cd19bdbSCharles Keepax static inline int wm_adsp_buffer_read(struct wm_adsp_compr_buf *buf, 32812cd19bdbSCharles Keepax unsigned int field_offset, u32 *data) 32822cd19bdbSCharles Keepax { 3283fb13f19dSAndrew Ford return wm_adsp_read_data_word(buf->dsp, buf->host_buf_mem_type, 32842cd19bdbSCharles Keepax buf->host_buf_ptr + field_offset, data); 32852cd19bdbSCharles Keepax } 32862cd19bdbSCharles Keepax 32872cd19bdbSCharles Keepax static inline int wm_adsp_buffer_write(struct wm_adsp_compr_buf *buf, 32882cd19bdbSCharles Keepax unsigned int field_offset, u32 data) 32892cd19bdbSCharles Keepax { 3290fb13f19dSAndrew Ford return wm_adsp_write_data_word(buf->dsp, buf->host_buf_mem_type, 32912cd19bdbSCharles Keepax buf->host_buf_ptr + field_offset, data); 32922cd19bdbSCharles Keepax } 32932cd19bdbSCharles Keepax 3294cc7d6ce9SCharles Keepax static void wm_adsp_remove_padding(u32 *buf, int nwords, int data_word_size) 3295cc7d6ce9SCharles Keepax { 3296cc7d6ce9SCharles Keepax u8 *pack_in = (u8 *)buf; 3297cc7d6ce9SCharles Keepax u8 *pack_out = (u8 *)buf; 3298cc7d6ce9SCharles Keepax int i, j; 3299cc7d6ce9SCharles Keepax 3300cc7d6ce9SCharles Keepax /* Remove the padding bytes from the data read from the DSP */ 3301cc7d6ce9SCharles Keepax for (i = 0; i < nwords; i++) { 3302cc7d6ce9SCharles Keepax for (j = 0; j < data_word_size; j++) 3303cc7d6ce9SCharles Keepax *pack_out++ = *pack_in++; 3304cc7d6ce9SCharles Keepax 3305cc7d6ce9SCharles Keepax pack_in += sizeof(*buf) - data_word_size; 3306cc7d6ce9SCharles Keepax } 3307cc7d6ce9SCharles Keepax } 3308cc7d6ce9SCharles Keepax 33091e38f069SCharles Keepax static int wm_adsp_buffer_populate(struct wm_adsp_compr_buf *buf) 33101e38f069SCharles Keepax { 33111e38f069SCharles Keepax const struct wm_adsp_fw_caps *caps = wm_adsp_fw[buf->dsp->fw].caps; 33121e38f069SCharles Keepax struct wm_adsp_buffer_region *region; 33131e38f069SCharles Keepax u32 offset = 0; 33141e38f069SCharles Keepax int i, ret; 33151e38f069SCharles Keepax 3316a792af69SCharles Keepax buf->regions = kcalloc(caps->num_regions, sizeof(*buf->regions), 3317a792af69SCharles Keepax GFP_KERNEL); 3318a792af69SCharles Keepax if (!buf->regions) 3319a792af69SCharles Keepax return -ENOMEM; 3320a792af69SCharles Keepax 33211e38f069SCharles Keepax for (i = 0; i < caps->num_regions; ++i) { 33221e38f069SCharles Keepax region = &buf->regions[i]; 33231e38f069SCharles Keepax 33241e38f069SCharles Keepax region->offset = offset; 33251e38f069SCharles Keepax region->mem_type = caps->region_defs[i].mem_type; 33261e38f069SCharles Keepax 33271e38f069SCharles Keepax ret = wm_adsp_buffer_read(buf, caps->region_defs[i].base_offset, 33281e38f069SCharles Keepax ®ion->base_addr); 33291e38f069SCharles Keepax if (ret < 0) 33301e38f069SCharles Keepax return ret; 33311e38f069SCharles Keepax 33321e38f069SCharles Keepax ret = wm_adsp_buffer_read(buf, caps->region_defs[i].size_offset, 33331e38f069SCharles Keepax &offset); 33341e38f069SCharles Keepax if (ret < 0) 33351e38f069SCharles Keepax return ret; 33361e38f069SCharles Keepax 33371e38f069SCharles Keepax region->cumulative_size = offset; 33381e38f069SCharles Keepax 33390d3fba3eSCharles Keepax compr_dbg(buf, 33401e38f069SCharles Keepax "region=%d type=%d base=%08x off=%08x size=%08x\n", 33411e38f069SCharles Keepax i, region->mem_type, region->base_addr, 33421e38f069SCharles Keepax region->offset, region->cumulative_size); 33431e38f069SCharles Keepax } 33441e38f069SCharles Keepax 33451e38f069SCharles Keepax return 0; 33461e38f069SCharles Keepax } 33471e38f069SCharles Keepax 33481e38f069SCharles Keepax static void wm_adsp_buffer_clear(struct wm_adsp_compr_buf *buf) 33491e38f069SCharles Keepax { 33501e38f069SCharles Keepax buf->irq_count = 0xFFFFFFFF; 33511e38f069SCharles Keepax buf->read_index = -1; 33521e38f069SCharles Keepax buf->avail = 0; 33531e38f069SCharles Keepax } 33541e38f069SCharles Keepax 3355a792af69SCharles Keepax static struct wm_adsp_compr_buf *wm_adsp_buffer_alloc(struct wm_adsp *dsp) 3356a792af69SCharles Keepax { 3357a792af69SCharles Keepax struct wm_adsp_compr_buf *buf; 3358a792af69SCharles Keepax 3359a792af69SCharles Keepax buf = kzalloc(sizeof(*buf), GFP_KERNEL); 3360a792af69SCharles Keepax if (!buf) 3361a792af69SCharles Keepax return NULL; 3362a792af69SCharles Keepax 3363a792af69SCharles Keepax buf->dsp = dsp; 3364a792af69SCharles Keepax 3365a792af69SCharles Keepax wm_adsp_buffer_clear(buf); 3366a792af69SCharles Keepax 33674f2d4eabSStuart Henderson list_add_tail(&buf->list, &dsp->buffer_list); 3368a792af69SCharles Keepax 3369a792af69SCharles Keepax return buf; 3370a792af69SCharles Keepax } 3371a792af69SCharles Keepax 3372a792af69SCharles Keepax static int wm_adsp_buffer_parse_legacy(struct wm_adsp *dsp) 33732cd19bdbSCharles Keepax { 33742cd19bdbSCharles Keepax struct wm_adsp_alg_region *alg_region; 3375a792af69SCharles Keepax struct wm_adsp_compr_buf *buf; 33762cd19bdbSCharles Keepax u32 xmalg, addr, magic; 33772cd19bdbSCharles Keepax int i, ret; 33782cd19bdbSCharles Keepax 3379a792af69SCharles Keepax buf = wm_adsp_buffer_alloc(dsp); 3380a792af69SCharles Keepax if (!buf) 3381a792af69SCharles Keepax return -ENOMEM; 3382a792af69SCharles Keepax 33832cd19bdbSCharles Keepax alg_region = wm_adsp_find_alg_region(dsp, WMFW_ADSP2_XM, dsp->fw_id); 33842cd19bdbSCharles Keepax xmalg = sizeof(struct wm_adsp_system_config_xm_hdr) / sizeof(__be32); 33852cd19bdbSCharles Keepax 33862cd19bdbSCharles Keepax addr = alg_region->base + xmalg + ALG_XM_FIELD(magic); 33872cd19bdbSCharles Keepax ret = wm_adsp_read_data_word(dsp, WMFW_ADSP2_XM, addr, &magic); 33882cd19bdbSCharles Keepax if (ret < 0) 33892cd19bdbSCharles Keepax return ret; 33902cd19bdbSCharles Keepax 33912cd19bdbSCharles Keepax if (magic != WM_ADSP_ALG_XM_STRUCT_MAGIC) 3392a792af69SCharles Keepax return -ENODEV; 33932cd19bdbSCharles Keepax 33942cd19bdbSCharles Keepax addr = alg_region->base + xmalg + ALG_XM_FIELD(host_buf_ptr); 33952cd19bdbSCharles Keepax for (i = 0; i < 5; ++i) { 33962cd19bdbSCharles Keepax ret = wm_adsp_read_data_word(dsp, WMFW_ADSP2_XM, addr, 33972cd19bdbSCharles Keepax &buf->host_buf_ptr); 33982cd19bdbSCharles Keepax if (ret < 0) 33992cd19bdbSCharles Keepax return ret; 34002cd19bdbSCharles Keepax 34012cd19bdbSCharles Keepax if (buf->host_buf_ptr) 34022cd19bdbSCharles Keepax break; 34032cd19bdbSCharles Keepax 34042cd19bdbSCharles Keepax usleep_range(1000, 2000); 34052cd19bdbSCharles Keepax } 34062cd19bdbSCharles Keepax 34072cd19bdbSCharles Keepax if (!buf->host_buf_ptr) 34082cd19bdbSCharles Keepax return -EIO; 34092cd19bdbSCharles Keepax 3410fb13f19dSAndrew Ford buf->host_buf_mem_type = WMFW_ADSP2_XM; 3411fb13f19dSAndrew Ford 3412a792af69SCharles Keepax ret = wm_adsp_buffer_populate(buf); 3413a792af69SCharles Keepax if (ret < 0) 3414a792af69SCharles Keepax return ret; 3415a792af69SCharles Keepax 34160d3fba3eSCharles Keepax compr_dbg(buf, "legacy host_buf_ptr=%x\n", buf->host_buf_ptr); 34172cd19bdbSCharles Keepax 34182cd19bdbSCharles Keepax return 0; 34192cd19bdbSCharles Keepax } 34202cd19bdbSCharles Keepax 3421a792af69SCharles Keepax static int wm_adsp_buffer_parse_coeff(struct wm_coeff_ctl *ctl) 3422d52ed4b0SRichard Fitzgerald { 34234f2d4eabSStuart Henderson struct wm_adsp_host_buf_coeff_v1 coeff_v1; 3424a792af69SCharles Keepax struct wm_adsp_compr_buf *buf; 3425a792af69SCharles Keepax unsigned int val, reg; 3426a792af69SCharles Keepax int ret, i; 3427d52ed4b0SRichard Fitzgerald 3428d52ed4b0SRichard Fitzgerald ret = wm_coeff_base_reg(ctl, ®); 3429d52ed4b0SRichard Fitzgerald if (ret) 3430d52ed4b0SRichard Fitzgerald return ret; 3431d52ed4b0SRichard Fitzgerald 3432d52ed4b0SRichard Fitzgerald for (i = 0; i < 5; ++i) { 3433a792af69SCharles Keepax ret = regmap_raw_read(ctl->dsp->regmap, reg, &val, sizeof(val)); 3434d52ed4b0SRichard Fitzgerald if (ret < 0) 3435d52ed4b0SRichard Fitzgerald return ret; 3436d52ed4b0SRichard Fitzgerald 3437d52ed4b0SRichard Fitzgerald if (val) 3438d52ed4b0SRichard Fitzgerald break; 3439d52ed4b0SRichard Fitzgerald 3440d52ed4b0SRichard Fitzgerald usleep_range(1000, 2000); 3441d52ed4b0SRichard Fitzgerald } 3442d52ed4b0SRichard Fitzgerald 3443a792af69SCharles Keepax if (!val) { 3444a792af69SCharles Keepax adsp_err(ctl->dsp, "Failed to acquire host buffer\n"); 3445d52ed4b0SRichard Fitzgerald return -EIO; 3446d52ed4b0SRichard Fitzgerald } 3447d52ed4b0SRichard Fitzgerald 3448a792af69SCharles Keepax buf = wm_adsp_buffer_alloc(ctl->dsp); 34492cd19bdbSCharles Keepax if (!buf) 34502cd19bdbSCharles Keepax return -ENOMEM; 34512cd19bdbSCharles Keepax 3452a792af69SCharles Keepax buf->host_buf_mem_type = ctl->alg_region.type; 3453a792af69SCharles Keepax buf->host_buf_ptr = be32_to_cpu(val); 34542cd19bdbSCharles Keepax 34552cd19bdbSCharles Keepax ret = wm_adsp_buffer_populate(buf); 3456a792af69SCharles Keepax if (ret < 0) 3457a792af69SCharles Keepax return ret; 3458a792af69SCharles Keepax 34594f2d4eabSStuart Henderson /* 34604f2d4eabSStuart Henderson * v0 host_buffer coefficients didn't have versioning, so if the 34614f2d4eabSStuart Henderson * control is one word, assume version 0. 34624f2d4eabSStuart Henderson */ 34634f2d4eabSStuart Henderson if (ctl->len == 4) { 34640d3fba3eSCharles Keepax compr_dbg(buf, "host_buf_ptr=%x\n", buf->host_buf_ptr); 3465a792af69SCharles Keepax return 0; 34662cd19bdbSCharles Keepax } 34672cd19bdbSCharles Keepax 34684f2d4eabSStuart Henderson ret = regmap_raw_read(ctl->dsp->regmap, reg, &coeff_v1, 34694f2d4eabSStuart Henderson sizeof(coeff_v1)); 34704f2d4eabSStuart Henderson if (ret < 0) 34714f2d4eabSStuart Henderson return ret; 34724f2d4eabSStuart Henderson 34734f2d4eabSStuart Henderson coeff_v1.versions = be32_to_cpu(coeff_v1.versions); 34744f2d4eabSStuart Henderson val = coeff_v1.versions & HOST_BUF_COEFF_COMPAT_VER_MASK; 34754f2d4eabSStuart Henderson val >>= HOST_BUF_COEFF_COMPAT_VER_SHIFT; 34764f2d4eabSStuart Henderson 34774f2d4eabSStuart Henderson if (val > HOST_BUF_COEFF_SUPPORTED_COMPAT_VER) { 34784f2d4eabSStuart Henderson adsp_err(ctl->dsp, 34794f2d4eabSStuart Henderson "Host buffer coeff ver %u > supported version %u\n", 34804f2d4eabSStuart Henderson val, HOST_BUF_COEFF_SUPPORTED_COMPAT_VER); 34814f2d4eabSStuart Henderson return -EINVAL; 34824f2d4eabSStuart Henderson } 34834f2d4eabSStuart Henderson 34844f2d4eabSStuart Henderson for (i = 0; i < ARRAY_SIZE(coeff_v1.name); i++) 34854f2d4eabSStuart Henderson coeff_v1.name[i] = be32_to_cpu(coeff_v1.name[i]); 34864f2d4eabSStuart Henderson 34874f2d4eabSStuart Henderson wm_adsp_remove_padding((u32 *)&coeff_v1.name, 34884f2d4eabSStuart Henderson ARRAY_SIZE(coeff_v1.name), 34894f2d4eabSStuart Henderson WM_ADSP_DATA_WORD_SIZE); 34904f2d4eabSStuart Henderson 34914f2d4eabSStuart Henderson buf->name = kasprintf(GFP_KERNEL, "%s-dsp-%s", ctl->dsp->part, 34924f2d4eabSStuart Henderson (char *)&coeff_v1.name); 34934f2d4eabSStuart Henderson 34940d3fba3eSCharles Keepax compr_dbg(buf, "host_buf_ptr=%x coeff version %u\n", 34954f2d4eabSStuart Henderson buf->host_buf_ptr, val); 34964f2d4eabSStuart Henderson 34974f2d4eabSStuart Henderson return val; 34984f2d4eabSStuart Henderson } 34994f2d4eabSStuart Henderson 3500a792af69SCharles Keepax static int wm_adsp_buffer_init(struct wm_adsp *dsp) 3501a792af69SCharles Keepax { 3502a792af69SCharles Keepax struct wm_coeff_ctl *ctl; 3503a792af69SCharles Keepax int ret; 3504a792af69SCharles Keepax 3505a792af69SCharles Keepax list_for_each_entry(ctl, &dsp->ctl_list, list) { 3506a792af69SCharles Keepax if (ctl->type != WMFW_CTL_TYPE_HOST_BUFFER) 3507a792af69SCharles Keepax continue; 3508a792af69SCharles Keepax 3509a792af69SCharles Keepax if (!ctl->enabled) 3510a792af69SCharles Keepax continue; 3511a792af69SCharles Keepax 3512a792af69SCharles Keepax ret = wm_adsp_buffer_parse_coeff(ctl); 3513a792af69SCharles Keepax if (ret < 0) { 3514a792af69SCharles Keepax adsp_err(dsp, "Failed to parse coeff: %d\n", ret); 3515a792af69SCharles Keepax goto error; 35164f2d4eabSStuart Henderson } else if (ret == 0) { 35174f2d4eabSStuart Henderson /* Only one buffer supported for version 0 */ 3518a792af69SCharles Keepax return 0; 3519a792af69SCharles Keepax } 35204f2d4eabSStuart Henderson } 3521a792af69SCharles Keepax 35224f2d4eabSStuart Henderson if (list_empty(&dsp->buffer_list)) { 3523a792af69SCharles Keepax /* Fall back to legacy support */ 3524a792af69SCharles Keepax ret = wm_adsp_buffer_parse_legacy(dsp); 3525a792af69SCharles Keepax if (ret) { 3526a792af69SCharles Keepax adsp_err(dsp, "Failed to parse legacy: %d\n", ret); 3527a792af69SCharles Keepax goto error; 3528a792af69SCharles Keepax } 3529a792af69SCharles Keepax } 35302cd19bdbSCharles Keepax 35312cd19bdbSCharles Keepax return 0; 35322cd19bdbSCharles Keepax 3533a792af69SCharles Keepax error: 3534a792af69SCharles Keepax wm_adsp_buffer_free(dsp); 35352cd19bdbSCharles Keepax return ret; 35362cd19bdbSCharles Keepax } 35372cd19bdbSCharles Keepax 35382cd19bdbSCharles Keepax static int wm_adsp_buffer_free(struct wm_adsp *dsp) 35392cd19bdbSCharles Keepax { 35404f2d4eabSStuart Henderson struct wm_adsp_compr_buf *buf, *tmp; 3541721be3beSCharles Keepax 35424f2d4eabSStuart Henderson list_for_each_entry_safe(buf, tmp, &dsp->buffer_list, list) { 35434f2d4eabSStuart Henderson if (buf->compr) 35444f2d4eabSStuart Henderson wm_adsp_compr_detach(buf->compr); 35452cd19bdbSCharles Keepax 35464f2d4eabSStuart Henderson kfree(buf->name); 35474f2d4eabSStuart Henderson kfree(buf->regions); 35484f2d4eabSStuart Henderson list_del(&buf->list); 35494f2d4eabSStuart Henderson kfree(buf); 35502cd19bdbSCharles Keepax } 35512cd19bdbSCharles Keepax 35522cd19bdbSCharles Keepax return 0; 35532cd19bdbSCharles Keepax } 35542cd19bdbSCharles Keepax 3555f938f348SStuart Henderson static int wm_adsp_buffer_get_error(struct wm_adsp_compr_buf *buf) 3556f938f348SStuart Henderson { 3557f938f348SStuart Henderson int ret; 3558f938f348SStuart Henderson 3559f938f348SStuart Henderson ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(error), &buf->error); 3560f938f348SStuart Henderson if (ret < 0) { 356148ead31cSCharles Keepax compr_err(buf, "Failed to check buffer error: %d\n", ret); 3562f938f348SStuart Henderson return ret; 3563f938f348SStuart Henderson } 3564f938f348SStuart Henderson if (buf->error != 0) { 356548ead31cSCharles Keepax compr_err(buf, "Buffer error occurred: %d\n", buf->error); 3566f938f348SStuart Henderson return -EIO; 3567f938f348SStuart Henderson } 3568f938f348SStuart Henderson 3569f938f348SStuart Henderson return 0; 3570f938f348SStuart Henderson } 3571f938f348SStuart Henderson 357295fe9597SCharles Keepax int wm_adsp_compr_trigger(struct snd_compr_stream *stream, int cmd) 357395fe9597SCharles Keepax { 357495fe9597SCharles Keepax struct wm_adsp_compr *compr = stream->runtime->private_data; 357595fe9597SCharles Keepax struct wm_adsp *dsp = compr->dsp; 357695fe9597SCharles Keepax int ret = 0; 357795fe9597SCharles Keepax 35780d3fba3eSCharles Keepax compr_dbg(compr, "Trigger: %d\n", cmd); 357995fe9597SCharles Keepax 358095fe9597SCharles Keepax mutex_lock(&dsp->pwr_lock); 358195fe9597SCharles Keepax 358295fe9597SCharles Keepax switch (cmd) { 358395fe9597SCharles Keepax case SNDRV_PCM_TRIGGER_START: 358461fc060cSCharles Keepax if (!wm_adsp_compr_attached(compr)) { 358595fe9597SCharles Keepax ret = wm_adsp_compr_attach(compr); 358695fe9597SCharles Keepax if (ret < 0) { 35870d3fba3eSCharles Keepax compr_err(compr, "Failed to link buffer and stream: %d\n", 358895fe9597SCharles Keepax ret); 358995fe9597SCharles Keepax break; 359095fe9597SCharles Keepax } 359161fc060cSCharles Keepax } 359261fc060cSCharles Keepax 3593f938f348SStuart Henderson ret = wm_adsp_buffer_get_error(compr->buf); 3594f938f348SStuart Henderson if (ret < 0) 3595f938f348SStuart Henderson break; 3596f938f348SStuart Henderson 3597565ace46SCharles Keepax /* Trigger the IRQ at one fragment of data */ 3598565ace46SCharles Keepax ret = wm_adsp_buffer_write(compr->buf, 3599565ace46SCharles Keepax HOST_BUFFER_FIELD(high_water_mark), 3600565ace46SCharles Keepax wm_adsp_compr_frag_words(compr)); 3601565ace46SCharles Keepax if (ret < 0) { 36020d3fba3eSCharles Keepax compr_err(compr, "Failed to set high water mark: %d\n", 3603565ace46SCharles Keepax ret); 3604565ace46SCharles Keepax break; 3605565ace46SCharles Keepax } 360695fe9597SCharles Keepax break; 360795fe9597SCharles Keepax case SNDRV_PCM_TRIGGER_STOP: 3608639e5eb3SCharles Keepax wm_adsp_buffer_clear(compr->buf); 360995fe9597SCharles Keepax break; 361095fe9597SCharles Keepax default: 361195fe9597SCharles Keepax ret = -EINVAL; 361295fe9597SCharles Keepax break; 361395fe9597SCharles Keepax } 361495fe9597SCharles Keepax 361595fe9597SCharles Keepax mutex_unlock(&dsp->pwr_lock); 361695fe9597SCharles Keepax 361795fe9597SCharles Keepax return ret; 361895fe9597SCharles Keepax } 361995fe9597SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_trigger); 362095fe9597SCharles Keepax 3621565ace46SCharles Keepax static inline int wm_adsp_buffer_size(struct wm_adsp_compr_buf *buf) 3622565ace46SCharles Keepax { 3623565ace46SCharles Keepax int last_region = wm_adsp_fw[buf->dsp->fw].caps->num_regions - 1; 3624565ace46SCharles Keepax 3625565ace46SCharles Keepax return buf->regions[last_region].cumulative_size; 3626565ace46SCharles Keepax } 3627565ace46SCharles Keepax 3628565ace46SCharles Keepax static int wm_adsp_buffer_update_avail(struct wm_adsp_compr_buf *buf) 3629565ace46SCharles Keepax { 3630565ace46SCharles Keepax u32 next_read_index, next_write_index; 3631565ace46SCharles Keepax int write_index, read_index, avail; 3632565ace46SCharles Keepax int ret; 3633565ace46SCharles Keepax 3634565ace46SCharles Keepax /* Only sync read index if we haven't already read a valid index */ 3635565ace46SCharles Keepax if (buf->read_index < 0) { 3636565ace46SCharles Keepax ret = wm_adsp_buffer_read(buf, 3637565ace46SCharles Keepax HOST_BUFFER_FIELD(next_read_index), 3638565ace46SCharles Keepax &next_read_index); 3639565ace46SCharles Keepax if (ret < 0) 3640565ace46SCharles Keepax return ret; 3641565ace46SCharles Keepax 3642565ace46SCharles Keepax read_index = sign_extend32(next_read_index, 23); 3643565ace46SCharles Keepax 3644565ace46SCharles Keepax if (read_index < 0) { 36450d3fba3eSCharles Keepax compr_dbg(buf, "Avail check on unstarted stream\n"); 3646565ace46SCharles Keepax return 0; 3647565ace46SCharles Keepax } 3648565ace46SCharles Keepax 3649565ace46SCharles Keepax buf->read_index = read_index; 3650565ace46SCharles Keepax } 3651565ace46SCharles Keepax 3652565ace46SCharles Keepax ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(next_write_index), 3653565ace46SCharles Keepax &next_write_index); 3654565ace46SCharles Keepax if (ret < 0) 3655565ace46SCharles Keepax return ret; 3656565ace46SCharles Keepax 3657565ace46SCharles Keepax write_index = sign_extend32(next_write_index, 23); 3658565ace46SCharles Keepax 3659565ace46SCharles Keepax avail = write_index - buf->read_index; 3660565ace46SCharles Keepax if (avail < 0) 3661565ace46SCharles Keepax avail += wm_adsp_buffer_size(buf); 3662565ace46SCharles Keepax 36630d3fba3eSCharles Keepax compr_dbg(buf, "readindex=0x%x, writeindex=0x%x, avail=%d\n", 366433d740e0SCharles Keepax buf->read_index, write_index, avail * WM_ADSP_DATA_WORD_SIZE); 3665565ace46SCharles Keepax 3666565ace46SCharles Keepax buf->avail = avail; 3667565ace46SCharles Keepax 3668565ace46SCharles Keepax return 0; 3669565ace46SCharles Keepax } 3670565ace46SCharles Keepax 3671565ace46SCharles Keepax int wm_adsp_compr_handle_irq(struct wm_adsp *dsp) 3672565ace46SCharles Keepax { 3673612047f0SCharles Keepax struct wm_adsp_compr_buf *buf; 3674612047f0SCharles Keepax struct wm_adsp_compr *compr; 3675565ace46SCharles Keepax int ret = 0; 3676565ace46SCharles Keepax 3677565ace46SCharles Keepax mutex_lock(&dsp->pwr_lock); 3678565ace46SCharles Keepax 36794f2d4eabSStuart Henderson if (list_empty(&dsp->buffer_list)) { 3680565ace46SCharles Keepax ret = -ENODEV; 3681565ace46SCharles Keepax goto out; 3682565ace46SCharles Keepax } 36830d3fba3eSCharles Keepax 3684565ace46SCharles Keepax adsp_dbg(dsp, "Handling buffer IRQ\n"); 3685565ace46SCharles Keepax 36864f2d4eabSStuart Henderson list_for_each_entry(buf, &dsp->buffer_list, list) { 36874f2d4eabSStuart Henderson compr = buf->compr; 36884f2d4eabSStuart Henderson 36899771b18aSCharles Keepax ret = wm_adsp_buffer_get_error(buf); 36909771b18aSCharles Keepax if (ret < 0) 36915847609eSCharles Keepax goto out_notify; /* Wake poll to report error */ 3692565ace46SCharles Keepax 3693565ace46SCharles Keepax ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(irq_count), 3694565ace46SCharles Keepax &buf->irq_count); 3695565ace46SCharles Keepax if (ret < 0) { 36960d3fba3eSCharles Keepax compr_err(buf, "Failed to get irq_count: %d\n", ret); 3697565ace46SCharles Keepax goto out; 3698565ace46SCharles Keepax } 3699565ace46SCharles Keepax 3700565ace46SCharles Keepax ret = wm_adsp_buffer_update_avail(buf); 3701565ace46SCharles Keepax if (ret < 0) { 37020d3fba3eSCharles Keepax compr_err(buf, "Error reading avail: %d\n", ret); 3703565ace46SCharles Keepax goto out; 3704565ace46SCharles Keepax } 3705565ace46SCharles Keepax 370620b7f7c5SCharles Keepax if (wm_adsp_fw[dsp->fw].voice_trigger && buf->irq_count == 2) 370720b7f7c5SCharles Keepax ret = WM_ADSP_COMPR_VOICE_TRIGGER; 370820b7f7c5SCharles Keepax 37095847609eSCharles Keepax out_notify: 3710c7dae7c4SCharles Keepax if (compr && compr->stream) 371183a40ce9SCharles Keepax snd_compr_fragment_elapsed(compr->stream); 37124f2d4eabSStuart Henderson } 371383a40ce9SCharles Keepax 3714565ace46SCharles Keepax out: 3715565ace46SCharles Keepax mutex_unlock(&dsp->pwr_lock); 3716565ace46SCharles Keepax 3717565ace46SCharles Keepax return ret; 3718565ace46SCharles Keepax } 3719565ace46SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_handle_irq); 3720565ace46SCharles Keepax 3721565ace46SCharles Keepax static int wm_adsp_buffer_reenable_irq(struct wm_adsp_compr_buf *buf) 3722565ace46SCharles Keepax { 3723565ace46SCharles Keepax if (buf->irq_count & 0x01) 3724565ace46SCharles Keepax return 0; 3725565ace46SCharles Keepax 37260d3fba3eSCharles Keepax compr_dbg(buf, "Enable IRQ(0x%x) for next fragment\n", buf->irq_count); 3727565ace46SCharles Keepax 3728565ace46SCharles Keepax buf->irq_count |= 0x01; 3729565ace46SCharles Keepax 3730565ace46SCharles Keepax return wm_adsp_buffer_write(buf, HOST_BUFFER_FIELD(irq_ack), 3731565ace46SCharles Keepax buf->irq_count); 3732565ace46SCharles Keepax } 3733565ace46SCharles Keepax 3734565ace46SCharles Keepax int wm_adsp_compr_pointer(struct snd_compr_stream *stream, 3735565ace46SCharles Keepax struct snd_compr_tstamp *tstamp) 3736565ace46SCharles Keepax { 3737565ace46SCharles Keepax struct wm_adsp_compr *compr = stream->runtime->private_data; 3738565ace46SCharles Keepax struct wm_adsp *dsp = compr->dsp; 3739612047f0SCharles Keepax struct wm_adsp_compr_buf *buf; 3740565ace46SCharles Keepax int ret = 0; 3741565ace46SCharles Keepax 37420d3fba3eSCharles Keepax compr_dbg(compr, "Pointer request\n"); 3743565ace46SCharles Keepax 3744565ace46SCharles Keepax mutex_lock(&dsp->pwr_lock); 3745565ace46SCharles Keepax 3746612047f0SCharles Keepax buf = compr->buf; 3747612047f0SCharles Keepax 374828ee3d73SCharles Keepax if (!compr->buf || compr->buf->error) { 37498d280664SCharles Keepax snd_compr_stop_error(stream, SNDRV_PCM_STATE_XRUN); 3750565ace46SCharles Keepax ret = -EIO; 3751565ace46SCharles Keepax goto out; 3752565ace46SCharles Keepax } 3753565ace46SCharles Keepax 3754565ace46SCharles Keepax if (buf->avail < wm_adsp_compr_frag_words(compr)) { 3755565ace46SCharles Keepax ret = wm_adsp_buffer_update_avail(buf); 3756565ace46SCharles Keepax if (ret < 0) { 37570d3fba3eSCharles Keepax compr_err(compr, "Error reading avail: %d\n", ret); 3758565ace46SCharles Keepax goto out; 3759565ace46SCharles Keepax } 3760565ace46SCharles Keepax 3761565ace46SCharles Keepax /* 3762565ace46SCharles Keepax * If we really have less than 1 fragment available tell the 3763565ace46SCharles Keepax * DSP to inform us once a whole fragment is available. 3764565ace46SCharles Keepax */ 3765565ace46SCharles Keepax if (buf->avail < wm_adsp_compr_frag_words(compr)) { 37665847609eSCharles Keepax ret = wm_adsp_buffer_get_error(buf); 37678d280664SCharles Keepax if (ret < 0) { 37688d280664SCharles Keepax if (compr->buf->error) 37698d280664SCharles Keepax snd_compr_stop_error(stream, 37708d280664SCharles Keepax SNDRV_PCM_STATE_XRUN); 37715847609eSCharles Keepax goto out; 37728d280664SCharles Keepax } 37735847609eSCharles Keepax 3774565ace46SCharles Keepax ret = wm_adsp_buffer_reenable_irq(buf); 3775565ace46SCharles Keepax if (ret < 0) { 37760d3fba3eSCharles Keepax compr_err(compr, "Failed to re-enable buffer IRQ: %d\n", 3777565ace46SCharles Keepax ret); 3778565ace46SCharles Keepax goto out; 3779565ace46SCharles Keepax } 3780565ace46SCharles Keepax } 3781565ace46SCharles Keepax } 3782565ace46SCharles Keepax 3783565ace46SCharles Keepax tstamp->copied_total = compr->copied_total; 3784565ace46SCharles Keepax tstamp->copied_total += buf->avail * WM_ADSP_DATA_WORD_SIZE; 3785da2b3358SCharles Keepax tstamp->sampling_rate = compr->sample_rate; 3786565ace46SCharles Keepax 3787565ace46SCharles Keepax out: 3788565ace46SCharles Keepax mutex_unlock(&dsp->pwr_lock); 3789565ace46SCharles Keepax 3790565ace46SCharles Keepax return ret; 3791565ace46SCharles Keepax } 3792565ace46SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_pointer); 3793565ace46SCharles Keepax 379483a40ce9SCharles Keepax static int wm_adsp_buffer_capture_block(struct wm_adsp_compr *compr, int target) 379583a40ce9SCharles Keepax { 379683a40ce9SCharles Keepax struct wm_adsp_compr_buf *buf = compr->buf; 379783a40ce9SCharles Keepax unsigned int adsp_addr; 379883a40ce9SCharles Keepax int mem_type, nwords, max_read; 3799cc7d6ce9SCharles Keepax int i, ret; 380083a40ce9SCharles Keepax 380183a40ce9SCharles Keepax /* Calculate read parameters */ 380283a40ce9SCharles Keepax for (i = 0; i < wm_adsp_fw[buf->dsp->fw].caps->num_regions; ++i) 380383a40ce9SCharles Keepax if (buf->read_index < buf->regions[i].cumulative_size) 380483a40ce9SCharles Keepax break; 380583a40ce9SCharles Keepax 380683a40ce9SCharles Keepax if (i == wm_adsp_fw[buf->dsp->fw].caps->num_regions) 380783a40ce9SCharles Keepax return -EINVAL; 380883a40ce9SCharles Keepax 380983a40ce9SCharles Keepax mem_type = buf->regions[i].mem_type; 381083a40ce9SCharles Keepax adsp_addr = buf->regions[i].base_addr + 381183a40ce9SCharles Keepax (buf->read_index - buf->regions[i].offset); 381283a40ce9SCharles Keepax 381383a40ce9SCharles Keepax max_read = wm_adsp_compr_frag_words(compr); 381483a40ce9SCharles Keepax nwords = buf->regions[i].cumulative_size - buf->read_index; 381583a40ce9SCharles Keepax 381683a40ce9SCharles Keepax if (nwords > target) 381783a40ce9SCharles Keepax nwords = target; 381883a40ce9SCharles Keepax if (nwords > buf->avail) 381983a40ce9SCharles Keepax nwords = buf->avail; 382083a40ce9SCharles Keepax if (nwords > max_read) 382183a40ce9SCharles Keepax nwords = max_read; 382283a40ce9SCharles Keepax if (!nwords) 382383a40ce9SCharles Keepax return 0; 382483a40ce9SCharles Keepax 382583a40ce9SCharles Keepax /* Read data from DSP */ 382683a40ce9SCharles Keepax ret = wm_adsp_read_data_block(buf->dsp, mem_type, adsp_addr, 382783a40ce9SCharles Keepax nwords, compr->raw_buf); 382883a40ce9SCharles Keepax if (ret < 0) 382983a40ce9SCharles Keepax return ret; 383083a40ce9SCharles Keepax 3831cc7d6ce9SCharles Keepax wm_adsp_remove_padding(compr->raw_buf, nwords, WM_ADSP_DATA_WORD_SIZE); 383283a40ce9SCharles Keepax 383383a40ce9SCharles Keepax /* update read index to account for words read */ 383483a40ce9SCharles Keepax buf->read_index += nwords; 383583a40ce9SCharles Keepax if (buf->read_index == wm_adsp_buffer_size(buf)) 383683a40ce9SCharles Keepax buf->read_index = 0; 383783a40ce9SCharles Keepax 383883a40ce9SCharles Keepax ret = wm_adsp_buffer_write(buf, HOST_BUFFER_FIELD(next_read_index), 383983a40ce9SCharles Keepax buf->read_index); 384083a40ce9SCharles Keepax if (ret < 0) 384183a40ce9SCharles Keepax return ret; 384283a40ce9SCharles Keepax 384383a40ce9SCharles Keepax /* update avail to account for words read */ 384483a40ce9SCharles Keepax buf->avail -= nwords; 384583a40ce9SCharles Keepax 384683a40ce9SCharles Keepax return nwords; 384783a40ce9SCharles Keepax } 384883a40ce9SCharles Keepax 384983a40ce9SCharles Keepax static int wm_adsp_compr_read(struct wm_adsp_compr *compr, 385083a40ce9SCharles Keepax char __user *buf, size_t count) 385183a40ce9SCharles Keepax { 385283a40ce9SCharles Keepax int ntotal = 0; 385383a40ce9SCharles Keepax int nwords, nbytes; 385483a40ce9SCharles Keepax 38550d3fba3eSCharles Keepax compr_dbg(compr, "Requested read of %zu bytes\n", count); 385683a40ce9SCharles Keepax 385728ee3d73SCharles Keepax if (!compr->buf || compr->buf->error) { 38588d280664SCharles Keepax snd_compr_stop_error(compr->stream, SNDRV_PCM_STATE_XRUN); 385983a40ce9SCharles Keepax return -EIO; 38608d280664SCharles Keepax } 386183a40ce9SCharles Keepax 386283a40ce9SCharles Keepax count /= WM_ADSP_DATA_WORD_SIZE; 386383a40ce9SCharles Keepax 386483a40ce9SCharles Keepax do { 386583a40ce9SCharles Keepax nwords = wm_adsp_buffer_capture_block(compr, count); 386683a40ce9SCharles Keepax if (nwords < 0) { 38670d3fba3eSCharles Keepax compr_err(compr, "Failed to capture block: %d\n", 38680d3fba3eSCharles Keepax nwords); 386983a40ce9SCharles Keepax return nwords; 387083a40ce9SCharles Keepax } 387183a40ce9SCharles Keepax 387283a40ce9SCharles Keepax nbytes = nwords * WM_ADSP_DATA_WORD_SIZE; 387383a40ce9SCharles Keepax 38740d3fba3eSCharles Keepax compr_dbg(compr, "Read %d bytes\n", nbytes); 387583a40ce9SCharles Keepax 387683a40ce9SCharles Keepax if (copy_to_user(buf + ntotal, compr->raw_buf, nbytes)) { 38770d3fba3eSCharles Keepax compr_err(compr, "Failed to copy data to user: %d, %d\n", 387883a40ce9SCharles Keepax ntotal, nbytes); 387983a40ce9SCharles Keepax return -EFAULT; 388083a40ce9SCharles Keepax } 388183a40ce9SCharles Keepax 388283a40ce9SCharles Keepax count -= nwords; 388383a40ce9SCharles Keepax ntotal += nbytes; 388483a40ce9SCharles Keepax } while (nwords > 0 && count > 0); 388583a40ce9SCharles Keepax 388683a40ce9SCharles Keepax compr->copied_total += ntotal; 388783a40ce9SCharles Keepax 388883a40ce9SCharles Keepax return ntotal; 388983a40ce9SCharles Keepax } 389083a40ce9SCharles Keepax 389183a40ce9SCharles Keepax int wm_adsp_compr_copy(struct snd_compr_stream *stream, char __user *buf, 389283a40ce9SCharles Keepax size_t count) 389383a40ce9SCharles Keepax { 389483a40ce9SCharles Keepax struct wm_adsp_compr *compr = stream->runtime->private_data; 389583a40ce9SCharles Keepax struct wm_adsp *dsp = compr->dsp; 389683a40ce9SCharles Keepax int ret; 389783a40ce9SCharles Keepax 389883a40ce9SCharles Keepax mutex_lock(&dsp->pwr_lock); 389983a40ce9SCharles Keepax 390083a40ce9SCharles Keepax if (stream->direction == SND_COMPRESS_CAPTURE) 390183a40ce9SCharles Keepax ret = wm_adsp_compr_read(compr, buf, count); 390283a40ce9SCharles Keepax else 390383a40ce9SCharles Keepax ret = -ENOTSUPP; 390483a40ce9SCharles Keepax 390583a40ce9SCharles Keepax mutex_unlock(&dsp->pwr_lock); 390683a40ce9SCharles Keepax 390783a40ce9SCharles Keepax return ret; 390883a40ce9SCharles Keepax } 390983a40ce9SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_copy); 391083a40ce9SCharles Keepax 3911a2bcbc1bSCharles Keepax static void wm_adsp_fatal_error(struct wm_adsp *dsp) 3912a2bcbc1bSCharles Keepax { 3913a2bcbc1bSCharles Keepax struct wm_adsp_compr *compr; 3914a2bcbc1bSCharles Keepax 3915a2bcbc1bSCharles Keepax dsp->fatal_error = true; 3916a2bcbc1bSCharles Keepax 3917a2bcbc1bSCharles Keepax list_for_each_entry(compr, &dsp->compr_list, list) { 3918a2bcbc1bSCharles Keepax if (compr->stream) { 3919a2bcbc1bSCharles Keepax snd_compr_stop_error(compr->stream, 3920a2bcbc1bSCharles Keepax SNDRV_PCM_STATE_XRUN); 3921a2bcbc1bSCharles Keepax snd_compr_fragment_elapsed(compr->stream); 3922a2bcbc1bSCharles Keepax } 3923a2bcbc1bSCharles Keepax } 3924a2bcbc1bSCharles Keepax } 3925a2bcbc1bSCharles Keepax 392651a2c944SMayuresh Kulkarni irqreturn_t wm_adsp2_bus_error(struct wm_adsp *dsp) 392751a2c944SMayuresh Kulkarni { 392851a2c944SMayuresh Kulkarni unsigned int val; 392951a2c944SMayuresh Kulkarni struct regmap *regmap = dsp->regmap; 393051a2c944SMayuresh Kulkarni int ret = 0; 393151a2c944SMayuresh Kulkarni 3932a2225a6dSCharles Keepax mutex_lock(&dsp->pwr_lock); 3933a2225a6dSCharles Keepax 393451a2c944SMayuresh Kulkarni ret = regmap_read(regmap, dsp->base + ADSP2_LOCK_REGION_CTRL, &val); 393551a2c944SMayuresh Kulkarni if (ret) { 393651a2c944SMayuresh Kulkarni adsp_err(dsp, 393751a2c944SMayuresh Kulkarni "Failed to read Region Lock Ctrl register: %d\n", ret); 3938a2225a6dSCharles Keepax goto error; 393951a2c944SMayuresh Kulkarni } 394051a2c944SMayuresh Kulkarni 394151a2c944SMayuresh Kulkarni if (val & ADSP2_WDT_TIMEOUT_STS_MASK) { 394251a2c944SMayuresh Kulkarni adsp_err(dsp, "watchdog timeout error\n"); 394351a2c944SMayuresh Kulkarni wm_adsp_stop_watchdog(dsp); 3944a2bcbc1bSCharles Keepax wm_adsp_fatal_error(dsp); 394551a2c944SMayuresh Kulkarni } 394651a2c944SMayuresh Kulkarni 394751a2c944SMayuresh Kulkarni if (val & (ADSP2_SLAVE_ERR_MASK | ADSP2_REGION_LOCK_ERR_MASK)) { 394851a2c944SMayuresh Kulkarni if (val & ADSP2_SLAVE_ERR_MASK) 394951a2c944SMayuresh Kulkarni adsp_err(dsp, "bus error: slave error\n"); 395051a2c944SMayuresh Kulkarni else 395151a2c944SMayuresh Kulkarni adsp_err(dsp, "bus error: region lock error\n"); 395251a2c944SMayuresh Kulkarni 395351a2c944SMayuresh Kulkarni ret = regmap_read(regmap, dsp->base + ADSP2_BUS_ERR_ADDR, &val); 395451a2c944SMayuresh Kulkarni if (ret) { 395551a2c944SMayuresh Kulkarni adsp_err(dsp, 395651a2c944SMayuresh Kulkarni "Failed to read Bus Err Addr register: %d\n", 395751a2c944SMayuresh Kulkarni ret); 3958a2225a6dSCharles Keepax goto error; 395951a2c944SMayuresh Kulkarni } 396051a2c944SMayuresh Kulkarni 396151a2c944SMayuresh Kulkarni adsp_err(dsp, "bus error address = 0x%x\n", 396251a2c944SMayuresh Kulkarni val & ADSP2_BUS_ERR_ADDR_MASK); 396351a2c944SMayuresh Kulkarni 396451a2c944SMayuresh Kulkarni ret = regmap_read(regmap, 396551a2c944SMayuresh Kulkarni dsp->base + ADSP2_PMEM_ERR_ADDR_XMEM_ERR_ADDR, 396651a2c944SMayuresh Kulkarni &val); 396751a2c944SMayuresh Kulkarni if (ret) { 396851a2c944SMayuresh Kulkarni adsp_err(dsp, 396951a2c944SMayuresh Kulkarni "Failed to read Pmem Xmem Err Addr register: %d\n", 397051a2c944SMayuresh Kulkarni ret); 3971a2225a6dSCharles Keepax goto error; 397251a2c944SMayuresh Kulkarni } 397351a2c944SMayuresh Kulkarni 397451a2c944SMayuresh Kulkarni adsp_err(dsp, "xmem error address = 0x%x\n", 397551a2c944SMayuresh Kulkarni val & ADSP2_XMEM_ERR_ADDR_MASK); 397651a2c944SMayuresh Kulkarni adsp_err(dsp, "pmem error address = 0x%x\n", 397751a2c944SMayuresh Kulkarni (val & ADSP2_PMEM_ERR_ADDR_MASK) >> 397851a2c944SMayuresh Kulkarni ADSP2_PMEM_ERR_ADDR_SHIFT); 397951a2c944SMayuresh Kulkarni } 398051a2c944SMayuresh Kulkarni 398151a2c944SMayuresh Kulkarni regmap_update_bits(regmap, dsp->base + ADSP2_LOCK_REGION_CTRL, 398251a2c944SMayuresh Kulkarni ADSP2_CTRL_ERR_EINT, ADSP2_CTRL_ERR_EINT); 398351a2c944SMayuresh Kulkarni 3984a2225a6dSCharles Keepax error: 3985a2225a6dSCharles Keepax mutex_unlock(&dsp->pwr_lock); 3986a2225a6dSCharles Keepax 398751a2c944SMayuresh Kulkarni return IRQ_HANDLED; 398851a2c944SMayuresh Kulkarni } 398951a2c944SMayuresh Kulkarni EXPORT_SYMBOL_GPL(wm_adsp2_bus_error); 399051a2c944SMayuresh Kulkarni 39910a37c6efSPraveen Diwakar MODULE_LICENSE("GPL v2"); 3992