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 492159ad93SMark Brown #define ADSP1_CONTROL_1 0x00 502159ad93SMark Brown #define ADSP1_CONTROL_2 0x02 512159ad93SMark Brown #define ADSP1_CONTROL_3 0x03 522159ad93SMark Brown #define ADSP1_CONTROL_4 0x04 532159ad93SMark Brown #define ADSP1_CONTROL_5 0x06 542159ad93SMark Brown #define ADSP1_CONTROL_6 0x07 552159ad93SMark Brown #define ADSP1_CONTROL_7 0x08 562159ad93SMark Brown #define ADSP1_CONTROL_8 0x09 572159ad93SMark Brown #define ADSP1_CONTROL_9 0x0A 582159ad93SMark Brown #define ADSP1_CONTROL_10 0x0B 592159ad93SMark Brown #define ADSP1_CONTROL_11 0x0C 602159ad93SMark Brown #define ADSP1_CONTROL_12 0x0D 612159ad93SMark Brown #define ADSP1_CONTROL_13 0x0F 622159ad93SMark Brown #define ADSP1_CONTROL_14 0x10 632159ad93SMark Brown #define ADSP1_CONTROL_15 0x11 642159ad93SMark Brown #define ADSP1_CONTROL_16 0x12 652159ad93SMark Brown #define ADSP1_CONTROL_17 0x13 662159ad93SMark Brown #define ADSP1_CONTROL_18 0x14 672159ad93SMark Brown #define ADSP1_CONTROL_19 0x16 682159ad93SMark Brown #define ADSP1_CONTROL_20 0x17 692159ad93SMark Brown #define ADSP1_CONTROL_21 0x18 702159ad93SMark Brown #define ADSP1_CONTROL_22 0x1A 712159ad93SMark Brown #define ADSP1_CONTROL_23 0x1B 722159ad93SMark Brown #define ADSP1_CONTROL_24 0x1C 732159ad93SMark Brown #define ADSP1_CONTROL_25 0x1E 742159ad93SMark Brown #define ADSP1_CONTROL_26 0x20 752159ad93SMark Brown #define ADSP1_CONTROL_27 0x21 762159ad93SMark Brown #define ADSP1_CONTROL_28 0x22 772159ad93SMark Brown #define ADSP1_CONTROL_29 0x23 782159ad93SMark Brown #define ADSP1_CONTROL_30 0x24 792159ad93SMark Brown #define ADSP1_CONTROL_31 0x26 802159ad93SMark Brown 812159ad93SMark Brown /* 822159ad93SMark Brown * ADSP1 Control 19 832159ad93SMark Brown */ 842159ad93SMark Brown #define ADSP1_WDMA_BUFFER_LENGTH_MASK 0x00FF /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */ 852159ad93SMark Brown #define ADSP1_WDMA_BUFFER_LENGTH_SHIFT 0 /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */ 862159ad93SMark Brown #define ADSP1_WDMA_BUFFER_LENGTH_WIDTH 8 /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */ 872159ad93SMark Brown 882159ad93SMark Brown 892159ad93SMark Brown /* 902159ad93SMark Brown * ADSP1 Control 30 912159ad93SMark Brown */ 922159ad93SMark Brown #define ADSP1_DBG_CLK_ENA 0x0008 /* DSP1_DBG_CLK_ENA */ 932159ad93SMark Brown #define ADSP1_DBG_CLK_ENA_MASK 0x0008 /* DSP1_DBG_CLK_ENA */ 942159ad93SMark Brown #define ADSP1_DBG_CLK_ENA_SHIFT 3 /* DSP1_DBG_CLK_ENA */ 952159ad93SMark Brown #define ADSP1_DBG_CLK_ENA_WIDTH 1 /* DSP1_DBG_CLK_ENA */ 962159ad93SMark Brown #define ADSP1_SYS_ENA 0x0004 /* DSP1_SYS_ENA */ 972159ad93SMark Brown #define ADSP1_SYS_ENA_MASK 0x0004 /* DSP1_SYS_ENA */ 982159ad93SMark Brown #define ADSP1_SYS_ENA_SHIFT 2 /* DSP1_SYS_ENA */ 992159ad93SMark Brown #define ADSP1_SYS_ENA_WIDTH 1 /* DSP1_SYS_ENA */ 1002159ad93SMark Brown #define ADSP1_CORE_ENA 0x0002 /* DSP1_CORE_ENA */ 1012159ad93SMark Brown #define ADSP1_CORE_ENA_MASK 0x0002 /* DSP1_CORE_ENA */ 1022159ad93SMark Brown #define ADSP1_CORE_ENA_SHIFT 1 /* DSP1_CORE_ENA */ 1032159ad93SMark Brown #define ADSP1_CORE_ENA_WIDTH 1 /* DSP1_CORE_ENA */ 1042159ad93SMark Brown #define ADSP1_START 0x0001 /* DSP1_START */ 1052159ad93SMark Brown #define ADSP1_START_MASK 0x0001 /* DSP1_START */ 1062159ad93SMark Brown #define ADSP1_START_SHIFT 0 /* DSP1_START */ 1072159ad93SMark Brown #define ADSP1_START_WIDTH 1 /* DSP1_START */ 1082159ad93SMark Brown 10994e205bfSChris Rattray /* 11094e205bfSChris Rattray * ADSP1 Control 31 11194e205bfSChris Rattray */ 11294e205bfSChris Rattray #define ADSP1_CLK_SEL_MASK 0x0007 /* CLK_SEL_ENA */ 11394e205bfSChris Rattray #define ADSP1_CLK_SEL_SHIFT 0 /* CLK_SEL_ENA */ 11494e205bfSChris Rattray #define ADSP1_CLK_SEL_WIDTH 3 /* CLK_SEL_ENA */ 11594e205bfSChris Rattray 1162d30b575SMark Brown #define ADSP2_CONTROL 0x0 1172d30b575SMark Brown #define ADSP2_CLOCKING 0x1 118e1ea1879SRichard Fitzgerald #define ADSP2V2_CLOCKING 0x2 1192d30b575SMark Brown #define ADSP2_STATUS1 0x4 1202d30b575SMark Brown #define ADSP2_WDMA_CONFIG_1 0x30 1212d30b575SMark Brown #define ADSP2_WDMA_CONFIG_2 0x31 122e1ea1879SRichard Fitzgerald #define ADSP2V2_WDMA_CONFIG_2 0x32 1232d30b575SMark Brown #define ADSP2_RDMA_CONFIG_1 0x34 1242159ad93SMark Brown 12510337b07SRichard Fitzgerald #define ADSP2_SCRATCH0 0x40 12610337b07SRichard Fitzgerald #define ADSP2_SCRATCH1 0x41 12710337b07SRichard Fitzgerald #define ADSP2_SCRATCH2 0x42 12810337b07SRichard Fitzgerald #define ADSP2_SCRATCH3 0x43 12910337b07SRichard Fitzgerald 130e1ea1879SRichard Fitzgerald #define ADSP2V2_SCRATCH0_1 0x40 131e1ea1879SRichard Fitzgerald #define ADSP2V2_SCRATCH2_3 0x42 132e1ea1879SRichard Fitzgerald 1332159ad93SMark Brown /* 1342159ad93SMark Brown * ADSP2 Control 1352159ad93SMark Brown */ 1362159ad93SMark Brown 1372159ad93SMark Brown #define ADSP2_MEM_ENA 0x0010 /* DSP1_MEM_ENA */ 1382159ad93SMark Brown #define ADSP2_MEM_ENA_MASK 0x0010 /* DSP1_MEM_ENA */ 1392159ad93SMark Brown #define ADSP2_MEM_ENA_SHIFT 4 /* DSP1_MEM_ENA */ 1402159ad93SMark Brown #define ADSP2_MEM_ENA_WIDTH 1 /* DSP1_MEM_ENA */ 1412159ad93SMark Brown #define ADSP2_SYS_ENA 0x0004 /* DSP1_SYS_ENA */ 1422159ad93SMark Brown #define ADSP2_SYS_ENA_MASK 0x0004 /* DSP1_SYS_ENA */ 1432159ad93SMark Brown #define ADSP2_SYS_ENA_SHIFT 2 /* DSP1_SYS_ENA */ 1442159ad93SMark Brown #define ADSP2_SYS_ENA_WIDTH 1 /* DSP1_SYS_ENA */ 1452159ad93SMark Brown #define ADSP2_CORE_ENA 0x0002 /* DSP1_CORE_ENA */ 1462159ad93SMark Brown #define ADSP2_CORE_ENA_MASK 0x0002 /* DSP1_CORE_ENA */ 1472159ad93SMark Brown #define ADSP2_CORE_ENA_SHIFT 1 /* DSP1_CORE_ENA */ 1482159ad93SMark Brown #define ADSP2_CORE_ENA_WIDTH 1 /* DSP1_CORE_ENA */ 1492159ad93SMark Brown #define ADSP2_START 0x0001 /* DSP1_START */ 1502159ad93SMark Brown #define ADSP2_START_MASK 0x0001 /* DSP1_START */ 1512159ad93SMark Brown #define ADSP2_START_SHIFT 0 /* DSP1_START */ 1522159ad93SMark Brown #define ADSP2_START_WIDTH 1 /* DSP1_START */ 1532159ad93SMark Brown 1542159ad93SMark Brown /* 155973838a0SMark Brown * ADSP2 clocking 156973838a0SMark Brown */ 157973838a0SMark Brown #define ADSP2_CLK_SEL_MASK 0x0007 /* CLK_SEL_ENA */ 158973838a0SMark Brown #define ADSP2_CLK_SEL_SHIFT 0 /* CLK_SEL_ENA */ 159973838a0SMark Brown #define ADSP2_CLK_SEL_WIDTH 3 /* CLK_SEL_ENA */ 160973838a0SMark Brown 161973838a0SMark Brown /* 162e1ea1879SRichard Fitzgerald * ADSP2V2 clocking 163e1ea1879SRichard Fitzgerald */ 164e1ea1879SRichard Fitzgerald #define ADSP2V2_CLK_SEL_MASK 0x70000 /* CLK_SEL_ENA */ 165e1ea1879SRichard Fitzgerald #define ADSP2V2_CLK_SEL_SHIFT 16 /* CLK_SEL_ENA */ 166e1ea1879SRichard Fitzgerald #define ADSP2V2_CLK_SEL_WIDTH 3 /* CLK_SEL_ENA */ 167e1ea1879SRichard Fitzgerald 168e1ea1879SRichard Fitzgerald #define ADSP2V2_RATE_MASK 0x7800 /* DSP_RATE */ 169e1ea1879SRichard Fitzgerald #define ADSP2V2_RATE_SHIFT 11 /* DSP_RATE */ 170e1ea1879SRichard Fitzgerald #define ADSP2V2_RATE_WIDTH 4 /* DSP_RATE */ 171e1ea1879SRichard Fitzgerald 172e1ea1879SRichard Fitzgerald /* 1732159ad93SMark Brown * ADSP2 Status 1 1742159ad93SMark Brown */ 1752159ad93SMark Brown #define ADSP2_RAM_RDY 0x0001 1762159ad93SMark Brown #define ADSP2_RAM_RDY_MASK 0x0001 1772159ad93SMark Brown #define ADSP2_RAM_RDY_SHIFT 0 1782159ad93SMark Brown #define ADSP2_RAM_RDY_WIDTH 1 1792159ad93SMark Brown 18051a2c944SMayuresh Kulkarni /* 18151a2c944SMayuresh Kulkarni * ADSP2 Lock support 18251a2c944SMayuresh Kulkarni */ 18351a2c944SMayuresh Kulkarni #define ADSP2_LOCK_CODE_0 0x5555 18451a2c944SMayuresh Kulkarni #define ADSP2_LOCK_CODE_1 0xAAAA 18551a2c944SMayuresh Kulkarni 18651a2c944SMayuresh Kulkarni #define ADSP2_WATCHDOG 0x0A 18751a2c944SMayuresh Kulkarni #define ADSP2_BUS_ERR_ADDR 0x52 18851a2c944SMayuresh Kulkarni #define ADSP2_REGION_LOCK_STATUS 0x64 18951a2c944SMayuresh Kulkarni #define ADSP2_LOCK_REGION_1_LOCK_REGION_0 0x66 19051a2c944SMayuresh Kulkarni #define ADSP2_LOCK_REGION_3_LOCK_REGION_2 0x68 19151a2c944SMayuresh Kulkarni #define ADSP2_LOCK_REGION_5_LOCK_REGION_4 0x6A 19251a2c944SMayuresh Kulkarni #define ADSP2_LOCK_REGION_7_LOCK_REGION_6 0x6C 19351a2c944SMayuresh Kulkarni #define ADSP2_LOCK_REGION_9_LOCK_REGION_8 0x6E 19451a2c944SMayuresh Kulkarni #define ADSP2_LOCK_REGION_CTRL 0x7A 19551a2c944SMayuresh Kulkarni #define ADSP2_PMEM_ERR_ADDR_XMEM_ERR_ADDR 0x7C 19651a2c944SMayuresh Kulkarni 19751a2c944SMayuresh Kulkarni #define ADSP2_REGION_LOCK_ERR_MASK 0x8000 19851a2c944SMayuresh Kulkarni #define ADSP2_SLAVE_ERR_MASK 0x4000 19951a2c944SMayuresh Kulkarni #define ADSP2_WDT_TIMEOUT_STS_MASK 0x2000 20051a2c944SMayuresh Kulkarni #define ADSP2_CTRL_ERR_PAUSE_ENA 0x0002 20151a2c944SMayuresh Kulkarni #define ADSP2_CTRL_ERR_EINT 0x0001 20251a2c944SMayuresh Kulkarni 20351a2c944SMayuresh Kulkarni #define ADSP2_BUS_ERR_ADDR_MASK 0x00FFFFFF 20451a2c944SMayuresh Kulkarni #define ADSP2_XMEM_ERR_ADDR_MASK 0x0000FFFF 20551a2c944SMayuresh Kulkarni #define ADSP2_PMEM_ERR_ADDR_MASK 0x7FFF0000 20651a2c944SMayuresh Kulkarni #define ADSP2_PMEM_ERR_ADDR_SHIFT 16 20751a2c944SMayuresh Kulkarni #define ADSP2_WDT_ENA_MASK 0xFFFFFFFD 20851a2c944SMayuresh Kulkarni 20951a2c944SMayuresh Kulkarni #define ADSP2_LOCK_REGION_SHIFT 16 21051a2c944SMayuresh Kulkarni 2119ee78757SCharles Keepax #define ADSP_MAX_STD_CTRL_SIZE 512 2129ee78757SCharles Keepax 213f4f0c4c6SRichard Fitzgerald #define WM_ADSP_ACKED_CTL_TIMEOUT_MS 100 214f4f0c4c6SRichard Fitzgerald #define WM_ADSP_ACKED_CTL_N_QUICKPOLLS 10 215a23ebba8SRichard Fitzgerald #define WM_ADSP_ACKED_CTL_MIN_VALUE 0 216a23ebba8SRichard Fitzgerald #define WM_ADSP_ACKED_CTL_MAX_VALUE 0xFFFFFF 217f4f0c4c6SRichard Fitzgerald 218f4f0c4c6SRichard Fitzgerald /* 219f4f0c4c6SRichard Fitzgerald * Event control messages 220f4f0c4c6SRichard Fitzgerald */ 221f4f0c4c6SRichard Fitzgerald #define WM_ADSP_FW_EVENT_SHUTDOWN 0x000001 222f4f0c4c6SRichard Fitzgerald 223cf17c83cSMark Brown struct wm_adsp_buf { 224cf17c83cSMark Brown struct list_head list; 225cf17c83cSMark Brown void *buf; 226cf17c83cSMark Brown }; 227cf17c83cSMark Brown 228cf17c83cSMark Brown static struct wm_adsp_buf *wm_adsp_buf_alloc(const void *src, size_t len, 229cf17c83cSMark Brown struct list_head *list) 230cf17c83cSMark Brown { 231cf17c83cSMark Brown struct wm_adsp_buf *buf = kzalloc(sizeof(*buf), GFP_KERNEL); 232cf17c83cSMark Brown 233cf17c83cSMark Brown if (buf == NULL) 234cf17c83cSMark Brown return NULL; 235cf17c83cSMark Brown 236cdcd7f72SCharles Keepax buf->buf = vmalloc(len); 237cf17c83cSMark Brown if (!buf->buf) { 2384d41c74dSRichard Fitzgerald kfree(buf); 239cf17c83cSMark Brown return NULL; 240cf17c83cSMark Brown } 241cdcd7f72SCharles Keepax memcpy(buf->buf, src, len); 242cf17c83cSMark Brown 243cf17c83cSMark Brown if (list) 244cf17c83cSMark Brown list_add_tail(&buf->list, list); 245cf17c83cSMark Brown 246cf17c83cSMark Brown return buf; 247cf17c83cSMark Brown } 248cf17c83cSMark Brown 249cf17c83cSMark Brown static void wm_adsp_buf_free(struct list_head *list) 250cf17c83cSMark Brown { 251cf17c83cSMark Brown while (!list_empty(list)) { 252cf17c83cSMark Brown struct wm_adsp_buf *buf = list_first_entry(list, 253cf17c83cSMark Brown struct wm_adsp_buf, 254cf17c83cSMark Brown list); 255cf17c83cSMark Brown list_del(&buf->list); 256cdcd7f72SCharles Keepax vfree(buf->buf); 257cf17c83cSMark Brown kfree(buf); 258cf17c83cSMark Brown } 259cf17c83cSMark Brown } 260cf17c83cSMark Brown 261dd84f925SMark Brown #define WM_ADSP_FW_MBC_VSS 0 26204d1300fSCharles Keepax #define WM_ADSP_FW_HIFI 1 26304d1300fSCharles Keepax #define WM_ADSP_FW_TX 2 26404d1300fSCharles Keepax #define WM_ADSP_FW_TX_SPK 3 26504d1300fSCharles Keepax #define WM_ADSP_FW_RX 4 26604d1300fSCharles Keepax #define WM_ADSP_FW_RX_ANC 5 26704d1300fSCharles Keepax #define WM_ADSP_FW_CTRL 6 26804d1300fSCharles Keepax #define WM_ADSP_FW_ASR 7 26904d1300fSCharles Keepax #define WM_ADSP_FW_TRACE 8 27004d1300fSCharles Keepax #define WM_ADSP_FW_SPK_PROT 9 27104d1300fSCharles Keepax #define WM_ADSP_FW_MISC 10 27204d1300fSCharles Keepax 27304d1300fSCharles Keepax #define WM_ADSP_NUM_FW 11 274dd84f925SMark Brown 2751023dbd9SMark Brown static const char *wm_adsp_fw_text[WM_ADSP_NUM_FW] = { 276dd84f925SMark Brown [WM_ADSP_FW_MBC_VSS] = "MBC/VSS", 27704d1300fSCharles Keepax [WM_ADSP_FW_HIFI] = "MasterHiFi", 278dd84f925SMark Brown [WM_ADSP_FW_TX] = "Tx", 279dd84f925SMark Brown [WM_ADSP_FW_TX_SPK] = "Tx Speaker", 28004d1300fSCharles Keepax [WM_ADSP_FW_RX] = "Rx", 281dd84f925SMark Brown [WM_ADSP_FW_RX_ANC] = "Rx ANC", 28204d1300fSCharles Keepax [WM_ADSP_FW_CTRL] = "Voice Ctrl", 28304d1300fSCharles Keepax [WM_ADSP_FW_ASR] = "ASR Assist", 28404d1300fSCharles Keepax [WM_ADSP_FW_TRACE] = "Dbg Trace", 28504d1300fSCharles Keepax [WM_ADSP_FW_SPK_PROT] = "Protection", 28604d1300fSCharles Keepax [WM_ADSP_FW_MISC] = "Misc", 2871023dbd9SMark Brown }; 2881023dbd9SMark Brown 2892cd19bdbSCharles Keepax struct wm_adsp_system_config_xm_hdr { 2902cd19bdbSCharles Keepax __be32 sys_enable; 2912cd19bdbSCharles Keepax __be32 fw_id; 2922cd19bdbSCharles Keepax __be32 fw_rev; 2932cd19bdbSCharles Keepax __be32 boot_status; 2942cd19bdbSCharles Keepax __be32 watchdog; 2952cd19bdbSCharles Keepax __be32 dma_buffer_size; 2962cd19bdbSCharles Keepax __be32 rdma[6]; 2972cd19bdbSCharles Keepax __be32 wdma[8]; 2982cd19bdbSCharles Keepax __be32 build_job_name[3]; 2992cd19bdbSCharles Keepax __be32 build_job_number; 3002cd19bdbSCharles Keepax }; 3012cd19bdbSCharles Keepax 3022cd19bdbSCharles Keepax struct wm_adsp_alg_xm_struct { 3032cd19bdbSCharles Keepax __be32 magic; 3042cd19bdbSCharles Keepax __be32 smoothing; 3052cd19bdbSCharles Keepax __be32 threshold; 3062cd19bdbSCharles Keepax __be32 host_buf_ptr; 3072cd19bdbSCharles Keepax __be32 start_seq; 3082cd19bdbSCharles Keepax __be32 high_water_mark; 3092cd19bdbSCharles Keepax __be32 low_water_mark; 3102cd19bdbSCharles Keepax __be64 smoothed_power; 3112cd19bdbSCharles Keepax }; 3122cd19bdbSCharles Keepax 3132cd19bdbSCharles Keepax struct wm_adsp_buffer { 3142a2aefa4SRichard Fitzgerald __be32 buf1_base; /* Base addr of first buffer area */ 3152a2aefa4SRichard Fitzgerald __be32 buf1_size; /* Size of buf1 area in DSP words */ 3162a2aefa4SRichard Fitzgerald __be32 buf2_base; /* Base addr of 2nd buffer area */ 3172a2aefa4SRichard Fitzgerald __be32 buf1_buf2_size; /* Size of buf1+buf2 in DSP words */ 3182a2aefa4SRichard Fitzgerald __be32 buf3_base; /* Base addr of buf3 area */ 3192a2aefa4SRichard Fitzgerald __be32 buf_total_size; /* Size of buf1+buf2+buf3 in DSP words */ 3202cd19bdbSCharles Keepax __be32 high_water_mark; /* Point at which IRQ is asserted */ 3212cd19bdbSCharles Keepax __be32 irq_count; /* bits 1-31 count IRQ assertions */ 3222cd19bdbSCharles Keepax __be32 irq_ack; /* acked IRQ count, bit 0 enables IRQ */ 3232cd19bdbSCharles Keepax __be32 next_write_index; /* word index of next write */ 3242cd19bdbSCharles Keepax __be32 next_read_index; /* word index of next read */ 3252cd19bdbSCharles Keepax __be32 error; /* error if any */ 3262cd19bdbSCharles Keepax __be32 oldest_block_index; /* word index of oldest surviving */ 3272cd19bdbSCharles Keepax __be32 requested_rewind; /* how many blocks rewind was done */ 3282cd19bdbSCharles Keepax __be32 reserved_space; /* internal */ 3292cd19bdbSCharles Keepax __be32 min_free; /* min free space since stream start */ 3302cd19bdbSCharles Keepax __be32 blocks_written[2]; /* total blocks written (64 bit) */ 3312cd19bdbSCharles Keepax __be32 words_written[2]; /* total words written (64 bit) */ 3322cd19bdbSCharles Keepax }; 3332cd19bdbSCharles Keepax 334721be3beSCharles Keepax struct wm_adsp_compr; 335721be3beSCharles Keepax 3362cd19bdbSCharles Keepax struct wm_adsp_compr_buf { 3372cd19bdbSCharles Keepax struct wm_adsp *dsp; 338721be3beSCharles Keepax struct wm_adsp_compr *compr; 3392cd19bdbSCharles Keepax 3402cd19bdbSCharles Keepax struct wm_adsp_buffer_region *regions; 3412cd19bdbSCharles Keepax u32 host_buf_ptr; 342565ace46SCharles Keepax 343565ace46SCharles Keepax u32 error; 344565ace46SCharles Keepax u32 irq_count; 345565ace46SCharles Keepax int read_index; 346565ace46SCharles Keepax int avail; 3472cd19bdbSCharles Keepax }; 3482cd19bdbSCharles Keepax 349406abc95SCharles Keepax struct wm_adsp_compr { 350406abc95SCharles Keepax struct wm_adsp *dsp; 35195fe9597SCharles Keepax struct wm_adsp_compr_buf *buf; 352406abc95SCharles Keepax 353406abc95SCharles Keepax struct snd_compr_stream *stream; 354406abc95SCharles Keepax struct snd_compressed_buffer size; 355565ace46SCharles Keepax 35683a40ce9SCharles Keepax u32 *raw_buf; 357565ace46SCharles Keepax unsigned int copied_total; 358da2b3358SCharles Keepax 359da2b3358SCharles Keepax unsigned int sample_rate; 360406abc95SCharles Keepax }; 361406abc95SCharles Keepax 362406abc95SCharles Keepax #define WM_ADSP_DATA_WORD_SIZE 3 363406abc95SCharles Keepax 364406abc95SCharles Keepax #define WM_ADSP_MIN_FRAGMENTS 1 365406abc95SCharles Keepax #define WM_ADSP_MAX_FRAGMENTS 256 366406abc95SCharles Keepax #define WM_ADSP_MIN_FRAGMENT_SIZE (64 * WM_ADSP_DATA_WORD_SIZE) 367406abc95SCharles Keepax #define WM_ADSP_MAX_FRAGMENT_SIZE (4096 * WM_ADSP_DATA_WORD_SIZE) 368406abc95SCharles Keepax 3692cd19bdbSCharles Keepax #define WM_ADSP_ALG_XM_STRUCT_MAGIC 0x49aec7 3702cd19bdbSCharles Keepax 3712cd19bdbSCharles Keepax #define HOST_BUFFER_FIELD(field) \ 3722cd19bdbSCharles Keepax (offsetof(struct wm_adsp_buffer, field) / sizeof(__be32)) 3732cd19bdbSCharles Keepax 3742cd19bdbSCharles Keepax #define ALG_XM_FIELD(field) \ 3752cd19bdbSCharles Keepax (offsetof(struct wm_adsp_alg_xm_struct, field) / sizeof(__be32)) 3762cd19bdbSCharles Keepax 3772cd19bdbSCharles Keepax static int wm_adsp_buffer_init(struct wm_adsp *dsp); 3782cd19bdbSCharles Keepax static int wm_adsp_buffer_free(struct wm_adsp *dsp); 3792cd19bdbSCharles Keepax 3802cd19bdbSCharles Keepax struct wm_adsp_buffer_region { 3812cd19bdbSCharles Keepax unsigned int offset; 3822cd19bdbSCharles Keepax unsigned int cumulative_size; 3832cd19bdbSCharles Keepax unsigned int mem_type; 3842cd19bdbSCharles Keepax unsigned int base_addr; 3852cd19bdbSCharles Keepax }; 3862cd19bdbSCharles Keepax 3872cd19bdbSCharles Keepax struct wm_adsp_buffer_region_def { 3882cd19bdbSCharles Keepax unsigned int mem_type; 3892cd19bdbSCharles Keepax unsigned int base_offset; 3902cd19bdbSCharles Keepax unsigned int size_offset; 3912cd19bdbSCharles Keepax }; 3922cd19bdbSCharles Keepax 3933a9686c4SCharles Keepax static const struct wm_adsp_buffer_region_def default_regions[] = { 3942cd19bdbSCharles Keepax { 3952cd19bdbSCharles Keepax .mem_type = WMFW_ADSP2_XM, 3962a2aefa4SRichard Fitzgerald .base_offset = HOST_BUFFER_FIELD(buf1_base), 3972a2aefa4SRichard Fitzgerald .size_offset = HOST_BUFFER_FIELD(buf1_size), 3982cd19bdbSCharles Keepax }, 3992cd19bdbSCharles Keepax { 4002cd19bdbSCharles Keepax .mem_type = WMFW_ADSP2_XM, 4012a2aefa4SRichard Fitzgerald .base_offset = HOST_BUFFER_FIELD(buf2_base), 4022a2aefa4SRichard Fitzgerald .size_offset = HOST_BUFFER_FIELD(buf1_buf2_size), 4032cd19bdbSCharles Keepax }, 4042cd19bdbSCharles Keepax { 4052cd19bdbSCharles Keepax .mem_type = WMFW_ADSP2_YM, 4062a2aefa4SRichard Fitzgerald .base_offset = HOST_BUFFER_FIELD(buf3_base), 4072a2aefa4SRichard Fitzgerald .size_offset = HOST_BUFFER_FIELD(buf_total_size), 4082cd19bdbSCharles Keepax }, 4092cd19bdbSCharles Keepax }; 4102cd19bdbSCharles Keepax 411406abc95SCharles Keepax struct wm_adsp_fw_caps { 412406abc95SCharles Keepax u32 id; 413406abc95SCharles Keepax struct snd_codec_desc desc; 4142cd19bdbSCharles Keepax int num_regions; 4153a9686c4SCharles Keepax const struct wm_adsp_buffer_region_def *region_defs; 416406abc95SCharles Keepax }; 417406abc95SCharles Keepax 418e6d00f34SCharles Keepax static const struct wm_adsp_fw_caps ctrl_caps[] = { 419406abc95SCharles Keepax { 420406abc95SCharles Keepax .id = SND_AUDIOCODEC_BESPOKE, 421406abc95SCharles Keepax .desc = { 4223bbc2705SRichard Fitzgerald .max_ch = 8, 423406abc95SCharles Keepax .sample_rates = { 16000 }, 424406abc95SCharles Keepax .num_sample_rates = 1, 425406abc95SCharles Keepax .formats = SNDRV_PCM_FMTBIT_S16_LE, 426406abc95SCharles Keepax }, 427e6d00f34SCharles Keepax .num_regions = ARRAY_SIZE(default_regions), 428e6d00f34SCharles Keepax .region_defs = default_regions, 429406abc95SCharles Keepax }, 430406abc95SCharles Keepax }; 431406abc95SCharles Keepax 4327ce4283cSCharles Keepax static const struct wm_adsp_fw_caps trace_caps[] = { 4337ce4283cSCharles Keepax { 4347ce4283cSCharles Keepax .id = SND_AUDIOCODEC_BESPOKE, 4357ce4283cSCharles Keepax .desc = { 4367ce4283cSCharles Keepax .max_ch = 8, 4377ce4283cSCharles Keepax .sample_rates = { 4387ce4283cSCharles Keepax 4000, 8000, 11025, 12000, 16000, 22050, 4397ce4283cSCharles Keepax 24000, 32000, 44100, 48000, 64000, 88200, 4407ce4283cSCharles Keepax 96000, 176400, 192000 4417ce4283cSCharles Keepax }, 4427ce4283cSCharles Keepax .num_sample_rates = 15, 4437ce4283cSCharles Keepax .formats = SNDRV_PCM_FMTBIT_S16_LE, 4447ce4283cSCharles Keepax }, 4457ce4283cSCharles Keepax .num_regions = ARRAY_SIZE(default_regions), 4467ce4283cSCharles Keepax .region_defs = default_regions, 447406abc95SCharles Keepax }, 448406abc95SCharles Keepax }; 449406abc95SCharles Keepax 450406abc95SCharles Keepax static const struct { 4511023dbd9SMark Brown const char *file; 452406abc95SCharles Keepax int compr_direction; 453406abc95SCharles Keepax int num_caps; 454406abc95SCharles Keepax const struct wm_adsp_fw_caps *caps; 45520b7f7c5SCharles Keepax bool voice_trigger; 4561023dbd9SMark Brown } wm_adsp_fw[WM_ADSP_NUM_FW] = { 457dd84f925SMark Brown [WM_ADSP_FW_MBC_VSS] = { .file = "mbc-vss" }, 45804d1300fSCharles Keepax [WM_ADSP_FW_HIFI] = { .file = "hifi" }, 459dd84f925SMark Brown [WM_ADSP_FW_TX] = { .file = "tx" }, 460dd84f925SMark Brown [WM_ADSP_FW_TX_SPK] = { .file = "tx-spk" }, 46104d1300fSCharles Keepax [WM_ADSP_FW_RX] = { .file = "rx" }, 462dd84f925SMark Brown [WM_ADSP_FW_RX_ANC] = { .file = "rx-anc" }, 463406abc95SCharles Keepax [WM_ADSP_FW_CTRL] = { 464406abc95SCharles Keepax .file = "ctrl", 465406abc95SCharles Keepax .compr_direction = SND_COMPRESS_CAPTURE, 466e6d00f34SCharles Keepax .num_caps = ARRAY_SIZE(ctrl_caps), 467e6d00f34SCharles Keepax .caps = ctrl_caps, 46820b7f7c5SCharles Keepax .voice_trigger = true, 469406abc95SCharles Keepax }, 47004d1300fSCharles Keepax [WM_ADSP_FW_ASR] = { .file = "asr" }, 4717ce4283cSCharles Keepax [WM_ADSP_FW_TRACE] = { 4727ce4283cSCharles Keepax .file = "trace", 4737ce4283cSCharles Keepax .compr_direction = SND_COMPRESS_CAPTURE, 4747ce4283cSCharles Keepax .num_caps = ARRAY_SIZE(trace_caps), 4757ce4283cSCharles Keepax .caps = trace_caps, 4767ce4283cSCharles Keepax }, 47704d1300fSCharles Keepax [WM_ADSP_FW_SPK_PROT] = { .file = "spk-prot" }, 47804d1300fSCharles Keepax [WM_ADSP_FW_MISC] = { .file = "misc" }, 4791023dbd9SMark Brown }; 4801023dbd9SMark Brown 4816ab2b7b4SDimitris Papastamos struct wm_coeff_ctl_ops { 4826ab2b7b4SDimitris Papastamos int (*xget)(struct snd_kcontrol *kcontrol, 4836ab2b7b4SDimitris Papastamos struct snd_ctl_elem_value *ucontrol); 4846ab2b7b4SDimitris Papastamos int (*xput)(struct snd_kcontrol *kcontrol, 4856ab2b7b4SDimitris Papastamos struct snd_ctl_elem_value *ucontrol); 4866ab2b7b4SDimitris Papastamos }; 4876ab2b7b4SDimitris Papastamos 4886ab2b7b4SDimitris Papastamos struct wm_coeff_ctl { 4896ab2b7b4SDimitris Papastamos const char *name; 4902323736dSCharles Keepax const char *fw_name; 4913809f001SCharles Keepax struct wm_adsp_alg_region alg_region; 4926ab2b7b4SDimitris Papastamos struct wm_coeff_ctl_ops ops; 4933809f001SCharles Keepax struct wm_adsp *dsp; 4946ab2b7b4SDimitris Papastamos unsigned int enabled:1; 4956ab2b7b4SDimitris Papastamos struct list_head list; 4966ab2b7b4SDimitris Papastamos void *cache; 4972323736dSCharles Keepax unsigned int offset; 4986ab2b7b4SDimitris Papastamos size_t len; 4990c2e3f34SDimitris Papastamos unsigned int set:1; 5009ee78757SCharles Keepax struct soc_bytes_ext bytes_ext; 50126c22a19SCharles Keepax unsigned int flags; 5028eb084d0SStuart Henderson unsigned int type; 5036ab2b7b4SDimitris Papastamos }; 5046ab2b7b4SDimitris Papastamos 5059ce5e6e6SRichard Fitzgerald static const char *wm_adsp_mem_region_name(unsigned int type) 5069ce5e6e6SRichard Fitzgerald { 5079ce5e6e6SRichard Fitzgerald switch (type) { 5089ce5e6e6SRichard Fitzgerald case WMFW_ADSP1_PM: 5099ce5e6e6SRichard Fitzgerald return "PM"; 5109ce5e6e6SRichard Fitzgerald case WMFW_ADSP1_DM: 5119ce5e6e6SRichard Fitzgerald return "DM"; 5129ce5e6e6SRichard Fitzgerald case WMFW_ADSP2_XM: 5139ce5e6e6SRichard Fitzgerald return "XM"; 5149ce5e6e6SRichard Fitzgerald case WMFW_ADSP2_YM: 5159ce5e6e6SRichard Fitzgerald return "YM"; 5169ce5e6e6SRichard Fitzgerald case WMFW_ADSP1_ZM: 5179ce5e6e6SRichard Fitzgerald return "ZM"; 5189ce5e6e6SRichard Fitzgerald default: 5199ce5e6e6SRichard Fitzgerald return NULL; 5209ce5e6e6SRichard Fitzgerald } 5219ce5e6e6SRichard Fitzgerald } 5229ce5e6e6SRichard Fitzgerald 523f9f55e31SRichard Fitzgerald #ifdef CONFIG_DEBUG_FS 524f9f55e31SRichard Fitzgerald static void wm_adsp_debugfs_save_wmfwname(struct wm_adsp *dsp, const char *s) 525f9f55e31SRichard Fitzgerald { 526f9f55e31SRichard Fitzgerald char *tmp = kasprintf(GFP_KERNEL, "%s\n", s); 527f9f55e31SRichard Fitzgerald 528f9f55e31SRichard Fitzgerald kfree(dsp->wmfw_file_name); 529f9f55e31SRichard Fitzgerald dsp->wmfw_file_name = tmp; 530f9f55e31SRichard Fitzgerald } 531f9f55e31SRichard Fitzgerald 532f9f55e31SRichard Fitzgerald static void wm_adsp_debugfs_save_binname(struct wm_adsp *dsp, const char *s) 533f9f55e31SRichard Fitzgerald { 534f9f55e31SRichard Fitzgerald char *tmp = kasprintf(GFP_KERNEL, "%s\n", s); 535f9f55e31SRichard Fitzgerald 536f9f55e31SRichard Fitzgerald kfree(dsp->bin_file_name); 537f9f55e31SRichard Fitzgerald dsp->bin_file_name = tmp; 538f9f55e31SRichard Fitzgerald } 539f9f55e31SRichard Fitzgerald 540f9f55e31SRichard Fitzgerald static void wm_adsp_debugfs_clear(struct wm_adsp *dsp) 541f9f55e31SRichard Fitzgerald { 542f9f55e31SRichard Fitzgerald kfree(dsp->wmfw_file_name); 543f9f55e31SRichard Fitzgerald kfree(dsp->bin_file_name); 544f9f55e31SRichard Fitzgerald dsp->wmfw_file_name = NULL; 545f9f55e31SRichard Fitzgerald dsp->bin_file_name = NULL; 546f9f55e31SRichard Fitzgerald } 547f9f55e31SRichard Fitzgerald 548f9f55e31SRichard Fitzgerald static ssize_t wm_adsp_debugfs_wmfw_read(struct file *file, 549f9f55e31SRichard Fitzgerald char __user *user_buf, 550f9f55e31SRichard Fitzgerald size_t count, loff_t *ppos) 551f9f55e31SRichard Fitzgerald { 552f9f55e31SRichard Fitzgerald struct wm_adsp *dsp = file->private_data; 553f9f55e31SRichard Fitzgerald ssize_t ret; 554f9f55e31SRichard Fitzgerald 555078e7183SCharles Keepax mutex_lock(&dsp->pwr_lock); 556f9f55e31SRichard Fitzgerald 55728823ebaSCharles Keepax if (!dsp->wmfw_file_name || !dsp->booted) 558f9f55e31SRichard Fitzgerald ret = 0; 559f9f55e31SRichard Fitzgerald else 560f9f55e31SRichard Fitzgerald ret = simple_read_from_buffer(user_buf, count, ppos, 561f9f55e31SRichard Fitzgerald dsp->wmfw_file_name, 562f9f55e31SRichard Fitzgerald strlen(dsp->wmfw_file_name)); 563f9f55e31SRichard Fitzgerald 564078e7183SCharles Keepax mutex_unlock(&dsp->pwr_lock); 565f9f55e31SRichard Fitzgerald return ret; 566f9f55e31SRichard Fitzgerald } 567f9f55e31SRichard Fitzgerald 568f9f55e31SRichard Fitzgerald static ssize_t wm_adsp_debugfs_bin_read(struct file *file, 569f9f55e31SRichard Fitzgerald char __user *user_buf, 570f9f55e31SRichard Fitzgerald size_t count, loff_t *ppos) 571f9f55e31SRichard Fitzgerald { 572f9f55e31SRichard Fitzgerald struct wm_adsp *dsp = file->private_data; 573f9f55e31SRichard Fitzgerald ssize_t ret; 574f9f55e31SRichard Fitzgerald 575078e7183SCharles Keepax mutex_lock(&dsp->pwr_lock); 576f9f55e31SRichard Fitzgerald 57728823ebaSCharles Keepax if (!dsp->bin_file_name || !dsp->booted) 578f9f55e31SRichard Fitzgerald ret = 0; 579f9f55e31SRichard Fitzgerald else 580f9f55e31SRichard Fitzgerald ret = simple_read_from_buffer(user_buf, count, ppos, 581f9f55e31SRichard Fitzgerald dsp->bin_file_name, 582f9f55e31SRichard Fitzgerald strlen(dsp->bin_file_name)); 583f9f55e31SRichard Fitzgerald 584078e7183SCharles Keepax mutex_unlock(&dsp->pwr_lock); 585f9f55e31SRichard Fitzgerald return ret; 586f9f55e31SRichard Fitzgerald } 587f9f55e31SRichard Fitzgerald 588f9f55e31SRichard Fitzgerald static const struct { 589f9f55e31SRichard Fitzgerald const char *name; 590f9f55e31SRichard Fitzgerald const struct file_operations fops; 591f9f55e31SRichard Fitzgerald } wm_adsp_debugfs_fops[] = { 592f9f55e31SRichard Fitzgerald { 593f9f55e31SRichard Fitzgerald .name = "wmfw_file_name", 594f9f55e31SRichard Fitzgerald .fops = { 595f9f55e31SRichard Fitzgerald .open = simple_open, 596f9f55e31SRichard Fitzgerald .read = wm_adsp_debugfs_wmfw_read, 597f9f55e31SRichard Fitzgerald }, 598f9f55e31SRichard Fitzgerald }, 599f9f55e31SRichard Fitzgerald { 600f9f55e31SRichard Fitzgerald .name = "bin_file_name", 601f9f55e31SRichard Fitzgerald .fops = { 602f9f55e31SRichard Fitzgerald .open = simple_open, 603f9f55e31SRichard Fitzgerald .read = wm_adsp_debugfs_bin_read, 604f9f55e31SRichard Fitzgerald }, 605f9f55e31SRichard Fitzgerald }, 606f9f55e31SRichard Fitzgerald }; 607f9f55e31SRichard Fitzgerald 608f9f55e31SRichard Fitzgerald static void wm_adsp2_init_debugfs(struct wm_adsp *dsp, 6090fe1daa6SKuninori Morimoto struct snd_soc_component *component) 610f9f55e31SRichard Fitzgerald { 611f9f55e31SRichard Fitzgerald struct dentry *root = NULL; 612f9f55e31SRichard Fitzgerald int i; 613f9f55e31SRichard Fitzgerald 6140fe1daa6SKuninori Morimoto if (!component->debugfs_root) { 615f9f55e31SRichard Fitzgerald adsp_err(dsp, "No codec debugfs root\n"); 616f9f55e31SRichard Fitzgerald goto err; 617f9f55e31SRichard Fitzgerald } 618f9f55e31SRichard Fitzgerald 619605391d0SRichard Fitzgerald root = debugfs_create_dir(dsp->name, component->debugfs_root); 620f9f55e31SRichard Fitzgerald 621f9f55e31SRichard Fitzgerald if (!root) 622f9f55e31SRichard Fitzgerald goto err; 623f9f55e31SRichard Fitzgerald 6246a73cf46SJoe Perches if (!debugfs_create_bool("booted", 0444, root, &dsp->booted)) 62528823ebaSCharles Keepax goto err; 62628823ebaSCharles Keepax 6276a73cf46SJoe Perches if (!debugfs_create_bool("running", 0444, root, &dsp->running)) 628f9f55e31SRichard Fitzgerald goto err; 629f9f55e31SRichard Fitzgerald 6306a73cf46SJoe Perches if (!debugfs_create_x32("fw_id", 0444, root, &dsp->fw_id)) 631f9f55e31SRichard Fitzgerald goto err; 632f9f55e31SRichard Fitzgerald 6336a73cf46SJoe Perches if (!debugfs_create_x32("fw_version", 0444, root, &dsp->fw_id_version)) 634f9f55e31SRichard Fitzgerald goto err; 635f9f55e31SRichard Fitzgerald 636f9f55e31SRichard Fitzgerald for (i = 0; i < ARRAY_SIZE(wm_adsp_debugfs_fops); ++i) { 637f9f55e31SRichard Fitzgerald if (!debugfs_create_file(wm_adsp_debugfs_fops[i].name, 6386a73cf46SJoe Perches 0444, root, dsp, 639f9f55e31SRichard Fitzgerald &wm_adsp_debugfs_fops[i].fops)) 640f9f55e31SRichard Fitzgerald goto err; 641f9f55e31SRichard Fitzgerald } 642f9f55e31SRichard Fitzgerald 643f9f55e31SRichard Fitzgerald dsp->debugfs_root = root; 644f9f55e31SRichard Fitzgerald return; 645f9f55e31SRichard Fitzgerald 646f9f55e31SRichard Fitzgerald err: 647f9f55e31SRichard Fitzgerald debugfs_remove_recursive(root); 648f9f55e31SRichard Fitzgerald adsp_err(dsp, "Failed to create debugfs\n"); 649f9f55e31SRichard Fitzgerald } 650f9f55e31SRichard Fitzgerald 651f9f55e31SRichard Fitzgerald static void wm_adsp2_cleanup_debugfs(struct wm_adsp *dsp) 652f9f55e31SRichard Fitzgerald { 653f9f55e31SRichard Fitzgerald wm_adsp_debugfs_clear(dsp); 654f9f55e31SRichard Fitzgerald debugfs_remove_recursive(dsp->debugfs_root); 655f9f55e31SRichard Fitzgerald } 656f9f55e31SRichard Fitzgerald #else 657f9f55e31SRichard Fitzgerald static inline void wm_adsp2_init_debugfs(struct wm_adsp *dsp, 6580fe1daa6SKuninori Morimoto struct snd_soc_component *component) 659f9f55e31SRichard Fitzgerald { 660f9f55e31SRichard Fitzgerald } 661f9f55e31SRichard Fitzgerald 662f9f55e31SRichard Fitzgerald static inline void wm_adsp2_cleanup_debugfs(struct wm_adsp *dsp) 663f9f55e31SRichard Fitzgerald { 664f9f55e31SRichard Fitzgerald } 665f9f55e31SRichard Fitzgerald 666f9f55e31SRichard Fitzgerald static inline void wm_adsp_debugfs_save_wmfwname(struct wm_adsp *dsp, 667f9f55e31SRichard Fitzgerald const char *s) 668f9f55e31SRichard Fitzgerald { 669f9f55e31SRichard Fitzgerald } 670f9f55e31SRichard Fitzgerald 671f9f55e31SRichard Fitzgerald static inline void wm_adsp_debugfs_save_binname(struct wm_adsp *dsp, 672f9f55e31SRichard Fitzgerald const char *s) 673f9f55e31SRichard Fitzgerald { 674f9f55e31SRichard Fitzgerald } 675f9f55e31SRichard Fitzgerald 676f9f55e31SRichard Fitzgerald static inline void wm_adsp_debugfs_clear(struct wm_adsp *dsp) 677f9f55e31SRichard Fitzgerald { 678f9f55e31SRichard Fitzgerald } 679f9f55e31SRichard Fitzgerald #endif 680f9f55e31SRichard Fitzgerald 6810a047f07SRichard Fitzgerald int wm_adsp_fw_get(struct snd_kcontrol *kcontrol, 6821023dbd9SMark Brown struct snd_ctl_elem_value *ucontrol) 6831023dbd9SMark Brown { 6840fe1daa6SKuninori Morimoto struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); 6851023dbd9SMark Brown struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; 6860fe1daa6SKuninori Morimoto struct wm_adsp *dsp = snd_soc_component_get_drvdata(component); 6871023dbd9SMark Brown 68815c66570STakashi Iwai ucontrol->value.enumerated.item[0] = dsp[e->shift_l].fw; 6891023dbd9SMark Brown 6901023dbd9SMark Brown return 0; 6911023dbd9SMark Brown } 6920a047f07SRichard Fitzgerald EXPORT_SYMBOL_GPL(wm_adsp_fw_get); 6931023dbd9SMark Brown 6940a047f07SRichard Fitzgerald int wm_adsp_fw_put(struct snd_kcontrol *kcontrol, 6951023dbd9SMark Brown struct snd_ctl_elem_value *ucontrol) 6961023dbd9SMark Brown { 6970fe1daa6SKuninori Morimoto struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); 6981023dbd9SMark Brown struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; 6990fe1daa6SKuninori Morimoto struct wm_adsp *dsp = snd_soc_component_get_drvdata(component); 700d27c5e15SCharles Keepax int ret = 0; 7011023dbd9SMark Brown 70215c66570STakashi Iwai if (ucontrol->value.enumerated.item[0] == dsp[e->shift_l].fw) 7031023dbd9SMark Brown return 0; 7041023dbd9SMark Brown 70515c66570STakashi Iwai if (ucontrol->value.enumerated.item[0] >= WM_ADSP_NUM_FW) 7061023dbd9SMark Brown return -EINVAL; 7071023dbd9SMark Brown 708d27c5e15SCharles Keepax mutex_lock(&dsp[e->shift_l].pwr_lock); 7091023dbd9SMark Brown 71028823ebaSCharles Keepax if (dsp[e->shift_l].booted || dsp[e->shift_l].compr) 711d27c5e15SCharles Keepax ret = -EBUSY; 712d27c5e15SCharles Keepax else 71315c66570STakashi Iwai dsp[e->shift_l].fw = ucontrol->value.enumerated.item[0]; 7141023dbd9SMark Brown 715d27c5e15SCharles Keepax mutex_unlock(&dsp[e->shift_l].pwr_lock); 716d27c5e15SCharles Keepax 717d27c5e15SCharles Keepax return ret; 7181023dbd9SMark Brown } 7190a047f07SRichard Fitzgerald EXPORT_SYMBOL_GPL(wm_adsp_fw_put); 7201023dbd9SMark Brown 7210a047f07SRichard Fitzgerald const struct soc_enum wm_adsp_fw_enum[] = { 7221023dbd9SMark Brown SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 7231023dbd9SMark Brown SOC_ENUM_SINGLE(0, 1, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 7241023dbd9SMark Brown SOC_ENUM_SINGLE(0, 2, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 7251023dbd9SMark Brown SOC_ENUM_SINGLE(0, 3, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 726e1ea1879SRichard Fitzgerald SOC_ENUM_SINGLE(0, 4, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 727e1ea1879SRichard Fitzgerald SOC_ENUM_SINGLE(0, 5, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 728e1ea1879SRichard Fitzgerald SOC_ENUM_SINGLE(0, 6, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 7291023dbd9SMark Brown }; 7300a047f07SRichard Fitzgerald EXPORT_SYMBOL_GPL(wm_adsp_fw_enum); 7312159ad93SMark Brown 7322159ad93SMark Brown static struct wm_adsp_region const *wm_adsp_find_region(struct wm_adsp *dsp, 7332159ad93SMark Brown int type) 7342159ad93SMark Brown { 7352159ad93SMark Brown int i; 7362159ad93SMark Brown 7372159ad93SMark Brown for (i = 0; i < dsp->num_mems; i++) 7382159ad93SMark Brown if (dsp->mem[i].type == type) 7392159ad93SMark Brown return &dsp->mem[i]; 7402159ad93SMark Brown 7412159ad93SMark Brown return NULL; 7422159ad93SMark Brown } 7432159ad93SMark Brown 7443809f001SCharles Keepax static unsigned int wm_adsp_region_to_reg(struct wm_adsp_region const *mem, 74545b9ee72SMark Brown unsigned int offset) 74645b9ee72SMark Brown { 7473809f001SCharles Keepax if (WARN_ON(!mem)) 7486c452bdaSTakashi Iwai return offset; 7493809f001SCharles Keepax switch (mem->type) { 75045b9ee72SMark Brown case WMFW_ADSP1_PM: 7513809f001SCharles Keepax return mem->base + (offset * 3); 75245b9ee72SMark Brown case WMFW_ADSP1_DM: 7533809f001SCharles Keepax return mem->base + (offset * 2); 75445b9ee72SMark Brown case WMFW_ADSP2_XM: 7553809f001SCharles Keepax return mem->base + (offset * 2); 75645b9ee72SMark Brown case WMFW_ADSP2_YM: 7573809f001SCharles Keepax return mem->base + (offset * 2); 75845b9ee72SMark Brown case WMFW_ADSP1_ZM: 7593809f001SCharles Keepax return mem->base + (offset * 2); 76045b9ee72SMark Brown default: 7616c452bdaSTakashi Iwai WARN(1, "Unknown memory region type"); 76245b9ee72SMark Brown return offset; 76345b9ee72SMark Brown } 76445b9ee72SMark Brown } 76545b9ee72SMark Brown 76610337b07SRichard Fitzgerald static void wm_adsp2_show_fw_status(struct wm_adsp *dsp) 76710337b07SRichard Fitzgerald { 76820e00db2SRichard Fitzgerald unsigned int scratch[4]; 76920e00db2SRichard Fitzgerald unsigned int addr = dsp->base + ADSP2_SCRATCH0; 77020e00db2SRichard Fitzgerald unsigned int i; 77110337b07SRichard Fitzgerald int ret; 77210337b07SRichard Fitzgerald 77320e00db2SRichard Fitzgerald for (i = 0; i < ARRAY_SIZE(scratch); ++i) { 77420e00db2SRichard Fitzgerald ret = regmap_read(dsp->regmap, addr + i, &scratch[i]); 77510337b07SRichard Fitzgerald if (ret) { 77620e00db2SRichard Fitzgerald adsp_err(dsp, "Failed to read SCRATCH%u: %d\n", i, ret); 77710337b07SRichard Fitzgerald return; 77810337b07SRichard Fitzgerald } 77920e00db2SRichard Fitzgerald } 78010337b07SRichard Fitzgerald 78110337b07SRichard Fitzgerald adsp_dbg(dsp, "FW SCRATCH 0:0x%x 1:0x%x 2:0x%x 3:0x%x\n", 78220e00db2SRichard Fitzgerald scratch[0], scratch[1], scratch[2], scratch[3]); 78310337b07SRichard Fitzgerald } 78410337b07SRichard Fitzgerald 785e1ea1879SRichard Fitzgerald static void wm_adsp2v2_show_fw_status(struct wm_adsp *dsp) 786e1ea1879SRichard Fitzgerald { 78720e00db2SRichard Fitzgerald unsigned int scratch[2]; 788e1ea1879SRichard Fitzgerald int ret; 789e1ea1879SRichard Fitzgerald 79020e00db2SRichard Fitzgerald ret = regmap_read(dsp->regmap, dsp->base + ADSP2V2_SCRATCH0_1, 79120e00db2SRichard Fitzgerald &scratch[0]); 792e1ea1879SRichard Fitzgerald if (ret) { 79320e00db2SRichard Fitzgerald adsp_err(dsp, "Failed to read SCRATCH0_1: %d\n", ret); 794e1ea1879SRichard Fitzgerald return; 795e1ea1879SRichard Fitzgerald } 796e1ea1879SRichard Fitzgerald 79720e00db2SRichard Fitzgerald ret = regmap_read(dsp->regmap, dsp->base + ADSP2V2_SCRATCH2_3, 79820e00db2SRichard Fitzgerald &scratch[1]); 79920e00db2SRichard Fitzgerald if (ret) { 80020e00db2SRichard Fitzgerald adsp_err(dsp, "Failed to read SCRATCH2_3: %d\n", ret); 80120e00db2SRichard Fitzgerald return; 80220e00db2SRichard Fitzgerald } 803e1ea1879SRichard Fitzgerald 804e1ea1879SRichard Fitzgerald adsp_dbg(dsp, "FW SCRATCH 0:0x%x 1:0x%x 2:0x%x 3:0x%x\n", 805e1ea1879SRichard Fitzgerald scratch[0] & 0xFFFF, 806e1ea1879SRichard Fitzgerald scratch[0] >> 16, 807e1ea1879SRichard Fitzgerald scratch[1] & 0xFFFF, 808e1ea1879SRichard Fitzgerald scratch[1] >> 16); 809e1ea1879SRichard Fitzgerald } 810e1ea1879SRichard Fitzgerald 8119ee78757SCharles Keepax static inline struct wm_coeff_ctl *bytes_ext_to_ctl(struct soc_bytes_ext *ext) 8129ee78757SCharles Keepax { 8139ee78757SCharles Keepax return container_of(ext, struct wm_coeff_ctl, bytes_ext); 8149ee78757SCharles Keepax } 8159ee78757SCharles Keepax 816b396ebcaSRichard Fitzgerald static int wm_coeff_base_reg(struct wm_coeff_ctl *ctl, unsigned int *reg) 817b396ebcaSRichard Fitzgerald { 818b396ebcaSRichard Fitzgerald const struct wm_adsp_alg_region *alg_region = &ctl->alg_region; 819b396ebcaSRichard Fitzgerald struct wm_adsp *dsp = ctl->dsp; 820b396ebcaSRichard Fitzgerald const struct wm_adsp_region *mem; 821b396ebcaSRichard Fitzgerald 822b396ebcaSRichard Fitzgerald mem = wm_adsp_find_region(dsp, alg_region->type); 823b396ebcaSRichard Fitzgerald if (!mem) { 824b396ebcaSRichard Fitzgerald adsp_err(dsp, "No base for region %x\n", 825b396ebcaSRichard Fitzgerald alg_region->type); 826b396ebcaSRichard Fitzgerald return -EINVAL; 827b396ebcaSRichard Fitzgerald } 828b396ebcaSRichard Fitzgerald 829b396ebcaSRichard Fitzgerald *reg = wm_adsp_region_to_reg(mem, ctl->alg_region.base + ctl->offset); 830b396ebcaSRichard Fitzgerald 831b396ebcaSRichard Fitzgerald return 0; 832b396ebcaSRichard Fitzgerald } 833b396ebcaSRichard Fitzgerald 8347585a5b0SCharles Keepax static int wm_coeff_info(struct snd_kcontrol *kctl, 8356ab2b7b4SDimitris Papastamos struct snd_ctl_elem_info *uinfo) 8366ab2b7b4SDimitris Papastamos { 8379ee78757SCharles Keepax struct soc_bytes_ext *bytes_ext = 8389ee78757SCharles Keepax (struct soc_bytes_ext *)kctl->private_value; 8399ee78757SCharles Keepax struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); 8406ab2b7b4SDimitris Papastamos 841a23ebba8SRichard Fitzgerald switch (ctl->type) { 842a23ebba8SRichard Fitzgerald case WMFW_CTL_TYPE_ACKED: 843a23ebba8SRichard Fitzgerald uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 844a23ebba8SRichard Fitzgerald uinfo->value.integer.min = WM_ADSP_ACKED_CTL_MIN_VALUE; 845a23ebba8SRichard Fitzgerald uinfo->value.integer.max = WM_ADSP_ACKED_CTL_MAX_VALUE; 846a23ebba8SRichard Fitzgerald uinfo->value.integer.step = 1; 847a23ebba8SRichard Fitzgerald uinfo->count = 1; 848a23ebba8SRichard Fitzgerald break; 849a23ebba8SRichard Fitzgerald default: 8506ab2b7b4SDimitris Papastamos uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; 8516ab2b7b4SDimitris Papastamos uinfo->count = ctl->len; 852a23ebba8SRichard Fitzgerald break; 853a23ebba8SRichard Fitzgerald } 854a23ebba8SRichard Fitzgerald 8556ab2b7b4SDimitris Papastamos return 0; 8566ab2b7b4SDimitris Papastamos } 8576ab2b7b4SDimitris Papastamos 858f4f0c4c6SRichard Fitzgerald static int wm_coeff_write_acked_control(struct wm_coeff_ctl *ctl, 859f4f0c4c6SRichard Fitzgerald unsigned int event_id) 860f4f0c4c6SRichard Fitzgerald { 861f4f0c4c6SRichard Fitzgerald struct wm_adsp *dsp = ctl->dsp; 862f4f0c4c6SRichard Fitzgerald u32 val = cpu_to_be32(event_id); 863f4f0c4c6SRichard Fitzgerald unsigned int reg; 864f4f0c4c6SRichard Fitzgerald int i, ret; 865f4f0c4c6SRichard Fitzgerald 866f4f0c4c6SRichard Fitzgerald ret = wm_coeff_base_reg(ctl, ®); 867f4f0c4c6SRichard Fitzgerald if (ret) 868f4f0c4c6SRichard Fitzgerald return ret; 869f4f0c4c6SRichard Fitzgerald 870f4f0c4c6SRichard Fitzgerald adsp_dbg(dsp, "Sending 0x%x to acked control alg 0x%x %s:0x%x\n", 871f4f0c4c6SRichard Fitzgerald event_id, ctl->alg_region.alg, 872f4f0c4c6SRichard Fitzgerald wm_adsp_mem_region_name(ctl->alg_region.type), ctl->offset); 873f4f0c4c6SRichard Fitzgerald 874f4f0c4c6SRichard Fitzgerald ret = regmap_raw_write(dsp->regmap, reg, &val, sizeof(val)); 875f4f0c4c6SRichard Fitzgerald if (ret) { 876f4f0c4c6SRichard Fitzgerald adsp_err(dsp, "Failed to write %x: %d\n", reg, ret); 877f4f0c4c6SRichard Fitzgerald return ret; 878f4f0c4c6SRichard Fitzgerald } 879f4f0c4c6SRichard Fitzgerald 880f4f0c4c6SRichard Fitzgerald /* 881f4f0c4c6SRichard Fitzgerald * Poll for ack, we initially poll at ~1ms intervals for firmwares 882f4f0c4c6SRichard Fitzgerald * that respond quickly, then go to ~10ms polls. A firmware is unlikely 883f4f0c4c6SRichard Fitzgerald * to ack instantly so we do the first 1ms delay before reading the 884f4f0c4c6SRichard Fitzgerald * control to avoid a pointless bus transaction 885f4f0c4c6SRichard Fitzgerald */ 886f4f0c4c6SRichard Fitzgerald for (i = 0; i < WM_ADSP_ACKED_CTL_TIMEOUT_MS;) { 887f4f0c4c6SRichard Fitzgerald switch (i) { 888f4f0c4c6SRichard Fitzgerald case 0 ... WM_ADSP_ACKED_CTL_N_QUICKPOLLS - 1: 889f4f0c4c6SRichard Fitzgerald usleep_range(1000, 2000); 890f4f0c4c6SRichard Fitzgerald i++; 891f4f0c4c6SRichard Fitzgerald break; 892f4f0c4c6SRichard Fitzgerald default: 893f4f0c4c6SRichard Fitzgerald usleep_range(10000, 20000); 894f4f0c4c6SRichard Fitzgerald i += 10; 895f4f0c4c6SRichard Fitzgerald break; 896f4f0c4c6SRichard Fitzgerald } 897f4f0c4c6SRichard Fitzgerald 898f4f0c4c6SRichard Fitzgerald ret = regmap_raw_read(dsp->regmap, reg, &val, sizeof(val)); 899f4f0c4c6SRichard Fitzgerald if (ret) { 900f4f0c4c6SRichard Fitzgerald adsp_err(dsp, "Failed to read %x: %d\n", reg, ret); 901f4f0c4c6SRichard Fitzgerald return ret; 902f4f0c4c6SRichard Fitzgerald } 903f4f0c4c6SRichard Fitzgerald 904f4f0c4c6SRichard Fitzgerald if (val == 0) { 905f4f0c4c6SRichard Fitzgerald adsp_dbg(dsp, "Acked control ACKED at poll %u\n", i); 906f4f0c4c6SRichard Fitzgerald return 0; 907f4f0c4c6SRichard Fitzgerald } 908f4f0c4c6SRichard Fitzgerald } 909f4f0c4c6SRichard Fitzgerald 910f4f0c4c6SRichard Fitzgerald adsp_warn(dsp, "Acked control @0x%x alg:0x%x %s:0x%x timed out\n", 911f4f0c4c6SRichard Fitzgerald reg, ctl->alg_region.alg, 912f4f0c4c6SRichard Fitzgerald wm_adsp_mem_region_name(ctl->alg_region.type), 913f4f0c4c6SRichard Fitzgerald ctl->offset); 914f4f0c4c6SRichard Fitzgerald 915f4f0c4c6SRichard Fitzgerald return -ETIMEDOUT; 916f4f0c4c6SRichard Fitzgerald } 917f4f0c4c6SRichard Fitzgerald 918c9f8dd71SCharles Keepax static int wm_coeff_write_control(struct wm_coeff_ctl *ctl, 9196ab2b7b4SDimitris Papastamos const void *buf, size_t len) 9206ab2b7b4SDimitris Papastamos { 9213809f001SCharles Keepax struct wm_adsp *dsp = ctl->dsp; 9226ab2b7b4SDimitris Papastamos void *scratch; 9236ab2b7b4SDimitris Papastamos int ret; 9246ab2b7b4SDimitris Papastamos unsigned int reg; 9256ab2b7b4SDimitris Papastamos 926b396ebcaSRichard Fitzgerald ret = wm_coeff_base_reg(ctl, ®); 927b396ebcaSRichard Fitzgerald if (ret) 928b396ebcaSRichard Fitzgerald return ret; 9296ab2b7b4SDimitris Papastamos 9304f8ea6d7SCharles Keepax scratch = kmemdup(buf, len, GFP_KERNEL | GFP_DMA); 9316ab2b7b4SDimitris Papastamos if (!scratch) 9326ab2b7b4SDimitris Papastamos return -ENOMEM; 9336ab2b7b4SDimitris Papastamos 9343809f001SCharles Keepax ret = regmap_raw_write(dsp->regmap, reg, scratch, 9354f8ea6d7SCharles Keepax len); 9366ab2b7b4SDimitris Papastamos if (ret) { 9373809f001SCharles Keepax adsp_err(dsp, "Failed to write %zu bytes to %x: %d\n", 9384f8ea6d7SCharles Keepax len, reg, ret); 9396ab2b7b4SDimitris Papastamos kfree(scratch); 9406ab2b7b4SDimitris Papastamos return ret; 9416ab2b7b4SDimitris Papastamos } 9424f8ea6d7SCharles Keepax adsp_dbg(dsp, "Wrote %zu bytes to %x\n", len, reg); 9436ab2b7b4SDimitris Papastamos 9446ab2b7b4SDimitris Papastamos kfree(scratch); 9456ab2b7b4SDimitris Papastamos 9466ab2b7b4SDimitris Papastamos return 0; 9476ab2b7b4SDimitris Papastamos } 9486ab2b7b4SDimitris Papastamos 9497585a5b0SCharles Keepax static int wm_coeff_put(struct snd_kcontrol *kctl, 9506ab2b7b4SDimitris Papastamos struct snd_ctl_elem_value *ucontrol) 9516ab2b7b4SDimitris Papastamos { 9529ee78757SCharles Keepax struct soc_bytes_ext *bytes_ext = 9539ee78757SCharles Keepax (struct soc_bytes_ext *)kctl->private_value; 9549ee78757SCharles Keepax struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); 9556ab2b7b4SDimitris Papastamos char *p = ucontrol->value.bytes.data; 956168d10e7SCharles Keepax int ret = 0; 957168d10e7SCharles Keepax 958168d10e7SCharles Keepax mutex_lock(&ctl->dsp->pwr_lock); 9596ab2b7b4SDimitris Papastamos 96067430a39SCharles Keepax if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) 96167430a39SCharles Keepax ret = -EPERM; 96267430a39SCharles Keepax else 9636ab2b7b4SDimitris Papastamos memcpy(ctl->cache, p, ctl->len); 9646ab2b7b4SDimitris Papastamos 9650c2e3f34SDimitris Papastamos ctl->set = 1; 966cef45771SCharles Keepax if (ctl->enabled && ctl->dsp->running) 967168d10e7SCharles Keepax ret = wm_coeff_write_control(ctl, p, ctl->len); 9686ab2b7b4SDimitris Papastamos 969168d10e7SCharles Keepax mutex_unlock(&ctl->dsp->pwr_lock); 970168d10e7SCharles Keepax 971168d10e7SCharles Keepax return ret; 9726ab2b7b4SDimitris Papastamos } 9736ab2b7b4SDimitris Papastamos 9749ee78757SCharles Keepax static int wm_coeff_tlv_put(struct snd_kcontrol *kctl, 9759ee78757SCharles Keepax const unsigned int __user *bytes, unsigned int size) 9769ee78757SCharles Keepax { 9779ee78757SCharles Keepax struct soc_bytes_ext *bytes_ext = 9789ee78757SCharles Keepax (struct soc_bytes_ext *)kctl->private_value; 9799ee78757SCharles Keepax struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); 9809ee78757SCharles Keepax int ret = 0; 9819ee78757SCharles Keepax 9829ee78757SCharles Keepax mutex_lock(&ctl->dsp->pwr_lock); 9839ee78757SCharles Keepax 9849ee78757SCharles Keepax if (copy_from_user(ctl->cache, bytes, size)) { 9859ee78757SCharles Keepax ret = -EFAULT; 9869ee78757SCharles Keepax } else { 9879ee78757SCharles Keepax ctl->set = 1; 988cef45771SCharles Keepax if (ctl->enabled && ctl->dsp->running) 9899ee78757SCharles Keepax ret = wm_coeff_write_control(ctl, ctl->cache, size); 99067430a39SCharles Keepax else if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) 99167430a39SCharles Keepax ret = -EPERM; 9929ee78757SCharles Keepax } 9939ee78757SCharles Keepax 9949ee78757SCharles Keepax mutex_unlock(&ctl->dsp->pwr_lock); 9959ee78757SCharles Keepax 9969ee78757SCharles Keepax return ret; 9979ee78757SCharles Keepax } 9989ee78757SCharles Keepax 999a23ebba8SRichard Fitzgerald static int wm_coeff_put_acked(struct snd_kcontrol *kctl, 1000a23ebba8SRichard Fitzgerald struct snd_ctl_elem_value *ucontrol) 1001a23ebba8SRichard Fitzgerald { 1002a23ebba8SRichard Fitzgerald struct soc_bytes_ext *bytes_ext = 1003a23ebba8SRichard Fitzgerald (struct soc_bytes_ext *)kctl->private_value; 1004a23ebba8SRichard Fitzgerald struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); 1005a23ebba8SRichard Fitzgerald unsigned int val = ucontrol->value.integer.value[0]; 1006a23ebba8SRichard Fitzgerald int ret; 1007a23ebba8SRichard Fitzgerald 1008a23ebba8SRichard Fitzgerald if (val == 0) 1009a23ebba8SRichard Fitzgerald return 0; /* 0 means no event */ 1010a23ebba8SRichard Fitzgerald 1011a23ebba8SRichard Fitzgerald mutex_lock(&ctl->dsp->pwr_lock); 1012a23ebba8SRichard Fitzgerald 10137b4af793SCharles Keepax if (ctl->enabled && ctl->dsp->running) 1014a23ebba8SRichard Fitzgerald ret = wm_coeff_write_acked_control(ctl, val); 1015a23ebba8SRichard Fitzgerald else 1016a23ebba8SRichard Fitzgerald ret = -EPERM; 1017a23ebba8SRichard Fitzgerald 1018a23ebba8SRichard Fitzgerald mutex_unlock(&ctl->dsp->pwr_lock); 1019a23ebba8SRichard Fitzgerald 1020a23ebba8SRichard Fitzgerald return ret; 1021a23ebba8SRichard Fitzgerald } 1022a23ebba8SRichard Fitzgerald 1023c9f8dd71SCharles Keepax static int wm_coeff_read_control(struct wm_coeff_ctl *ctl, 10246ab2b7b4SDimitris Papastamos void *buf, size_t len) 10256ab2b7b4SDimitris Papastamos { 10263809f001SCharles Keepax struct wm_adsp *dsp = ctl->dsp; 10276ab2b7b4SDimitris Papastamos void *scratch; 10286ab2b7b4SDimitris Papastamos int ret; 10296ab2b7b4SDimitris Papastamos unsigned int reg; 10306ab2b7b4SDimitris Papastamos 1031b396ebcaSRichard Fitzgerald ret = wm_coeff_base_reg(ctl, ®); 1032b396ebcaSRichard Fitzgerald if (ret) 1033b396ebcaSRichard Fitzgerald return ret; 10346ab2b7b4SDimitris Papastamos 10354f8ea6d7SCharles Keepax scratch = kmalloc(len, GFP_KERNEL | GFP_DMA); 10366ab2b7b4SDimitris Papastamos if (!scratch) 10376ab2b7b4SDimitris Papastamos return -ENOMEM; 10386ab2b7b4SDimitris Papastamos 10394f8ea6d7SCharles Keepax ret = regmap_raw_read(dsp->regmap, reg, scratch, len); 10406ab2b7b4SDimitris Papastamos if (ret) { 10413809f001SCharles Keepax adsp_err(dsp, "Failed to read %zu bytes from %x: %d\n", 10425602a643SCharles Keepax len, reg, ret); 10436ab2b7b4SDimitris Papastamos kfree(scratch); 10446ab2b7b4SDimitris Papastamos return ret; 10456ab2b7b4SDimitris Papastamos } 10464f8ea6d7SCharles Keepax adsp_dbg(dsp, "Read %zu bytes from %x\n", len, reg); 10476ab2b7b4SDimitris Papastamos 10484f8ea6d7SCharles Keepax memcpy(buf, scratch, len); 10496ab2b7b4SDimitris Papastamos kfree(scratch); 10506ab2b7b4SDimitris Papastamos 10516ab2b7b4SDimitris Papastamos return 0; 10526ab2b7b4SDimitris Papastamos } 10536ab2b7b4SDimitris Papastamos 10547585a5b0SCharles Keepax static int wm_coeff_get(struct snd_kcontrol *kctl, 10556ab2b7b4SDimitris Papastamos struct snd_ctl_elem_value *ucontrol) 10566ab2b7b4SDimitris Papastamos { 10579ee78757SCharles Keepax struct soc_bytes_ext *bytes_ext = 10589ee78757SCharles Keepax (struct soc_bytes_ext *)kctl->private_value; 10599ee78757SCharles Keepax struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); 10606ab2b7b4SDimitris Papastamos char *p = ucontrol->value.bytes.data; 1061168d10e7SCharles Keepax int ret = 0; 1062168d10e7SCharles Keepax 1063168d10e7SCharles Keepax mutex_lock(&ctl->dsp->pwr_lock); 10646ab2b7b4SDimitris Papastamos 106526c22a19SCharles Keepax if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) { 1066cef45771SCharles Keepax if (ctl->enabled && ctl->dsp->running) 1067168d10e7SCharles Keepax ret = wm_coeff_read_control(ctl, p, ctl->len); 106826c22a19SCharles Keepax else 1069168d10e7SCharles Keepax ret = -EPERM; 1070168d10e7SCharles Keepax } else { 1071cef45771SCharles Keepax if (!ctl->flags && ctl->enabled && ctl->dsp->running) 1072bc1765d6SCharles Keepax ret = wm_coeff_read_control(ctl, ctl->cache, ctl->len); 1073bc1765d6SCharles Keepax 1074168d10e7SCharles Keepax memcpy(p, ctl->cache, ctl->len); 107526c22a19SCharles Keepax } 107626c22a19SCharles Keepax 1077168d10e7SCharles Keepax mutex_unlock(&ctl->dsp->pwr_lock); 107826c22a19SCharles Keepax 1079168d10e7SCharles Keepax return ret; 10806ab2b7b4SDimitris Papastamos } 10816ab2b7b4SDimitris Papastamos 10829ee78757SCharles Keepax static int wm_coeff_tlv_get(struct snd_kcontrol *kctl, 10839ee78757SCharles Keepax unsigned int __user *bytes, unsigned int size) 10849ee78757SCharles Keepax { 10859ee78757SCharles Keepax struct soc_bytes_ext *bytes_ext = 10869ee78757SCharles Keepax (struct soc_bytes_ext *)kctl->private_value; 10879ee78757SCharles Keepax struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); 10889ee78757SCharles Keepax int ret = 0; 10899ee78757SCharles Keepax 10909ee78757SCharles Keepax mutex_lock(&ctl->dsp->pwr_lock); 10919ee78757SCharles Keepax 10929ee78757SCharles Keepax if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) { 1093cef45771SCharles Keepax if (ctl->enabled && ctl->dsp->running) 10949ee78757SCharles Keepax ret = wm_coeff_read_control(ctl, ctl->cache, size); 10959ee78757SCharles Keepax else 10969ee78757SCharles Keepax ret = -EPERM; 10979ee78757SCharles Keepax } else { 1098cef45771SCharles Keepax if (!ctl->flags && ctl->enabled && ctl->dsp->running) 10999ee78757SCharles Keepax ret = wm_coeff_read_control(ctl, ctl->cache, size); 11009ee78757SCharles Keepax } 11019ee78757SCharles Keepax 11029ee78757SCharles Keepax if (!ret && copy_to_user(bytes, ctl->cache, size)) 11039ee78757SCharles Keepax ret = -EFAULT; 11049ee78757SCharles Keepax 11059ee78757SCharles Keepax mutex_unlock(&ctl->dsp->pwr_lock); 11069ee78757SCharles Keepax 11079ee78757SCharles Keepax return ret; 11089ee78757SCharles Keepax } 11099ee78757SCharles Keepax 1110a23ebba8SRichard Fitzgerald static int wm_coeff_get_acked(struct snd_kcontrol *kcontrol, 1111a23ebba8SRichard Fitzgerald struct snd_ctl_elem_value *ucontrol) 1112a23ebba8SRichard Fitzgerald { 1113a23ebba8SRichard Fitzgerald /* 1114a23ebba8SRichard Fitzgerald * Although it's not useful to read an acked control, we must satisfy 1115a23ebba8SRichard Fitzgerald * user-side assumptions that all controls are readable and that a 1116a23ebba8SRichard Fitzgerald * write of the same value should be filtered out (it's valid to send 1117a23ebba8SRichard Fitzgerald * the same event number again to the firmware). We therefore return 0, 1118a23ebba8SRichard Fitzgerald * meaning "no event" so valid event numbers will always be a change 1119a23ebba8SRichard Fitzgerald */ 1120a23ebba8SRichard Fitzgerald ucontrol->value.integer.value[0] = 0; 1121a23ebba8SRichard Fitzgerald 1122a23ebba8SRichard Fitzgerald return 0; 1123a23ebba8SRichard Fitzgerald } 1124a23ebba8SRichard Fitzgerald 11256ab2b7b4SDimitris Papastamos struct wmfw_ctl_work { 11263809f001SCharles Keepax struct wm_adsp *dsp; 11276ab2b7b4SDimitris Papastamos struct wm_coeff_ctl *ctl; 11286ab2b7b4SDimitris Papastamos struct work_struct work; 11296ab2b7b4SDimitris Papastamos }; 11306ab2b7b4SDimitris Papastamos 11319ee78757SCharles Keepax static unsigned int wmfw_convert_flags(unsigned int in, unsigned int len) 11329ee78757SCharles Keepax { 11339ee78757SCharles Keepax unsigned int out, rd, wr, vol; 11349ee78757SCharles Keepax 11359ee78757SCharles Keepax if (len > ADSP_MAX_STD_CTRL_SIZE) { 11369ee78757SCharles Keepax rd = SNDRV_CTL_ELEM_ACCESS_TLV_READ; 11379ee78757SCharles Keepax wr = SNDRV_CTL_ELEM_ACCESS_TLV_WRITE; 11389ee78757SCharles Keepax vol = SNDRV_CTL_ELEM_ACCESS_VOLATILE; 11399ee78757SCharles Keepax 11409ee78757SCharles Keepax out = SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK; 11419ee78757SCharles Keepax } else { 11429ee78757SCharles Keepax rd = SNDRV_CTL_ELEM_ACCESS_READ; 11439ee78757SCharles Keepax wr = SNDRV_CTL_ELEM_ACCESS_WRITE; 11449ee78757SCharles Keepax vol = SNDRV_CTL_ELEM_ACCESS_VOLATILE; 11459ee78757SCharles Keepax 11469ee78757SCharles Keepax out = 0; 11479ee78757SCharles Keepax } 11489ee78757SCharles Keepax 11499ee78757SCharles Keepax if (in) { 11509ee78757SCharles Keepax if (in & WMFW_CTL_FLAG_READABLE) 11519ee78757SCharles Keepax out |= rd; 11529ee78757SCharles Keepax if (in & WMFW_CTL_FLAG_WRITEABLE) 11539ee78757SCharles Keepax out |= wr; 11549ee78757SCharles Keepax if (in & WMFW_CTL_FLAG_VOLATILE) 11559ee78757SCharles Keepax out |= vol; 11569ee78757SCharles Keepax } else { 11579ee78757SCharles Keepax out |= rd | wr | vol; 11589ee78757SCharles Keepax } 11599ee78757SCharles Keepax 11609ee78757SCharles Keepax return out; 11619ee78757SCharles Keepax } 11629ee78757SCharles Keepax 11633809f001SCharles Keepax static int wmfw_add_ctl(struct wm_adsp *dsp, struct wm_coeff_ctl *ctl) 11646ab2b7b4SDimitris Papastamos { 11656ab2b7b4SDimitris Papastamos struct snd_kcontrol_new *kcontrol; 11666ab2b7b4SDimitris Papastamos int ret; 11676ab2b7b4SDimitris Papastamos 116892bb4c32SDimitris Papastamos if (!ctl || !ctl->name) 11696ab2b7b4SDimitris Papastamos return -EINVAL; 11706ab2b7b4SDimitris Papastamos 11716ab2b7b4SDimitris Papastamos kcontrol = kzalloc(sizeof(*kcontrol), GFP_KERNEL); 11726ab2b7b4SDimitris Papastamos if (!kcontrol) 11736ab2b7b4SDimitris Papastamos return -ENOMEM; 11746ab2b7b4SDimitris Papastamos 11756ab2b7b4SDimitris Papastamos kcontrol->name = ctl->name; 11766ab2b7b4SDimitris Papastamos kcontrol->info = wm_coeff_info; 11779ee78757SCharles Keepax kcontrol->iface = SNDRV_CTL_ELEM_IFACE_MIXER; 11789ee78757SCharles Keepax kcontrol->tlv.c = snd_soc_bytes_tlv_callback; 11799ee78757SCharles Keepax kcontrol->private_value = (unsigned long)&ctl->bytes_ext; 1180a23ebba8SRichard Fitzgerald kcontrol->access = wmfw_convert_flags(ctl->flags, ctl->len); 1181a23ebba8SRichard Fitzgerald 1182a23ebba8SRichard Fitzgerald switch (ctl->type) { 1183a23ebba8SRichard Fitzgerald case WMFW_CTL_TYPE_ACKED: 1184a23ebba8SRichard Fitzgerald kcontrol->get = wm_coeff_get_acked; 1185a23ebba8SRichard Fitzgerald kcontrol->put = wm_coeff_put_acked; 1186a23ebba8SRichard Fitzgerald break; 1187a23ebba8SRichard Fitzgerald default: 1188d7789f5bSRichard Fitzgerald if (kcontrol->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) { 11899ee78757SCharles Keepax ctl->bytes_ext.max = ctl->len; 11909ee78757SCharles Keepax ctl->bytes_ext.get = wm_coeff_tlv_get; 11919ee78757SCharles Keepax ctl->bytes_ext.put = wm_coeff_tlv_put; 1192d7789f5bSRichard Fitzgerald } else { 1193d7789f5bSRichard Fitzgerald kcontrol->get = wm_coeff_get; 1194d7789f5bSRichard Fitzgerald kcontrol->put = wm_coeff_put; 1195d7789f5bSRichard Fitzgerald } 1196a23ebba8SRichard Fitzgerald break; 1197a23ebba8SRichard Fitzgerald } 119826c22a19SCharles Keepax 11990fe1daa6SKuninori Morimoto ret = snd_soc_add_component_controls(dsp->component, kcontrol, 1); 12006ab2b7b4SDimitris Papastamos if (ret < 0) 12016ab2b7b4SDimitris Papastamos goto err_kcontrol; 12026ab2b7b4SDimitris Papastamos 12036ab2b7b4SDimitris Papastamos kfree(kcontrol); 12046ab2b7b4SDimitris Papastamos 12056ab2b7b4SDimitris Papastamos return 0; 12066ab2b7b4SDimitris Papastamos 12076ab2b7b4SDimitris Papastamos err_kcontrol: 12086ab2b7b4SDimitris Papastamos kfree(kcontrol); 12096ab2b7b4SDimitris Papastamos return ret; 12106ab2b7b4SDimitris Papastamos } 12116ab2b7b4SDimitris Papastamos 1212b21acc1cSCharles Keepax static int wm_coeff_init_control_caches(struct wm_adsp *dsp) 1213b21acc1cSCharles Keepax { 1214b21acc1cSCharles Keepax struct wm_coeff_ctl *ctl; 1215b21acc1cSCharles Keepax int ret; 1216b21acc1cSCharles Keepax 1217b21acc1cSCharles Keepax list_for_each_entry(ctl, &dsp->ctl_list, list) { 1218b21acc1cSCharles Keepax if (!ctl->enabled || ctl->set) 1219b21acc1cSCharles Keepax continue; 122026c22a19SCharles Keepax if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) 122126c22a19SCharles Keepax continue; 122226c22a19SCharles Keepax 122304ff40a9SRichard Fitzgerald /* 122404ff40a9SRichard Fitzgerald * For readable controls populate the cache from the DSP memory. 122504ff40a9SRichard Fitzgerald * For non-readable controls the cache was zero-filled when 122604ff40a9SRichard Fitzgerald * created so we don't need to do anything. 122704ff40a9SRichard Fitzgerald */ 122804ff40a9SRichard Fitzgerald if (!ctl->flags || (ctl->flags & WMFW_CTL_FLAG_READABLE)) { 12297d00cd97SCharles Keepax ret = wm_coeff_read_control(ctl, ctl->cache, ctl->len); 1230b21acc1cSCharles Keepax if (ret < 0) 1231b21acc1cSCharles Keepax return ret; 1232b21acc1cSCharles Keepax } 123304ff40a9SRichard Fitzgerald } 1234b21acc1cSCharles Keepax 1235b21acc1cSCharles Keepax return 0; 1236b21acc1cSCharles Keepax } 1237b21acc1cSCharles Keepax 1238b21acc1cSCharles Keepax static int wm_coeff_sync_controls(struct wm_adsp *dsp) 1239b21acc1cSCharles Keepax { 1240b21acc1cSCharles Keepax struct wm_coeff_ctl *ctl; 1241b21acc1cSCharles Keepax int ret; 1242b21acc1cSCharles Keepax 1243b21acc1cSCharles Keepax list_for_each_entry(ctl, &dsp->ctl_list, list) { 1244b21acc1cSCharles Keepax if (!ctl->enabled) 1245b21acc1cSCharles Keepax continue; 124626c22a19SCharles Keepax if (ctl->set && !(ctl->flags & WMFW_CTL_FLAG_VOLATILE)) { 12477d00cd97SCharles Keepax ret = wm_coeff_write_control(ctl, ctl->cache, ctl->len); 1248b21acc1cSCharles Keepax if (ret < 0) 1249b21acc1cSCharles Keepax return ret; 1250b21acc1cSCharles Keepax } 1251b21acc1cSCharles Keepax } 1252b21acc1cSCharles Keepax 1253b21acc1cSCharles Keepax return 0; 1254b21acc1cSCharles Keepax } 1255b21acc1cSCharles Keepax 1256f4f0c4c6SRichard Fitzgerald static void wm_adsp_signal_event_controls(struct wm_adsp *dsp, 1257f4f0c4c6SRichard Fitzgerald unsigned int event) 1258f4f0c4c6SRichard Fitzgerald { 1259f4f0c4c6SRichard Fitzgerald struct wm_coeff_ctl *ctl; 1260f4f0c4c6SRichard Fitzgerald int ret; 1261f4f0c4c6SRichard Fitzgerald 1262f4f0c4c6SRichard Fitzgerald list_for_each_entry(ctl, &dsp->ctl_list, list) { 1263f4f0c4c6SRichard Fitzgerald if (ctl->type != WMFW_CTL_TYPE_HOSTEVENT) 1264f4f0c4c6SRichard Fitzgerald continue; 1265f4f0c4c6SRichard Fitzgerald 126687aa6374SCharles Keepax if (!ctl->enabled) 126787aa6374SCharles Keepax continue; 126887aa6374SCharles Keepax 1269f4f0c4c6SRichard Fitzgerald ret = wm_coeff_write_acked_control(ctl, event); 1270f4f0c4c6SRichard Fitzgerald if (ret) 1271f4f0c4c6SRichard Fitzgerald adsp_warn(dsp, 1272f4f0c4c6SRichard Fitzgerald "Failed to send 0x%x event to alg 0x%x (%d)\n", 1273f4f0c4c6SRichard Fitzgerald event, ctl->alg_region.alg, ret); 1274f4f0c4c6SRichard Fitzgerald } 1275f4f0c4c6SRichard Fitzgerald } 1276f4f0c4c6SRichard Fitzgerald 1277b21acc1cSCharles Keepax static void wm_adsp_ctl_work(struct work_struct *work) 1278b21acc1cSCharles Keepax { 1279b21acc1cSCharles Keepax struct wmfw_ctl_work *ctl_work = container_of(work, 1280b21acc1cSCharles Keepax struct wmfw_ctl_work, 1281b21acc1cSCharles Keepax work); 1282b21acc1cSCharles Keepax 1283b21acc1cSCharles Keepax wmfw_add_ctl(ctl_work->dsp, ctl_work->ctl); 1284b21acc1cSCharles Keepax kfree(ctl_work); 1285b21acc1cSCharles Keepax } 1286b21acc1cSCharles Keepax 128766225e98SRichard Fitzgerald static void wm_adsp_free_ctl_blk(struct wm_coeff_ctl *ctl) 128866225e98SRichard Fitzgerald { 128966225e98SRichard Fitzgerald kfree(ctl->cache); 129066225e98SRichard Fitzgerald kfree(ctl->name); 129166225e98SRichard Fitzgerald kfree(ctl); 129266225e98SRichard Fitzgerald } 129366225e98SRichard Fitzgerald 1294b21acc1cSCharles Keepax static int wm_adsp_create_control(struct wm_adsp *dsp, 1295b21acc1cSCharles Keepax const struct wm_adsp_alg_region *alg_region, 12962323736dSCharles Keepax unsigned int offset, unsigned int len, 129726c22a19SCharles Keepax const char *subname, unsigned int subname_len, 12988eb084d0SStuart Henderson unsigned int flags, unsigned int type) 1299b21acc1cSCharles Keepax { 1300b21acc1cSCharles Keepax struct wm_coeff_ctl *ctl; 1301b21acc1cSCharles Keepax struct wmfw_ctl_work *ctl_work; 1302b21acc1cSCharles Keepax char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; 13039ce5e6e6SRichard Fitzgerald const char *region_name; 1304b21acc1cSCharles Keepax int ret; 1305b21acc1cSCharles Keepax 13069ce5e6e6SRichard Fitzgerald region_name = wm_adsp_mem_region_name(alg_region->type); 13079ce5e6e6SRichard Fitzgerald if (!region_name) { 13082323736dSCharles Keepax adsp_err(dsp, "Unknown region type: %d\n", alg_region->type); 1309b21acc1cSCharles Keepax return -EINVAL; 1310b21acc1cSCharles Keepax } 1311b21acc1cSCharles Keepax 1312cb5b57a9SCharles Keepax switch (dsp->fw_ver) { 1313cb5b57a9SCharles Keepax case 0: 1314cb5b57a9SCharles Keepax case 1: 1315605391d0SRichard Fitzgerald snprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "%s %s %x", 1316605391d0SRichard Fitzgerald dsp->name, region_name, alg_region->alg); 1317cb5b57a9SCharles Keepax break; 1318cb5b57a9SCharles Keepax default: 1319cb5b57a9SCharles Keepax ret = snprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, 1320605391d0SRichard Fitzgerald "%s%c %.12s %x", dsp->name, *region_name, 1321cb5b57a9SCharles Keepax wm_adsp_fw_text[dsp->fw], alg_region->alg); 1322cb5b57a9SCharles Keepax 1323cb5b57a9SCharles Keepax /* Truncate the subname from the start if it is too long */ 1324cb5b57a9SCharles Keepax if (subname) { 1325cb5b57a9SCharles Keepax int avail = SNDRV_CTL_ELEM_ID_NAME_MAXLEN - ret - 2; 1326cb5b57a9SCharles Keepax int skip = 0; 1327cb5b57a9SCharles Keepax 1328b7ede5afSCharles Keepax if (dsp->component->name_prefix) 1329b7ede5afSCharles Keepax avail -= strlen(dsp->component->name_prefix) + 1; 1330b7ede5afSCharles Keepax 1331cb5b57a9SCharles Keepax if (subname_len > avail) 1332cb5b57a9SCharles Keepax skip = subname_len - avail; 1333cb5b57a9SCharles Keepax 1334cb5b57a9SCharles Keepax snprintf(name + ret, 1335cb5b57a9SCharles Keepax SNDRV_CTL_ELEM_ID_NAME_MAXLEN - ret, " %.*s", 1336cb5b57a9SCharles Keepax subname_len - skip, subname + skip); 1337cb5b57a9SCharles Keepax } 1338cb5b57a9SCharles Keepax break; 1339cb5b57a9SCharles Keepax } 1340b21acc1cSCharles Keepax 13417585a5b0SCharles Keepax list_for_each_entry(ctl, &dsp->ctl_list, list) { 1342b21acc1cSCharles Keepax if (!strcmp(ctl->name, name)) { 1343b21acc1cSCharles Keepax if (!ctl->enabled) 1344b21acc1cSCharles Keepax ctl->enabled = 1; 1345b21acc1cSCharles Keepax return 0; 1346b21acc1cSCharles Keepax } 1347b21acc1cSCharles Keepax } 1348b21acc1cSCharles Keepax 1349b21acc1cSCharles Keepax ctl = kzalloc(sizeof(*ctl), GFP_KERNEL); 1350b21acc1cSCharles Keepax if (!ctl) 1351b21acc1cSCharles Keepax return -ENOMEM; 13522323736dSCharles Keepax ctl->fw_name = wm_adsp_fw_text[dsp->fw]; 1353b21acc1cSCharles Keepax ctl->alg_region = *alg_region; 1354b21acc1cSCharles Keepax ctl->name = kmemdup(name, strlen(name) + 1, GFP_KERNEL); 1355b21acc1cSCharles Keepax if (!ctl->name) { 1356b21acc1cSCharles Keepax ret = -ENOMEM; 1357b21acc1cSCharles Keepax goto err_ctl; 1358b21acc1cSCharles Keepax } 1359b21acc1cSCharles Keepax ctl->enabled = 1; 1360b21acc1cSCharles Keepax ctl->set = 0; 1361b21acc1cSCharles Keepax ctl->ops.xget = wm_coeff_get; 1362b21acc1cSCharles Keepax ctl->ops.xput = wm_coeff_put; 1363b21acc1cSCharles Keepax ctl->dsp = dsp; 1364b21acc1cSCharles Keepax 136526c22a19SCharles Keepax ctl->flags = flags; 13668eb084d0SStuart Henderson ctl->type = type; 13672323736dSCharles Keepax ctl->offset = offset; 1368b21acc1cSCharles Keepax ctl->len = len; 1369b21acc1cSCharles Keepax ctl->cache = kzalloc(ctl->len, GFP_KERNEL); 1370b21acc1cSCharles Keepax if (!ctl->cache) { 1371b21acc1cSCharles Keepax ret = -ENOMEM; 1372b21acc1cSCharles Keepax goto err_ctl_name; 1373b21acc1cSCharles Keepax } 1374b21acc1cSCharles Keepax 13752323736dSCharles Keepax list_add(&ctl->list, &dsp->ctl_list); 13762323736dSCharles Keepax 13778eb084d0SStuart Henderson if (flags & WMFW_CTL_FLAG_SYS) 13788eb084d0SStuart Henderson return 0; 13798eb084d0SStuart Henderson 1380b21acc1cSCharles Keepax ctl_work = kzalloc(sizeof(*ctl_work), GFP_KERNEL); 1381b21acc1cSCharles Keepax if (!ctl_work) { 1382b21acc1cSCharles Keepax ret = -ENOMEM; 1383b21acc1cSCharles Keepax goto err_ctl_cache; 1384b21acc1cSCharles Keepax } 1385b21acc1cSCharles Keepax 1386b21acc1cSCharles Keepax ctl_work->dsp = dsp; 1387b21acc1cSCharles Keepax ctl_work->ctl = ctl; 1388b21acc1cSCharles Keepax INIT_WORK(&ctl_work->work, wm_adsp_ctl_work); 1389b21acc1cSCharles Keepax schedule_work(&ctl_work->work); 1390b21acc1cSCharles Keepax 1391b21acc1cSCharles Keepax return 0; 1392b21acc1cSCharles Keepax 1393b21acc1cSCharles Keepax err_ctl_cache: 1394b21acc1cSCharles Keepax kfree(ctl->cache); 1395b21acc1cSCharles Keepax err_ctl_name: 1396b21acc1cSCharles Keepax kfree(ctl->name); 1397b21acc1cSCharles Keepax err_ctl: 1398b21acc1cSCharles Keepax kfree(ctl); 1399b21acc1cSCharles Keepax 1400b21acc1cSCharles Keepax return ret; 1401b21acc1cSCharles Keepax } 1402b21acc1cSCharles Keepax 14032323736dSCharles Keepax struct wm_coeff_parsed_alg { 14042323736dSCharles Keepax int id; 14052323736dSCharles Keepax const u8 *name; 14062323736dSCharles Keepax int name_len; 14072323736dSCharles Keepax int ncoeff; 14082323736dSCharles Keepax }; 14092323736dSCharles Keepax 14102323736dSCharles Keepax struct wm_coeff_parsed_coeff { 14112323736dSCharles Keepax int offset; 14122323736dSCharles Keepax int mem_type; 14132323736dSCharles Keepax const u8 *name; 14142323736dSCharles Keepax int name_len; 14152323736dSCharles Keepax int ctl_type; 14162323736dSCharles Keepax int flags; 14172323736dSCharles Keepax int len; 14182323736dSCharles Keepax }; 14192323736dSCharles Keepax 1420cb5b57a9SCharles Keepax static int wm_coeff_parse_string(int bytes, const u8 **pos, const u8 **str) 1421cb5b57a9SCharles Keepax { 1422cb5b57a9SCharles Keepax int length; 1423cb5b57a9SCharles Keepax 1424cb5b57a9SCharles Keepax switch (bytes) { 1425cb5b57a9SCharles Keepax case 1: 1426cb5b57a9SCharles Keepax length = **pos; 1427cb5b57a9SCharles Keepax break; 1428cb5b57a9SCharles Keepax case 2: 14298299ee81SCharles Keepax length = le16_to_cpu(*((__le16 *)*pos)); 1430cb5b57a9SCharles Keepax break; 1431cb5b57a9SCharles Keepax default: 1432cb5b57a9SCharles Keepax return 0; 1433cb5b57a9SCharles Keepax } 1434cb5b57a9SCharles Keepax 1435cb5b57a9SCharles Keepax if (str) 1436cb5b57a9SCharles Keepax *str = *pos + bytes; 1437cb5b57a9SCharles Keepax 1438cb5b57a9SCharles Keepax *pos += ((length + bytes) + 3) & ~0x03; 1439cb5b57a9SCharles Keepax 1440cb5b57a9SCharles Keepax return length; 1441cb5b57a9SCharles Keepax } 1442cb5b57a9SCharles Keepax 1443cb5b57a9SCharles Keepax static int wm_coeff_parse_int(int bytes, const u8 **pos) 1444cb5b57a9SCharles Keepax { 1445cb5b57a9SCharles Keepax int val = 0; 1446cb5b57a9SCharles Keepax 1447cb5b57a9SCharles Keepax switch (bytes) { 1448cb5b57a9SCharles Keepax case 2: 14498299ee81SCharles Keepax val = le16_to_cpu(*((__le16 *)*pos)); 1450cb5b57a9SCharles Keepax break; 1451cb5b57a9SCharles Keepax case 4: 14528299ee81SCharles Keepax val = le32_to_cpu(*((__le32 *)*pos)); 1453cb5b57a9SCharles Keepax break; 1454cb5b57a9SCharles Keepax default: 1455cb5b57a9SCharles Keepax break; 1456cb5b57a9SCharles Keepax } 1457cb5b57a9SCharles Keepax 1458cb5b57a9SCharles Keepax *pos += bytes; 1459cb5b57a9SCharles Keepax 1460cb5b57a9SCharles Keepax return val; 1461cb5b57a9SCharles Keepax } 1462cb5b57a9SCharles Keepax 14632323736dSCharles Keepax static inline void wm_coeff_parse_alg(struct wm_adsp *dsp, const u8 **data, 14642323736dSCharles Keepax struct wm_coeff_parsed_alg *blk) 14652323736dSCharles Keepax { 14662323736dSCharles Keepax const struct wmfw_adsp_alg_data *raw; 14672323736dSCharles Keepax 1468cb5b57a9SCharles Keepax switch (dsp->fw_ver) { 1469cb5b57a9SCharles Keepax case 0: 1470cb5b57a9SCharles Keepax case 1: 14712323736dSCharles Keepax raw = (const struct wmfw_adsp_alg_data *)*data; 14722323736dSCharles Keepax *data = raw->data; 14732323736dSCharles Keepax 14742323736dSCharles Keepax blk->id = le32_to_cpu(raw->id); 14752323736dSCharles Keepax blk->name = raw->name; 14762323736dSCharles Keepax blk->name_len = strlen(raw->name); 14772323736dSCharles Keepax blk->ncoeff = le32_to_cpu(raw->ncoeff); 1478cb5b57a9SCharles Keepax break; 1479cb5b57a9SCharles Keepax default: 1480cb5b57a9SCharles Keepax blk->id = wm_coeff_parse_int(sizeof(raw->id), data); 1481cb5b57a9SCharles Keepax blk->name_len = wm_coeff_parse_string(sizeof(u8), data, 1482cb5b57a9SCharles Keepax &blk->name); 1483cb5b57a9SCharles Keepax wm_coeff_parse_string(sizeof(u16), data, NULL); 1484cb5b57a9SCharles Keepax blk->ncoeff = wm_coeff_parse_int(sizeof(raw->ncoeff), data); 1485cb5b57a9SCharles Keepax break; 1486cb5b57a9SCharles Keepax } 14872323736dSCharles Keepax 14882323736dSCharles Keepax adsp_dbg(dsp, "Algorithm ID: %#x\n", blk->id); 14892323736dSCharles Keepax adsp_dbg(dsp, "Algorithm name: %.*s\n", blk->name_len, blk->name); 14902323736dSCharles Keepax adsp_dbg(dsp, "# of coefficient descriptors: %#x\n", blk->ncoeff); 14912323736dSCharles Keepax } 14922323736dSCharles Keepax 14932323736dSCharles Keepax static inline void wm_coeff_parse_coeff(struct wm_adsp *dsp, const u8 **data, 14942323736dSCharles Keepax struct wm_coeff_parsed_coeff *blk) 14952323736dSCharles Keepax { 14962323736dSCharles Keepax const struct wmfw_adsp_coeff_data *raw; 1497cb5b57a9SCharles Keepax const u8 *tmp; 1498cb5b57a9SCharles Keepax int length; 14992323736dSCharles Keepax 1500cb5b57a9SCharles Keepax switch (dsp->fw_ver) { 1501cb5b57a9SCharles Keepax case 0: 1502cb5b57a9SCharles Keepax case 1: 15032323736dSCharles Keepax raw = (const struct wmfw_adsp_coeff_data *)*data; 15042323736dSCharles Keepax *data = *data + sizeof(raw->hdr) + le32_to_cpu(raw->hdr.size); 15052323736dSCharles Keepax 15062323736dSCharles Keepax blk->offset = le16_to_cpu(raw->hdr.offset); 15072323736dSCharles Keepax blk->mem_type = le16_to_cpu(raw->hdr.type); 15082323736dSCharles Keepax blk->name = raw->name; 15092323736dSCharles Keepax blk->name_len = strlen(raw->name); 15102323736dSCharles Keepax blk->ctl_type = le16_to_cpu(raw->ctl_type); 15112323736dSCharles Keepax blk->flags = le16_to_cpu(raw->flags); 15122323736dSCharles Keepax blk->len = le32_to_cpu(raw->len); 1513cb5b57a9SCharles Keepax break; 1514cb5b57a9SCharles Keepax default: 1515cb5b57a9SCharles Keepax tmp = *data; 1516cb5b57a9SCharles Keepax blk->offset = wm_coeff_parse_int(sizeof(raw->hdr.offset), &tmp); 1517cb5b57a9SCharles Keepax blk->mem_type = wm_coeff_parse_int(sizeof(raw->hdr.type), &tmp); 1518cb5b57a9SCharles Keepax length = wm_coeff_parse_int(sizeof(raw->hdr.size), &tmp); 1519cb5b57a9SCharles Keepax blk->name_len = wm_coeff_parse_string(sizeof(u8), &tmp, 1520cb5b57a9SCharles Keepax &blk->name); 1521cb5b57a9SCharles Keepax wm_coeff_parse_string(sizeof(u8), &tmp, NULL); 1522cb5b57a9SCharles Keepax wm_coeff_parse_string(sizeof(u16), &tmp, NULL); 1523cb5b57a9SCharles Keepax blk->ctl_type = wm_coeff_parse_int(sizeof(raw->ctl_type), &tmp); 1524cb5b57a9SCharles Keepax blk->flags = wm_coeff_parse_int(sizeof(raw->flags), &tmp); 1525cb5b57a9SCharles Keepax blk->len = wm_coeff_parse_int(sizeof(raw->len), &tmp); 1526cb5b57a9SCharles Keepax 1527cb5b57a9SCharles Keepax *data = *data + sizeof(raw->hdr) + length; 1528cb5b57a9SCharles Keepax break; 1529cb5b57a9SCharles Keepax } 15302323736dSCharles Keepax 15312323736dSCharles Keepax adsp_dbg(dsp, "\tCoefficient type: %#x\n", blk->mem_type); 15322323736dSCharles Keepax adsp_dbg(dsp, "\tCoefficient offset: %#x\n", blk->offset); 15332323736dSCharles Keepax adsp_dbg(dsp, "\tCoefficient name: %.*s\n", blk->name_len, blk->name); 15342323736dSCharles Keepax adsp_dbg(dsp, "\tCoefficient flags: %#x\n", blk->flags); 15352323736dSCharles Keepax adsp_dbg(dsp, "\tALSA control type: %#x\n", blk->ctl_type); 15362323736dSCharles Keepax adsp_dbg(dsp, "\tALSA control len: %#x\n", blk->len); 15372323736dSCharles Keepax } 15382323736dSCharles Keepax 1539f4f0c4c6SRichard Fitzgerald static int wm_adsp_check_coeff_flags(struct wm_adsp *dsp, 1540f4f0c4c6SRichard Fitzgerald const struct wm_coeff_parsed_coeff *coeff_blk, 1541f4f0c4c6SRichard Fitzgerald unsigned int f_required, 1542f4f0c4c6SRichard Fitzgerald unsigned int f_illegal) 1543f4f0c4c6SRichard Fitzgerald { 1544f4f0c4c6SRichard Fitzgerald if ((coeff_blk->flags & f_illegal) || 1545f4f0c4c6SRichard Fitzgerald ((coeff_blk->flags & f_required) != f_required)) { 1546f4f0c4c6SRichard Fitzgerald adsp_err(dsp, "Illegal flags 0x%x for control type 0x%x\n", 1547f4f0c4c6SRichard Fitzgerald coeff_blk->flags, coeff_blk->ctl_type); 1548f4f0c4c6SRichard Fitzgerald return -EINVAL; 1549f4f0c4c6SRichard Fitzgerald } 1550f4f0c4c6SRichard Fitzgerald 1551f4f0c4c6SRichard Fitzgerald return 0; 1552f4f0c4c6SRichard Fitzgerald } 1553f4f0c4c6SRichard Fitzgerald 15542323736dSCharles Keepax static int wm_adsp_parse_coeff(struct wm_adsp *dsp, 15552323736dSCharles Keepax const struct wmfw_region *region) 15562323736dSCharles Keepax { 15572323736dSCharles Keepax struct wm_adsp_alg_region alg_region = {}; 15582323736dSCharles Keepax struct wm_coeff_parsed_alg alg_blk; 15592323736dSCharles Keepax struct wm_coeff_parsed_coeff coeff_blk; 15602323736dSCharles Keepax const u8 *data = region->data; 15612323736dSCharles Keepax int i, ret; 15622323736dSCharles Keepax 15632323736dSCharles Keepax wm_coeff_parse_alg(dsp, &data, &alg_blk); 15642323736dSCharles Keepax for (i = 0; i < alg_blk.ncoeff; i++) { 15652323736dSCharles Keepax wm_coeff_parse_coeff(dsp, &data, &coeff_blk); 15662323736dSCharles Keepax 15672323736dSCharles Keepax switch (coeff_blk.ctl_type) { 15682323736dSCharles Keepax case SNDRV_CTL_ELEM_TYPE_BYTES: 15692323736dSCharles Keepax break; 1570a23ebba8SRichard Fitzgerald case WMFW_CTL_TYPE_ACKED: 1571a23ebba8SRichard Fitzgerald if (coeff_blk.flags & WMFW_CTL_FLAG_SYS) 1572a23ebba8SRichard Fitzgerald continue; /* ignore */ 1573a23ebba8SRichard Fitzgerald 1574a23ebba8SRichard Fitzgerald ret = wm_adsp_check_coeff_flags(dsp, &coeff_blk, 1575a23ebba8SRichard Fitzgerald WMFW_CTL_FLAG_VOLATILE | 1576a23ebba8SRichard Fitzgerald WMFW_CTL_FLAG_WRITEABLE | 1577a23ebba8SRichard Fitzgerald WMFW_CTL_FLAG_READABLE, 1578a23ebba8SRichard Fitzgerald 0); 1579a23ebba8SRichard Fitzgerald if (ret) 1580a23ebba8SRichard Fitzgerald return -EINVAL; 1581a23ebba8SRichard Fitzgerald break; 1582f4f0c4c6SRichard Fitzgerald case WMFW_CTL_TYPE_HOSTEVENT: 1583f4f0c4c6SRichard Fitzgerald ret = wm_adsp_check_coeff_flags(dsp, &coeff_blk, 1584f4f0c4c6SRichard Fitzgerald WMFW_CTL_FLAG_SYS | 1585f4f0c4c6SRichard Fitzgerald WMFW_CTL_FLAG_VOLATILE | 1586f4f0c4c6SRichard Fitzgerald WMFW_CTL_FLAG_WRITEABLE | 1587f4f0c4c6SRichard Fitzgerald WMFW_CTL_FLAG_READABLE, 1588f4f0c4c6SRichard Fitzgerald 0); 1589f4f0c4c6SRichard Fitzgerald if (ret) 1590f4f0c4c6SRichard Fitzgerald return -EINVAL; 1591f4f0c4c6SRichard Fitzgerald break; 1592d52ed4b0SRichard Fitzgerald case WMFW_CTL_TYPE_HOST_BUFFER: 1593d52ed4b0SRichard Fitzgerald ret = wm_adsp_check_coeff_flags(dsp, &coeff_blk, 1594d52ed4b0SRichard Fitzgerald WMFW_CTL_FLAG_SYS | 1595d52ed4b0SRichard Fitzgerald WMFW_CTL_FLAG_VOLATILE | 1596d52ed4b0SRichard Fitzgerald WMFW_CTL_FLAG_READABLE, 1597d52ed4b0SRichard Fitzgerald 0); 1598d52ed4b0SRichard Fitzgerald if (ret) 1599d52ed4b0SRichard Fitzgerald return -EINVAL; 1600d52ed4b0SRichard Fitzgerald break; 16012323736dSCharles Keepax default: 16022323736dSCharles Keepax adsp_err(dsp, "Unknown control type: %d\n", 16032323736dSCharles Keepax coeff_blk.ctl_type); 16042323736dSCharles Keepax return -EINVAL; 16052323736dSCharles Keepax } 16062323736dSCharles Keepax 16072323736dSCharles Keepax alg_region.type = coeff_blk.mem_type; 16082323736dSCharles Keepax alg_region.alg = alg_blk.id; 16092323736dSCharles Keepax 16102323736dSCharles Keepax ret = wm_adsp_create_control(dsp, &alg_region, 16112323736dSCharles Keepax coeff_blk.offset, 16122323736dSCharles Keepax coeff_blk.len, 16132323736dSCharles Keepax coeff_blk.name, 161426c22a19SCharles Keepax coeff_blk.name_len, 16158eb084d0SStuart Henderson coeff_blk.flags, 16168eb084d0SStuart Henderson coeff_blk.ctl_type); 16172323736dSCharles Keepax if (ret < 0) 16182323736dSCharles Keepax adsp_err(dsp, "Failed to create control: %.*s, %d\n", 16192323736dSCharles Keepax coeff_blk.name_len, coeff_blk.name, ret); 16202323736dSCharles Keepax } 16212323736dSCharles Keepax 16222323736dSCharles Keepax return 0; 16232323736dSCharles Keepax } 16242323736dSCharles Keepax 16252159ad93SMark Brown static int wm_adsp_load(struct wm_adsp *dsp) 16262159ad93SMark Brown { 1627cf17c83cSMark Brown LIST_HEAD(buf_list); 16282159ad93SMark Brown const struct firmware *firmware; 16292159ad93SMark Brown struct regmap *regmap = dsp->regmap; 16302159ad93SMark Brown unsigned int pos = 0; 16312159ad93SMark Brown const struct wmfw_header *header; 16322159ad93SMark Brown const struct wmfw_adsp1_sizes *adsp1_sizes; 16332159ad93SMark Brown const struct wmfw_adsp2_sizes *adsp2_sizes; 16342159ad93SMark Brown const struct wmfw_footer *footer; 16352159ad93SMark Brown const struct wmfw_region *region; 16362159ad93SMark Brown const struct wm_adsp_region *mem; 16372159ad93SMark Brown const char *region_name; 16381cab2a84SRichard Fitzgerald char *file, *text = NULL; 1639cf17c83cSMark Brown struct wm_adsp_buf *buf; 16402159ad93SMark Brown unsigned int reg; 16412159ad93SMark Brown int regions = 0; 16422159ad93SMark Brown int ret, offset, type, sizes; 16432159ad93SMark Brown 16442159ad93SMark Brown file = kzalloc(PAGE_SIZE, GFP_KERNEL); 16452159ad93SMark Brown if (file == NULL) 16462159ad93SMark Brown return -ENOMEM; 16472159ad93SMark Brown 1648605391d0SRichard Fitzgerald snprintf(file, PAGE_SIZE, "%s-%s-%s.wmfw", dsp->part, dsp->fwf_name, 16491023dbd9SMark Brown wm_adsp_fw[dsp->fw].file); 16502159ad93SMark Brown file[PAGE_SIZE - 1] = '\0'; 16512159ad93SMark Brown 16522159ad93SMark Brown ret = request_firmware(&firmware, file, dsp->dev); 16532159ad93SMark Brown if (ret != 0) { 16542159ad93SMark Brown adsp_err(dsp, "Failed to request '%s'\n", file); 16552159ad93SMark Brown goto out; 16562159ad93SMark Brown } 16572159ad93SMark Brown ret = -EINVAL; 16582159ad93SMark Brown 16592159ad93SMark Brown pos = sizeof(*header) + sizeof(*adsp1_sizes) + sizeof(*footer); 16602159ad93SMark Brown if (pos >= firmware->size) { 16612159ad93SMark Brown adsp_err(dsp, "%s: file too short, %zu bytes\n", 16622159ad93SMark Brown file, firmware->size); 16632159ad93SMark Brown goto out_fw; 16642159ad93SMark Brown } 16652159ad93SMark Brown 16662159ad93SMark Brown header = (void *)&firmware->data[0]; 16672159ad93SMark Brown 16682159ad93SMark Brown if (memcmp(&header->magic[0], "WMFW", 4) != 0) { 16692159ad93SMark Brown adsp_err(dsp, "%s: invalid magic\n", file); 16702159ad93SMark Brown goto out_fw; 16712159ad93SMark Brown } 16722159ad93SMark Brown 16732323736dSCharles Keepax switch (header->ver) { 16742323736dSCharles Keepax case 0: 1675c61e59feSCharles Keepax adsp_warn(dsp, "%s: Depreciated file format %d\n", 1676c61e59feSCharles Keepax file, header->ver); 1677c61e59feSCharles Keepax break; 16782323736dSCharles Keepax case 1: 1679cb5b57a9SCharles Keepax case 2: 16802323736dSCharles Keepax break; 16812323736dSCharles Keepax default: 16822159ad93SMark Brown adsp_err(dsp, "%s: unknown file format %d\n", 16832159ad93SMark Brown file, header->ver); 16842159ad93SMark Brown goto out_fw; 16852159ad93SMark Brown } 16862323736dSCharles Keepax 16873626992aSDimitris Papastamos adsp_info(dsp, "Firmware version: %d\n", header->ver); 16882323736dSCharles Keepax dsp->fw_ver = header->ver; 16892159ad93SMark Brown 16902159ad93SMark Brown if (header->core != dsp->type) { 16912159ad93SMark Brown adsp_err(dsp, "%s: invalid core %d != %d\n", 16922159ad93SMark Brown file, header->core, dsp->type); 16932159ad93SMark Brown goto out_fw; 16942159ad93SMark Brown } 16952159ad93SMark Brown 16962159ad93SMark Brown switch (dsp->type) { 16972159ad93SMark Brown case WMFW_ADSP1: 16982159ad93SMark Brown pos = sizeof(*header) + sizeof(*adsp1_sizes) + sizeof(*footer); 16992159ad93SMark Brown adsp1_sizes = (void *)&(header[1]); 17002159ad93SMark Brown footer = (void *)&(adsp1_sizes[1]); 17012159ad93SMark Brown sizes = sizeof(*adsp1_sizes); 17022159ad93SMark Brown 17032159ad93SMark Brown adsp_dbg(dsp, "%s: %d DM, %d PM, %d ZM\n", 17042159ad93SMark Brown file, le32_to_cpu(adsp1_sizes->dm), 17052159ad93SMark Brown le32_to_cpu(adsp1_sizes->pm), 17062159ad93SMark Brown le32_to_cpu(adsp1_sizes->zm)); 17072159ad93SMark Brown break; 17082159ad93SMark Brown 17092159ad93SMark Brown case WMFW_ADSP2: 17102159ad93SMark Brown pos = sizeof(*header) + sizeof(*adsp2_sizes) + sizeof(*footer); 17112159ad93SMark Brown adsp2_sizes = (void *)&(header[1]); 17122159ad93SMark Brown footer = (void *)&(adsp2_sizes[1]); 17132159ad93SMark Brown sizes = sizeof(*adsp2_sizes); 17142159ad93SMark Brown 17152159ad93SMark Brown adsp_dbg(dsp, "%s: %d XM, %d YM %d PM, %d ZM\n", 17162159ad93SMark Brown file, le32_to_cpu(adsp2_sizes->xm), 17172159ad93SMark Brown le32_to_cpu(adsp2_sizes->ym), 17182159ad93SMark Brown le32_to_cpu(adsp2_sizes->pm), 17192159ad93SMark Brown le32_to_cpu(adsp2_sizes->zm)); 17202159ad93SMark Brown break; 17212159ad93SMark Brown 17222159ad93SMark Brown default: 17236c452bdaSTakashi Iwai WARN(1, "Unknown DSP type"); 17242159ad93SMark Brown goto out_fw; 17252159ad93SMark Brown } 17262159ad93SMark Brown 17272159ad93SMark Brown if (le32_to_cpu(header->len) != sizeof(*header) + 17282159ad93SMark Brown sizes + sizeof(*footer)) { 17292159ad93SMark Brown adsp_err(dsp, "%s: unexpected header length %d\n", 17302159ad93SMark Brown file, le32_to_cpu(header->len)); 17312159ad93SMark Brown goto out_fw; 17322159ad93SMark Brown } 17332159ad93SMark Brown 17342159ad93SMark Brown adsp_dbg(dsp, "%s: timestamp %llu\n", file, 17352159ad93SMark Brown le64_to_cpu(footer->timestamp)); 17362159ad93SMark Brown 17372159ad93SMark Brown while (pos < firmware->size && 173850dd2ea8SBen Hutchings sizeof(*region) < firmware->size - pos) { 17392159ad93SMark Brown region = (void *)&(firmware->data[pos]); 17402159ad93SMark Brown region_name = "Unknown"; 17412159ad93SMark Brown reg = 0; 17422159ad93SMark Brown text = NULL; 17432159ad93SMark Brown offset = le32_to_cpu(region->offset) & 0xffffff; 17442159ad93SMark Brown type = be32_to_cpu(region->type) & 0xff; 17452159ad93SMark Brown mem = wm_adsp_find_region(dsp, type); 17462159ad93SMark Brown 17472159ad93SMark Brown switch (type) { 17482159ad93SMark Brown case WMFW_NAME_TEXT: 17492159ad93SMark Brown region_name = "Firmware name"; 17502159ad93SMark Brown text = kzalloc(le32_to_cpu(region->len) + 1, 17512159ad93SMark Brown GFP_KERNEL); 17522159ad93SMark Brown break; 17532323736dSCharles Keepax case WMFW_ALGORITHM_DATA: 17542323736dSCharles Keepax region_name = "Algorithm"; 17552323736dSCharles Keepax ret = wm_adsp_parse_coeff(dsp, region); 17562323736dSCharles Keepax if (ret != 0) 17572323736dSCharles Keepax goto out_fw; 17582323736dSCharles Keepax break; 17592159ad93SMark Brown case WMFW_INFO_TEXT: 17602159ad93SMark Brown region_name = "Information"; 17612159ad93SMark Brown text = kzalloc(le32_to_cpu(region->len) + 1, 17622159ad93SMark Brown GFP_KERNEL); 17632159ad93SMark Brown break; 17642159ad93SMark Brown case WMFW_ABSOLUTE: 17652159ad93SMark Brown region_name = "Absolute"; 17662159ad93SMark Brown reg = offset; 17672159ad93SMark Brown break; 17682159ad93SMark Brown case WMFW_ADSP1_PM: 17692159ad93SMark Brown case WMFW_ADSP1_DM: 17702159ad93SMark Brown case WMFW_ADSP2_XM: 17712159ad93SMark Brown case WMFW_ADSP2_YM: 17722159ad93SMark Brown case WMFW_ADSP1_ZM: 17739ce5e6e6SRichard Fitzgerald region_name = wm_adsp_mem_region_name(type); 177445b9ee72SMark Brown reg = wm_adsp_region_to_reg(mem, offset); 17752159ad93SMark Brown break; 17762159ad93SMark Brown default: 17772159ad93SMark Brown adsp_warn(dsp, 17782159ad93SMark Brown "%s.%d: Unknown region type %x at %d(%x)\n", 17792159ad93SMark Brown file, regions, type, pos, pos); 17802159ad93SMark Brown break; 17812159ad93SMark Brown } 17822159ad93SMark Brown 17832159ad93SMark Brown adsp_dbg(dsp, "%s.%d: %d bytes at %d in %s\n", file, 17842159ad93SMark Brown regions, le32_to_cpu(region->len), offset, 17852159ad93SMark Brown region_name); 17862159ad93SMark Brown 178750dd2ea8SBen Hutchings if (le32_to_cpu(region->len) > 178850dd2ea8SBen Hutchings firmware->size - pos - sizeof(*region)) { 17891cab2a84SRichard Fitzgerald adsp_err(dsp, 17901cab2a84SRichard Fitzgerald "%s.%d: %s region len %d bytes exceeds file length %zu\n", 17911cab2a84SRichard Fitzgerald file, regions, region_name, 17921cab2a84SRichard Fitzgerald le32_to_cpu(region->len), firmware->size); 17931cab2a84SRichard Fitzgerald ret = -EINVAL; 17941cab2a84SRichard Fitzgerald goto out_fw; 17951cab2a84SRichard Fitzgerald } 17961cab2a84SRichard Fitzgerald 17972159ad93SMark Brown if (text) { 17982159ad93SMark Brown memcpy(text, region->data, le32_to_cpu(region->len)); 17992159ad93SMark Brown adsp_info(dsp, "%s: %s\n", file, text); 18002159ad93SMark Brown kfree(text); 18011cab2a84SRichard Fitzgerald text = NULL; 18022159ad93SMark Brown } 18032159ad93SMark Brown 18042159ad93SMark Brown if (reg) { 1805cdcd7f72SCharles Keepax buf = wm_adsp_buf_alloc(region->data, 1806cdcd7f72SCharles Keepax le32_to_cpu(region->len), 1807cf17c83cSMark Brown &buf_list); 1808a76fefabSMark Brown if (!buf) { 1809a76fefabSMark Brown adsp_err(dsp, "Out of memory\n"); 18107328823dSDimitris Papastamos ret = -ENOMEM; 18117328823dSDimitris Papastamos goto out_fw; 1812a76fefabSMark Brown } 1813a76fefabSMark Brown 1814cdcd7f72SCharles Keepax ret = regmap_raw_write_async(regmap, reg, buf->buf, 1815cdcd7f72SCharles Keepax le32_to_cpu(region->len)); 18162159ad93SMark Brown if (ret != 0) { 18172159ad93SMark Brown adsp_err(dsp, 1818cdcd7f72SCharles Keepax "%s.%d: Failed to write %d bytes at %d in %s: %d\n", 18192159ad93SMark Brown file, regions, 1820cdcd7f72SCharles Keepax le32_to_cpu(region->len), offset, 18212159ad93SMark Brown region_name, ret); 18222159ad93SMark Brown goto out_fw; 18232159ad93SMark Brown } 18242159ad93SMark Brown } 18252159ad93SMark Brown 18262159ad93SMark Brown pos += le32_to_cpu(region->len) + sizeof(*region); 18272159ad93SMark Brown regions++; 18282159ad93SMark Brown } 18292159ad93SMark Brown 1830cf17c83cSMark Brown ret = regmap_async_complete(regmap); 1831cf17c83cSMark Brown if (ret != 0) { 1832cf17c83cSMark Brown adsp_err(dsp, "Failed to complete async write: %d\n", ret); 1833cf17c83cSMark Brown goto out_fw; 1834cf17c83cSMark Brown } 1835cf17c83cSMark Brown 18362159ad93SMark Brown if (pos > firmware->size) 18372159ad93SMark Brown adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n", 18382159ad93SMark Brown file, regions, pos - firmware->size); 18392159ad93SMark Brown 1840f9f55e31SRichard Fitzgerald wm_adsp_debugfs_save_wmfwname(dsp, file); 1841f9f55e31SRichard Fitzgerald 18422159ad93SMark Brown out_fw: 1843cf17c83cSMark Brown regmap_async_complete(regmap); 1844cf17c83cSMark Brown wm_adsp_buf_free(&buf_list); 18452159ad93SMark Brown release_firmware(firmware); 18461cab2a84SRichard Fitzgerald kfree(text); 18472159ad93SMark Brown out: 18482159ad93SMark Brown kfree(file); 18492159ad93SMark Brown 18502159ad93SMark Brown return ret; 18512159ad93SMark Brown } 18522159ad93SMark Brown 18532323736dSCharles Keepax static void wm_adsp_ctl_fixup_base(struct wm_adsp *dsp, 18542323736dSCharles Keepax const struct wm_adsp_alg_region *alg_region) 18552323736dSCharles Keepax { 18562323736dSCharles Keepax struct wm_coeff_ctl *ctl; 18572323736dSCharles Keepax 18582323736dSCharles Keepax list_for_each_entry(ctl, &dsp->ctl_list, list) { 18592323736dSCharles Keepax if (ctl->fw_name == wm_adsp_fw_text[dsp->fw] && 18602323736dSCharles Keepax alg_region->alg == ctl->alg_region.alg && 18612323736dSCharles Keepax alg_region->type == ctl->alg_region.type) { 18622323736dSCharles Keepax ctl->alg_region.base = alg_region->base; 18632323736dSCharles Keepax } 18642323736dSCharles Keepax } 18652323736dSCharles Keepax } 18662323736dSCharles Keepax 18673809f001SCharles Keepax static void *wm_adsp_read_algs(struct wm_adsp *dsp, size_t n_algs, 18687f7cca08SCharles Keepax const struct wm_adsp_region *mem, 1869b618a185SCharles Keepax unsigned int pos, unsigned int len) 1870db40517cSMark Brown { 1871b618a185SCharles Keepax void *alg; 18727f7cca08SCharles Keepax unsigned int reg; 1873b618a185SCharles Keepax int ret; 1874db40517cSMark Brown __be32 val; 1875db40517cSMark Brown 18763809f001SCharles Keepax if (n_algs == 0) { 1877b618a185SCharles Keepax adsp_err(dsp, "No algorithms\n"); 1878b618a185SCharles Keepax return ERR_PTR(-EINVAL); 1879db40517cSMark Brown } 1880db40517cSMark Brown 18813809f001SCharles Keepax if (n_algs > 1024) { 18823809f001SCharles Keepax adsp_err(dsp, "Algorithm count %zx excessive\n", n_algs); 1883b618a185SCharles Keepax return ERR_PTR(-EINVAL); 1884b618a185SCharles Keepax } 1885b618a185SCharles Keepax 1886b618a185SCharles Keepax /* Read the terminator first to validate the length */ 18877f7cca08SCharles Keepax reg = wm_adsp_region_to_reg(mem, pos + len); 18887f7cca08SCharles Keepax 18897f7cca08SCharles Keepax ret = regmap_raw_read(dsp->regmap, reg, &val, sizeof(val)); 1890b618a185SCharles Keepax if (ret != 0) { 1891b618a185SCharles Keepax adsp_err(dsp, "Failed to read algorithm list end: %d\n", 1892b618a185SCharles Keepax ret); 1893b618a185SCharles Keepax return ERR_PTR(ret); 1894b618a185SCharles Keepax } 1895b618a185SCharles Keepax 1896b618a185SCharles Keepax if (be32_to_cpu(val) != 0xbedead) 1897503ada8aSRichard Fitzgerald adsp_warn(dsp, "Algorithm list end %x 0x%x != 0xbedead\n", 18987f7cca08SCharles Keepax reg, be32_to_cpu(val)); 18997f7cca08SCharles Keepax 19007f7cca08SCharles Keepax /* Convert length from DSP words to bytes */ 19017f7cca08SCharles Keepax len *= sizeof(u32); 1902b618a185SCharles Keepax 1903517ee74eSCharles Keepax alg = kzalloc(len, GFP_KERNEL | GFP_DMA); 1904b618a185SCharles Keepax if (!alg) 1905b618a185SCharles Keepax return ERR_PTR(-ENOMEM); 1906b618a185SCharles Keepax 19077f7cca08SCharles Keepax reg = wm_adsp_region_to_reg(mem, pos); 19087f7cca08SCharles Keepax 19097f7cca08SCharles Keepax ret = regmap_raw_read(dsp->regmap, reg, alg, len); 1910b618a185SCharles Keepax if (ret != 0) { 19117d00cd97SCharles Keepax adsp_err(dsp, "Failed to read algorithm list: %d\n", ret); 1912b618a185SCharles Keepax kfree(alg); 1913b618a185SCharles Keepax return ERR_PTR(ret); 1914b618a185SCharles Keepax } 1915b618a185SCharles Keepax 1916b618a185SCharles Keepax return alg; 1917b618a185SCharles Keepax } 1918b618a185SCharles Keepax 191914197095SCharles Keepax static struct wm_adsp_alg_region * 192014197095SCharles Keepax wm_adsp_find_alg_region(struct wm_adsp *dsp, int type, unsigned int id) 192114197095SCharles Keepax { 192214197095SCharles Keepax struct wm_adsp_alg_region *alg_region; 192314197095SCharles Keepax 192414197095SCharles Keepax list_for_each_entry(alg_region, &dsp->alg_regions, list) { 192514197095SCharles Keepax if (id == alg_region->alg && type == alg_region->type) 192614197095SCharles Keepax return alg_region; 192714197095SCharles Keepax } 192814197095SCharles Keepax 192914197095SCharles Keepax return NULL; 193014197095SCharles Keepax } 193114197095SCharles Keepax 1932d9d20e17SCharles Keepax static struct wm_adsp_alg_region *wm_adsp_create_region(struct wm_adsp *dsp, 1933d9d20e17SCharles Keepax int type, __be32 id, 1934d9d20e17SCharles Keepax __be32 base) 1935d9d20e17SCharles Keepax { 1936d9d20e17SCharles Keepax struct wm_adsp_alg_region *alg_region; 1937d9d20e17SCharles Keepax 1938d9d20e17SCharles Keepax alg_region = kzalloc(sizeof(*alg_region), GFP_KERNEL); 1939d9d20e17SCharles Keepax if (!alg_region) 1940d9d20e17SCharles Keepax return ERR_PTR(-ENOMEM); 1941d9d20e17SCharles Keepax 1942d9d20e17SCharles Keepax alg_region->type = type; 1943d9d20e17SCharles Keepax alg_region->alg = be32_to_cpu(id); 1944d9d20e17SCharles Keepax alg_region->base = be32_to_cpu(base); 1945d9d20e17SCharles Keepax 1946d9d20e17SCharles Keepax list_add_tail(&alg_region->list, &dsp->alg_regions); 1947d9d20e17SCharles Keepax 19482323736dSCharles Keepax if (dsp->fw_ver > 0) 19492323736dSCharles Keepax wm_adsp_ctl_fixup_base(dsp, alg_region); 19502323736dSCharles Keepax 1951d9d20e17SCharles Keepax return alg_region; 1952d9d20e17SCharles Keepax } 1953d9d20e17SCharles Keepax 195456574d54SRichard Fitzgerald static void wm_adsp_free_alg_regions(struct wm_adsp *dsp) 195556574d54SRichard Fitzgerald { 195656574d54SRichard Fitzgerald struct wm_adsp_alg_region *alg_region; 195756574d54SRichard Fitzgerald 195856574d54SRichard Fitzgerald while (!list_empty(&dsp->alg_regions)) { 195956574d54SRichard Fitzgerald alg_region = list_first_entry(&dsp->alg_regions, 196056574d54SRichard Fitzgerald struct wm_adsp_alg_region, 196156574d54SRichard Fitzgerald list); 196256574d54SRichard Fitzgerald list_del(&alg_region->list); 196356574d54SRichard Fitzgerald kfree(alg_region); 196456574d54SRichard Fitzgerald } 196556574d54SRichard Fitzgerald } 196656574d54SRichard Fitzgerald 1967b618a185SCharles Keepax static int wm_adsp1_setup_algs(struct wm_adsp *dsp) 1968b618a185SCharles Keepax { 1969b618a185SCharles Keepax struct wmfw_adsp1_id_hdr adsp1_id; 1970b618a185SCharles Keepax struct wmfw_adsp1_alg_hdr *adsp1_alg; 19713809f001SCharles Keepax struct wm_adsp_alg_region *alg_region; 1972b618a185SCharles Keepax const struct wm_adsp_region *mem; 1973b618a185SCharles Keepax unsigned int pos, len; 19743809f001SCharles Keepax size_t n_algs; 1975b618a185SCharles Keepax int i, ret; 1976b618a185SCharles Keepax 1977b618a185SCharles Keepax mem = wm_adsp_find_region(dsp, WMFW_ADSP1_DM); 19786c452bdaSTakashi Iwai if (WARN_ON(!mem)) 1979db40517cSMark Brown return -EINVAL; 1980db40517cSMark Brown 1981b618a185SCharles Keepax ret = regmap_raw_read(dsp->regmap, mem->base, &adsp1_id, 1982db40517cSMark Brown sizeof(adsp1_id)); 1983db40517cSMark Brown if (ret != 0) { 1984db40517cSMark Brown adsp_err(dsp, "Failed to read algorithm info: %d\n", 1985db40517cSMark Brown ret); 1986db40517cSMark Brown return ret; 1987db40517cSMark Brown } 1988db40517cSMark Brown 19893809f001SCharles Keepax n_algs = be32_to_cpu(adsp1_id.n_algs); 1990f395a218SMark Brown dsp->fw_id = be32_to_cpu(adsp1_id.fw.id); 1991db40517cSMark Brown adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n", 1992f395a218SMark Brown dsp->fw_id, 1993db40517cSMark Brown (be32_to_cpu(adsp1_id.fw.ver) & 0xff0000) >> 16, 1994db40517cSMark Brown (be32_to_cpu(adsp1_id.fw.ver) & 0xff00) >> 8, 1995db40517cSMark Brown be32_to_cpu(adsp1_id.fw.ver) & 0xff, 19963809f001SCharles Keepax n_algs); 1997db40517cSMark Brown 1998d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_ZM, 1999d9d20e17SCharles Keepax adsp1_id.fw.id, adsp1_id.zm); 2000d9d20e17SCharles Keepax if (IS_ERR(alg_region)) 2001d9d20e17SCharles Keepax return PTR_ERR(alg_region); 2002ac50009fSMark Brown 2003d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_DM, 2004d9d20e17SCharles Keepax adsp1_id.fw.id, adsp1_id.dm); 2005d9d20e17SCharles Keepax if (IS_ERR(alg_region)) 2006d9d20e17SCharles Keepax return PTR_ERR(alg_region); 2007ac50009fSMark Brown 20087f7cca08SCharles Keepax /* Calculate offset and length in DSP words */ 20097f7cca08SCharles Keepax pos = sizeof(adsp1_id) / sizeof(u32); 20107f7cca08SCharles Keepax len = (sizeof(*adsp1_alg) * n_algs) / sizeof(u32); 2011db40517cSMark Brown 20127f7cca08SCharles Keepax adsp1_alg = wm_adsp_read_algs(dsp, n_algs, mem, pos, len); 2013b618a185SCharles Keepax if (IS_ERR(adsp1_alg)) 2014b618a185SCharles Keepax return PTR_ERR(adsp1_alg); 2015db40517cSMark Brown 20163809f001SCharles Keepax for (i = 0; i < n_algs; i++) { 2017471f4885SMark Brown adsp_info(dsp, "%d: ID %x v%d.%d.%d DM@%x ZM@%x\n", 2018db40517cSMark Brown i, be32_to_cpu(adsp1_alg[i].alg.id), 2019db40517cSMark Brown (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff0000) >> 16, 2020db40517cSMark Brown (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff00) >> 8, 2021471f4885SMark Brown be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff, 2022471f4885SMark Brown be32_to_cpu(adsp1_alg[i].dm), 2023471f4885SMark Brown be32_to_cpu(adsp1_alg[i].zm)); 2024471f4885SMark Brown 2025d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_DM, 2026d9d20e17SCharles Keepax adsp1_alg[i].alg.id, 2027d9d20e17SCharles Keepax adsp1_alg[i].dm); 2028d9d20e17SCharles Keepax if (IS_ERR(alg_region)) { 2029d9d20e17SCharles Keepax ret = PTR_ERR(alg_region); 2030d6d52179SJS Park goto out; 2031d6d52179SJS Park } 20322323736dSCharles Keepax if (dsp->fw_ver == 0) { 20333809f001SCharles Keepax if (i + 1 < n_algs) { 20346958eb2aSCharles Keepax len = be32_to_cpu(adsp1_alg[i + 1].dm); 20356958eb2aSCharles Keepax len -= be32_to_cpu(adsp1_alg[i].dm); 20366958eb2aSCharles Keepax len *= 4; 20372323736dSCharles Keepax wm_adsp_create_control(dsp, alg_region, 0, 20388eb084d0SStuart Henderson len, NULL, 0, 0, 20398eb084d0SStuart Henderson SNDRV_CTL_ELEM_TYPE_BYTES); 20406ab2b7b4SDimitris Papastamos } else { 20416ab2b7b4SDimitris Papastamos adsp_warn(dsp, "Missing length info for region DM with ID %x\n", 20426ab2b7b4SDimitris Papastamos be32_to_cpu(adsp1_alg[i].alg.id)); 20436ab2b7b4SDimitris Papastamos } 20442323736dSCharles Keepax } 2045471f4885SMark Brown 2046d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP1_ZM, 2047d9d20e17SCharles Keepax adsp1_alg[i].alg.id, 2048d9d20e17SCharles Keepax adsp1_alg[i].zm); 2049d9d20e17SCharles Keepax if (IS_ERR(alg_region)) { 2050d9d20e17SCharles Keepax ret = PTR_ERR(alg_region); 2051d6d52179SJS Park goto out; 2052d6d52179SJS Park } 20532323736dSCharles Keepax if (dsp->fw_ver == 0) { 20543809f001SCharles Keepax if (i + 1 < n_algs) { 20556958eb2aSCharles Keepax len = be32_to_cpu(adsp1_alg[i + 1].zm); 20566958eb2aSCharles Keepax len -= be32_to_cpu(adsp1_alg[i].zm); 20576958eb2aSCharles Keepax len *= 4; 20582323736dSCharles Keepax wm_adsp_create_control(dsp, alg_region, 0, 20598eb084d0SStuart Henderson len, NULL, 0, 0, 20608eb084d0SStuart Henderson SNDRV_CTL_ELEM_TYPE_BYTES); 20616ab2b7b4SDimitris Papastamos } else { 20626ab2b7b4SDimitris Papastamos adsp_warn(dsp, "Missing length info for region ZM with ID %x\n", 20636ab2b7b4SDimitris Papastamos be32_to_cpu(adsp1_alg[i].alg.id)); 20646ab2b7b4SDimitris Papastamos } 2065b618a185SCharles Keepax } 20662323736dSCharles Keepax } 2067db40517cSMark Brown 2068b618a185SCharles Keepax out: 2069b618a185SCharles Keepax kfree(adsp1_alg); 2070b618a185SCharles Keepax return ret; 2071b618a185SCharles Keepax } 2072b618a185SCharles Keepax 2073b618a185SCharles Keepax static int wm_adsp2_setup_algs(struct wm_adsp *dsp) 2074b618a185SCharles Keepax { 2075b618a185SCharles Keepax struct wmfw_adsp2_id_hdr adsp2_id; 2076b618a185SCharles Keepax struct wmfw_adsp2_alg_hdr *adsp2_alg; 20773809f001SCharles Keepax struct wm_adsp_alg_region *alg_region; 2078b618a185SCharles Keepax const struct wm_adsp_region *mem; 2079b618a185SCharles Keepax unsigned int pos, len; 20803809f001SCharles Keepax size_t n_algs; 2081b618a185SCharles Keepax int i, ret; 2082b618a185SCharles Keepax 2083b618a185SCharles Keepax mem = wm_adsp_find_region(dsp, WMFW_ADSP2_XM); 2084b618a185SCharles Keepax if (WARN_ON(!mem)) 2085b618a185SCharles Keepax return -EINVAL; 2086b618a185SCharles Keepax 2087b618a185SCharles Keepax ret = regmap_raw_read(dsp->regmap, mem->base, &adsp2_id, 2088b618a185SCharles Keepax sizeof(adsp2_id)); 2089b618a185SCharles Keepax if (ret != 0) { 2090b618a185SCharles Keepax adsp_err(dsp, "Failed to read algorithm info: %d\n", 2091b618a185SCharles Keepax ret); 2092b618a185SCharles Keepax return ret; 2093b618a185SCharles Keepax } 2094b618a185SCharles Keepax 20953809f001SCharles Keepax n_algs = be32_to_cpu(adsp2_id.n_algs); 2096b618a185SCharles Keepax dsp->fw_id = be32_to_cpu(adsp2_id.fw.id); 2097f9f55e31SRichard Fitzgerald dsp->fw_id_version = be32_to_cpu(adsp2_id.fw.ver); 2098b618a185SCharles Keepax adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n", 2099b618a185SCharles Keepax dsp->fw_id, 2100f9f55e31SRichard Fitzgerald (dsp->fw_id_version & 0xff0000) >> 16, 2101f9f55e31SRichard Fitzgerald (dsp->fw_id_version & 0xff00) >> 8, 2102f9f55e31SRichard Fitzgerald dsp->fw_id_version & 0xff, 21033809f001SCharles Keepax n_algs); 2104b618a185SCharles Keepax 2105d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_XM, 2106d9d20e17SCharles Keepax adsp2_id.fw.id, adsp2_id.xm); 2107d9d20e17SCharles Keepax if (IS_ERR(alg_region)) 2108d9d20e17SCharles Keepax return PTR_ERR(alg_region); 2109b618a185SCharles Keepax 2110d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_YM, 2111d9d20e17SCharles Keepax adsp2_id.fw.id, adsp2_id.ym); 2112d9d20e17SCharles Keepax if (IS_ERR(alg_region)) 2113d9d20e17SCharles Keepax return PTR_ERR(alg_region); 2114b618a185SCharles Keepax 2115d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_ZM, 2116d9d20e17SCharles Keepax adsp2_id.fw.id, adsp2_id.zm); 2117d9d20e17SCharles Keepax if (IS_ERR(alg_region)) 2118d9d20e17SCharles Keepax return PTR_ERR(alg_region); 2119b618a185SCharles Keepax 21207f7cca08SCharles Keepax /* Calculate offset and length in DSP words */ 21217f7cca08SCharles Keepax pos = sizeof(adsp2_id) / sizeof(u32); 21227f7cca08SCharles Keepax len = (sizeof(*adsp2_alg) * n_algs) / sizeof(u32); 2123b618a185SCharles Keepax 21247f7cca08SCharles Keepax adsp2_alg = wm_adsp_read_algs(dsp, n_algs, mem, pos, len); 2125b618a185SCharles Keepax if (IS_ERR(adsp2_alg)) 2126b618a185SCharles Keepax return PTR_ERR(adsp2_alg); 2127b618a185SCharles Keepax 21283809f001SCharles Keepax for (i = 0; i < n_algs; i++) { 2129471f4885SMark Brown adsp_info(dsp, 2130471f4885SMark Brown "%d: ID %x v%d.%d.%d XM@%x YM@%x ZM@%x\n", 2131db40517cSMark Brown i, be32_to_cpu(adsp2_alg[i].alg.id), 2132db40517cSMark Brown (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff0000) >> 16, 2133db40517cSMark Brown (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff00) >> 8, 2134471f4885SMark Brown be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff, 2135471f4885SMark Brown be32_to_cpu(adsp2_alg[i].xm), 2136471f4885SMark Brown be32_to_cpu(adsp2_alg[i].ym), 2137471f4885SMark Brown be32_to_cpu(adsp2_alg[i].zm)); 2138471f4885SMark Brown 2139d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_XM, 2140d9d20e17SCharles Keepax adsp2_alg[i].alg.id, 2141d9d20e17SCharles Keepax adsp2_alg[i].xm); 2142d9d20e17SCharles Keepax if (IS_ERR(alg_region)) { 2143d9d20e17SCharles Keepax ret = PTR_ERR(alg_region); 2144d6d52179SJS Park goto out; 2145d6d52179SJS Park } 21462323736dSCharles Keepax if (dsp->fw_ver == 0) { 21473809f001SCharles Keepax if (i + 1 < n_algs) { 21486958eb2aSCharles Keepax len = be32_to_cpu(adsp2_alg[i + 1].xm); 21496958eb2aSCharles Keepax len -= be32_to_cpu(adsp2_alg[i].xm); 21506958eb2aSCharles Keepax len *= 4; 21512323736dSCharles Keepax wm_adsp_create_control(dsp, alg_region, 0, 21528eb084d0SStuart Henderson len, NULL, 0, 0, 21538eb084d0SStuart Henderson SNDRV_CTL_ELEM_TYPE_BYTES); 21546ab2b7b4SDimitris Papastamos } else { 21556ab2b7b4SDimitris Papastamos adsp_warn(dsp, "Missing length info for region XM with ID %x\n", 21566ab2b7b4SDimitris Papastamos be32_to_cpu(adsp2_alg[i].alg.id)); 21576ab2b7b4SDimitris Papastamos } 21582323736dSCharles Keepax } 2159471f4885SMark Brown 2160d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_YM, 2161d9d20e17SCharles Keepax adsp2_alg[i].alg.id, 2162d9d20e17SCharles Keepax adsp2_alg[i].ym); 2163d9d20e17SCharles Keepax if (IS_ERR(alg_region)) { 2164d9d20e17SCharles Keepax ret = PTR_ERR(alg_region); 2165d6d52179SJS Park goto out; 2166d6d52179SJS Park } 21672323736dSCharles Keepax if (dsp->fw_ver == 0) { 21683809f001SCharles Keepax if (i + 1 < n_algs) { 21696958eb2aSCharles Keepax len = be32_to_cpu(adsp2_alg[i + 1].ym); 21706958eb2aSCharles Keepax len -= be32_to_cpu(adsp2_alg[i].ym); 21716958eb2aSCharles Keepax len *= 4; 21722323736dSCharles Keepax wm_adsp_create_control(dsp, alg_region, 0, 21738eb084d0SStuart Henderson len, NULL, 0, 0, 21748eb084d0SStuart Henderson SNDRV_CTL_ELEM_TYPE_BYTES); 21756ab2b7b4SDimitris Papastamos } else { 21766ab2b7b4SDimitris Papastamos adsp_warn(dsp, "Missing length info for region YM with ID %x\n", 21776ab2b7b4SDimitris Papastamos be32_to_cpu(adsp2_alg[i].alg.id)); 21786ab2b7b4SDimitris Papastamos } 21792323736dSCharles Keepax } 2180471f4885SMark Brown 2181d9d20e17SCharles Keepax alg_region = wm_adsp_create_region(dsp, WMFW_ADSP2_ZM, 2182d9d20e17SCharles Keepax adsp2_alg[i].alg.id, 2183d9d20e17SCharles Keepax adsp2_alg[i].zm); 2184d9d20e17SCharles Keepax if (IS_ERR(alg_region)) { 2185d9d20e17SCharles Keepax ret = PTR_ERR(alg_region); 2186d6d52179SJS Park goto out; 2187d6d52179SJS Park } 21882323736dSCharles Keepax if (dsp->fw_ver == 0) { 21893809f001SCharles Keepax if (i + 1 < n_algs) { 21906958eb2aSCharles Keepax len = be32_to_cpu(adsp2_alg[i + 1].zm); 21916958eb2aSCharles Keepax len -= be32_to_cpu(adsp2_alg[i].zm); 21926958eb2aSCharles Keepax len *= 4; 21932323736dSCharles Keepax wm_adsp_create_control(dsp, alg_region, 0, 21948eb084d0SStuart Henderson len, NULL, 0, 0, 21958eb084d0SStuart Henderson SNDRV_CTL_ELEM_TYPE_BYTES); 21966ab2b7b4SDimitris Papastamos } else { 21976ab2b7b4SDimitris Papastamos adsp_warn(dsp, "Missing length info for region ZM with ID %x\n", 21986ab2b7b4SDimitris Papastamos be32_to_cpu(adsp2_alg[i].alg.id)); 21996ab2b7b4SDimitris Papastamos } 2200db40517cSMark Brown } 22012323736dSCharles Keepax } 2202db40517cSMark Brown 2203db40517cSMark Brown out: 2204b618a185SCharles Keepax kfree(adsp2_alg); 2205db40517cSMark Brown return ret; 2206db40517cSMark Brown } 2207db40517cSMark Brown 22082159ad93SMark Brown static int wm_adsp_load_coeff(struct wm_adsp *dsp) 22092159ad93SMark Brown { 2210cf17c83cSMark Brown LIST_HEAD(buf_list); 22112159ad93SMark Brown struct regmap *regmap = dsp->regmap; 22122159ad93SMark Brown struct wmfw_coeff_hdr *hdr; 22132159ad93SMark Brown struct wmfw_coeff_item *blk; 22142159ad93SMark Brown const struct firmware *firmware; 2215471f4885SMark Brown const struct wm_adsp_region *mem; 2216471f4885SMark Brown struct wm_adsp_alg_region *alg_region; 22172159ad93SMark Brown const char *region_name; 22182159ad93SMark Brown int ret, pos, blocks, type, offset, reg; 22192159ad93SMark Brown char *file; 2220cf17c83cSMark Brown struct wm_adsp_buf *buf; 22212159ad93SMark Brown 22222159ad93SMark Brown file = kzalloc(PAGE_SIZE, GFP_KERNEL); 22232159ad93SMark Brown if (file == NULL) 22242159ad93SMark Brown return -ENOMEM; 22252159ad93SMark Brown 2226605391d0SRichard Fitzgerald snprintf(file, PAGE_SIZE, "%s-%s-%s.bin", dsp->part, dsp->fwf_name, 22271023dbd9SMark Brown wm_adsp_fw[dsp->fw].file); 22282159ad93SMark Brown file[PAGE_SIZE - 1] = '\0'; 22292159ad93SMark Brown 22302159ad93SMark Brown ret = request_firmware(&firmware, file, dsp->dev); 22312159ad93SMark Brown if (ret != 0) { 22322159ad93SMark Brown adsp_warn(dsp, "Failed to request '%s'\n", file); 22332159ad93SMark Brown ret = 0; 22342159ad93SMark Brown goto out; 22352159ad93SMark Brown } 22362159ad93SMark Brown ret = -EINVAL; 22372159ad93SMark Brown 22382159ad93SMark Brown if (sizeof(*hdr) >= firmware->size) { 22392159ad93SMark Brown adsp_err(dsp, "%s: file too short, %zu bytes\n", 22402159ad93SMark Brown file, firmware->size); 22412159ad93SMark Brown goto out_fw; 22422159ad93SMark Brown } 22432159ad93SMark Brown 22442159ad93SMark Brown hdr = (void *)&firmware->data[0]; 22452159ad93SMark Brown if (memcmp(hdr->magic, "WMDR", 4) != 0) { 22462159ad93SMark Brown adsp_err(dsp, "%s: invalid magic\n", file); 2247a4cdbec7SCharles Keepax goto out_fw; 22482159ad93SMark Brown } 22492159ad93SMark Brown 2250c712326dSMark Brown switch (be32_to_cpu(hdr->rev) & 0xff) { 2251c712326dSMark Brown case 1: 2252c712326dSMark Brown break; 2253c712326dSMark Brown default: 2254c712326dSMark Brown adsp_err(dsp, "%s: Unsupported coefficient file format %d\n", 2255c712326dSMark Brown file, be32_to_cpu(hdr->rev) & 0xff); 2256c712326dSMark Brown ret = -EINVAL; 2257c712326dSMark Brown goto out_fw; 2258c712326dSMark Brown } 2259c712326dSMark Brown 22602159ad93SMark Brown adsp_dbg(dsp, "%s: v%d.%d.%d\n", file, 22612159ad93SMark Brown (le32_to_cpu(hdr->ver) >> 16) & 0xff, 22622159ad93SMark Brown (le32_to_cpu(hdr->ver) >> 8) & 0xff, 22632159ad93SMark Brown le32_to_cpu(hdr->ver) & 0xff); 22642159ad93SMark Brown 22652159ad93SMark Brown pos = le32_to_cpu(hdr->len); 22662159ad93SMark Brown 22672159ad93SMark Brown blocks = 0; 22682159ad93SMark Brown while (pos < firmware->size && 226950dd2ea8SBen Hutchings sizeof(*blk) < firmware->size - pos) { 22702159ad93SMark Brown blk = (void *)(&firmware->data[pos]); 22712159ad93SMark Brown 2272c712326dSMark Brown type = le16_to_cpu(blk->type); 2273c712326dSMark Brown offset = le16_to_cpu(blk->offset); 22742159ad93SMark Brown 22752159ad93SMark Brown adsp_dbg(dsp, "%s.%d: %x v%d.%d.%d\n", 22762159ad93SMark Brown file, blocks, le32_to_cpu(blk->id), 22772159ad93SMark Brown (le32_to_cpu(blk->ver) >> 16) & 0xff, 22782159ad93SMark Brown (le32_to_cpu(blk->ver) >> 8) & 0xff, 22792159ad93SMark Brown le32_to_cpu(blk->ver) & 0xff); 22802159ad93SMark Brown adsp_dbg(dsp, "%s.%d: %d bytes at 0x%x in %x\n", 22812159ad93SMark Brown file, blocks, le32_to_cpu(blk->len), offset, type); 22822159ad93SMark Brown 22832159ad93SMark Brown reg = 0; 22842159ad93SMark Brown region_name = "Unknown"; 22852159ad93SMark Brown switch (type) { 2286c712326dSMark Brown case (WMFW_NAME_TEXT << 8): 2287c712326dSMark Brown case (WMFW_INFO_TEXT << 8): 22882159ad93SMark Brown break; 2289c712326dSMark Brown case (WMFW_ABSOLUTE << 8): 2290f395a218SMark Brown /* 2291f395a218SMark Brown * Old files may use this for global 2292f395a218SMark Brown * coefficients. 2293f395a218SMark Brown */ 2294f395a218SMark Brown if (le32_to_cpu(blk->id) == dsp->fw_id && 2295f395a218SMark Brown offset == 0) { 2296f395a218SMark Brown region_name = "global coefficients"; 2297f395a218SMark Brown mem = wm_adsp_find_region(dsp, type); 2298f395a218SMark Brown if (!mem) { 2299f395a218SMark Brown adsp_err(dsp, "No ZM\n"); 2300f395a218SMark Brown break; 2301f395a218SMark Brown } 2302f395a218SMark Brown reg = wm_adsp_region_to_reg(mem, 0); 2303f395a218SMark Brown 2304f395a218SMark Brown } else { 23052159ad93SMark Brown region_name = "register"; 23062159ad93SMark Brown reg = offset; 2307f395a218SMark Brown } 23082159ad93SMark Brown break; 2309471f4885SMark Brown 2310471f4885SMark Brown case WMFW_ADSP1_DM: 2311471f4885SMark Brown case WMFW_ADSP1_ZM: 2312471f4885SMark Brown case WMFW_ADSP2_XM: 2313471f4885SMark Brown case WMFW_ADSP2_YM: 2314471f4885SMark Brown adsp_dbg(dsp, "%s.%d: %d bytes in %x for %x\n", 2315471f4885SMark Brown file, blocks, le32_to_cpu(blk->len), 2316471f4885SMark Brown type, le32_to_cpu(blk->id)); 2317471f4885SMark Brown 2318471f4885SMark Brown mem = wm_adsp_find_region(dsp, type); 2319471f4885SMark Brown if (!mem) { 2320471f4885SMark Brown adsp_err(dsp, "No base for region %x\n", type); 2321471f4885SMark Brown break; 2322471f4885SMark Brown } 2323471f4885SMark Brown 232414197095SCharles Keepax alg_region = wm_adsp_find_alg_region(dsp, type, 232514197095SCharles Keepax le32_to_cpu(blk->id)); 232614197095SCharles Keepax if (alg_region) { 2327338c5188SMark Brown reg = alg_region->base; 232814197095SCharles Keepax reg = wm_adsp_region_to_reg(mem, reg); 2329338c5188SMark Brown reg += offset; 233014197095SCharles Keepax } else { 2331471f4885SMark Brown adsp_err(dsp, "No %x for algorithm %x\n", 2332471f4885SMark Brown type, le32_to_cpu(blk->id)); 233314197095SCharles Keepax } 2334471f4885SMark Brown break; 2335471f4885SMark Brown 23362159ad93SMark Brown default: 233725c62f7eSMark Brown adsp_err(dsp, "%s.%d: Unknown region type %x at %d\n", 233825c62f7eSMark Brown file, blocks, type, pos); 23392159ad93SMark Brown break; 23402159ad93SMark Brown } 23412159ad93SMark Brown 23422159ad93SMark Brown if (reg) { 234350dd2ea8SBen Hutchings if (le32_to_cpu(blk->len) > 234450dd2ea8SBen Hutchings firmware->size - pos - sizeof(*blk)) { 23451cab2a84SRichard Fitzgerald adsp_err(dsp, 23461cab2a84SRichard Fitzgerald "%s.%d: %s region len %d bytes exceeds file length %zu\n", 23471cab2a84SRichard Fitzgerald file, blocks, region_name, 23481cab2a84SRichard Fitzgerald le32_to_cpu(blk->len), 23491cab2a84SRichard Fitzgerald firmware->size); 23501cab2a84SRichard Fitzgerald ret = -EINVAL; 23511cab2a84SRichard Fitzgerald goto out_fw; 23521cab2a84SRichard Fitzgerald } 23531cab2a84SRichard Fitzgerald 2354cf17c83cSMark Brown buf = wm_adsp_buf_alloc(blk->data, 2355cf17c83cSMark Brown le32_to_cpu(blk->len), 2356cf17c83cSMark Brown &buf_list); 2357a76fefabSMark Brown if (!buf) { 2358a76fefabSMark Brown adsp_err(dsp, "Out of memory\n"); 2359f4b82812SWei Yongjun ret = -ENOMEM; 2360f4b82812SWei Yongjun goto out_fw; 2361a76fefabSMark Brown } 2362a76fefabSMark Brown 236320da6d5aSMark Brown adsp_dbg(dsp, "%s.%d: Writing %d bytes at %x\n", 236420da6d5aSMark Brown file, blocks, le32_to_cpu(blk->len), 236520da6d5aSMark Brown reg); 2366cf17c83cSMark Brown ret = regmap_raw_write_async(regmap, reg, buf->buf, 23672159ad93SMark Brown le32_to_cpu(blk->len)); 23682159ad93SMark Brown if (ret != 0) { 23692159ad93SMark Brown adsp_err(dsp, 237043bc3bf6SDimitris Papastamos "%s.%d: Failed to write to %x in %s: %d\n", 237143bc3bf6SDimitris Papastamos file, blocks, reg, region_name, ret); 23722159ad93SMark Brown } 23732159ad93SMark Brown } 23742159ad93SMark Brown 2375be951017SCharles Keepax pos += (le32_to_cpu(blk->len) + sizeof(*blk) + 3) & ~0x03; 23762159ad93SMark Brown blocks++; 23772159ad93SMark Brown } 23782159ad93SMark Brown 2379cf17c83cSMark Brown ret = regmap_async_complete(regmap); 2380cf17c83cSMark Brown if (ret != 0) 2381cf17c83cSMark Brown adsp_err(dsp, "Failed to complete async write: %d\n", ret); 2382cf17c83cSMark Brown 23832159ad93SMark Brown if (pos > firmware->size) 23842159ad93SMark Brown adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n", 23852159ad93SMark Brown file, blocks, pos - firmware->size); 23862159ad93SMark Brown 2387f9f55e31SRichard Fitzgerald wm_adsp_debugfs_save_binname(dsp, file); 2388f9f55e31SRichard Fitzgerald 23892159ad93SMark Brown out_fw: 23909da7a5a9SCharles Keepax regmap_async_complete(regmap); 23912159ad93SMark Brown release_firmware(firmware); 2392cf17c83cSMark Brown wm_adsp_buf_free(&buf_list); 23932159ad93SMark Brown out: 23942159ad93SMark Brown kfree(file); 2395f4b82812SWei Yongjun return ret; 23962159ad93SMark Brown } 23972159ad93SMark Brown 2398605391d0SRichard Fitzgerald static int wm_adsp_create_name(struct wm_adsp *dsp) 2399605391d0SRichard Fitzgerald { 2400605391d0SRichard Fitzgerald char *p; 2401605391d0SRichard Fitzgerald 2402605391d0SRichard Fitzgerald if (!dsp->name) { 2403605391d0SRichard Fitzgerald dsp->name = devm_kasprintf(dsp->dev, GFP_KERNEL, "DSP%d", 2404605391d0SRichard Fitzgerald dsp->num); 2405605391d0SRichard Fitzgerald if (!dsp->name) 2406605391d0SRichard Fitzgerald return -ENOMEM; 2407605391d0SRichard Fitzgerald } 2408605391d0SRichard Fitzgerald 2409605391d0SRichard Fitzgerald if (!dsp->fwf_name) { 2410605391d0SRichard Fitzgerald p = devm_kstrdup(dsp->dev, dsp->name, GFP_KERNEL); 2411605391d0SRichard Fitzgerald if (!p) 2412605391d0SRichard Fitzgerald return -ENOMEM; 2413605391d0SRichard Fitzgerald 2414605391d0SRichard Fitzgerald dsp->fwf_name = p; 2415605391d0SRichard Fitzgerald for (; *p != 0; ++p) 2416605391d0SRichard Fitzgerald *p = tolower(*p); 2417605391d0SRichard Fitzgerald } 2418605391d0SRichard Fitzgerald 2419605391d0SRichard Fitzgerald return 0; 2420605391d0SRichard Fitzgerald } 2421605391d0SRichard Fitzgerald 24223809f001SCharles Keepax int wm_adsp1_init(struct wm_adsp *dsp) 24235e7a7a22SMark Brown { 2424605391d0SRichard Fitzgerald int ret; 2425605391d0SRichard Fitzgerald 2426605391d0SRichard Fitzgerald ret = wm_adsp_create_name(dsp); 2427605391d0SRichard Fitzgerald if (ret) 2428605391d0SRichard Fitzgerald return ret; 2429605391d0SRichard Fitzgerald 24303809f001SCharles Keepax INIT_LIST_HEAD(&dsp->alg_regions); 24315e7a7a22SMark Brown 2432078e7183SCharles Keepax mutex_init(&dsp->pwr_lock); 2433078e7183SCharles Keepax 24345e7a7a22SMark Brown return 0; 24355e7a7a22SMark Brown } 24365e7a7a22SMark Brown EXPORT_SYMBOL_GPL(wm_adsp1_init); 24375e7a7a22SMark Brown 24382159ad93SMark Brown int wm_adsp1_event(struct snd_soc_dapm_widget *w, 24392159ad93SMark Brown struct snd_kcontrol *kcontrol, 24402159ad93SMark Brown int event) 24412159ad93SMark Brown { 24420fe1daa6SKuninori Morimoto struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); 24430fe1daa6SKuninori Morimoto struct wm_adsp *dsps = snd_soc_component_get_drvdata(component); 24442159ad93SMark Brown struct wm_adsp *dsp = &dsps[w->shift]; 24456ab2b7b4SDimitris Papastamos struct wm_coeff_ctl *ctl; 24462159ad93SMark Brown int ret; 24477585a5b0SCharles Keepax unsigned int val; 24482159ad93SMark Brown 24490fe1daa6SKuninori Morimoto dsp->component = component; 245092bb4c32SDimitris Papastamos 2451078e7183SCharles Keepax mutex_lock(&dsp->pwr_lock); 2452078e7183SCharles Keepax 24532159ad93SMark Brown switch (event) { 24542159ad93SMark Brown case SND_SOC_DAPM_POST_PMU: 24552159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 24562159ad93SMark Brown ADSP1_SYS_ENA, ADSP1_SYS_ENA); 24572159ad93SMark Brown 245894e205bfSChris Rattray /* 245994e205bfSChris Rattray * For simplicity set the DSP clock rate to be the 246094e205bfSChris Rattray * SYSCLK rate rather than making it configurable. 246194e205bfSChris Rattray */ 246294e205bfSChris Rattray if (dsp->sysclk_reg) { 246394e205bfSChris Rattray ret = regmap_read(dsp->regmap, dsp->sysclk_reg, &val); 246494e205bfSChris Rattray if (ret != 0) { 246594e205bfSChris Rattray adsp_err(dsp, "Failed to read SYSCLK state: %d\n", 246694e205bfSChris Rattray ret); 2467078e7183SCharles Keepax goto err_mutex; 246894e205bfSChris Rattray } 246994e205bfSChris Rattray 24707d00cd97SCharles Keepax val = (val & dsp->sysclk_mask) >> dsp->sysclk_shift; 247194e205bfSChris Rattray 247294e205bfSChris Rattray ret = regmap_update_bits(dsp->regmap, 247394e205bfSChris Rattray dsp->base + ADSP1_CONTROL_31, 247494e205bfSChris Rattray ADSP1_CLK_SEL_MASK, val); 247594e205bfSChris Rattray if (ret != 0) { 247694e205bfSChris Rattray adsp_err(dsp, "Failed to set clock rate: %d\n", 247794e205bfSChris Rattray ret); 2478078e7183SCharles Keepax goto err_mutex; 247994e205bfSChris Rattray } 248094e205bfSChris Rattray } 248194e205bfSChris Rattray 24822159ad93SMark Brown ret = wm_adsp_load(dsp); 24832159ad93SMark Brown if (ret != 0) 2484078e7183SCharles Keepax goto err_ena; 24852159ad93SMark Brown 2486b618a185SCharles Keepax ret = wm_adsp1_setup_algs(dsp); 2487db40517cSMark Brown if (ret != 0) 2488078e7183SCharles Keepax goto err_ena; 2489db40517cSMark Brown 24902159ad93SMark Brown ret = wm_adsp_load_coeff(dsp); 24912159ad93SMark Brown if (ret != 0) 2492078e7183SCharles Keepax goto err_ena; 24932159ad93SMark Brown 24940c2e3f34SDimitris Papastamos /* Initialize caches for enabled and unset controls */ 249581ad93ecSDimitris Papastamos ret = wm_coeff_init_control_caches(dsp); 24966ab2b7b4SDimitris Papastamos if (ret != 0) 2497078e7183SCharles Keepax goto err_ena; 24986ab2b7b4SDimitris Papastamos 24990c2e3f34SDimitris Papastamos /* Sync set controls */ 250081ad93ecSDimitris Papastamos ret = wm_coeff_sync_controls(dsp); 25016ab2b7b4SDimitris Papastamos if (ret != 0) 2502078e7183SCharles Keepax goto err_ena; 25036ab2b7b4SDimitris Papastamos 250428823ebaSCharles Keepax dsp->booted = true; 250528823ebaSCharles Keepax 25062159ad93SMark Brown /* Start the core running */ 25072159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 25082159ad93SMark Brown ADSP1_CORE_ENA | ADSP1_START, 25092159ad93SMark Brown ADSP1_CORE_ENA | ADSP1_START); 251028823ebaSCharles Keepax 251128823ebaSCharles Keepax dsp->running = true; 25122159ad93SMark Brown break; 25132159ad93SMark Brown 25142159ad93SMark Brown case SND_SOC_DAPM_PRE_PMD: 251528823ebaSCharles Keepax dsp->running = false; 251628823ebaSCharles Keepax dsp->booted = false; 251728823ebaSCharles Keepax 25182159ad93SMark Brown /* Halt the core */ 25192159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 25202159ad93SMark Brown ADSP1_CORE_ENA | ADSP1_START, 0); 25212159ad93SMark Brown 25222159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_19, 25232159ad93SMark Brown ADSP1_WDMA_BUFFER_LENGTH_MASK, 0); 25242159ad93SMark Brown 25252159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 25262159ad93SMark Brown ADSP1_SYS_ENA, 0); 25276ab2b7b4SDimitris Papastamos 252881ad93ecSDimitris Papastamos list_for_each_entry(ctl, &dsp->ctl_list, list) 25296ab2b7b4SDimitris Papastamos ctl->enabled = 0; 2530b0101b4fSDimitris Papastamos 253156574d54SRichard Fitzgerald 253256574d54SRichard Fitzgerald wm_adsp_free_alg_regions(dsp); 25332159ad93SMark Brown break; 25342159ad93SMark Brown 25352159ad93SMark Brown default: 25362159ad93SMark Brown break; 25372159ad93SMark Brown } 25382159ad93SMark Brown 2539078e7183SCharles Keepax mutex_unlock(&dsp->pwr_lock); 2540078e7183SCharles Keepax 25412159ad93SMark Brown return 0; 25422159ad93SMark Brown 2543078e7183SCharles Keepax err_ena: 25442159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, 25452159ad93SMark Brown ADSP1_SYS_ENA, 0); 2546078e7183SCharles Keepax err_mutex: 2547078e7183SCharles Keepax mutex_unlock(&dsp->pwr_lock); 2548078e7183SCharles Keepax 25492159ad93SMark Brown return ret; 25502159ad93SMark Brown } 25512159ad93SMark Brown EXPORT_SYMBOL_GPL(wm_adsp1_event); 25522159ad93SMark Brown 25532159ad93SMark Brown static int wm_adsp2_ena(struct wm_adsp *dsp) 25542159ad93SMark Brown { 25552159ad93SMark Brown unsigned int val; 25562159ad93SMark Brown int ret, count; 25572159ad93SMark Brown 2558e1ea1879SRichard Fitzgerald switch (dsp->rev) { 2559e1ea1879SRichard Fitzgerald case 0: 2560e1ea1879SRichard Fitzgerald ret = regmap_update_bits_async(dsp->regmap, 2561e1ea1879SRichard Fitzgerald dsp->base + ADSP2_CONTROL, 25622159ad93SMark Brown ADSP2_SYS_ENA, ADSP2_SYS_ENA); 25632159ad93SMark Brown if (ret != 0) 25642159ad93SMark Brown return ret; 2565e1ea1879SRichard Fitzgerald break; 2566e1ea1879SRichard Fitzgerald default: 2567e1ea1879SRichard Fitzgerald break; 2568e1ea1879SRichard Fitzgerald } 25692159ad93SMark Brown 25702159ad93SMark Brown /* Wait for the RAM to start, should be near instantaneous */ 2571939fd1e8SCharles Keepax for (count = 0; count < 10; ++count) { 25727d00cd97SCharles Keepax ret = regmap_read(dsp->regmap, dsp->base + ADSP2_STATUS1, &val); 25732159ad93SMark Brown if (ret != 0) 25742159ad93SMark Brown return ret; 2575939fd1e8SCharles Keepax 2576939fd1e8SCharles Keepax if (val & ADSP2_RAM_RDY) 2577939fd1e8SCharles Keepax break; 2578939fd1e8SCharles Keepax 25791fa96f3fSCharles Keepax usleep_range(250, 500); 2580939fd1e8SCharles Keepax } 25812159ad93SMark Brown 25822159ad93SMark Brown if (!(val & ADSP2_RAM_RDY)) { 25832159ad93SMark Brown adsp_err(dsp, "Failed to start DSP RAM\n"); 25842159ad93SMark Brown return -EBUSY; 25852159ad93SMark Brown } 25862159ad93SMark Brown 25872159ad93SMark Brown adsp_dbg(dsp, "RAM ready after %d polls\n", count); 25882159ad93SMark Brown 25892159ad93SMark Brown return 0; 25902159ad93SMark Brown } 25912159ad93SMark Brown 259218b1a902SCharles Keepax static void wm_adsp2_boot_work(struct work_struct *work) 25932159ad93SMark Brown { 2594d8a64d6aSCharles Keepax struct wm_adsp *dsp = container_of(work, 2595d8a64d6aSCharles Keepax struct wm_adsp, 2596d8a64d6aSCharles Keepax boot_work); 25972159ad93SMark Brown int ret; 25982159ad93SMark Brown 2599078e7183SCharles Keepax mutex_lock(&dsp->pwr_lock); 2600078e7183SCharles Keepax 260190d19ba5SCharles Keepax ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 260290d19ba5SCharles Keepax ADSP2_MEM_ENA, ADSP2_MEM_ENA); 260390d19ba5SCharles Keepax if (ret != 0) 260490d19ba5SCharles Keepax goto err_mutex; 260590d19ba5SCharles Keepax 26062159ad93SMark Brown ret = wm_adsp2_ena(dsp); 26072159ad93SMark Brown if (ret != 0) 2608d589d8b8SCharles Keepax goto err_mem; 26092159ad93SMark Brown 26102159ad93SMark Brown ret = wm_adsp_load(dsp); 26112159ad93SMark Brown if (ret != 0) 2612078e7183SCharles Keepax goto err_ena; 26132159ad93SMark Brown 2614b618a185SCharles Keepax ret = wm_adsp2_setup_algs(dsp); 2615db40517cSMark Brown if (ret != 0) 2616078e7183SCharles Keepax goto err_ena; 2617db40517cSMark Brown 26182159ad93SMark Brown ret = wm_adsp_load_coeff(dsp); 26192159ad93SMark Brown if (ret != 0) 2620078e7183SCharles Keepax goto err_ena; 26212159ad93SMark Brown 26220c2e3f34SDimitris Papastamos /* Initialize caches for enabled and unset controls */ 262381ad93ecSDimitris Papastamos ret = wm_coeff_init_control_caches(dsp); 26246ab2b7b4SDimitris Papastamos if (ret != 0) 2625078e7183SCharles Keepax goto err_ena; 26266ab2b7b4SDimitris Papastamos 2627e1ea1879SRichard Fitzgerald switch (dsp->rev) { 2628e1ea1879SRichard Fitzgerald case 0: 262990d19ba5SCharles Keepax /* Turn DSP back off until we are ready to run */ 263090d19ba5SCharles Keepax ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 263190d19ba5SCharles Keepax ADSP2_SYS_ENA, 0); 263290d19ba5SCharles Keepax if (ret != 0) 263390d19ba5SCharles Keepax goto err_ena; 2634e1ea1879SRichard Fitzgerald break; 2635e1ea1879SRichard Fitzgerald default: 2636e1ea1879SRichard Fitzgerald break; 2637e1ea1879SRichard Fitzgerald } 263890d19ba5SCharles Keepax 2639e779974bSCharles Keepax dsp->booted = true; 2640e779974bSCharles Keepax 2641078e7183SCharles Keepax mutex_unlock(&dsp->pwr_lock); 2642078e7183SCharles Keepax 2643d8a64d6aSCharles Keepax return; 2644d8a64d6aSCharles Keepax 2645078e7183SCharles Keepax err_ena: 2646d8a64d6aSCharles Keepax regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 2647d8a64d6aSCharles Keepax ADSP2_SYS_ENA | ADSP2_CORE_ENA | ADSP2_START, 0); 2648d589d8b8SCharles Keepax err_mem: 2649d589d8b8SCharles Keepax regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 2650d589d8b8SCharles Keepax ADSP2_MEM_ENA, 0); 2651078e7183SCharles Keepax err_mutex: 2652078e7183SCharles Keepax mutex_unlock(&dsp->pwr_lock); 2653d8a64d6aSCharles Keepax } 2654d8a64d6aSCharles Keepax 2655d82d767fSCharles Keepax static void wm_adsp2_set_dspclk(struct wm_adsp *dsp, unsigned int freq) 2656d82d767fSCharles Keepax { 2657d82d767fSCharles Keepax int ret; 2658d82d767fSCharles Keepax 2659e1ea1879SRichard Fitzgerald switch (dsp->rev) { 2660e1ea1879SRichard Fitzgerald case 0: 2661d82d767fSCharles Keepax ret = regmap_update_bits_async(dsp->regmap, 2662d82d767fSCharles Keepax dsp->base + ADSP2_CLOCKING, 2663d82d767fSCharles Keepax ADSP2_CLK_SEL_MASK, 2664d82d767fSCharles Keepax freq << ADSP2_CLK_SEL_SHIFT); 2665e1ea1879SRichard Fitzgerald if (ret) { 2666d82d767fSCharles Keepax adsp_err(dsp, "Failed to set clock rate: %d\n", ret); 2667e1ea1879SRichard Fitzgerald return; 2668e1ea1879SRichard Fitzgerald } 2669e1ea1879SRichard Fitzgerald break; 2670e1ea1879SRichard Fitzgerald default: 2671e1ea1879SRichard Fitzgerald /* clock is handled by parent codec driver */ 2672e1ea1879SRichard Fitzgerald break; 2673e1ea1879SRichard Fitzgerald } 2674d82d767fSCharles Keepax } 2675d82d767fSCharles Keepax 2676af813a6fSCharles Keepax int wm_adsp2_preloader_get(struct snd_kcontrol *kcontrol, 2677af813a6fSCharles Keepax struct snd_ctl_elem_value *ucontrol) 2678af813a6fSCharles Keepax { 26790fe1daa6SKuninori Morimoto struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); 2680b1470d4cSAjit Pandey struct wm_adsp *dsps = snd_soc_component_get_drvdata(component); 2681b1470d4cSAjit Pandey struct soc_mixer_control *mc = 2682b1470d4cSAjit Pandey (struct soc_mixer_control *)kcontrol->private_value; 2683b1470d4cSAjit Pandey struct wm_adsp *dsp = &dsps[mc->shift - 1]; 2684af813a6fSCharles Keepax 2685af813a6fSCharles Keepax ucontrol->value.integer.value[0] = dsp->preloaded; 2686af813a6fSCharles Keepax 2687af813a6fSCharles Keepax return 0; 2688af813a6fSCharles Keepax } 2689af813a6fSCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp2_preloader_get); 2690af813a6fSCharles Keepax 2691af813a6fSCharles Keepax int wm_adsp2_preloader_put(struct snd_kcontrol *kcontrol, 2692af813a6fSCharles Keepax struct snd_ctl_elem_value *ucontrol) 2693af813a6fSCharles Keepax { 26940fe1daa6SKuninori Morimoto struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); 2695b1470d4cSAjit Pandey struct wm_adsp *dsps = snd_soc_component_get_drvdata(component); 26960fe1daa6SKuninori Morimoto struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); 2697af813a6fSCharles Keepax struct soc_mixer_control *mc = 2698af813a6fSCharles Keepax (struct soc_mixer_control *)kcontrol->private_value; 2699b1470d4cSAjit Pandey struct wm_adsp *dsp = &dsps[mc->shift - 1]; 2700af813a6fSCharles Keepax char preload[32]; 2701af813a6fSCharles Keepax 2702605391d0SRichard Fitzgerald snprintf(preload, ARRAY_SIZE(preload), "%s Preload", dsp->name); 2703af813a6fSCharles Keepax 2704af813a6fSCharles Keepax dsp->preloaded = ucontrol->value.integer.value[0]; 2705af813a6fSCharles Keepax 2706af813a6fSCharles Keepax if (ucontrol->value.integer.value[0]) 270795a594d0SCharles Keepax snd_soc_component_force_enable_pin(component, preload); 2708af813a6fSCharles Keepax else 270995a594d0SCharles Keepax snd_soc_component_disable_pin(component, preload); 2710af813a6fSCharles Keepax 2711af813a6fSCharles Keepax snd_soc_dapm_sync(dapm); 2712af813a6fSCharles Keepax 2713868e49a4SStuart Henderson flush_work(&dsp->boot_work); 2714868e49a4SStuart Henderson 2715af813a6fSCharles Keepax return 0; 2716af813a6fSCharles Keepax } 2717af813a6fSCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp2_preloader_put); 2718af813a6fSCharles Keepax 271951a2c944SMayuresh Kulkarni static void wm_adsp_stop_watchdog(struct wm_adsp *dsp) 272051a2c944SMayuresh Kulkarni { 272151a2c944SMayuresh Kulkarni switch (dsp->rev) { 272251a2c944SMayuresh Kulkarni case 0: 272351a2c944SMayuresh Kulkarni case 1: 272451a2c944SMayuresh Kulkarni return; 272551a2c944SMayuresh Kulkarni default: 272651a2c944SMayuresh Kulkarni regmap_update_bits(dsp->regmap, dsp->base + ADSP2_WATCHDOG, 272751a2c944SMayuresh Kulkarni ADSP2_WDT_ENA_MASK, 0); 272851a2c944SMayuresh Kulkarni } 272951a2c944SMayuresh Kulkarni } 273051a2c944SMayuresh Kulkarni 273112db5eddSCharles Keepax int wm_adsp2_early_event(struct snd_soc_dapm_widget *w, 2732d82d767fSCharles Keepax struct snd_kcontrol *kcontrol, int event, 2733d82d767fSCharles Keepax unsigned int freq) 273412db5eddSCharles Keepax { 27350fe1daa6SKuninori Morimoto struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); 27360fe1daa6SKuninori Morimoto struct wm_adsp *dsps = snd_soc_component_get_drvdata(component); 273712db5eddSCharles Keepax struct wm_adsp *dsp = &dsps[w->shift]; 273857a60cc3SCharles Keepax struct wm_coeff_ctl *ctl; 273912db5eddSCharles Keepax 274012db5eddSCharles Keepax switch (event) { 274112db5eddSCharles Keepax case SND_SOC_DAPM_PRE_PMU: 2742d82d767fSCharles Keepax wm_adsp2_set_dspclk(dsp, freq); 274312db5eddSCharles Keepax queue_work(system_unbound_wq, &dsp->boot_work); 274412db5eddSCharles Keepax break; 274557a60cc3SCharles Keepax case SND_SOC_DAPM_PRE_PMD: 2746bb24ee41SCharles Keepax mutex_lock(&dsp->pwr_lock); 2747bb24ee41SCharles Keepax 274857a60cc3SCharles Keepax wm_adsp_debugfs_clear(dsp); 274957a60cc3SCharles Keepax 275057a60cc3SCharles Keepax dsp->fw_id = 0; 275157a60cc3SCharles Keepax dsp->fw_id_version = 0; 275257a60cc3SCharles Keepax 275357a60cc3SCharles Keepax dsp->booted = false; 275457a60cc3SCharles Keepax 275557a60cc3SCharles Keepax regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 275657a60cc3SCharles Keepax ADSP2_MEM_ENA, 0); 275757a60cc3SCharles Keepax 275857a60cc3SCharles Keepax list_for_each_entry(ctl, &dsp->ctl_list, list) 275957a60cc3SCharles Keepax ctl->enabled = 0; 276057a60cc3SCharles Keepax 276157a60cc3SCharles Keepax wm_adsp_free_alg_regions(dsp); 276257a60cc3SCharles Keepax 2763bb24ee41SCharles Keepax mutex_unlock(&dsp->pwr_lock); 2764bb24ee41SCharles Keepax 276557a60cc3SCharles Keepax adsp_dbg(dsp, "Shutdown complete\n"); 276657a60cc3SCharles Keepax break; 276712db5eddSCharles Keepax default: 276812db5eddSCharles Keepax break; 2769cab27258SCharles Keepax } 277012db5eddSCharles Keepax 277112db5eddSCharles Keepax return 0; 277212db5eddSCharles Keepax } 277312db5eddSCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp2_early_event); 277412db5eddSCharles Keepax 2775d8a64d6aSCharles Keepax int wm_adsp2_event(struct snd_soc_dapm_widget *w, 2776d8a64d6aSCharles Keepax struct snd_kcontrol *kcontrol, int event) 2777d8a64d6aSCharles Keepax { 27780fe1daa6SKuninori Morimoto struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); 27790fe1daa6SKuninori Morimoto struct wm_adsp *dsps = snd_soc_component_get_drvdata(component); 2780d8a64d6aSCharles Keepax struct wm_adsp *dsp = &dsps[w->shift]; 2781d8a64d6aSCharles Keepax int ret; 2782d8a64d6aSCharles Keepax 2783d8a64d6aSCharles Keepax switch (event) { 2784d8a64d6aSCharles Keepax case SND_SOC_DAPM_POST_PMU: 2785d8a64d6aSCharles Keepax flush_work(&dsp->boot_work); 2786d8a64d6aSCharles Keepax 2787bb24ee41SCharles Keepax mutex_lock(&dsp->pwr_lock); 2788bb24ee41SCharles Keepax 2789bb24ee41SCharles Keepax if (!dsp->booted) { 2790bb24ee41SCharles Keepax ret = -EIO; 2791bb24ee41SCharles Keepax goto err; 2792bb24ee41SCharles Keepax } 2793d8a64d6aSCharles Keepax 279490d19ba5SCharles Keepax ret = wm_adsp2_ena(dsp); 279590d19ba5SCharles Keepax if (ret != 0) 279690d19ba5SCharles Keepax goto err; 279790d19ba5SCharles Keepax 2798cef45771SCharles Keepax /* Sync set controls */ 2799cef45771SCharles Keepax ret = wm_coeff_sync_controls(dsp); 2800cef45771SCharles Keepax if (ret != 0) 2801cef45771SCharles Keepax goto err; 2802cef45771SCharles Keepax 280351a2c944SMayuresh Kulkarni wm_adsp2_lock(dsp, dsp->lock_regions); 280451a2c944SMayuresh Kulkarni 2805d8a64d6aSCharles Keepax ret = regmap_update_bits(dsp->regmap, 2806d8a64d6aSCharles Keepax dsp->base + ADSP2_CONTROL, 280700e4c3b6SCharles Keepax ADSP2_CORE_ENA | ADSP2_START, 280800e4c3b6SCharles Keepax ADSP2_CORE_ENA | ADSP2_START); 2809d8a64d6aSCharles Keepax if (ret != 0) 2810d8a64d6aSCharles Keepax goto err; 28112cd19bdbSCharles Keepax 281248c2c993SCharles Keepax if (wm_adsp_fw[dsp->fw].num_caps != 0) { 28132cd19bdbSCharles Keepax ret = wm_adsp_buffer_init(dsp); 2814bb24ee41SCharles Keepax if (ret < 0) 281548c2c993SCharles Keepax goto err; 281648c2c993SCharles Keepax } 28172cd19bdbSCharles Keepax 2818e779974bSCharles Keepax dsp->running = true; 28191023dbd9SMark Brown 2820612047f0SCharles Keepax mutex_unlock(&dsp->pwr_lock); 2821612047f0SCharles Keepax 28222159ad93SMark Brown break; 28232159ad93SMark Brown 28242159ad93SMark Brown case SND_SOC_DAPM_PRE_PMD: 2825f4f0c4c6SRichard Fitzgerald /* Tell the firmware to cleanup */ 2826f4f0c4c6SRichard Fitzgerald wm_adsp_signal_event_controls(dsp, WM_ADSP_FW_EVENT_SHUTDOWN); 2827f4f0c4c6SRichard Fitzgerald 282851a2c944SMayuresh Kulkarni wm_adsp_stop_watchdog(dsp); 282951a2c944SMayuresh Kulkarni 283010337b07SRichard Fitzgerald /* Log firmware state, it can be useful for analysis */ 2831e1ea1879SRichard Fitzgerald switch (dsp->rev) { 2832e1ea1879SRichard Fitzgerald case 0: 283310337b07SRichard Fitzgerald wm_adsp2_show_fw_status(dsp); 2834e1ea1879SRichard Fitzgerald break; 2835e1ea1879SRichard Fitzgerald default: 2836e1ea1879SRichard Fitzgerald wm_adsp2v2_show_fw_status(dsp); 2837e1ea1879SRichard Fitzgerald break; 2838e1ea1879SRichard Fitzgerald } 283910337b07SRichard Fitzgerald 2840078e7183SCharles Keepax mutex_lock(&dsp->pwr_lock); 2841078e7183SCharles Keepax 28421023dbd9SMark Brown dsp->running = false; 28431023dbd9SMark Brown 2844e1ea1879SRichard Fitzgerald regmap_update_bits(dsp->regmap, 2845e1ea1879SRichard Fitzgerald dsp->base + ADSP2_CONTROL, 284657a60cc3SCharles Keepax ADSP2_CORE_ENA | ADSP2_START, 0); 2847973838a0SMark Brown 28482d30b575SMark Brown /* Make sure DMAs are quiesced */ 2849e1ea1879SRichard Fitzgerald switch (dsp->rev) { 2850e1ea1879SRichard Fitzgerald case 0: 2851e1ea1879SRichard Fitzgerald regmap_write(dsp->regmap, 2852e1ea1879SRichard Fitzgerald dsp->base + ADSP2_RDMA_CONFIG_1, 0); 2853e1ea1879SRichard Fitzgerald regmap_write(dsp->regmap, 2854e1ea1879SRichard Fitzgerald dsp->base + ADSP2_WDMA_CONFIG_1, 0); 2855e1ea1879SRichard Fitzgerald regmap_write(dsp->regmap, 2856e1ea1879SRichard Fitzgerald dsp->base + ADSP2_WDMA_CONFIG_2, 0); 28576facd2d1SSimon Trimmer 2858e1ea1879SRichard Fitzgerald regmap_update_bits(dsp->regmap, 2859e1ea1879SRichard Fitzgerald dsp->base + ADSP2_CONTROL, 28606facd2d1SSimon Trimmer ADSP2_SYS_ENA, 0); 2861e1ea1879SRichard Fitzgerald break; 2862e1ea1879SRichard Fitzgerald default: 2863e1ea1879SRichard Fitzgerald regmap_write(dsp->regmap, 2864e1ea1879SRichard Fitzgerald dsp->base + ADSP2_RDMA_CONFIG_1, 0); 2865e1ea1879SRichard Fitzgerald regmap_write(dsp->regmap, 2866e1ea1879SRichard Fitzgerald dsp->base + ADSP2_WDMA_CONFIG_1, 0); 2867e1ea1879SRichard Fitzgerald regmap_write(dsp->regmap, 2868e1ea1879SRichard Fitzgerald dsp->base + ADSP2V2_WDMA_CONFIG_2, 0); 2869e1ea1879SRichard Fitzgerald break; 2870e1ea1879SRichard Fitzgerald } 28712d30b575SMark Brown 28722cd19bdbSCharles Keepax if (wm_adsp_fw[dsp->fw].num_caps != 0) 28732cd19bdbSCharles Keepax wm_adsp_buffer_free(dsp); 28742cd19bdbSCharles Keepax 2875078e7183SCharles Keepax mutex_unlock(&dsp->pwr_lock); 2876078e7183SCharles Keepax 287757a60cc3SCharles Keepax adsp_dbg(dsp, "Execution stopped\n"); 28782159ad93SMark Brown break; 28792159ad93SMark Brown 28802159ad93SMark Brown default: 28812159ad93SMark Brown break; 28822159ad93SMark Brown } 28832159ad93SMark Brown 28842159ad93SMark Brown return 0; 28852159ad93SMark Brown err: 28862159ad93SMark Brown regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 2887a7f9be7eSMark Brown ADSP2_SYS_ENA | ADSP2_CORE_ENA | ADSP2_START, 0); 2888bb24ee41SCharles Keepax mutex_unlock(&dsp->pwr_lock); 28892159ad93SMark Brown return ret; 28902159ad93SMark Brown } 28912159ad93SMark Brown EXPORT_SYMBOL_GPL(wm_adsp2_event); 2892973838a0SMark Brown 28930fe1daa6SKuninori Morimoto int wm_adsp2_component_probe(struct wm_adsp *dsp, struct snd_soc_component *component) 2894f5e2ce92SRichard Fitzgerald { 2895af813a6fSCharles Keepax char preload[32]; 2896af813a6fSCharles Keepax 2897605391d0SRichard Fitzgerald snprintf(preload, ARRAY_SIZE(preload), "%s Preload", dsp->name); 289895a594d0SCharles Keepax snd_soc_component_disable_pin(component, preload); 2899685f51a5SRichard Fitzgerald 29000fe1daa6SKuninori Morimoto wm_adsp2_init_debugfs(dsp, component); 2901f9f55e31SRichard Fitzgerald 29020fe1daa6SKuninori Morimoto dsp->component = component; 2903af813a6fSCharles Keepax 29040a047f07SRichard Fitzgerald return 0; 2905f5e2ce92SRichard Fitzgerald } 29060fe1daa6SKuninori Morimoto EXPORT_SYMBOL_GPL(wm_adsp2_component_probe); 2907f5e2ce92SRichard Fitzgerald 29080fe1daa6SKuninori Morimoto int wm_adsp2_component_remove(struct wm_adsp *dsp, struct snd_soc_component *component) 2909f5e2ce92SRichard Fitzgerald { 2910f9f55e31SRichard Fitzgerald wm_adsp2_cleanup_debugfs(dsp); 2911f9f55e31SRichard Fitzgerald 2912f5e2ce92SRichard Fitzgerald return 0; 2913f5e2ce92SRichard Fitzgerald } 29140fe1daa6SKuninori Morimoto EXPORT_SYMBOL_GPL(wm_adsp2_component_remove); 2915f5e2ce92SRichard Fitzgerald 291681ac58b1SRichard Fitzgerald int wm_adsp2_init(struct wm_adsp *dsp) 2917973838a0SMark Brown { 2918973838a0SMark Brown int ret; 2919973838a0SMark Brown 2920605391d0SRichard Fitzgerald ret = wm_adsp_create_name(dsp); 2921605391d0SRichard Fitzgerald if (ret) 2922605391d0SRichard Fitzgerald return ret; 2923605391d0SRichard Fitzgerald 2924e1ea1879SRichard Fitzgerald switch (dsp->rev) { 2925e1ea1879SRichard Fitzgerald case 0: 292610a2b662SMark Brown /* 292710a2b662SMark Brown * Disable the DSP memory by default when in reset for a small 292810a2b662SMark Brown * power saving. 292910a2b662SMark Brown */ 29303809f001SCharles Keepax ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, 293110a2b662SMark Brown ADSP2_MEM_ENA, 0); 2932e1ea1879SRichard Fitzgerald if (ret) { 2933e1ea1879SRichard Fitzgerald adsp_err(dsp, 2934e1ea1879SRichard Fitzgerald "Failed to clear memory retention: %d\n", ret); 293510a2b662SMark Brown return ret; 293610a2b662SMark Brown } 2937e1ea1879SRichard Fitzgerald break; 2938e1ea1879SRichard Fitzgerald default: 2939e1ea1879SRichard Fitzgerald break; 2940e1ea1879SRichard Fitzgerald } 294110a2b662SMark Brown 29423809f001SCharles Keepax INIT_LIST_HEAD(&dsp->alg_regions); 29433809f001SCharles Keepax INIT_LIST_HEAD(&dsp->ctl_list); 29443809f001SCharles Keepax INIT_WORK(&dsp->boot_work, wm_adsp2_boot_work); 29456ab2b7b4SDimitris Papastamos 2946078e7183SCharles Keepax mutex_init(&dsp->pwr_lock); 2947078e7183SCharles Keepax 2948973838a0SMark Brown return 0; 2949973838a0SMark Brown } 2950973838a0SMark Brown EXPORT_SYMBOL_GPL(wm_adsp2_init); 29510a37c6efSPraveen Diwakar 295266225e98SRichard Fitzgerald void wm_adsp2_remove(struct wm_adsp *dsp) 295366225e98SRichard Fitzgerald { 295466225e98SRichard Fitzgerald struct wm_coeff_ctl *ctl; 295566225e98SRichard Fitzgerald 295666225e98SRichard Fitzgerald while (!list_empty(&dsp->ctl_list)) { 295766225e98SRichard Fitzgerald ctl = list_first_entry(&dsp->ctl_list, struct wm_coeff_ctl, 295866225e98SRichard Fitzgerald list); 295966225e98SRichard Fitzgerald list_del(&ctl->list); 296066225e98SRichard Fitzgerald wm_adsp_free_ctl_blk(ctl); 296166225e98SRichard Fitzgerald } 296266225e98SRichard Fitzgerald } 296366225e98SRichard Fitzgerald EXPORT_SYMBOL_GPL(wm_adsp2_remove); 296466225e98SRichard Fitzgerald 2965edd71350SCharles Keepax static inline int wm_adsp_compr_attached(struct wm_adsp_compr *compr) 2966edd71350SCharles Keepax { 2967edd71350SCharles Keepax return compr->buf != NULL; 2968edd71350SCharles Keepax } 2969edd71350SCharles Keepax 2970edd71350SCharles Keepax static int wm_adsp_compr_attach(struct wm_adsp_compr *compr) 2971edd71350SCharles Keepax { 2972edd71350SCharles Keepax /* 2973edd71350SCharles Keepax * Note this will be more complex once each DSP can support multiple 2974edd71350SCharles Keepax * streams 2975edd71350SCharles Keepax */ 2976edd71350SCharles Keepax if (!compr->dsp->buffer) 2977edd71350SCharles Keepax return -EINVAL; 2978edd71350SCharles Keepax 2979edd71350SCharles Keepax compr->buf = compr->dsp->buffer; 2980721be3beSCharles Keepax compr->buf->compr = compr; 2981edd71350SCharles Keepax 2982edd71350SCharles Keepax return 0; 2983edd71350SCharles Keepax } 2984edd71350SCharles Keepax 2985721be3beSCharles Keepax static void wm_adsp_compr_detach(struct wm_adsp_compr *compr) 2986721be3beSCharles Keepax { 2987721be3beSCharles Keepax if (!compr) 2988721be3beSCharles Keepax return; 2989721be3beSCharles Keepax 2990721be3beSCharles Keepax /* Wake the poll so it can see buffer is no longer attached */ 2991721be3beSCharles Keepax if (compr->stream) 2992721be3beSCharles Keepax snd_compr_fragment_elapsed(compr->stream); 2993721be3beSCharles Keepax 2994721be3beSCharles Keepax if (wm_adsp_compr_attached(compr)) { 2995721be3beSCharles Keepax compr->buf->compr = NULL; 2996721be3beSCharles Keepax compr->buf = NULL; 2997721be3beSCharles Keepax } 2998721be3beSCharles Keepax } 2999721be3beSCharles Keepax 3000406abc95SCharles Keepax int wm_adsp_compr_open(struct wm_adsp *dsp, struct snd_compr_stream *stream) 3001406abc95SCharles Keepax { 3002406abc95SCharles Keepax struct wm_adsp_compr *compr; 3003406abc95SCharles Keepax int ret = 0; 3004406abc95SCharles Keepax 3005406abc95SCharles Keepax mutex_lock(&dsp->pwr_lock); 3006406abc95SCharles Keepax 3007406abc95SCharles Keepax if (wm_adsp_fw[dsp->fw].num_caps == 0) { 3008406abc95SCharles Keepax adsp_err(dsp, "Firmware does not support compressed API\n"); 3009406abc95SCharles Keepax ret = -ENXIO; 3010406abc95SCharles Keepax goto out; 3011406abc95SCharles Keepax } 3012406abc95SCharles Keepax 3013406abc95SCharles Keepax if (wm_adsp_fw[dsp->fw].compr_direction != stream->direction) { 3014406abc95SCharles Keepax adsp_err(dsp, "Firmware does not support stream direction\n"); 3015406abc95SCharles Keepax ret = -EINVAL; 3016406abc95SCharles Keepax goto out; 3017406abc95SCharles Keepax } 3018406abc95SCharles Keepax 301995fe9597SCharles Keepax if (dsp->compr) { 302095fe9597SCharles Keepax /* It is expect this limitation will be removed in future */ 302195fe9597SCharles Keepax adsp_err(dsp, "Only a single stream supported per DSP\n"); 302295fe9597SCharles Keepax ret = -EBUSY; 302395fe9597SCharles Keepax goto out; 302495fe9597SCharles Keepax } 302595fe9597SCharles Keepax 3026406abc95SCharles Keepax compr = kzalloc(sizeof(*compr), GFP_KERNEL); 3027406abc95SCharles Keepax if (!compr) { 3028406abc95SCharles Keepax ret = -ENOMEM; 3029406abc95SCharles Keepax goto out; 3030406abc95SCharles Keepax } 3031406abc95SCharles Keepax 3032406abc95SCharles Keepax compr->dsp = dsp; 3033406abc95SCharles Keepax compr->stream = stream; 3034406abc95SCharles Keepax 3035406abc95SCharles Keepax dsp->compr = compr; 3036406abc95SCharles Keepax 3037406abc95SCharles Keepax stream->runtime->private_data = compr; 3038406abc95SCharles Keepax 3039406abc95SCharles Keepax out: 3040406abc95SCharles Keepax mutex_unlock(&dsp->pwr_lock); 3041406abc95SCharles Keepax 3042406abc95SCharles Keepax return ret; 3043406abc95SCharles Keepax } 3044406abc95SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_open); 3045406abc95SCharles Keepax 3046406abc95SCharles Keepax int wm_adsp_compr_free(struct snd_compr_stream *stream) 3047406abc95SCharles Keepax { 3048406abc95SCharles Keepax struct wm_adsp_compr *compr = stream->runtime->private_data; 3049406abc95SCharles Keepax struct wm_adsp *dsp = compr->dsp; 3050406abc95SCharles Keepax 3051406abc95SCharles Keepax mutex_lock(&dsp->pwr_lock); 3052406abc95SCharles Keepax 3053721be3beSCharles Keepax wm_adsp_compr_detach(compr); 3054406abc95SCharles Keepax dsp->compr = NULL; 3055406abc95SCharles Keepax 305683a40ce9SCharles Keepax kfree(compr->raw_buf); 3057406abc95SCharles Keepax kfree(compr); 3058406abc95SCharles Keepax 3059406abc95SCharles Keepax mutex_unlock(&dsp->pwr_lock); 3060406abc95SCharles Keepax 3061406abc95SCharles Keepax return 0; 3062406abc95SCharles Keepax } 3063406abc95SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_free); 3064406abc95SCharles Keepax 3065406abc95SCharles Keepax static int wm_adsp_compr_check_params(struct snd_compr_stream *stream, 3066406abc95SCharles Keepax struct snd_compr_params *params) 3067406abc95SCharles Keepax { 3068406abc95SCharles Keepax struct wm_adsp_compr *compr = stream->runtime->private_data; 3069406abc95SCharles Keepax struct wm_adsp *dsp = compr->dsp; 3070406abc95SCharles Keepax const struct wm_adsp_fw_caps *caps; 3071406abc95SCharles Keepax const struct snd_codec_desc *desc; 3072406abc95SCharles Keepax int i, j; 3073406abc95SCharles Keepax 3074406abc95SCharles Keepax if (params->buffer.fragment_size < WM_ADSP_MIN_FRAGMENT_SIZE || 3075406abc95SCharles Keepax params->buffer.fragment_size > WM_ADSP_MAX_FRAGMENT_SIZE || 3076406abc95SCharles Keepax params->buffer.fragments < WM_ADSP_MIN_FRAGMENTS || 3077406abc95SCharles Keepax params->buffer.fragments > WM_ADSP_MAX_FRAGMENTS || 3078406abc95SCharles Keepax params->buffer.fragment_size % WM_ADSP_DATA_WORD_SIZE) { 3079406abc95SCharles Keepax adsp_err(dsp, "Invalid buffer fragsize=%d fragments=%d\n", 3080406abc95SCharles Keepax params->buffer.fragment_size, 3081406abc95SCharles Keepax params->buffer.fragments); 3082406abc95SCharles Keepax 3083406abc95SCharles Keepax return -EINVAL; 3084406abc95SCharles Keepax } 3085406abc95SCharles Keepax 3086406abc95SCharles Keepax for (i = 0; i < wm_adsp_fw[dsp->fw].num_caps; i++) { 3087406abc95SCharles Keepax caps = &wm_adsp_fw[dsp->fw].caps[i]; 3088406abc95SCharles Keepax desc = &caps->desc; 3089406abc95SCharles Keepax 3090406abc95SCharles Keepax if (caps->id != params->codec.id) 3091406abc95SCharles Keepax continue; 3092406abc95SCharles Keepax 3093406abc95SCharles Keepax if (stream->direction == SND_COMPRESS_PLAYBACK) { 3094406abc95SCharles Keepax if (desc->max_ch < params->codec.ch_out) 3095406abc95SCharles Keepax continue; 3096406abc95SCharles Keepax } else { 3097406abc95SCharles Keepax if (desc->max_ch < params->codec.ch_in) 3098406abc95SCharles Keepax continue; 3099406abc95SCharles Keepax } 3100406abc95SCharles Keepax 3101406abc95SCharles Keepax if (!(desc->formats & (1 << params->codec.format))) 3102406abc95SCharles Keepax continue; 3103406abc95SCharles Keepax 3104406abc95SCharles Keepax for (j = 0; j < desc->num_sample_rates; ++j) 3105406abc95SCharles Keepax if (desc->sample_rates[j] == params->codec.sample_rate) 3106406abc95SCharles Keepax return 0; 3107406abc95SCharles Keepax } 3108406abc95SCharles Keepax 3109406abc95SCharles Keepax adsp_err(dsp, "Invalid params id=%u ch=%u,%u rate=%u fmt=%u\n", 3110406abc95SCharles Keepax params->codec.id, params->codec.ch_in, params->codec.ch_out, 3111406abc95SCharles Keepax params->codec.sample_rate, params->codec.format); 3112406abc95SCharles Keepax return -EINVAL; 3113406abc95SCharles Keepax } 3114406abc95SCharles Keepax 3115565ace46SCharles Keepax static inline unsigned int wm_adsp_compr_frag_words(struct wm_adsp_compr *compr) 3116565ace46SCharles Keepax { 3117565ace46SCharles Keepax return compr->size.fragment_size / WM_ADSP_DATA_WORD_SIZE; 3118565ace46SCharles Keepax } 3119565ace46SCharles Keepax 3120406abc95SCharles Keepax int wm_adsp_compr_set_params(struct snd_compr_stream *stream, 3121406abc95SCharles Keepax struct snd_compr_params *params) 3122406abc95SCharles Keepax { 3123406abc95SCharles Keepax struct wm_adsp_compr *compr = stream->runtime->private_data; 312483a40ce9SCharles Keepax unsigned int size; 3125406abc95SCharles Keepax int ret; 3126406abc95SCharles Keepax 3127406abc95SCharles Keepax ret = wm_adsp_compr_check_params(stream, params); 3128406abc95SCharles Keepax if (ret) 3129406abc95SCharles Keepax return ret; 3130406abc95SCharles Keepax 3131406abc95SCharles Keepax compr->size = params->buffer; 3132406abc95SCharles Keepax 3133406abc95SCharles Keepax adsp_dbg(compr->dsp, "fragment_size=%d fragments=%d\n", 3134406abc95SCharles Keepax compr->size.fragment_size, compr->size.fragments); 3135406abc95SCharles Keepax 313683a40ce9SCharles Keepax size = wm_adsp_compr_frag_words(compr) * sizeof(*compr->raw_buf); 313783a40ce9SCharles Keepax compr->raw_buf = kmalloc(size, GFP_DMA | GFP_KERNEL); 313883a40ce9SCharles Keepax if (!compr->raw_buf) 313983a40ce9SCharles Keepax return -ENOMEM; 314083a40ce9SCharles Keepax 3141da2b3358SCharles Keepax compr->sample_rate = params->codec.sample_rate; 3142da2b3358SCharles Keepax 3143406abc95SCharles Keepax return 0; 3144406abc95SCharles Keepax } 3145406abc95SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_set_params); 3146406abc95SCharles Keepax 3147406abc95SCharles Keepax int wm_adsp_compr_get_caps(struct snd_compr_stream *stream, 3148406abc95SCharles Keepax struct snd_compr_caps *caps) 3149406abc95SCharles Keepax { 3150406abc95SCharles Keepax struct wm_adsp_compr *compr = stream->runtime->private_data; 3151406abc95SCharles Keepax int fw = compr->dsp->fw; 3152406abc95SCharles Keepax int i; 3153406abc95SCharles Keepax 3154406abc95SCharles Keepax if (wm_adsp_fw[fw].caps) { 3155406abc95SCharles Keepax for (i = 0; i < wm_adsp_fw[fw].num_caps; i++) 3156406abc95SCharles Keepax caps->codecs[i] = wm_adsp_fw[fw].caps[i].id; 3157406abc95SCharles Keepax 3158406abc95SCharles Keepax caps->num_codecs = i; 3159406abc95SCharles Keepax caps->direction = wm_adsp_fw[fw].compr_direction; 3160406abc95SCharles Keepax 3161406abc95SCharles Keepax caps->min_fragment_size = WM_ADSP_MIN_FRAGMENT_SIZE; 3162406abc95SCharles Keepax caps->max_fragment_size = WM_ADSP_MAX_FRAGMENT_SIZE; 3163406abc95SCharles Keepax caps->min_fragments = WM_ADSP_MIN_FRAGMENTS; 3164406abc95SCharles Keepax caps->max_fragments = WM_ADSP_MAX_FRAGMENTS; 3165406abc95SCharles Keepax } 3166406abc95SCharles Keepax 3167406abc95SCharles Keepax return 0; 3168406abc95SCharles Keepax } 3169406abc95SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_get_caps); 3170406abc95SCharles Keepax 31712cd19bdbSCharles Keepax static int wm_adsp_read_data_block(struct wm_adsp *dsp, int mem_type, 31722cd19bdbSCharles Keepax unsigned int mem_addr, 31732cd19bdbSCharles Keepax unsigned int num_words, u32 *data) 31742cd19bdbSCharles Keepax { 31752cd19bdbSCharles Keepax struct wm_adsp_region const *mem = wm_adsp_find_region(dsp, mem_type); 31762cd19bdbSCharles Keepax unsigned int i, reg; 31772cd19bdbSCharles Keepax int ret; 31782cd19bdbSCharles Keepax 31792cd19bdbSCharles Keepax if (!mem) 31802cd19bdbSCharles Keepax return -EINVAL; 31812cd19bdbSCharles Keepax 31822cd19bdbSCharles Keepax reg = wm_adsp_region_to_reg(mem, mem_addr); 31832cd19bdbSCharles Keepax 31842cd19bdbSCharles Keepax ret = regmap_raw_read(dsp->regmap, reg, data, 31852cd19bdbSCharles Keepax sizeof(*data) * num_words); 31862cd19bdbSCharles Keepax if (ret < 0) 31872cd19bdbSCharles Keepax return ret; 31882cd19bdbSCharles Keepax 31892cd19bdbSCharles Keepax for (i = 0; i < num_words; ++i) 31902cd19bdbSCharles Keepax data[i] = be32_to_cpu(data[i]) & 0x00ffffffu; 31912cd19bdbSCharles Keepax 31922cd19bdbSCharles Keepax return 0; 31932cd19bdbSCharles Keepax } 31942cd19bdbSCharles Keepax 31952cd19bdbSCharles Keepax static inline int wm_adsp_read_data_word(struct wm_adsp *dsp, int mem_type, 31962cd19bdbSCharles Keepax unsigned int mem_addr, u32 *data) 31972cd19bdbSCharles Keepax { 31982cd19bdbSCharles Keepax return wm_adsp_read_data_block(dsp, mem_type, mem_addr, 1, data); 31992cd19bdbSCharles Keepax } 32002cd19bdbSCharles Keepax 32012cd19bdbSCharles Keepax static int wm_adsp_write_data_word(struct wm_adsp *dsp, int mem_type, 32022cd19bdbSCharles Keepax unsigned int mem_addr, u32 data) 32032cd19bdbSCharles Keepax { 32042cd19bdbSCharles Keepax struct wm_adsp_region const *mem = wm_adsp_find_region(dsp, mem_type); 32052cd19bdbSCharles Keepax unsigned int reg; 32062cd19bdbSCharles Keepax 32072cd19bdbSCharles Keepax if (!mem) 32082cd19bdbSCharles Keepax return -EINVAL; 32092cd19bdbSCharles Keepax 32102cd19bdbSCharles Keepax reg = wm_adsp_region_to_reg(mem, mem_addr); 32112cd19bdbSCharles Keepax 32122cd19bdbSCharles Keepax data = cpu_to_be32(data & 0x00ffffffu); 32132cd19bdbSCharles Keepax 32142cd19bdbSCharles Keepax return regmap_raw_write(dsp->regmap, reg, &data, sizeof(data)); 32152cd19bdbSCharles Keepax } 32162cd19bdbSCharles Keepax 32172cd19bdbSCharles Keepax static inline int wm_adsp_buffer_read(struct wm_adsp_compr_buf *buf, 32182cd19bdbSCharles Keepax unsigned int field_offset, u32 *data) 32192cd19bdbSCharles Keepax { 32202cd19bdbSCharles Keepax return wm_adsp_read_data_word(buf->dsp, WMFW_ADSP2_XM, 32212cd19bdbSCharles Keepax buf->host_buf_ptr + field_offset, data); 32222cd19bdbSCharles Keepax } 32232cd19bdbSCharles Keepax 32242cd19bdbSCharles Keepax static inline int wm_adsp_buffer_write(struct wm_adsp_compr_buf *buf, 32252cd19bdbSCharles Keepax unsigned int field_offset, u32 data) 32262cd19bdbSCharles Keepax { 32272cd19bdbSCharles Keepax return wm_adsp_write_data_word(buf->dsp, WMFW_ADSP2_XM, 32282cd19bdbSCharles Keepax buf->host_buf_ptr + field_offset, data); 32292cd19bdbSCharles Keepax } 32302cd19bdbSCharles Keepax 3231d52ed4b0SRichard Fitzgerald static int wm_adsp_legacy_host_buf_addr(struct wm_adsp_compr_buf *buf) 32322cd19bdbSCharles Keepax { 32332cd19bdbSCharles Keepax struct wm_adsp_alg_region *alg_region; 32342cd19bdbSCharles Keepax struct wm_adsp *dsp = buf->dsp; 32352cd19bdbSCharles Keepax u32 xmalg, addr, magic; 32362cd19bdbSCharles Keepax int i, ret; 32372cd19bdbSCharles Keepax 32382cd19bdbSCharles Keepax alg_region = wm_adsp_find_alg_region(dsp, WMFW_ADSP2_XM, dsp->fw_id); 32392cd19bdbSCharles Keepax xmalg = sizeof(struct wm_adsp_system_config_xm_hdr) / sizeof(__be32); 32402cd19bdbSCharles Keepax 32412cd19bdbSCharles Keepax addr = alg_region->base + xmalg + ALG_XM_FIELD(magic); 32422cd19bdbSCharles Keepax ret = wm_adsp_read_data_word(dsp, WMFW_ADSP2_XM, addr, &magic); 32432cd19bdbSCharles Keepax if (ret < 0) 32442cd19bdbSCharles Keepax return ret; 32452cd19bdbSCharles Keepax 32462cd19bdbSCharles Keepax if (magic != WM_ADSP_ALG_XM_STRUCT_MAGIC) 32472cd19bdbSCharles Keepax return -EINVAL; 32482cd19bdbSCharles Keepax 32492cd19bdbSCharles Keepax addr = alg_region->base + xmalg + ALG_XM_FIELD(host_buf_ptr); 32502cd19bdbSCharles Keepax for (i = 0; i < 5; ++i) { 32512cd19bdbSCharles Keepax ret = wm_adsp_read_data_word(dsp, WMFW_ADSP2_XM, addr, 32522cd19bdbSCharles Keepax &buf->host_buf_ptr); 32532cd19bdbSCharles Keepax if (ret < 0) 32542cd19bdbSCharles Keepax return ret; 32552cd19bdbSCharles Keepax 32562cd19bdbSCharles Keepax if (buf->host_buf_ptr) 32572cd19bdbSCharles Keepax break; 32582cd19bdbSCharles Keepax 32592cd19bdbSCharles Keepax usleep_range(1000, 2000); 32602cd19bdbSCharles Keepax } 32612cd19bdbSCharles Keepax 32622cd19bdbSCharles Keepax if (!buf->host_buf_ptr) 32632cd19bdbSCharles Keepax return -EIO; 32642cd19bdbSCharles Keepax 32652cd19bdbSCharles Keepax adsp_dbg(dsp, "host_buf_ptr=%x\n", buf->host_buf_ptr); 32662cd19bdbSCharles Keepax 32672cd19bdbSCharles Keepax return 0; 32682cd19bdbSCharles Keepax } 32692cd19bdbSCharles Keepax 3270d52ed4b0SRichard Fitzgerald static struct wm_coeff_ctl * 3271d52ed4b0SRichard Fitzgerald wm_adsp_find_host_buffer_ctrl(struct wm_adsp_compr_buf *buf) 3272d52ed4b0SRichard Fitzgerald { 3273d52ed4b0SRichard Fitzgerald struct wm_adsp *dsp = buf->dsp; 3274d52ed4b0SRichard Fitzgerald struct wm_coeff_ctl *ctl; 3275d52ed4b0SRichard Fitzgerald 3276d52ed4b0SRichard Fitzgerald list_for_each_entry(ctl, &dsp->ctl_list, list) { 3277d52ed4b0SRichard Fitzgerald if (ctl->type != WMFW_CTL_TYPE_HOST_BUFFER) 3278d52ed4b0SRichard Fitzgerald continue; 3279d52ed4b0SRichard Fitzgerald 3280d52ed4b0SRichard Fitzgerald if (!ctl->enabled) 3281d52ed4b0SRichard Fitzgerald continue; 3282d52ed4b0SRichard Fitzgerald 3283d52ed4b0SRichard Fitzgerald return ctl; 3284d52ed4b0SRichard Fitzgerald } 3285d52ed4b0SRichard Fitzgerald 3286d52ed4b0SRichard Fitzgerald return NULL; 3287d52ed4b0SRichard Fitzgerald } 3288d52ed4b0SRichard Fitzgerald 3289d52ed4b0SRichard Fitzgerald static int wm_adsp_buffer_locate(struct wm_adsp_compr_buf *buf) 3290d52ed4b0SRichard Fitzgerald { 3291d52ed4b0SRichard Fitzgerald struct wm_adsp *dsp = buf->dsp; 3292d52ed4b0SRichard Fitzgerald struct wm_coeff_ctl *ctl; 3293d52ed4b0SRichard Fitzgerald unsigned int reg; 3294d52ed4b0SRichard Fitzgerald u32 val; 3295d52ed4b0SRichard Fitzgerald int i, ret; 3296d52ed4b0SRichard Fitzgerald 3297d52ed4b0SRichard Fitzgerald ctl = wm_adsp_find_host_buffer_ctrl(buf); 3298d52ed4b0SRichard Fitzgerald if (!ctl) 3299d52ed4b0SRichard Fitzgerald return wm_adsp_legacy_host_buf_addr(buf); 3300d52ed4b0SRichard Fitzgerald 3301d52ed4b0SRichard Fitzgerald ret = wm_coeff_base_reg(ctl, ®); 3302d52ed4b0SRichard Fitzgerald if (ret) 3303d52ed4b0SRichard Fitzgerald return ret; 3304d52ed4b0SRichard Fitzgerald 3305d52ed4b0SRichard Fitzgerald for (i = 0; i < 5; ++i) { 3306d52ed4b0SRichard Fitzgerald ret = regmap_raw_read(dsp->regmap, reg, &val, sizeof(val)); 3307d52ed4b0SRichard Fitzgerald if (ret < 0) 3308d52ed4b0SRichard Fitzgerald return ret; 3309d52ed4b0SRichard Fitzgerald 3310d52ed4b0SRichard Fitzgerald if (val) 3311d52ed4b0SRichard Fitzgerald break; 3312d52ed4b0SRichard Fitzgerald 3313d52ed4b0SRichard Fitzgerald usleep_range(1000, 2000); 3314d52ed4b0SRichard Fitzgerald } 3315d52ed4b0SRichard Fitzgerald 3316d52ed4b0SRichard Fitzgerald if (!val) 3317d52ed4b0SRichard Fitzgerald return -EIO; 3318d52ed4b0SRichard Fitzgerald 3319d52ed4b0SRichard Fitzgerald buf->host_buf_ptr = be32_to_cpu(val); 3320d52ed4b0SRichard Fitzgerald adsp_dbg(dsp, "host_buf_ptr=%x\n", buf->host_buf_ptr); 3321d52ed4b0SRichard Fitzgerald 3322d52ed4b0SRichard Fitzgerald return 0; 3323d52ed4b0SRichard Fitzgerald } 3324d52ed4b0SRichard Fitzgerald 33252cd19bdbSCharles Keepax static int wm_adsp_buffer_populate(struct wm_adsp_compr_buf *buf) 33262cd19bdbSCharles Keepax { 33272cd19bdbSCharles Keepax const struct wm_adsp_fw_caps *caps = wm_adsp_fw[buf->dsp->fw].caps; 33282cd19bdbSCharles Keepax struct wm_adsp_buffer_region *region; 33292cd19bdbSCharles Keepax u32 offset = 0; 33302cd19bdbSCharles Keepax int i, ret; 33312cd19bdbSCharles Keepax 33322cd19bdbSCharles Keepax for (i = 0; i < caps->num_regions; ++i) { 33332cd19bdbSCharles Keepax region = &buf->regions[i]; 33342cd19bdbSCharles Keepax 33352cd19bdbSCharles Keepax region->offset = offset; 33362cd19bdbSCharles Keepax region->mem_type = caps->region_defs[i].mem_type; 33372cd19bdbSCharles Keepax 33382cd19bdbSCharles Keepax ret = wm_adsp_buffer_read(buf, caps->region_defs[i].base_offset, 33392cd19bdbSCharles Keepax ®ion->base_addr); 33402cd19bdbSCharles Keepax if (ret < 0) 33412cd19bdbSCharles Keepax return ret; 33422cd19bdbSCharles Keepax 33432cd19bdbSCharles Keepax ret = wm_adsp_buffer_read(buf, caps->region_defs[i].size_offset, 33442cd19bdbSCharles Keepax &offset); 33452cd19bdbSCharles Keepax if (ret < 0) 33462cd19bdbSCharles Keepax return ret; 33472cd19bdbSCharles Keepax 33482cd19bdbSCharles Keepax region->cumulative_size = offset; 33492cd19bdbSCharles Keepax 33502cd19bdbSCharles Keepax adsp_dbg(buf->dsp, 3351e3a360b8SRichard Fitzgerald "region=%d type=%d base=%08x off=%08x size=%08x\n", 33522cd19bdbSCharles Keepax i, region->mem_type, region->base_addr, 33532cd19bdbSCharles Keepax region->offset, region->cumulative_size); 33542cd19bdbSCharles Keepax } 33552cd19bdbSCharles Keepax 33562cd19bdbSCharles Keepax return 0; 33572cd19bdbSCharles Keepax } 33582cd19bdbSCharles Keepax 335961fc060cSCharles Keepax static void wm_adsp_buffer_clear(struct wm_adsp_compr_buf *buf) 336061fc060cSCharles Keepax { 336161fc060cSCharles Keepax buf->irq_count = 0xFFFFFFFF; 336261fc060cSCharles Keepax buf->read_index = -1; 336361fc060cSCharles Keepax buf->avail = 0; 336461fc060cSCharles Keepax } 336561fc060cSCharles Keepax 33662cd19bdbSCharles Keepax static int wm_adsp_buffer_init(struct wm_adsp *dsp) 33672cd19bdbSCharles Keepax { 33682cd19bdbSCharles Keepax struct wm_adsp_compr_buf *buf; 33692cd19bdbSCharles Keepax int ret; 33702cd19bdbSCharles Keepax 33712cd19bdbSCharles Keepax buf = kzalloc(sizeof(*buf), GFP_KERNEL); 33722cd19bdbSCharles Keepax if (!buf) 33732cd19bdbSCharles Keepax return -ENOMEM; 33742cd19bdbSCharles Keepax 33752cd19bdbSCharles Keepax buf->dsp = dsp; 337661fc060cSCharles Keepax 337761fc060cSCharles Keepax wm_adsp_buffer_clear(buf); 33782cd19bdbSCharles Keepax 33792cd19bdbSCharles Keepax ret = wm_adsp_buffer_locate(buf); 33802cd19bdbSCharles Keepax if (ret < 0) { 33812cd19bdbSCharles Keepax adsp_err(dsp, "Failed to acquire host buffer: %d\n", ret); 33822cd19bdbSCharles Keepax goto err_buffer; 33832cd19bdbSCharles Keepax } 33842cd19bdbSCharles Keepax 33852cd19bdbSCharles Keepax buf->regions = kcalloc(wm_adsp_fw[dsp->fw].caps->num_regions, 33862cd19bdbSCharles Keepax sizeof(*buf->regions), GFP_KERNEL); 33872cd19bdbSCharles Keepax if (!buf->regions) { 33882cd19bdbSCharles Keepax ret = -ENOMEM; 33892cd19bdbSCharles Keepax goto err_buffer; 33902cd19bdbSCharles Keepax } 33912cd19bdbSCharles Keepax 33922cd19bdbSCharles Keepax ret = wm_adsp_buffer_populate(buf); 33932cd19bdbSCharles Keepax if (ret < 0) { 33942cd19bdbSCharles Keepax adsp_err(dsp, "Failed to populate host buffer: %d\n", ret); 33952cd19bdbSCharles Keepax goto err_regions; 33962cd19bdbSCharles Keepax } 33972cd19bdbSCharles Keepax 33982cd19bdbSCharles Keepax dsp->buffer = buf; 33992cd19bdbSCharles Keepax 34002cd19bdbSCharles Keepax return 0; 34012cd19bdbSCharles Keepax 34022cd19bdbSCharles Keepax err_regions: 34032cd19bdbSCharles Keepax kfree(buf->regions); 34042cd19bdbSCharles Keepax err_buffer: 34052cd19bdbSCharles Keepax kfree(buf); 34062cd19bdbSCharles Keepax return ret; 34072cd19bdbSCharles Keepax } 34082cd19bdbSCharles Keepax 34092cd19bdbSCharles Keepax static int wm_adsp_buffer_free(struct wm_adsp *dsp) 34102cd19bdbSCharles Keepax { 34112cd19bdbSCharles Keepax if (dsp->buffer) { 3412721be3beSCharles Keepax wm_adsp_compr_detach(dsp->buffer->compr); 3413721be3beSCharles Keepax 34142cd19bdbSCharles Keepax kfree(dsp->buffer->regions); 34152cd19bdbSCharles Keepax kfree(dsp->buffer); 34162cd19bdbSCharles Keepax 34172cd19bdbSCharles Keepax dsp->buffer = NULL; 34182cd19bdbSCharles Keepax } 34192cd19bdbSCharles Keepax 34202cd19bdbSCharles Keepax return 0; 34212cd19bdbSCharles Keepax } 34222cd19bdbSCharles Keepax 342395fe9597SCharles Keepax int wm_adsp_compr_trigger(struct snd_compr_stream *stream, int cmd) 342495fe9597SCharles Keepax { 342595fe9597SCharles Keepax struct wm_adsp_compr *compr = stream->runtime->private_data; 342695fe9597SCharles Keepax struct wm_adsp *dsp = compr->dsp; 342795fe9597SCharles Keepax int ret = 0; 342895fe9597SCharles Keepax 342995fe9597SCharles Keepax adsp_dbg(dsp, "Trigger: %d\n", cmd); 343095fe9597SCharles Keepax 343195fe9597SCharles Keepax mutex_lock(&dsp->pwr_lock); 343295fe9597SCharles Keepax 343395fe9597SCharles Keepax switch (cmd) { 343495fe9597SCharles Keepax case SNDRV_PCM_TRIGGER_START: 343561fc060cSCharles Keepax if (!wm_adsp_compr_attached(compr)) { 343695fe9597SCharles Keepax ret = wm_adsp_compr_attach(compr); 343795fe9597SCharles Keepax if (ret < 0) { 343895fe9597SCharles Keepax adsp_err(dsp, "Failed to link buffer and stream: %d\n", 343995fe9597SCharles Keepax ret); 344095fe9597SCharles Keepax break; 344195fe9597SCharles Keepax } 344261fc060cSCharles Keepax } 344361fc060cSCharles Keepax 344461fc060cSCharles Keepax wm_adsp_buffer_clear(compr->buf); 3445565ace46SCharles Keepax 3446565ace46SCharles Keepax /* Trigger the IRQ at one fragment of data */ 3447565ace46SCharles Keepax ret = wm_adsp_buffer_write(compr->buf, 3448565ace46SCharles Keepax HOST_BUFFER_FIELD(high_water_mark), 3449565ace46SCharles Keepax wm_adsp_compr_frag_words(compr)); 3450565ace46SCharles Keepax if (ret < 0) { 3451565ace46SCharles Keepax adsp_err(dsp, "Failed to set high water mark: %d\n", 3452565ace46SCharles Keepax ret); 3453565ace46SCharles Keepax break; 3454565ace46SCharles Keepax } 345595fe9597SCharles Keepax break; 345695fe9597SCharles Keepax case SNDRV_PCM_TRIGGER_STOP: 345795fe9597SCharles Keepax break; 345895fe9597SCharles Keepax default: 345995fe9597SCharles Keepax ret = -EINVAL; 346095fe9597SCharles Keepax break; 346195fe9597SCharles Keepax } 346295fe9597SCharles Keepax 346395fe9597SCharles Keepax mutex_unlock(&dsp->pwr_lock); 346495fe9597SCharles Keepax 346595fe9597SCharles Keepax return ret; 346695fe9597SCharles Keepax } 346795fe9597SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_trigger); 346895fe9597SCharles Keepax 3469565ace46SCharles Keepax static inline int wm_adsp_buffer_size(struct wm_adsp_compr_buf *buf) 3470565ace46SCharles Keepax { 3471565ace46SCharles Keepax int last_region = wm_adsp_fw[buf->dsp->fw].caps->num_regions - 1; 3472565ace46SCharles Keepax 3473565ace46SCharles Keepax return buf->regions[last_region].cumulative_size; 3474565ace46SCharles Keepax } 3475565ace46SCharles Keepax 3476565ace46SCharles Keepax static int wm_adsp_buffer_update_avail(struct wm_adsp_compr_buf *buf) 3477565ace46SCharles Keepax { 3478565ace46SCharles Keepax u32 next_read_index, next_write_index; 3479565ace46SCharles Keepax int write_index, read_index, avail; 3480565ace46SCharles Keepax int ret; 3481565ace46SCharles Keepax 3482565ace46SCharles Keepax /* Only sync read index if we haven't already read a valid index */ 3483565ace46SCharles Keepax if (buf->read_index < 0) { 3484565ace46SCharles Keepax ret = wm_adsp_buffer_read(buf, 3485565ace46SCharles Keepax HOST_BUFFER_FIELD(next_read_index), 3486565ace46SCharles Keepax &next_read_index); 3487565ace46SCharles Keepax if (ret < 0) 3488565ace46SCharles Keepax return ret; 3489565ace46SCharles Keepax 3490565ace46SCharles Keepax read_index = sign_extend32(next_read_index, 23); 3491565ace46SCharles Keepax 3492565ace46SCharles Keepax if (read_index < 0) { 3493565ace46SCharles Keepax adsp_dbg(buf->dsp, "Avail check on unstarted stream\n"); 3494565ace46SCharles Keepax return 0; 3495565ace46SCharles Keepax } 3496565ace46SCharles Keepax 3497565ace46SCharles Keepax buf->read_index = read_index; 3498565ace46SCharles Keepax } 3499565ace46SCharles Keepax 3500565ace46SCharles Keepax ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(next_write_index), 3501565ace46SCharles Keepax &next_write_index); 3502565ace46SCharles Keepax if (ret < 0) 3503565ace46SCharles Keepax return ret; 3504565ace46SCharles Keepax 3505565ace46SCharles Keepax write_index = sign_extend32(next_write_index, 23); 3506565ace46SCharles Keepax 3507565ace46SCharles Keepax avail = write_index - buf->read_index; 3508565ace46SCharles Keepax if (avail < 0) 3509565ace46SCharles Keepax avail += wm_adsp_buffer_size(buf); 3510565ace46SCharles Keepax 3511565ace46SCharles Keepax adsp_dbg(buf->dsp, "readindex=0x%x, writeindex=0x%x, avail=%d\n", 351233d740e0SCharles Keepax buf->read_index, write_index, avail * WM_ADSP_DATA_WORD_SIZE); 3513565ace46SCharles Keepax 3514565ace46SCharles Keepax buf->avail = avail; 3515565ace46SCharles Keepax 3516565ace46SCharles Keepax return 0; 3517565ace46SCharles Keepax } 3518565ace46SCharles Keepax 35199771b18aSCharles Keepax static int wm_adsp_buffer_get_error(struct wm_adsp_compr_buf *buf) 35209771b18aSCharles Keepax { 35219771b18aSCharles Keepax int ret; 35229771b18aSCharles Keepax 35239771b18aSCharles Keepax ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(error), &buf->error); 35249771b18aSCharles Keepax if (ret < 0) { 35259771b18aSCharles Keepax adsp_err(buf->dsp, "Failed to check buffer error: %d\n", ret); 35269771b18aSCharles Keepax return ret; 35279771b18aSCharles Keepax } 35289771b18aSCharles Keepax if (buf->error != 0) { 35299771b18aSCharles Keepax adsp_err(buf->dsp, "Buffer error occurred: %d\n", buf->error); 35309771b18aSCharles Keepax return -EIO; 35319771b18aSCharles Keepax } 35329771b18aSCharles Keepax 35339771b18aSCharles Keepax return 0; 35349771b18aSCharles Keepax } 35359771b18aSCharles Keepax 3536565ace46SCharles Keepax int wm_adsp_compr_handle_irq(struct wm_adsp *dsp) 3537565ace46SCharles Keepax { 3538612047f0SCharles Keepax struct wm_adsp_compr_buf *buf; 3539612047f0SCharles Keepax struct wm_adsp_compr *compr; 3540565ace46SCharles Keepax int ret = 0; 3541565ace46SCharles Keepax 3542565ace46SCharles Keepax mutex_lock(&dsp->pwr_lock); 3543565ace46SCharles Keepax 3544612047f0SCharles Keepax buf = dsp->buffer; 3545612047f0SCharles Keepax compr = dsp->compr; 3546612047f0SCharles Keepax 3547565ace46SCharles Keepax if (!buf) { 3548565ace46SCharles Keepax ret = -ENODEV; 3549565ace46SCharles Keepax goto out; 3550565ace46SCharles Keepax } 3551565ace46SCharles Keepax 3552565ace46SCharles Keepax adsp_dbg(dsp, "Handling buffer IRQ\n"); 3553565ace46SCharles Keepax 35549771b18aSCharles Keepax ret = wm_adsp_buffer_get_error(buf); 35559771b18aSCharles Keepax if (ret < 0) 35565847609eSCharles Keepax goto out_notify; /* Wake poll to report error */ 3557565ace46SCharles Keepax 3558565ace46SCharles Keepax ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(irq_count), 3559565ace46SCharles Keepax &buf->irq_count); 3560565ace46SCharles Keepax if (ret < 0) { 3561565ace46SCharles Keepax adsp_err(dsp, "Failed to get irq_count: %d\n", ret); 3562565ace46SCharles Keepax goto out; 3563565ace46SCharles Keepax } 3564565ace46SCharles Keepax 3565565ace46SCharles Keepax ret = wm_adsp_buffer_update_avail(buf); 3566565ace46SCharles Keepax if (ret < 0) { 3567565ace46SCharles Keepax adsp_err(dsp, "Error reading avail: %d\n", ret); 3568565ace46SCharles Keepax goto out; 3569565ace46SCharles Keepax } 3570565ace46SCharles Keepax 357120b7f7c5SCharles Keepax if (wm_adsp_fw[dsp->fw].voice_trigger && buf->irq_count == 2) 357220b7f7c5SCharles Keepax ret = WM_ADSP_COMPR_VOICE_TRIGGER; 357320b7f7c5SCharles Keepax 35745847609eSCharles Keepax out_notify: 3575c7dae7c4SCharles Keepax if (compr && compr->stream) 357683a40ce9SCharles Keepax snd_compr_fragment_elapsed(compr->stream); 357783a40ce9SCharles Keepax 3578565ace46SCharles Keepax out: 3579565ace46SCharles Keepax mutex_unlock(&dsp->pwr_lock); 3580565ace46SCharles Keepax 3581565ace46SCharles Keepax return ret; 3582565ace46SCharles Keepax } 3583565ace46SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_handle_irq); 3584565ace46SCharles Keepax 3585565ace46SCharles Keepax static int wm_adsp_buffer_reenable_irq(struct wm_adsp_compr_buf *buf) 3586565ace46SCharles Keepax { 3587565ace46SCharles Keepax if (buf->irq_count & 0x01) 3588565ace46SCharles Keepax return 0; 3589565ace46SCharles Keepax 3590565ace46SCharles Keepax adsp_dbg(buf->dsp, "Enable IRQ(0x%x) for next fragment\n", 3591565ace46SCharles Keepax buf->irq_count); 3592565ace46SCharles Keepax 3593565ace46SCharles Keepax buf->irq_count |= 0x01; 3594565ace46SCharles Keepax 3595565ace46SCharles Keepax return wm_adsp_buffer_write(buf, HOST_BUFFER_FIELD(irq_ack), 3596565ace46SCharles Keepax buf->irq_count); 3597565ace46SCharles Keepax } 3598565ace46SCharles Keepax 3599565ace46SCharles Keepax int wm_adsp_compr_pointer(struct snd_compr_stream *stream, 3600565ace46SCharles Keepax struct snd_compr_tstamp *tstamp) 3601565ace46SCharles Keepax { 3602565ace46SCharles Keepax struct wm_adsp_compr *compr = stream->runtime->private_data; 3603565ace46SCharles Keepax struct wm_adsp *dsp = compr->dsp; 3604612047f0SCharles Keepax struct wm_adsp_compr_buf *buf; 3605565ace46SCharles Keepax int ret = 0; 3606565ace46SCharles Keepax 3607565ace46SCharles Keepax adsp_dbg(dsp, "Pointer request\n"); 3608565ace46SCharles Keepax 3609565ace46SCharles Keepax mutex_lock(&dsp->pwr_lock); 3610565ace46SCharles Keepax 3611612047f0SCharles Keepax buf = compr->buf; 3612612047f0SCharles Keepax 361328ee3d73SCharles Keepax if (!compr->buf || compr->buf->error) { 36148d280664SCharles Keepax snd_compr_stop_error(stream, SNDRV_PCM_STATE_XRUN); 3615565ace46SCharles Keepax ret = -EIO; 3616565ace46SCharles Keepax goto out; 3617565ace46SCharles Keepax } 3618565ace46SCharles Keepax 3619565ace46SCharles Keepax if (buf->avail < wm_adsp_compr_frag_words(compr)) { 3620565ace46SCharles Keepax ret = wm_adsp_buffer_update_avail(buf); 3621565ace46SCharles Keepax if (ret < 0) { 3622565ace46SCharles Keepax adsp_err(dsp, "Error reading avail: %d\n", ret); 3623565ace46SCharles Keepax goto out; 3624565ace46SCharles Keepax } 3625565ace46SCharles Keepax 3626565ace46SCharles Keepax /* 3627565ace46SCharles Keepax * If we really have less than 1 fragment available tell the 3628565ace46SCharles Keepax * DSP to inform us once a whole fragment is available. 3629565ace46SCharles Keepax */ 3630565ace46SCharles Keepax if (buf->avail < wm_adsp_compr_frag_words(compr)) { 36315847609eSCharles Keepax ret = wm_adsp_buffer_get_error(buf); 36328d280664SCharles Keepax if (ret < 0) { 36338d280664SCharles Keepax if (compr->buf->error) 36348d280664SCharles Keepax snd_compr_stop_error(stream, 36358d280664SCharles Keepax SNDRV_PCM_STATE_XRUN); 36365847609eSCharles Keepax goto out; 36378d280664SCharles Keepax } 36385847609eSCharles Keepax 3639565ace46SCharles Keepax ret = wm_adsp_buffer_reenable_irq(buf); 3640565ace46SCharles Keepax if (ret < 0) { 3641565ace46SCharles Keepax adsp_err(dsp, 3642565ace46SCharles Keepax "Failed to re-enable buffer IRQ: %d\n", 3643565ace46SCharles Keepax ret); 3644565ace46SCharles Keepax goto out; 3645565ace46SCharles Keepax } 3646565ace46SCharles Keepax } 3647565ace46SCharles Keepax } 3648565ace46SCharles Keepax 3649565ace46SCharles Keepax tstamp->copied_total = compr->copied_total; 3650565ace46SCharles Keepax tstamp->copied_total += buf->avail * WM_ADSP_DATA_WORD_SIZE; 3651da2b3358SCharles Keepax tstamp->sampling_rate = compr->sample_rate; 3652565ace46SCharles Keepax 3653565ace46SCharles Keepax out: 3654565ace46SCharles Keepax mutex_unlock(&dsp->pwr_lock); 3655565ace46SCharles Keepax 3656565ace46SCharles Keepax return ret; 3657565ace46SCharles Keepax } 3658565ace46SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_pointer); 3659565ace46SCharles Keepax 366083a40ce9SCharles Keepax static int wm_adsp_buffer_capture_block(struct wm_adsp_compr *compr, int target) 366183a40ce9SCharles Keepax { 366283a40ce9SCharles Keepax struct wm_adsp_compr_buf *buf = compr->buf; 366383a40ce9SCharles Keepax u8 *pack_in = (u8 *)compr->raw_buf; 366483a40ce9SCharles Keepax u8 *pack_out = (u8 *)compr->raw_buf; 366583a40ce9SCharles Keepax unsigned int adsp_addr; 366683a40ce9SCharles Keepax int mem_type, nwords, max_read; 366783a40ce9SCharles Keepax int i, j, ret; 366883a40ce9SCharles Keepax 366983a40ce9SCharles Keepax /* Calculate read parameters */ 367083a40ce9SCharles Keepax for (i = 0; i < wm_adsp_fw[buf->dsp->fw].caps->num_regions; ++i) 367183a40ce9SCharles Keepax if (buf->read_index < buf->regions[i].cumulative_size) 367283a40ce9SCharles Keepax break; 367383a40ce9SCharles Keepax 367483a40ce9SCharles Keepax if (i == wm_adsp_fw[buf->dsp->fw].caps->num_regions) 367583a40ce9SCharles Keepax return -EINVAL; 367683a40ce9SCharles Keepax 367783a40ce9SCharles Keepax mem_type = buf->regions[i].mem_type; 367883a40ce9SCharles Keepax adsp_addr = buf->regions[i].base_addr + 367983a40ce9SCharles Keepax (buf->read_index - buf->regions[i].offset); 368083a40ce9SCharles Keepax 368183a40ce9SCharles Keepax max_read = wm_adsp_compr_frag_words(compr); 368283a40ce9SCharles Keepax nwords = buf->regions[i].cumulative_size - buf->read_index; 368383a40ce9SCharles Keepax 368483a40ce9SCharles Keepax if (nwords > target) 368583a40ce9SCharles Keepax nwords = target; 368683a40ce9SCharles Keepax if (nwords > buf->avail) 368783a40ce9SCharles Keepax nwords = buf->avail; 368883a40ce9SCharles Keepax if (nwords > max_read) 368983a40ce9SCharles Keepax nwords = max_read; 369083a40ce9SCharles Keepax if (!nwords) 369183a40ce9SCharles Keepax return 0; 369283a40ce9SCharles Keepax 369383a40ce9SCharles Keepax /* Read data from DSP */ 369483a40ce9SCharles Keepax ret = wm_adsp_read_data_block(buf->dsp, mem_type, adsp_addr, 369583a40ce9SCharles Keepax nwords, compr->raw_buf); 369683a40ce9SCharles Keepax if (ret < 0) 369783a40ce9SCharles Keepax return ret; 369883a40ce9SCharles Keepax 369983a40ce9SCharles Keepax /* Remove the padding bytes from the data read from the DSP */ 370083a40ce9SCharles Keepax for (i = 0; i < nwords; i++) { 370183a40ce9SCharles Keepax for (j = 0; j < WM_ADSP_DATA_WORD_SIZE; j++) 370283a40ce9SCharles Keepax *pack_out++ = *pack_in++; 370383a40ce9SCharles Keepax 370483a40ce9SCharles Keepax pack_in += sizeof(*(compr->raw_buf)) - WM_ADSP_DATA_WORD_SIZE; 370583a40ce9SCharles Keepax } 370683a40ce9SCharles Keepax 370783a40ce9SCharles Keepax /* update read index to account for words read */ 370883a40ce9SCharles Keepax buf->read_index += nwords; 370983a40ce9SCharles Keepax if (buf->read_index == wm_adsp_buffer_size(buf)) 371083a40ce9SCharles Keepax buf->read_index = 0; 371183a40ce9SCharles Keepax 371283a40ce9SCharles Keepax ret = wm_adsp_buffer_write(buf, HOST_BUFFER_FIELD(next_read_index), 371383a40ce9SCharles Keepax buf->read_index); 371483a40ce9SCharles Keepax if (ret < 0) 371583a40ce9SCharles Keepax return ret; 371683a40ce9SCharles Keepax 371783a40ce9SCharles Keepax /* update avail to account for words read */ 371883a40ce9SCharles Keepax buf->avail -= nwords; 371983a40ce9SCharles Keepax 372083a40ce9SCharles Keepax return nwords; 372183a40ce9SCharles Keepax } 372283a40ce9SCharles Keepax 372383a40ce9SCharles Keepax static int wm_adsp_compr_read(struct wm_adsp_compr *compr, 372483a40ce9SCharles Keepax char __user *buf, size_t count) 372583a40ce9SCharles Keepax { 372683a40ce9SCharles Keepax struct wm_adsp *dsp = compr->dsp; 372783a40ce9SCharles Keepax int ntotal = 0; 372883a40ce9SCharles Keepax int nwords, nbytes; 372983a40ce9SCharles Keepax 373083a40ce9SCharles Keepax adsp_dbg(dsp, "Requested read of %zu bytes\n", count); 373183a40ce9SCharles Keepax 373228ee3d73SCharles Keepax if (!compr->buf || compr->buf->error) { 37338d280664SCharles Keepax snd_compr_stop_error(compr->stream, SNDRV_PCM_STATE_XRUN); 373483a40ce9SCharles Keepax return -EIO; 37358d280664SCharles Keepax } 373683a40ce9SCharles Keepax 373783a40ce9SCharles Keepax count /= WM_ADSP_DATA_WORD_SIZE; 373883a40ce9SCharles Keepax 373983a40ce9SCharles Keepax do { 374083a40ce9SCharles Keepax nwords = wm_adsp_buffer_capture_block(compr, count); 374183a40ce9SCharles Keepax if (nwords < 0) { 374283a40ce9SCharles Keepax adsp_err(dsp, "Failed to capture block: %d\n", nwords); 374383a40ce9SCharles Keepax return nwords; 374483a40ce9SCharles Keepax } 374583a40ce9SCharles Keepax 374683a40ce9SCharles Keepax nbytes = nwords * WM_ADSP_DATA_WORD_SIZE; 374783a40ce9SCharles Keepax 374883a40ce9SCharles Keepax adsp_dbg(dsp, "Read %d bytes\n", nbytes); 374983a40ce9SCharles Keepax 375083a40ce9SCharles Keepax if (copy_to_user(buf + ntotal, compr->raw_buf, nbytes)) { 375183a40ce9SCharles Keepax adsp_err(dsp, "Failed to copy data to user: %d, %d\n", 375283a40ce9SCharles Keepax ntotal, nbytes); 375383a40ce9SCharles Keepax return -EFAULT; 375483a40ce9SCharles Keepax } 375583a40ce9SCharles Keepax 375683a40ce9SCharles Keepax count -= nwords; 375783a40ce9SCharles Keepax ntotal += nbytes; 375883a40ce9SCharles Keepax } while (nwords > 0 && count > 0); 375983a40ce9SCharles Keepax 376083a40ce9SCharles Keepax compr->copied_total += ntotal; 376183a40ce9SCharles Keepax 376283a40ce9SCharles Keepax return ntotal; 376383a40ce9SCharles Keepax } 376483a40ce9SCharles Keepax 376583a40ce9SCharles Keepax int wm_adsp_compr_copy(struct snd_compr_stream *stream, char __user *buf, 376683a40ce9SCharles Keepax size_t count) 376783a40ce9SCharles Keepax { 376883a40ce9SCharles Keepax struct wm_adsp_compr *compr = stream->runtime->private_data; 376983a40ce9SCharles Keepax struct wm_adsp *dsp = compr->dsp; 377083a40ce9SCharles Keepax int ret; 377183a40ce9SCharles Keepax 377283a40ce9SCharles Keepax mutex_lock(&dsp->pwr_lock); 377383a40ce9SCharles Keepax 377483a40ce9SCharles Keepax if (stream->direction == SND_COMPRESS_CAPTURE) 377583a40ce9SCharles Keepax ret = wm_adsp_compr_read(compr, buf, count); 377683a40ce9SCharles Keepax else 377783a40ce9SCharles Keepax ret = -ENOTSUPP; 377883a40ce9SCharles Keepax 377983a40ce9SCharles Keepax mutex_unlock(&dsp->pwr_lock); 378083a40ce9SCharles Keepax 378183a40ce9SCharles Keepax return ret; 378283a40ce9SCharles Keepax } 378383a40ce9SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_copy); 378483a40ce9SCharles Keepax 378551a2c944SMayuresh Kulkarni int wm_adsp2_lock(struct wm_adsp *dsp, unsigned int lock_regions) 378651a2c944SMayuresh Kulkarni { 378751a2c944SMayuresh Kulkarni struct regmap *regmap = dsp->regmap; 378851a2c944SMayuresh Kulkarni unsigned int code0, code1, lock_reg; 378951a2c944SMayuresh Kulkarni 379051a2c944SMayuresh Kulkarni if (!(lock_regions & WM_ADSP2_REGION_ALL)) 379151a2c944SMayuresh Kulkarni return 0; 379251a2c944SMayuresh Kulkarni 379351a2c944SMayuresh Kulkarni lock_regions &= WM_ADSP2_REGION_ALL; 379451a2c944SMayuresh Kulkarni lock_reg = dsp->base + ADSP2_LOCK_REGION_1_LOCK_REGION_0; 379551a2c944SMayuresh Kulkarni 379651a2c944SMayuresh Kulkarni while (lock_regions) { 379751a2c944SMayuresh Kulkarni code0 = code1 = 0; 379851a2c944SMayuresh Kulkarni if (lock_regions & BIT(0)) { 379951a2c944SMayuresh Kulkarni code0 = ADSP2_LOCK_CODE_0; 380051a2c944SMayuresh Kulkarni code1 = ADSP2_LOCK_CODE_1; 380151a2c944SMayuresh Kulkarni } 380251a2c944SMayuresh Kulkarni if (lock_regions & BIT(1)) { 380351a2c944SMayuresh Kulkarni code0 |= ADSP2_LOCK_CODE_0 << ADSP2_LOCK_REGION_SHIFT; 380451a2c944SMayuresh Kulkarni code1 |= ADSP2_LOCK_CODE_1 << ADSP2_LOCK_REGION_SHIFT; 380551a2c944SMayuresh Kulkarni } 380651a2c944SMayuresh Kulkarni regmap_write(regmap, lock_reg, code0); 380751a2c944SMayuresh Kulkarni regmap_write(regmap, lock_reg, code1); 380851a2c944SMayuresh Kulkarni lock_regions >>= 2; 380951a2c944SMayuresh Kulkarni lock_reg += 2; 381051a2c944SMayuresh Kulkarni } 381151a2c944SMayuresh Kulkarni 381251a2c944SMayuresh Kulkarni return 0; 381351a2c944SMayuresh Kulkarni } 381451a2c944SMayuresh Kulkarni EXPORT_SYMBOL_GPL(wm_adsp2_lock); 381551a2c944SMayuresh Kulkarni 381651a2c944SMayuresh Kulkarni irqreturn_t wm_adsp2_bus_error(struct wm_adsp *dsp) 381751a2c944SMayuresh Kulkarni { 381851a2c944SMayuresh Kulkarni unsigned int val; 381951a2c944SMayuresh Kulkarni struct regmap *regmap = dsp->regmap; 382051a2c944SMayuresh Kulkarni int ret = 0; 382151a2c944SMayuresh Kulkarni 382251a2c944SMayuresh Kulkarni ret = regmap_read(regmap, dsp->base + ADSP2_LOCK_REGION_CTRL, &val); 382351a2c944SMayuresh Kulkarni if (ret) { 382451a2c944SMayuresh Kulkarni adsp_err(dsp, 382551a2c944SMayuresh Kulkarni "Failed to read Region Lock Ctrl register: %d\n", ret); 382651a2c944SMayuresh Kulkarni return IRQ_HANDLED; 382751a2c944SMayuresh Kulkarni } 382851a2c944SMayuresh Kulkarni 382951a2c944SMayuresh Kulkarni if (val & ADSP2_WDT_TIMEOUT_STS_MASK) { 383051a2c944SMayuresh Kulkarni adsp_err(dsp, "watchdog timeout error\n"); 383151a2c944SMayuresh Kulkarni wm_adsp_stop_watchdog(dsp); 383251a2c944SMayuresh Kulkarni } 383351a2c944SMayuresh Kulkarni 383451a2c944SMayuresh Kulkarni if (val & (ADSP2_SLAVE_ERR_MASK | ADSP2_REGION_LOCK_ERR_MASK)) { 383551a2c944SMayuresh Kulkarni if (val & ADSP2_SLAVE_ERR_MASK) 383651a2c944SMayuresh Kulkarni adsp_err(dsp, "bus error: slave error\n"); 383751a2c944SMayuresh Kulkarni else 383851a2c944SMayuresh Kulkarni adsp_err(dsp, "bus error: region lock error\n"); 383951a2c944SMayuresh Kulkarni 384051a2c944SMayuresh Kulkarni ret = regmap_read(regmap, dsp->base + ADSP2_BUS_ERR_ADDR, &val); 384151a2c944SMayuresh Kulkarni if (ret) { 384251a2c944SMayuresh Kulkarni adsp_err(dsp, 384351a2c944SMayuresh Kulkarni "Failed to read Bus Err Addr register: %d\n", 384451a2c944SMayuresh Kulkarni ret); 384551a2c944SMayuresh Kulkarni return IRQ_HANDLED; 384651a2c944SMayuresh Kulkarni } 384751a2c944SMayuresh Kulkarni 384851a2c944SMayuresh Kulkarni adsp_err(dsp, "bus error address = 0x%x\n", 384951a2c944SMayuresh Kulkarni val & ADSP2_BUS_ERR_ADDR_MASK); 385051a2c944SMayuresh Kulkarni 385151a2c944SMayuresh Kulkarni ret = regmap_read(regmap, 385251a2c944SMayuresh Kulkarni dsp->base + ADSP2_PMEM_ERR_ADDR_XMEM_ERR_ADDR, 385351a2c944SMayuresh Kulkarni &val); 385451a2c944SMayuresh Kulkarni if (ret) { 385551a2c944SMayuresh Kulkarni adsp_err(dsp, 385651a2c944SMayuresh Kulkarni "Failed to read Pmem Xmem Err Addr register: %d\n", 385751a2c944SMayuresh Kulkarni ret); 385851a2c944SMayuresh Kulkarni return IRQ_HANDLED; 385951a2c944SMayuresh Kulkarni } 386051a2c944SMayuresh Kulkarni 386151a2c944SMayuresh Kulkarni adsp_err(dsp, "xmem error address = 0x%x\n", 386251a2c944SMayuresh Kulkarni val & ADSP2_XMEM_ERR_ADDR_MASK); 386351a2c944SMayuresh Kulkarni adsp_err(dsp, "pmem error address = 0x%x\n", 386451a2c944SMayuresh Kulkarni (val & ADSP2_PMEM_ERR_ADDR_MASK) >> 386551a2c944SMayuresh Kulkarni ADSP2_PMEM_ERR_ADDR_SHIFT); 386651a2c944SMayuresh Kulkarni } 386751a2c944SMayuresh Kulkarni 386851a2c944SMayuresh Kulkarni regmap_update_bits(regmap, dsp->base + ADSP2_LOCK_REGION_CTRL, 386951a2c944SMayuresh Kulkarni ADSP2_CTRL_ERR_EINT, ADSP2_CTRL_ERR_EINT); 387051a2c944SMayuresh Kulkarni 387151a2c944SMayuresh Kulkarni return IRQ_HANDLED; 387251a2c944SMayuresh Kulkarni } 387351a2c944SMayuresh Kulkarni EXPORT_SYMBOL_GPL(wm_adsp2_bus_error); 387451a2c944SMayuresh Kulkarni 38750a37c6efSPraveen Diwakar MODULE_LICENSE("GPL v2"); 3876