1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 22159ad93SMark Brown /* 32159ad93SMark Brown * wm_adsp.c -- Wolfson ADSP support 42159ad93SMark Brown * 52159ad93SMark Brown * Copyright 2012 Wolfson Microelectronics plc 62159ad93SMark Brown * 72159ad93SMark Brown * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> 82159ad93SMark Brown */ 92159ad93SMark Brown 10605391d0SRichard Fitzgerald #include <linux/ctype.h> 112159ad93SMark Brown #include <linux/module.h> 122159ad93SMark Brown #include <linux/moduleparam.h> 132159ad93SMark Brown #include <linux/init.h> 142159ad93SMark Brown #include <linux/delay.h> 152159ad93SMark Brown #include <linux/firmware.h> 16cf17c83cSMark Brown #include <linux/list.h> 172159ad93SMark Brown #include <linux/pm.h> 182159ad93SMark Brown #include <linux/pm_runtime.h> 192159ad93SMark Brown #include <linux/regmap.h> 20973838a0SMark Brown #include <linux/regulator/consumer.h> 212159ad93SMark Brown #include <linux/slab.h> 226ab2b7b4SDimitris Papastamos #include <linux/workqueue.h> 23f9f55e31SRichard Fitzgerald #include <linux/debugfs.h> 242159ad93SMark Brown #include <sound/core.h> 252159ad93SMark Brown #include <sound/pcm.h> 262159ad93SMark Brown #include <sound/pcm_params.h> 272159ad93SMark Brown #include <sound/soc.h> 282159ad93SMark Brown #include <sound/jack.h> 292159ad93SMark Brown #include <sound/initval.h> 302159ad93SMark Brown #include <sound/tlv.h> 312159ad93SMark Brown 322159ad93SMark Brown #include "wm_adsp.h" 332159ad93SMark Brown 342159ad93SMark Brown #define adsp_crit(_dsp, fmt, ...) \ 35e1468202SSimon Trimmer dev_crit(_dsp->cs_dsp.dev, "%s: " fmt, _dsp->cs_dsp.name, ##__VA_ARGS__) 362159ad93SMark Brown #define adsp_err(_dsp, fmt, ...) \ 37e1468202SSimon Trimmer dev_err(_dsp->cs_dsp.dev, "%s: " fmt, _dsp->cs_dsp.name, ##__VA_ARGS__) 382159ad93SMark Brown #define adsp_warn(_dsp, fmt, ...) \ 39e1468202SSimon Trimmer dev_warn(_dsp->cs_dsp.dev, "%s: " fmt, _dsp->cs_dsp.name, ##__VA_ARGS__) 402159ad93SMark Brown #define adsp_info(_dsp, fmt, ...) \ 41e1468202SSimon Trimmer dev_info(_dsp->cs_dsp.dev, "%s: " fmt, _dsp->cs_dsp.name, ##__VA_ARGS__) 422159ad93SMark Brown #define adsp_dbg(_dsp, fmt, ...) \ 43e1468202SSimon Trimmer dev_dbg(_dsp->cs_dsp.dev, "%s: " fmt, _dsp->cs_dsp.name, ##__VA_ARGS__) 442159ad93SMark Brown 450d3fba3eSCharles Keepax #define compr_err(_obj, fmt, ...) \ 460d3fba3eSCharles Keepax adsp_err(_obj->dsp, "%s: " fmt, _obj->name ? _obj->name : "legacy", \ 470d3fba3eSCharles Keepax ##__VA_ARGS__) 480d3fba3eSCharles Keepax #define compr_dbg(_obj, fmt, ...) \ 490d3fba3eSCharles Keepax adsp_dbg(_obj->dsp, "%s: " fmt, _obj->name ? _obj->name : "legacy", \ 500d3fba3eSCharles Keepax ##__VA_ARGS__) 510d3fba3eSCharles Keepax 529ee78757SCharles Keepax #define ADSP_MAX_STD_CTRL_SIZE 512 539ee78757SCharles Keepax 542dd04464SSimon Trimmer static const struct cs_dsp_client_ops wm_adsp1_client_ops; 552dd04464SSimon Trimmer static const struct cs_dsp_client_ops wm_adsp2_client_ops; 562dd04464SSimon Trimmer 57dd84f925SMark Brown #define WM_ADSP_FW_MBC_VSS 0 5804d1300fSCharles Keepax #define WM_ADSP_FW_HIFI 1 5904d1300fSCharles Keepax #define WM_ADSP_FW_TX 2 6004d1300fSCharles Keepax #define WM_ADSP_FW_TX_SPK 3 6104d1300fSCharles Keepax #define WM_ADSP_FW_RX 4 6204d1300fSCharles Keepax #define WM_ADSP_FW_RX_ANC 5 6304d1300fSCharles Keepax #define WM_ADSP_FW_CTRL 6 6404d1300fSCharles Keepax #define WM_ADSP_FW_ASR 7 6504d1300fSCharles Keepax #define WM_ADSP_FW_TRACE 8 6604d1300fSCharles Keepax #define WM_ADSP_FW_SPK_PROT 9 67d6fea46eSVlad Karpovich #define WM_ADSP_FW_SPK_CALI 10 68d6fea46eSVlad Karpovich #define WM_ADSP_FW_SPK_DIAG 11 69d6fea46eSVlad Karpovich #define WM_ADSP_FW_MISC 12 7004d1300fSCharles Keepax 71d6fea46eSVlad Karpovich #define WM_ADSP_NUM_FW 13 72dd84f925SMark Brown 731023dbd9SMark Brown static const char *wm_adsp_fw_text[WM_ADSP_NUM_FW] = { 74dd84f925SMark Brown [WM_ADSP_FW_MBC_VSS] = "MBC/VSS", 7504d1300fSCharles Keepax [WM_ADSP_FW_HIFI] = "MasterHiFi", 76dd84f925SMark Brown [WM_ADSP_FW_TX] = "Tx", 77dd84f925SMark Brown [WM_ADSP_FW_TX_SPK] = "Tx Speaker", 7804d1300fSCharles Keepax [WM_ADSP_FW_RX] = "Rx", 79dd84f925SMark Brown [WM_ADSP_FW_RX_ANC] = "Rx ANC", 8004d1300fSCharles Keepax [WM_ADSP_FW_CTRL] = "Voice Ctrl", 8104d1300fSCharles Keepax [WM_ADSP_FW_ASR] = "ASR Assist", 8204d1300fSCharles Keepax [WM_ADSP_FW_TRACE] = "Dbg Trace", 8304d1300fSCharles Keepax [WM_ADSP_FW_SPK_PROT] = "Protection", 84d6fea46eSVlad Karpovich [WM_ADSP_FW_SPK_CALI] = "Calibration", 85d6fea46eSVlad Karpovich [WM_ADSP_FW_SPK_DIAG] = "Diagnostic", 8604d1300fSCharles Keepax [WM_ADSP_FW_MISC] = "Misc", 871023dbd9SMark Brown }; 881023dbd9SMark Brown 892cd19bdbSCharles Keepax struct wm_adsp_system_config_xm_hdr { 902cd19bdbSCharles Keepax __be32 sys_enable; 912cd19bdbSCharles Keepax __be32 fw_id; 922cd19bdbSCharles Keepax __be32 fw_rev; 932cd19bdbSCharles Keepax __be32 boot_status; 942cd19bdbSCharles Keepax __be32 watchdog; 952cd19bdbSCharles Keepax __be32 dma_buffer_size; 962cd19bdbSCharles Keepax __be32 rdma[6]; 972cd19bdbSCharles Keepax __be32 wdma[8]; 982cd19bdbSCharles Keepax __be32 build_job_name[3]; 992cd19bdbSCharles Keepax __be32 build_job_number; 100*353bb6a5SSimon Trimmer } __packed; 1012cd19bdbSCharles Keepax 102170b1e12SWen Shi struct wm_halo_system_config_xm_hdr { 103170b1e12SWen Shi __be32 halo_heartbeat; 104170b1e12SWen Shi __be32 build_job_name[3]; 105170b1e12SWen Shi __be32 build_job_number; 106*353bb6a5SSimon Trimmer } __packed; 107170b1e12SWen Shi 1082cd19bdbSCharles Keepax struct wm_adsp_alg_xm_struct { 1092cd19bdbSCharles Keepax __be32 magic; 1102cd19bdbSCharles Keepax __be32 smoothing; 1112cd19bdbSCharles Keepax __be32 threshold; 1122cd19bdbSCharles Keepax __be32 host_buf_ptr; 1132cd19bdbSCharles Keepax __be32 start_seq; 1142cd19bdbSCharles Keepax __be32 high_water_mark; 1152cd19bdbSCharles Keepax __be32 low_water_mark; 1162cd19bdbSCharles Keepax __be64 smoothed_power; 117*353bb6a5SSimon Trimmer } __packed; 1182cd19bdbSCharles Keepax 1194f2d4eabSStuart Henderson struct wm_adsp_host_buf_coeff_v1 { 1204f2d4eabSStuart Henderson __be32 host_buf_ptr; /* Host buffer pointer */ 1214f2d4eabSStuart Henderson __be32 versions; /* Version numbers */ 1224f2d4eabSStuart Henderson __be32 name[4]; /* The buffer name */ 123*353bb6a5SSimon Trimmer } __packed; 1244f2d4eabSStuart Henderson 1252cd19bdbSCharles Keepax struct wm_adsp_buffer { 1262a2aefa4SRichard Fitzgerald __be32 buf1_base; /* Base addr of first buffer area */ 1272a2aefa4SRichard Fitzgerald __be32 buf1_size; /* Size of buf1 area in DSP words */ 1282a2aefa4SRichard Fitzgerald __be32 buf2_base; /* Base addr of 2nd buffer area */ 1292a2aefa4SRichard Fitzgerald __be32 buf1_buf2_size; /* Size of buf1+buf2 in DSP words */ 1302a2aefa4SRichard Fitzgerald __be32 buf3_base; /* Base addr of buf3 area */ 1312a2aefa4SRichard Fitzgerald __be32 buf_total_size; /* Size of buf1+buf2+buf3 in DSP words */ 1322cd19bdbSCharles Keepax __be32 high_water_mark; /* Point at which IRQ is asserted */ 1332cd19bdbSCharles Keepax __be32 irq_count; /* bits 1-31 count IRQ assertions */ 1342cd19bdbSCharles Keepax __be32 irq_ack; /* acked IRQ count, bit 0 enables IRQ */ 1352cd19bdbSCharles Keepax __be32 next_write_index; /* word index of next write */ 1362cd19bdbSCharles Keepax __be32 next_read_index; /* word index of next read */ 1372cd19bdbSCharles Keepax __be32 error; /* error if any */ 1382cd19bdbSCharles Keepax __be32 oldest_block_index; /* word index of oldest surviving */ 1392cd19bdbSCharles Keepax __be32 requested_rewind; /* how many blocks rewind was done */ 1402cd19bdbSCharles Keepax __be32 reserved_space; /* internal */ 1412cd19bdbSCharles Keepax __be32 min_free; /* min free space since stream start */ 1422cd19bdbSCharles Keepax __be32 blocks_written[2]; /* total blocks written (64 bit) */ 1432cd19bdbSCharles Keepax __be32 words_written[2]; /* total words written (64 bit) */ 144*353bb6a5SSimon Trimmer } __packed; 1452cd19bdbSCharles Keepax 146721be3beSCharles Keepax struct wm_adsp_compr; 147721be3beSCharles Keepax 1482cd19bdbSCharles Keepax struct wm_adsp_compr_buf { 1494f2d4eabSStuart Henderson struct list_head list; 1502cd19bdbSCharles Keepax struct wm_adsp *dsp; 151721be3beSCharles Keepax struct wm_adsp_compr *compr; 1522cd19bdbSCharles Keepax 1532cd19bdbSCharles Keepax struct wm_adsp_buffer_region *regions; 1542cd19bdbSCharles Keepax u32 host_buf_ptr; 155565ace46SCharles Keepax 156565ace46SCharles Keepax u32 error; 157565ace46SCharles Keepax u32 irq_count; 158565ace46SCharles Keepax int read_index; 159565ace46SCharles Keepax int avail; 160fb13f19dSAndrew Ford int host_buf_mem_type; 1614f2d4eabSStuart Henderson 1624f2d4eabSStuart Henderson char *name; 1632cd19bdbSCharles Keepax }; 1642cd19bdbSCharles Keepax 165406abc95SCharles Keepax struct wm_adsp_compr { 1664f2d4eabSStuart Henderson struct list_head list; 167406abc95SCharles Keepax struct wm_adsp *dsp; 16895fe9597SCharles Keepax struct wm_adsp_compr_buf *buf; 169406abc95SCharles Keepax 170406abc95SCharles Keepax struct snd_compr_stream *stream; 171406abc95SCharles Keepax struct snd_compressed_buffer size; 172565ace46SCharles Keepax 17383a40ce9SCharles Keepax u32 *raw_buf; 174565ace46SCharles Keepax unsigned int copied_total; 175da2b3358SCharles Keepax 176da2b3358SCharles Keepax unsigned int sample_rate; 1774f2d4eabSStuart Henderson 1784f2d4eabSStuart Henderson const char *name; 179406abc95SCharles Keepax }; 180406abc95SCharles Keepax 181406abc95SCharles Keepax #define WM_ADSP_MIN_FRAGMENTS 1 182406abc95SCharles Keepax #define WM_ADSP_MAX_FRAGMENTS 256 1835beb8eeaSSimon Trimmer #define WM_ADSP_MIN_FRAGMENT_SIZE (64 * CS_DSP_DATA_WORD_SIZE) 1845beb8eeaSSimon Trimmer #define WM_ADSP_MAX_FRAGMENT_SIZE (4096 * CS_DSP_DATA_WORD_SIZE) 185406abc95SCharles Keepax 1862cd19bdbSCharles Keepax #define WM_ADSP_ALG_XM_STRUCT_MAGIC 0x49aec7 1872cd19bdbSCharles Keepax 1882cd19bdbSCharles Keepax #define HOST_BUFFER_FIELD(field) \ 1892cd19bdbSCharles Keepax (offsetof(struct wm_adsp_buffer, field) / sizeof(__be32)) 1902cd19bdbSCharles Keepax 1912cd19bdbSCharles Keepax #define ALG_XM_FIELD(field) \ 1922cd19bdbSCharles Keepax (offsetof(struct wm_adsp_alg_xm_struct, field) / sizeof(__be32)) 1932cd19bdbSCharles Keepax 1944f2d4eabSStuart Henderson #define HOST_BUF_COEFF_SUPPORTED_COMPAT_VER 1 1954f2d4eabSStuart Henderson 1964f2d4eabSStuart Henderson #define HOST_BUF_COEFF_COMPAT_VER_MASK 0xFF00 1974f2d4eabSStuart Henderson #define HOST_BUF_COEFF_COMPAT_VER_SHIFT 8 1984f2d4eabSStuart Henderson 1992cd19bdbSCharles Keepax static int wm_adsp_buffer_init(struct wm_adsp *dsp); 2002cd19bdbSCharles Keepax static int wm_adsp_buffer_free(struct wm_adsp *dsp); 2012cd19bdbSCharles Keepax 2022cd19bdbSCharles Keepax struct wm_adsp_buffer_region { 2032cd19bdbSCharles Keepax unsigned int offset; 2042cd19bdbSCharles Keepax unsigned int cumulative_size; 2052cd19bdbSCharles Keepax unsigned int mem_type; 2062cd19bdbSCharles Keepax unsigned int base_addr; 2072cd19bdbSCharles Keepax }; 2082cd19bdbSCharles Keepax 2092cd19bdbSCharles Keepax struct wm_adsp_buffer_region_def { 2102cd19bdbSCharles Keepax unsigned int mem_type; 2112cd19bdbSCharles Keepax unsigned int base_offset; 2122cd19bdbSCharles Keepax unsigned int size_offset; 2132cd19bdbSCharles Keepax }; 2142cd19bdbSCharles Keepax 2153a9686c4SCharles Keepax static const struct wm_adsp_buffer_region_def default_regions[] = { 2162cd19bdbSCharles Keepax { 2172cd19bdbSCharles Keepax .mem_type = WMFW_ADSP2_XM, 2182a2aefa4SRichard Fitzgerald .base_offset = HOST_BUFFER_FIELD(buf1_base), 2192a2aefa4SRichard Fitzgerald .size_offset = HOST_BUFFER_FIELD(buf1_size), 2202cd19bdbSCharles Keepax }, 2212cd19bdbSCharles Keepax { 2222cd19bdbSCharles Keepax .mem_type = WMFW_ADSP2_XM, 2232a2aefa4SRichard Fitzgerald .base_offset = HOST_BUFFER_FIELD(buf2_base), 2242a2aefa4SRichard Fitzgerald .size_offset = HOST_BUFFER_FIELD(buf1_buf2_size), 2252cd19bdbSCharles Keepax }, 2262cd19bdbSCharles Keepax { 2272cd19bdbSCharles Keepax .mem_type = WMFW_ADSP2_YM, 2282a2aefa4SRichard Fitzgerald .base_offset = HOST_BUFFER_FIELD(buf3_base), 2292a2aefa4SRichard Fitzgerald .size_offset = HOST_BUFFER_FIELD(buf_total_size), 2302cd19bdbSCharles Keepax }, 2312cd19bdbSCharles Keepax }; 2322cd19bdbSCharles Keepax 233406abc95SCharles Keepax struct wm_adsp_fw_caps { 234406abc95SCharles Keepax u32 id; 235406abc95SCharles Keepax struct snd_codec_desc desc; 2362cd19bdbSCharles Keepax int num_regions; 2373a9686c4SCharles Keepax const struct wm_adsp_buffer_region_def *region_defs; 238406abc95SCharles Keepax }; 239406abc95SCharles Keepax 240e6d00f34SCharles Keepax static const struct wm_adsp_fw_caps ctrl_caps[] = { 241406abc95SCharles Keepax { 242406abc95SCharles Keepax .id = SND_AUDIOCODEC_BESPOKE, 243406abc95SCharles Keepax .desc = { 2443bbc2705SRichard Fitzgerald .max_ch = 8, 245406abc95SCharles Keepax .sample_rates = { 16000 }, 246406abc95SCharles Keepax .num_sample_rates = 1, 247406abc95SCharles Keepax .formats = SNDRV_PCM_FMTBIT_S16_LE, 248406abc95SCharles Keepax }, 249e6d00f34SCharles Keepax .num_regions = ARRAY_SIZE(default_regions), 250e6d00f34SCharles Keepax .region_defs = default_regions, 251406abc95SCharles Keepax }, 252406abc95SCharles Keepax }; 253406abc95SCharles Keepax 2547ce4283cSCharles Keepax static const struct wm_adsp_fw_caps trace_caps[] = { 2557ce4283cSCharles Keepax { 2567ce4283cSCharles Keepax .id = SND_AUDIOCODEC_BESPOKE, 2577ce4283cSCharles Keepax .desc = { 2587ce4283cSCharles Keepax .max_ch = 8, 2597ce4283cSCharles Keepax .sample_rates = { 2607ce4283cSCharles Keepax 4000, 8000, 11025, 12000, 16000, 22050, 2617ce4283cSCharles Keepax 24000, 32000, 44100, 48000, 64000, 88200, 2627ce4283cSCharles Keepax 96000, 176400, 192000 2637ce4283cSCharles Keepax }, 2647ce4283cSCharles Keepax .num_sample_rates = 15, 2657ce4283cSCharles Keepax .formats = SNDRV_PCM_FMTBIT_S16_LE, 2667ce4283cSCharles Keepax }, 2677ce4283cSCharles Keepax .num_regions = ARRAY_SIZE(default_regions), 2687ce4283cSCharles Keepax .region_defs = default_regions, 269406abc95SCharles Keepax }, 270406abc95SCharles Keepax }; 271406abc95SCharles Keepax 272406abc95SCharles Keepax static const struct { 2731023dbd9SMark Brown const char *file; 274406abc95SCharles Keepax int compr_direction; 275406abc95SCharles Keepax int num_caps; 276406abc95SCharles Keepax const struct wm_adsp_fw_caps *caps; 27720b7f7c5SCharles Keepax bool voice_trigger; 2781023dbd9SMark Brown } wm_adsp_fw[WM_ADSP_NUM_FW] = { 279dd84f925SMark Brown [WM_ADSP_FW_MBC_VSS] = { .file = "mbc-vss" }, 28004d1300fSCharles Keepax [WM_ADSP_FW_HIFI] = { .file = "hifi" }, 281dd84f925SMark Brown [WM_ADSP_FW_TX] = { .file = "tx" }, 282dd84f925SMark Brown [WM_ADSP_FW_TX_SPK] = { .file = "tx-spk" }, 28304d1300fSCharles Keepax [WM_ADSP_FW_RX] = { .file = "rx" }, 284dd84f925SMark Brown [WM_ADSP_FW_RX_ANC] = { .file = "rx-anc" }, 285406abc95SCharles Keepax [WM_ADSP_FW_CTRL] = { 286406abc95SCharles Keepax .file = "ctrl", 287406abc95SCharles Keepax .compr_direction = SND_COMPRESS_CAPTURE, 288e6d00f34SCharles Keepax .num_caps = ARRAY_SIZE(ctrl_caps), 289e6d00f34SCharles Keepax .caps = ctrl_caps, 29020b7f7c5SCharles Keepax .voice_trigger = true, 291406abc95SCharles Keepax }, 29204d1300fSCharles Keepax [WM_ADSP_FW_ASR] = { .file = "asr" }, 2937ce4283cSCharles Keepax [WM_ADSP_FW_TRACE] = { 2947ce4283cSCharles Keepax .file = "trace", 2957ce4283cSCharles Keepax .compr_direction = SND_COMPRESS_CAPTURE, 2967ce4283cSCharles Keepax .num_caps = ARRAY_SIZE(trace_caps), 2977ce4283cSCharles Keepax .caps = trace_caps, 2987ce4283cSCharles Keepax }, 29904d1300fSCharles Keepax [WM_ADSP_FW_SPK_PROT] = { .file = "spk-prot" }, 300d6fea46eSVlad Karpovich [WM_ADSP_FW_SPK_CALI] = { .file = "spk-cali" }, 301d6fea46eSVlad Karpovich [WM_ADSP_FW_SPK_DIAG] = { .file = "spk-diag" }, 30204d1300fSCharles Keepax [WM_ADSP_FW_MISC] = { .file = "misc" }, 3031023dbd9SMark Brown }; 3041023dbd9SMark Brown 3056ab2b7b4SDimitris Papastamos struct wm_coeff_ctl { 3066ab2b7b4SDimitris Papastamos const char *name; 3070700bc2fSSimon Trimmer struct cs_dsp_coeff_ctl *cs_ctl; 3089ee78757SCharles Keepax struct soc_bytes_ext bytes_ext; 309df6c505cSSimon Trimmer struct work_struct work; 3106ab2b7b4SDimitris Papastamos }; 3116ab2b7b4SDimitris Papastamos 3120a047f07SRichard Fitzgerald int wm_adsp_fw_get(struct snd_kcontrol *kcontrol, 3131023dbd9SMark Brown struct snd_ctl_elem_value *ucontrol) 3141023dbd9SMark Brown { 3150fe1daa6SKuninori Morimoto struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); 3161023dbd9SMark Brown struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; 3170fe1daa6SKuninori Morimoto struct wm_adsp *dsp = snd_soc_component_get_drvdata(component); 3181023dbd9SMark Brown 31915c66570STakashi Iwai ucontrol->value.enumerated.item[0] = dsp[e->shift_l].fw; 3201023dbd9SMark Brown 3211023dbd9SMark Brown return 0; 3221023dbd9SMark Brown } 3230a047f07SRichard Fitzgerald EXPORT_SYMBOL_GPL(wm_adsp_fw_get); 3241023dbd9SMark Brown 3250a047f07SRichard Fitzgerald int wm_adsp_fw_put(struct snd_kcontrol *kcontrol, 3261023dbd9SMark Brown struct snd_ctl_elem_value *ucontrol) 3271023dbd9SMark Brown { 3280fe1daa6SKuninori Morimoto struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); 3291023dbd9SMark Brown struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; 3300fe1daa6SKuninori Morimoto struct wm_adsp *dsp = snd_soc_component_get_drvdata(component); 331d27c5e15SCharles Keepax int ret = 0; 3321023dbd9SMark Brown 33315c66570STakashi Iwai if (ucontrol->value.enumerated.item[0] == dsp[e->shift_l].fw) 3341023dbd9SMark Brown return 0; 3351023dbd9SMark Brown 33615c66570STakashi Iwai if (ucontrol->value.enumerated.item[0] >= WM_ADSP_NUM_FW) 3371023dbd9SMark Brown return -EINVAL; 3381023dbd9SMark Brown 339e1468202SSimon Trimmer mutex_lock(&dsp[e->shift_l].cs_dsp.pwr_lock); 3401023dbd9SMark Brown 341e1468202SSimon Trimmer if (dsp[e->shift_l].cs_dsp.booted || !list_empty(&dsp[e->shift_l].compr_list)) 342d27c5e15SCharles Keepax ret = -EBUSY; 343d27c5e15SCharles Keepax else 34415c66570STakashi Iwai dsp[e->shift_l].fw = ucontrol->value.enumerated.item[0]; 3451023dbd9SMark Brown 346e1468202SSimon Trimmer mutex_unlock(&dsp[e->shift_l].cs_dsp.pwr_lock); 347d27c5e15SCharles Keepax 348d27c5e15SCharles Keepax return ret; 3491023dbd9SMark Brown } 3500a047f07SRichard Fitzgerald EXPORT_SYMBOL_GPL(wm_adsp_fw_put); 3511023dbd9SMark Brown 3520a047f07SRichard Fitzgerald const struct soc_enum wm_adsp_fw_enum[] = { 3531023dbd9SMark Brown SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 3541023dbd9SMark Brown SOC_ENUM_SINGLE(0, 1, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 3551023dbd9SMark Brown SOC_ENUM_SINGLE(0, 2, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 3561023dbd9SMark Brown SOC_ENUM_SINGLE(0, 3, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 357e1ea1879SRichard Fitzgerald SOC_ENUM_SINGLE(0, 4, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 358e1ea1879SRichard Fitzgerald SOC_ENUM_SINGLE(0, 5, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 359e1ea1879SRichard Fitzgerald SOC_ENUM_SINGLE(0, 6, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), 3601023dbd9SMark Brown }; 3610a047f07SRichard Fitzgerald EXPORT_SYMBOL_GPL(wm_adsp_fw_enum); 3622159ad93SMark Brown 3639ee78757SCharles Keepax static inline struct wm_coeff_ctl *bytes_ext_to_ctl(struct soc_bytes_ext *ext) 3649ee78757SCharles Keepax { 3659ee78757SCharles Keepax return container_of(ext, struct wm_coeff_ctl, bytes_ext); 3669ee78757SCharles Keepax } 3679ee78757SCharles Keepax 3687585a5b0SCharles Keepax static int wm_coeff_info(struct snd_kcontrol *kctl, 3696ab2b7b4SDimitris Papastamos struct snd_ctl_elem_info *uinfo) 3706ab2b7b4SDimitris Papastamos { 3719ee78757SCharles Keepax struct soc_bytes_ext *bytes_ext = 3729ee78757SCharles Keepax (struct soc_bytes_ext *)kctl->private_value; 3739ee78757SCharles Keepax struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); 3740700bc2fSSimon Trimmer struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl; 3756ab2b7b4SDimitris Papastamos 3760700bc2fSSimon Trimmer switch (cs_ctl->type) { 377a23ebba8SRichard Fitzgerald case WMFW_CTL_TYPE_ACKED: 378a23ebba8SRichard Fitzgerald uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 3795beb8eeaSSimon Trimmer uinfo->value.integer.min = CS_DSP_ACKED_CTL_MIN_VALUE; 3805beb8eeaSSimon Trimmer uinfo->value.integer.max = CS_DSP_ACKED_CTL_MAX_VALUE; 381a23ebba8SRichard Fitzgerald uinfo->value.integer.step = 1; 382a23ebba8SRichard Fitzgerald uinfo->count = 1; 383a23ebba8SRichard Fitzgerald break; 384a23ebba8SRichard Fitzgerald default: 3856ab2b7b4SDimitris Papastamos uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; 3860700bc2fSSimon Trimmer uinfo->count = cs_ctl->len; 387a23ebba8SRichard Fitzgerald break; 388a23ebba8SRichard Fitzgerald } 389a23ebba8SRichard Fitzgerald 3906ab2b7b4SDimitris Papastamos return 0; 3916ab2b7b4SDimitris Papastamos } 3926ab2b7b4SDimitris Papastamos 3937585a5b0SCharles Keepax static int wm_coeff_put(struct snd_kcontrol *kctl, 3946ab2b7b4SDimitris Papastamos struct snd_ctl_elem_value *ucontrol) 3956ab2b7b4SDimitris Papastamos { 3969ee78757SCharles Keepax struct soc_bytes_ext *bytes_ext = 3979ee78757SCharles Keepax (struct soc_bytes_ext *)kctl->private_value; 3989ee78757SCharles Keepax struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); 3990700bc2fSSimon Trimmer struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl; 4006ab2b7b4SDimitris Papastamos char *p = ucontrol->value.bytes.data; 401168d10e7SCharles Keepax int ret = 0; 402168d10e7SCharles Keepax 4030700bc2fSSimon Trimmer mutex_lock(&cs_ctl->dsp->pwr_lock); 404f444da38SCharles Keepax ret = cs_dsp_coeff_write_ctrl(cs_ctl, 0, p, cs_ctl->len); 4050700bc2fSSimon Trimmer mutex_unlock(&cs_ctl->dsp->pwr_lock); 406168d10e7SCharles Keepax 407168d10e7SCharles Keepax return ret; 4086ab2b7b4SDimitris Papastamos } 4096ab2b7b4SDimitris Papastamos 4109ee78757SCharles Keepax static int wm_coeff_tlv_put(struct snd_kcontrol *kctl, 4119ee78757SCharles Keepax const unsigned int __user *bytes, unsigned int size) 4129ee78757SCharles Keepax { 4139ee78757SCharles Keepax struct soc_bytes_ext *bytes_ext = 4149ee78757SCharles Keepax (struct soc_bytes_ext *)kctl->private_value; 4159ee78757SCharles Keepax struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); 4160700bc2fSSimon Trimmer struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl; 4179ee78757SCharles Keepax int ret = 0; 4189ee78757SCharles Keepax 4190700bc2fSSimon Trimmer mutex_lock(&cs_ctl->dsp->pwr_lock); 4209ee78757SCharles Keepax 4210700bc2fSSimon Trimmer if (copy_from_user(cs_ctl->cache, bytes, size)) 4229ee78757SCharles Keepax ret = -EFAULT; 42373ecf1a6SCharles Keepax else 424f444da38SCharles Keepax ret = cs_dsp_coeff_write_ctrl(cs_ctl, 0, cs_ctl->cache, size); 4259ee78757SCharles Keepax 4260700bc2fSSimon Trimmer mutex_unlock(&cs_ctl->dsp->pwr_lock); 4279ee78757SCharles Keepax 4289ee78757SCharles Keepax return ret; 4299ee78757SCharles Keepax } 4309ee78757SCharles Keepax 431a23ebba8SRichard Fitzgerald static int wm_coeff_put_acked(struct snd_kcontrol *kctl, 432a23ebba8SRichard Fitzgerald struct snd_ctl_elem_value *ucontrol) 433a23ebba8SRichard Fitzgerald { 434a23ebba8SRichard Fitzgerald struct soc_bytes_ext *bytes_ext = 435a23ebba8SRichard Fitzgerald (struct soc_bytes_ext *)kctl->private_value; 436a23ebba8SRichard Fitzgerald struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); 4370700bc2fSSimon Trimmer struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl; 438a23ebba8SRichard Fitzgerald unsigned int val = ucontrol->value.integer.value[0]; 439a23ebba8SRichard Fitzgerald int ret; 440a23ebba8SRichard Fitzgerald 441a23ebba8SRichard Fitzgerald if (val == 0) 442a23ebba8SRichard Fitzgerald return 0; /* 0 means no event */ 443a23ebba8SRichard Fitzgerald 4440700bc2fSSimon Trimmer mutex_lock(&cs_ctl->dsp->pwr_lock); 445a23ebba8SRichard Fitzgerald 446edb1d6d7SSimon Trimmer if (cs_ctl->enabled) 4470700bc2fSSimon Trimmer ret = cs_dsp_coeff_write_acked_control(cs_ctl, val); 448a23ebba8SRichard Fitzgerald else 449a23ebba8SRichard Fitzgerald ret = -EPERM; 450a23ebba8SRichard Fitzgerald 4510700bc2fSSimon Trimmer mutex_unlock(&cs_ctl->dsp->pwr_lock); 452a23ebba8SRichard Fitzgerald 453a23ebba8SRichard Fitzgerald return ret; 454a23ebba8SRichard Fitzgerald } 455a23ebba8SRichard Fitzgerald 4567585a5b0SCharles Keepax static int wm_coeff_get(struct snd_kcontrol *kctl, 4576ab2b7b4SDimitris Papastamos struct snd_ctl_elem_value *ucontrol) 4586ab2b7b4SDimitris Papastamos { 4599ee78757SCharles Keepax struct soc_bytes_ext *bytes_ext = 4609ee78757SCharles Keepax (struct soc_bytes_ext *)kctl->private_value; 4619ee78757SCharles Keepax struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); 4620700bc2fSSimon Trimmer struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl; 4636ab2b7b4SDimitris Papastamos char *p = ucontrol->value.bytes.data; 46473ecf1a6SCharles Keepax int ret; 465168d10e7SCharles Keepax 4660700bc2fSSimon Trimmer mutex_lock(&cs_ctl->dsp->pwr_lock); 467f444da38SCharles Keepax ret = cs_dsp_coeff_read_ctrl(cs_ctl, 0, p, cs_ctl->len); 4680700bc2fSSimon Trimmer mutex_unlock(&cs_ctl->dsp->pwr_lock); 46926c22a19SCharles Keepax 470168d10e7SCharles Keepax return ret; 4716ab2b7b4SDimitris Papastamos } 4726ab2b7b4SDimitris Papastamos 4739ee78757SCharles Keepax static int wm_coeff_tlv_get(struct snd_kcontrol *kctl, 4749ee78757SCharles Keepax unsigned int __user *bytes, unsigned int size) 4759ee78757SCharles Keepax { 4769ee78757SCharles Keepax struct soc_bytes_ext *bytes_ext = 4779ee78757SCharles Keepax (struct soc_bytes_ext *)kctl->private_value; 4789ee78757SCharles Keepax struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext); 4790700bc2fSSimon Trimmer struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl; 4809ee78757SCharles Keepax int ret = 0; 4819ee78757SCharles Keepax 4820700bc2fSSimon Trimmer mutex_lock(&cs_ctl->dsp->pwr_lock); 4839ee78757SCharles Keepax 484f444da38SCharles Keepax ret = cs_dsp_coeff_read_ctrl(cs_ctl, 0, cs_ctl->cache, size); 4859ee78757SCharles Keepax 4860700bc2fSSimon Trimmer if (!ret && copy_to_user(bytes, cs_ctl->cache, size)) 4879ee78757SCharles Keepax ret = -EFAULT; 4889ee78757SCharles Keepax 4890700bc2fSSimon Trimmer mutex_unlock(&cs_ctl->dsp->pwr_lock); 4909ee78757SCharles Keepax 4919ee78757SCharles Keepax return ret; 4929ee78757SCharles Keepax } 4939ee78757SCharles Keepax 494a23ebba8SRichard Fitzgerald static int wm_coeff_get_acked(struct snd_kcontrol *kcontrol, 495a23ebba8SRichard Fitzgerald struct snd_ctl_elem_value *ucontrol) 496a23ebba8SRichard Fitzgerald { 497a23ebba8SRichard Fitzgerald /* 498a23ebba8SRichard Fitzgerald * Although it's not useful to read an acked control, we must satisfy 499a23ebba8SRichard Fitzgerald * user-side assumptions that all controls are readable and that a 500a23ebba8SRichard Fitzgerald * write of the same value should be filtered out (it's valid to send 501a23ebba8SRichard Fitzgerald * the same event number again to the firmware). We therefore return 0, 502a23ebba8SRichard Fitzgerald * meaning "no event" so valid event numbers will always be a change 503a23ebba8SRichard Fitzgerald */ 504a23ebba8SRichard Fitzgerald ucontrol->value.integer.value[0] = 0; 505a23ebba8SRichard Fitzgerald 506a23ebba8SRichard Fitzgerald return 0; 507a23ebba8SRichard Fitzgerald } 508a23ebba8SRichard Fitzgerald 5099ee78757SCharles Keepax static unsigned int wmfw_convert_flags(unsigned int in, unsigned int len) 5109ee78757SCharles Keepax { 5119ee78757SCharles Keepax unsigned int out, rd, wr, vol; 5129ee78757SCharles Keepax 5139ee78757SCharles Keepax if (len > ADSP_MAX_STD_CTRL_SIZE) { 5149ee78757SCharles Keepax rd = SNDRV_CTL_ELEM_ACCESS_TLV_READ; 5159ee78757SCharles Keepax wr = SNDRV_CTL_ELEM_ACCESS_TLV_WRITE; 5169ee78757SCharles Keepax vol = SNDRV_CTL_ELEM_ACCESS_VOLATILE; 5179ee78757SCharles Keepax 5189ee78757SCharles Keepax out = SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK; 5199ee78757SCharles Keepax } else { 5209ee78757SCharles Keepax rd = SNDRV_CTL_ELEM_ACCESS_READ; 5219ee78757SCharles Keepax wr = SNDRV_CTL_ELEM_ACCESS_WRITE; 5229ee78757SCharles Keepax vol = SNDRV_CTL_ELEM_ACCESS_VOLATILE; 5239ee78757SCharles Keepax 5249ee78757SCharles Keepax out = 0; 5259ee78757SCharles Keepax } 5269ee78757SCharles Keepax 5279ee78757SCharles Keepax if (in) { 5289ee78757SCharles Keepax out |= rd; 5299ee78757SCharles Keepax if (in & WMFW_CTL_FLAG_WRITEABLE) 5309ee78757SCharles Keepax out |= wr; 5319ee78757SCharles Keepax if (in & WMFW_CTL_FLAG_VOLATILE) 5329ee78757SCharles Keepax out |= vol; 5339ee78757SCharles Keepax } else { 5349ee78757SCharles Keepax out |= rd | wr | vol; 5359ee78757SCharles Keepax } 5369ee78757SCharles Keepax 5379ee78757SCharles Keepax return out; 5389ee78757SCharles Keepax } 5399ee78757SCharles Keepax 54056717d72SCharles Keepax static void wm_adsp_ctl_work(struct work_struct *work) 5416ab2b7b4SDimitris Papastamos { 54256717d72SCharles Keepax struct wm_coeff_ctl *ctl = container_of(work, 54356717d72SCharles Keepax struct wm_coeff_ctl, 54456717d72SCharles Keepax work); 5450700bc2fSSimon Trimmer struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl; 54656717d72SCharles Keepax struct wm_adsp *dsp = container_of(cs_ctl->dsp, 54756717d72SCharles Keepax struct wm_adsp, 54856717d72SCharles Keepax cs_dsp); 5496ab2b7b4SDimitris Papastamos struct snd_kcontrol_new *kcontrol; 5506ab2b7b4SDimitris Papastamos 5516ab2b7b4SDimitris Papastamos kcontrol = kzalloc(sizeof(*kcontrol), GFP_KERNEL); 5526ab2b7b4SDimitris Papastamos if (!kcontrol) 55356717d72SCharles Keepax return; 5546ab2b7b4SDimitris Papastamos 5556ab2b7b4SDimitris Papastamos kcontrol->name = ctl->name; 5566ab2b7b4SDimitris Papastamos kcontrol->info = wm_coeff_info; 5579ee78757SCharles Keepax kcontrol->iface = SNDRV_CTL_ELEM_IFACE_MIXER; 5589ee78757SCharles Keepax kcontrol->tlv.c = snd_soc_bytes_tlv_callback; 5599ee78757SCharles Keepax kcontrol->private_value = (unsigned long)&ctl->bytes_ext; 5600700bc2fSSimon Trimmer kcontrol->access = wmfw_convert_flags(cs_ctl->flags, cs_ctl->len); 561a23ebba8SRichard Fitzgerald 5620700bc2fSSimon Trimmer switch (cs_ctl->type) { 563a23ebba8SRichard Fitzgerald case WMFW_CTL_TYPE_ACKED: 564a23ebba8SRichard Fitzgerald kcontrol->get = wm_coeff_get_acked; 565a23ebba8SRichard Fitzgerald kcontrol->put = wm_coeff_put_acked; 566a23ebba8SRichard Fitzgerald break; 567a23ebba8SRichard Fitzgerald default: 568d7789f5bSRichard Fitzgerald if (kcontrol->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) { 5690700bc2fSSimon Trimmer ctl->bytes_ext.max = cs_ctl->len; 5709ee78757SCharles Keepax ctl->bytes_ext.get = wm_coeff_tlv_get; 5719ee78757SCharles Keepax ctl->bytes_ext.put = wm_coeff_tlv_put; 572d7789f5bSRichard Fitzgerald } else { 573d7789f5bSRichard Fitzgerald kcontrol->get = wm_coeff_get; 574d7789f5bSRichard Fitzgerald kcontrol->put = wm_coeff_put; 575d7789f5bSRichard Fitzgerald } 576a23ebba8SRichard Fitzgerald break; 577a23ebba8SRichard Fitzgerald } 57826c22a19SCharles Keepax 57956717d72SCharles Keepax snd_soc_add_component_controls(dsp->component, kcontrol, 1); 5806ab2b7b4SDimitris Papastamos 5816ab2b7b4SDimitris Papastamos kfree(kcontrol); 582b21acc1cSCharles Keepax } 583b21acc1cSCharles Keepax 5840700bc2fSSimon Trimmer static int wm_adsp_control_add(struct cs_dsp_coeff_ctl *cs_ctl) 5850700bc2fSSimon Trimmer { 586e1468202SSimon Trimmer struct wm_adsp *dsp = container_of(cs_ctl->dsp, struct wm_adsp, cs_dsp); 587e1468202SSimon Trimmer struct cs_dsp *cs_dsp = &dsp->cs_dsp; 5880700bc2fSSimon Trimmer struct wm_coeff_ctl *ctl; 5890700bc2fSSimon Trimmer char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; 5900700bc2fSSimon Trimmer const char *region_name; 5910700bc2fSSimon Trimmer int ret; 5920700bc2fSSimon Trimmer 5930700bc2fSSimon Trimmer if (cs_ctl->flags & WMFW_CTL_FLAG_SYS) 5940700bc2fSSimon Trimmer return 0; 5950700bc2fSSimon Trimmer 5960700bc2fSSimon Trimmer region_name = cs_dsp_mem_region_name(cs_ctl->alg_region.type); 5970700bc2fSSimon Trimmer if (!region_name) { 5980700bc2fSSimon Trimmer adsp_err(dsp, "Unknown region type: %d\n", cs_ctl->alg_region.type); 5990700bc2fSSimon Trimmer return -EINVAL; 6000700bc2fSSimon Trimmer } 6010700bc2fSSimon Trimmer 602e1468202SSimon Trimmer switch (cs_dsp->fw_ver) { 6030700bc2fSSimon Trimmer case 0: 6040700bc2fSSimon Trimmer case 1: 605a6e849d0SSimon Trimmer ret = scnprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, 606a6e849d0SSimon Trimmer "%s %s %x", cs_dsp->name, region_name, 607a6e849d0SSimon Trimmer cs_ctl->alg_region.alg); 6080700bc2fSSimon Trimmer break; 6090700bc2fSSimon Trimmer case 2: 6100700bc2fSSimon Trimmer ret = scnprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, 611e1468202SSimon Trimmer "%s%c %.12s %x", cs_dsp->name, *region_name, 6120700bc2fSSimon Trimmer wm_adsp_fw_text[dsp->fw], cs_ctl->alg_region.alg); 6130700bc2fSSimon Trimmer break; 6140700bc2fSSimon Trimmer default: 6150700bc2fSSimon Trimmer ret = scnprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, 616e1468202SSimon Trimmer "%s %.12s %x", cs_dsp->name, 6170700bc2fSSimon Trimmer wm_adsp_fw_text[dsp->fw], cs_ctl->alg_region.alg); 6180700bc2fSSimon Trimmer break; 6190700bc2fSSimon Trimmer } 6200700bc2fSSimon Trimmer 6210700bc2fSSimon Trimmer if (cs_ctl->subname) { 6220700bc2fSSimon Trimmer int avail = SNDRV_CTL_ELEM_ID_NAME_MAXLEN - ret - 2; 6230700bc2fSSimon Trimmer int skip = 0; 6240700bc2fSSimon Trimmer 6250700bc2fSSimon Trimmer if (dsp->component->name_prefix) 6260700bc2fSSimon Trimmer avail -= strlen(dsp->component->name_prefix) + 1; 6270700bc2fSSimon Trimmer 6280700bc2fSSimon Trimmer /* Truncate the subname from the start if it is too long */ 6290700bc2fSSimon Trimmer if (cs_ctl->subname_len > avail) 6300700bc2fSSimon Trimmer skip = cs_ctl->subname_len - avail; 6310700bc2fSSimon Trimmer 6320700bc2fSSimon Trimmer snprintf(name + ret, SNDRV_CTL_ELEM_ID_NAME_MAXLEN - ret, 6330700bc2fSSimon Trimmer " %.*s", cs_ctl->subname_len - skip, cs_ctl->subname + skip); 6340700bc2fSSimon Trimmer } 6350700bc2fSSimon Trimmer 6360700bc2fSSimon Trimmer ctl = kzalloc(sizeof(*ctl), GFP_KERNEL); 6370700bc2fSSimon Trimmer if (!ctl) 6380700bc2fSSimon Trimmer return -ENOMEM; 6390700bc2fSSimon Trimmer ctl->cs_ctl = cs_ctl; 6400700bc2fSSimon Trimmer 6410700bc2fSSimon Trimmer ctl->name = kmemdup(name, strlen(name) + 1, GFP_KERNEL); 6420700bc2fSSimon Trimmer if (!ctl->name) { 6430700bc2fSSimon Trimmer ret = -ENOMEM; 6440700bc2fSSimon Trimmer goto err_ctl; 6450700bc2fSSimon Trimmer } 6460700bc2fSSimon Trimmer 6470700bc2fSSimon Trimmer cs_ctl->priv = ctl; 6480700bc2fSSimon Trimmer 6490700bc2fSSimon Trimmer INIT_WORK(&ctl->work, wm_adsp_ctl_work); 6500700bc2fSSimon Trimmer schedule_work(&ctl->work); 6510700bc2fSSimon Trimmer 6520700bc2fSSimon Trimmer return 0; 6530700bc2fSSimon Trimmer 6540700bc2fSSimon Trimmer err_ctl: 6550700bc2fSSimon Trimmer kfree(ctl); 6560700bc2fSSimon Trimmer 6570700bc2fSSimon Trimmer return ret; 6580700bc2fSSimon Trimmer } 6590700bc2fSSimon Trimmer 6600700bc2fSSimon Trimmer static void wm_adsp_control_remove(struct cs_dsp_coeff_ctl *cs_ctl) 6610700bc2fSSimon Trimmer { 6620700bc2fSSimon Trimmer struct wm_coeff_ctl *ctl = cs_ctl->priv; 6630700bc2fSSimon Trimmer 664df6c505cSSimon Trimmer cancel_work_sync(&ctl->work); 665df6c505cSSimon Trimmer 66666225e98SRichard Fitzgerald kfree(ctl->name); 66766225e98SRichard Fitzgerald kfree(ctl); 66866225e98SRichard Fitzgerald } 66966225e98SRichard Fitzgerald 670eb65ccdbSLi Xu int wm_adsp_write_ctl(struct wm_adsp *dsp, const char *name, int type, 671eb65ccdbSLi Xu unsigned int alg, void *buf, size_t len) 672eb65ccdbSLi Xu { 6730700bc2fSSimon Trimmer struct cs_dsp_coeff_ctl *cs_ctl; 674eb65ccdbSLi Xu struct wm_coeff_ctl *ctl; 675eb65ccdbSLi Xu struct snd_kcontrol *kcontrol; 67620441614SAdam Brickman char ctl_name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; 677eb65ccdbSLi Xu int ret; 678eb65ccdbSLi Xu 679e1468202SSimon Trimmer cs_ctl = cs_dsp_get_ctl(&dsp->cs_dsp, name, type, alg); 6800700bc2fSSimon Trimmer if (!cs_ctl) 681eb65ccdbSLi Xu return -EINVAL; 682eb65ccdbSLi Xu 6830700bc2fSSimon Trimmer ctl = cs_ctl->priv; 6840700bc2fSSimon Trimmer 6850700bc2fSSimon Trimmer if (len > cs_ctl->len) 686eb65ccdbSLi Xu return -EINVAL; 687eb65ccdbSLi Xu 688f444da38SCharles Keepax ret = cs_dsp_coeff_write_ctrl(cs_ctl, 0, buf, len); 68920441614SAdam Brickman if (ret) 69020441614SAdam Brickman return ret; 691eb65ccdbSLi Xu 6920700bc2fSSimon Trimmer if (cs_ctl->flags & WMFW_CTL_FLAG_SYS) 69320441614SAdam Brickman return 0; 69420441614SAdam Brickman 69520441614SAdam Brickman if (dsp->component->name_prefix) 69620441614SAdam Brickman snprintf(ctl_name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "%s %s", 69720441614SAdam Brickman dsp->component->name_prefix, ctl->name); 69820441614SAdam Brickman else 69920441614SAdam Brickman snprintf(ctl_name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "%s", 70020441614SAdam Brickman ctl->name); 70120441614SAdam Brickman 70220441614SAdam Brickman kcontrol = snd_soc_card_get_kcontrol(dsp->component->card, ctl_name); 70320441614SAdam Brickman if (!kcontrol) { 70420441614SAdam Brickman adsp_err(dsp, "Can't find kcontrol %s\n", ctl_name); 70520441614SAdam Brickman return -EINVAL; 70620441614SAdam Brickman } 70720441614SAdam Brickman 708eb65ccdbSLi Xu snd_ctl_notify(dsp->component->card->snd_card, 709eb65ccdbSLi Xu SNDRV_CTL_EVENT_MASK_VALUE, &kcontrol->id); 710eb65ccdbSLi Xu 711492df5b0SPierre-Louis Bossart return 0; 712eb65ccdbSLi Xu } 713eb65ccdbSLi Xu EXPORT_SYMBOL_GPL(wm_adsp_write_ctl); 714eb65ccdbSLi Xu 715eb65ccdbSLi Xu int wm_adsp_read_ctl(struct wm_adsp *dsp, const char *name, int type, 716eb65ccdbSLi Xu unsigned int alg, void *buf, size_t len) 717eb65ccdbSLi Xu { 7180700bc2fSSimon Trimmer struct cs_dsp_coeff_ctl *cs_ctl; 719eb65ccdbSLi Xu 720e1468202SSimon Trimmer cs_ctl = cs_dsp_get_ctl(&dsp->cs_dsp, name, type, alg); 7210700bc2fSSimon Trimmer if (!cs_ctl) 722eb65ccdbSLi Xu return -EINVAL; 723eb65ccdbSLi Xu 7240700bc2fSSimon Trimmer if (len > cs_ctl->len) 725eb65ccdbSLi Xu return -EINVAL; 726eb65ccdbSLi Xu 727f444da38SCharles Keepax return cs_dsp_coeff_read_ctrl(cs_ctl, 0, buf, len); 728eb65ccdbSLi Xu } 729eb65ccdbSLi Xu EXPORT_SYMBOL_GPL(wm_adsp_read_ctl); 730eb65ccdbSLi Xu 731f6bc909eSSimon Trimmer static void wm_adsp_release_firmware_files(struct wm_adsp *dsp, 732f6bc909eSSimon Trimmer const struct firmware *wmfw_firmware, 733f6bc909eSSimon Trimmer char *wmfw_filename, 734f6bc909eSSimon Trimmer const struct firmware *coeff_firmware, 735f6bc909eSSimon Trimmer char *coeff_filename) 7362323736dSCharles Keepax { 737f6bc909eSSimon Trimmer if (wmfw_firmware) 738f6bc909eSSimon Trimmer release_firmware(wmfw_firmware); 739f6bc909eSSimon Trimmer kfree(wmfw_filename); 7402323736dSCharles Keepax 741f6bc909eSSimon Trimmer if (coeff_firmware) 742f6bc909eSSimon Trimmer release_firmware(coeff_firmware); 743f6bc909eSSimon Trimmer kfree(coeff_filename); 7442323736dSCharles Keepax } 7452323736dSCharles Keepax 746f6bc909eSSimon Trimmer static int wm_adsp_request_firmware_file(struct wm_adsp *dsp, 747f6bc909eSSimon Trimmer const struct firmware **firmware, 748f6bc909eSSimon Trimmer char **filename, 749f6bc909eSSimon Trimmer char *suffix) 750db40517cSMark Brown { 751f6bc909eSSimon Trimmer struct cs_dsp *cs_dsp = &dsp->cs_dsp; 752f6bc909eSSimon Trimmer int ret = 0; 753db40517cSMark Brown 754f6bc909eSSimon Trimmer *filename = kasprintf(GFP_KERNEL, "%s-%s-%s.%s", dsp->part, dsp->fwf_name, 755f6bc909eSSimon Trimmer wm_adsp_fw[dsp->fw].file, suffix); 756f6bc909eSSimon Trimmer if (*filename == NULL) 757605391d0SRichard Fitzgerald return -ENOMEM; 758f6bc909eSSimon Trimmer 759f6bc909eSSimon Trimmer ret = request_firmware(firmware, *filename, cs_dsp->dev); 760f6bc909eSSimon Trimmer if (ret != 0) { 761f6bc909eSSimon Trimmer adsp_err(dsp, "Failed to request '%s'\n", *filename); 762f6bc909eSSimon Trimmer kfree(*filename); 763f6bc909eSSimon Trimmer *filename = NULL; 764605391d0SRichard Fitzgerald } 765605391d0SRichard Fitzgerald 766f6bc909eSSimon Trimmer return ret; 767605391d0SRichard Fitzgerald } 768605391d0SRichard Fitzgerald 769f6bc909eSSimon Trimmer static int wm_adsp_request_firmware_files(struct wm_adsp *dsp, 770f6bc909eSSimon Trimmer const struct firmware **wmfw_firmware, 771f6bc909eSSimon Trimmer char **wmfw_filename, 772f6bc909eSSimon Trimmer const struct firmware **coeff_firmware, 773f6bc909eSSimon Trimmer char **coeff_filename) 7745e7a7a22SMark Brown { 775f6bc909eSSimon Trimmer int ret = 0; 776605391d0SRichard Fitzgerald 777f6bc909eSSimon Trimmer ret = wm_adsp_request_firmware_file(dsp, wmfw_firmware, wmfw_filename, "wmfw"); 778f6bc909eSSimon Trimmer if (ret != 0) 779605391d0SRichard Fitzgerald return ret; 780605391d0SRichard Fitzgerald 781f6bc909eSSimon Trimmer wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename, "bin"); 782078e7183SCharles Keepax 7835e7a7a22SMark Brown return 0; 7845e7a7a22SMark Brown } 785dcad34f8SRichard Fitzgerald 786e1468202SSimon Trimmer static int wm_adsp_common_init(struct wm_adsp *dsp) 78725ca837bSSimon Trimmer { 788e1468202SSimon Trimmer char *p; 789e1468202SSimon Trimmer 79025ca837bSSimon Trimmer INIT_LIST_HEAD(&dsp->compr_list); 79125ca837bSSimon Trimmer INIT_LIST_HEAD(&dsp->buffer_list); 792e1468202SSimon Trimmer 793e1468202SSimon Trimmer if (!dsp->fwf_name) { 794e1468202SSimon Trimmer p = devm_kstrdup(dsp->cs_dsp.dev, dsp->cs_dsp.name, GFP_KERNEL); 795e1468202SSimon Trimmer if (!p) 796e1468202SSimon Trimmer return -ENOMEM; 797e1468202SSimon Trimmer 798e1468202SSimon Trimmer dsp->fwf_name = p; 799e1468202SSimon Trimmer for (; *p != 0; ++p) 800e1468202SSimon Trimmer *p = tolower(*p); 80125ca837bSSimon Trimmer } 80225ca837bSSimon Trimmer 803e1468202SSimon Trimmer return 0; 804e1468202SSimon Trimmer } 805e1468202SSimon Trimmer 80625ca837bSSimon Trimmer int wm_adsp1_init(struct wm_adsp *dsp) 80725ca837bSSimon Trimmer { 808e1468202SSimon Trimmer int ret; 80925ca837bSSimon Trimmer 8102dd04464SSimon Trimmer dsp->cs_dsp.client_ops = &wm_adsp1_client_ops; 8112dd04464SSimon Trimmer 812e1468202SSimon Trimmer ret = cs_dsp_adsp1_init(&dsp->cs_dsp); 813e1468202SSimon Trimmer if (ret) 814e1468202SSimon Trimmer return ret; 815e1468202SSimon Trimmer 816e1468202SSimon Trimmer return wm_adsp_common_init(dsp); 817dcad34f8SRichard Fitzgerald } 8185e7a7a22SMark Brown EXPORT_SYMBOL_GPL(wm_adsp1_init); 8195e7a7a22SMark Brown 820186152dfSSimon Trimmer int wm_adsp1_event(struct snd_soc_dapm_widget *w, 821186152dfSSimon Trimmer struct snd_kcontrol *kcontrol, 822186152dfSSimon Trimmer int event) 823186152dfSSimon Trimmer { 824186152dfSSimon Trimmer struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); 825186152dfSSimon Trimmer struct wm_adsp *dsps = snd_soc_component_get_drvdata(component); 826186152dfSSimon Trimmer struct wm_adsp *dsp = &dsps[w->shift]; 827186152dfSSimon Trimmer int ret = 0; 828a828056fSSimon Trimmer char *wmfw_filename = NULL; 829a828056fSSimon Trimmer const struct firmware *wmfw_firmware = NULL; 830a828056fSSimon Trimmer char *coeff_filename = NULL; 831a828056fSSimon Trimmer const struct firmware *coeff_firmware = NULL; 832186152dfSSimon Trimmer 833186152dfSSimon Trimmer dsp->component = component; 834186152dfSSimon Trimmer 835186152dfSSimon Trimmer switch (event) { 836186152dfSSimon Trimmer case SND_SOC_DAPM_POST_PMU: 837a828056fSSimon Trimmer ret = wm_adsp_request_firmware_files(dsp, 838a828056fSSimon Trimmer &wmfw_firmware, &wmfw_filename, 839a828056fSSimon Trimmer &coeff_firmware, &coeff_filename); 840a828056fSSimon Trimmer if (ret) 841a828056fSSimon Trimmer break; 842a828056fSSimon Trimmer 843e1468202SSimon Trimmer ret = cs_dsp_adsp1_power_up(&dsp->cs_dsp, 844a828056fSSimon Trimmer wmfw_firmware, wmfw_filename, 845a828056fSSimon Trimmer coeff_firmware, coeff_filename, 8462169f2f1SSimon Trimmer wm_adsp_fw_text[dsp->fw]); 847a828056fSSimon Trimmer 848a828056fSSimon Trimmer wm_adsp_release_firmware_files(dsp, 849a828056fSSimon Trimmer wmfw_firmware, wmfw_filename, 850a828056fSSimon Trimmer coeff_firmware, coeff_filename); 851186152dfSSimon Trimmer break; 852186152dfSSimon Trimmer case SND_SOC_DAPM_PRE_PMD: 853e1468202SSimon Trimmer cs_dsp_adsp1_power_down(&dsp->cs_dsp); 854186152dfSSimon Trimmer break; 8552159ad93SMark Brown default: 8562159ad93SMark Brown break; 8572159ad93SMark Brown } 8582159ad93SMark Brown 8592159ad93SMark Brown return ret; 8602159ad93SMark Brown } 8612159ad93SMark Brown EXPORT_SYMBOL_GPL(wm_adsp1_event); 8622159ad93SMark Brown 86325ca837bSSimon Trimmer int wm_adsp2_set_dspclk(struct snd_soc_dapm_widget *w, unsigned int freq) 86425ca837bSSimon Trimmer { 86525ca837bSSimon Trimmer struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); 86625ca837bSSimon Trimmer struct wm_adsp *dsps = snd_soc_component_get_drvdata(component); 86725ca837bSSimon Trimmer struct wm_adsp *dsp = &dsps[w->shift]; 86825ca837bSSimon Trimmer 869e1468202SSimon Trimmer return cs_dsp_set_dspclk(&dsp->cs_dsp, freq); 87025ca837bSSimon Trimmer } 871b9070df4SRichard Fitzgerald EXPORT_SYMBOL_GPL(wm_adsp2_set_dspclk); 872d82d767fSCharles Keepax 873af813a6fSCharles Keepax int wm_adsp2_preloader_get(struct snd_kcontrol *kcontrol, 874af813a6fSCharles Keepax struct snd_ctl_elem_value *ucontrol) 875af813a6fSCharles Keepax { 8760fe1daa6SKuninori Morimoto struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); 877b1470d4cSAjit Pandey struct wm_adsp *dsps = snd_soc_component_get_drvdata(component); 878b1470d4cSAjit Pandey struct soc_mixer_control *mc = 879b1470d4cSAjit Pandey (struct soc_mixer_control *)kcontrol->private_value; 880b1470d4cSAjit Pandey struct wm_adsp *dsp = &dsps[mc->shift - 1]; 881af813a6fSCharles Keepax 882af813a6fSCharles Keepax ucontrol->value.integer.value[0] = dsp->preloaded; 883af813a6fSCharles Keepax 884af813a6fSCharles Keepax return 0; 885af813a6fSCharles Keepax } 886af813a6fSCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp2_preloader_get); 887af813a6fSCharles Keepax 888af813a6fSCharles Keepax int wm_adsp2_preloader_put(struct snd_kcontrol *kcontrol, 889af813a6fSCharles Keepax struct snd_ctl_elem_value *ucontrol) 890af813a6fSCharles Keepax { 8910fe1daa6SKuninori Morimoto struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); 892b1470d4cSAjit Pandey struct wm_adsp *dsps = snd_soc_component_get_drvdata(component); 8930fe1daa6SKuninori Morimoto struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); 894af813a6fSCharles Keepax struct soc_mixer_control *mc = 895af813a6fSCharles Keepax (struct soc_mixer_control *)kcontrol->private_value; 896b1470d4cSAjit Pandey struct wm_adsp *dsp = &dsps[mc->shift - 1]; 897af813a6fSCharles Keepax char preload[32]; 898af813a6fSCharles Keepax 899ba235634SCharles Keepax if (dsp->preloaded == ucontrol->value.integer.value[0]) 900ba235634SCharles Keepax return 0; 901ba235634SCharles Keepax 902e1468202SSimon Trimmer snprintf(preload, ARRAY_SIZE(preload), "%s Preload", dsp->cs_dsp.name); 903af813a6fSCharles Keepax 904ba235634SCharles Keepax if (ucontrol->value.integer.value[0] || dsp->toggle_preload) 90595a594d0SCharles Keepax snd_soc_component_force_enable_pin(component, preload); 906af813a6fSCharles Keepax else 90795a594d0SCharles Keepax snd_soc_component_disable_pin(component, preload); 908af813a6fSCharles Keepax 909af813a6fSCharles Keepax snd_soc_dapm_sync(dapm); 910af813a6fSCharles Keepax 911868e49a4SStuart Henderson flush_work(&dsp->boot_work); 912868e49a4SStuart Henderson 913ba235634SCharles Keepax dsp->preloaded = ucontrol->value.integer.value[0]; 914ba235634SCharles Keepax 915ba235634SCharles Keepax if (dsp->toggle_preload) { 916ba235634SCharles Keepax snd_soc_component_disable_pin(component, preload); 917ba235634SCharles Keepax snd_soc_dapm_sync(dapm); 918ba235634SCharles Keepax } 919ba235634SCharles Keepax 920af813a6fSCharles Keepax return 0; 921af813a6fSCharles Keepax } 922af813a6fSCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp2_preloader_put); 923af813a6fSCharles Keepax 924186152dfSSimon Trimmer static void wm_adsp_boot_work(struct work_struct *work) 925186152dfSSimon Trimmer { 926186152dfSSimon Trimmer struct wm_adsp *dsp = container_of(work, 927186152dfSSimon Trimmer struct wm_adsp, 928186152dfSSimon Trimmer boot_work); 929a828056fSSimon Trimmer int ret = 0; 930a828056fSSimon Trimmer char *wmfw_filename = NULL; 931a828056fSSimon Trimmer const struct firmware *wmfw_firmware = NULL; 932a828056fSSimon Trimmer char *coeff_filename = NULL; 933a828056fSSimon Trimmer const struct firmware *coeff_firmware = NULL; 934a828056fSSimon Trimmer 935a828056fSSimon Trimmer ret = wm_adsp_request_firmware_files(dsp, 936a828056fSSimon Trimmer &wmfw_firmware, &wmfw_filename, 937a828056fSSimon Trimmer &coeff_firmware, &coeff_filename); 938a828056fSSimon Trimmer if (ret) 939a828056fSSimon Trimmer return; 940186152dfSSimon Trimmer 941e1468202SSimon Trimmer cs_dsp_power_up(&dsp->cs_dsp, 942a828056fSSimon Trimmer wmfw_firmware, wmfw_filename, 943a828056fSSimon Trimmer coeff_firmware, coeff_filename, 9442169f2f1SSimon Trimmer wm_adsp_fw_text[dsp->fw]); 945a828056fSSimon Trimmer 946a828056fSSimon Trimmer wm_adsp_release_firmware_files(dsp, 947a828056fSSimon Trimmer wmfw_firmware, wmfw_filename, 948a828056fSSimon Trimmer coeff_firmware, coeff_filename); 949186152dfSSimon Trimmer } 950186152dfSSimon Trimmer 951186152dfSSimon Trimmer int wm_adsp_early_event(struct snd_soc_dapm_widget *w, 952186152dfSSimon Trimmer struct snd_kcontrol *kcontrol, int event) 953186152dfSSimon Trimmer { 954186152dfSSimon Trimmer struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); 955186152dfSSimon Trimmer struct wm_adsp *dsps = snd_soc_component_get_drvdata(component); 956186152dfSSimon Trimmer struct wm_adsp *dsp = &dsps[w->shift]; 957186152dfSSimon Trimmer 958186152dfSSimon Trimmer switch (event) { 959186152dfSSimon Trimmer case SND_SOC_DAPM_PRE_PMU: 960186152dfSSimon Trimmer queue_work(system_unbound_wq, &dsp->boot_work); 961186152dfSSimon Trimmer break; 962186152dfSSimon Trimmer case SND_SOC_DAPM_PRE_PMD: 963e1468202SSimon Trimmer cs_dsp_power_down(&dsp->cs_dsp); 96457a60cc3SCharles Keepax break; 96512db5eddSCharles Keepax default: 96612db5eddSCharles Keepax break; 967cab27258SCharles Keepax } 96812db5eddSCharles Keepax 96912db5eddSCharles Keepax return 0; 97012db5eddSCharles Keepax } 9714e08d50dSCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_early_event); 97212db5eddSCharles Keepax 973e1468202SSimon Trimmer static int wm_adsp_event_post_run(struct cs_dsp *cs_dsp) 974d8a64d6aSCharles Keepax { 975e1468202SSimon Trimmer struct wm_adsp *dsp = container_of(cs_dsp, struct wm_adsp, cs_dsp); 976e1468202SSimon Trimmer 977186152dfSSimon Trimmer if (wm_adsp_fw[dsp->fw].num_caps != 0) 978186152dfSSimon Trimmer return wm_adsp_buffer_init(dsp); 979d8a64d6aSCharles Keepax 980186152dfSSimon Trimmer return 0; 981186152dfSSimon Trimmer } 982186152dfSSimon Trimmer 983e1468202SSimon Trimmer static void wm_adsp_event_post_stop(struct cs_dsp *cs_dsp) 984186152dfSSimon Trimmer { 985e1468202SSimon Trimmer struct wm_adsp *dsp = container_of(cs_dsp, struct wm_adsp, cs_dsp); 986e1468202SSimon Trimmer 987186152dfSSimon Trimmer if (wm_adsp_fw[dsp->fw].num_caps != 0) 988186152dfSSimon Trimmer wm_adsp_buffer_free(dsp); 989186152dfSSimon Trimmer 990186152dfSSimon Trimmer dsp->fatal_error = false; 991186152dfSSimon Trimmer } 992186152dfSSimon Trimmer 993186152dfSSimon Trimmer int wm_adsp_event(struct snd_soc_dapm_widget *w, 994186152dfSSimon Trimmer struct snd_kcontrol *kcontrol, int event) 995186152dfSSimon Trimmer { 996186152dfSSimon Trimmer struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); 997186152dfSSimon Trimmer struct wm_adsp *dsps = snd_soc_component_get_drvdata(component); 998186152dfSSimon Trimmer struct wm_adsp *dsp = &dsps[w->shift]; 999186152dfSSimon Trimmer int ret = 0; 1000186152dfSSimon Trimmer 1001186152dfSSimon Trimmer switch (event) { 1002186152dfSSimon Trimmer case SND_SOC_DAPM_POST_PMU: 1003186152dfSSimon Trimmer flush_work(&dsp->boot_work); 1004e1468202SSimon Trimmer ret = cs_dsp_run(&dsp->cs_dsp); 1005186152dfSSimon Trimmer break; 1006186152dfSSimon Trimmer case SND_SOC_DAPM_PRE_PMD: 1007e1468202SSimon Trimmer cs_dsp_stop(&dsp->cs_dsp); 1008186152dfSSimon Trimmer break; 10092159ad93SMark Brown default: 10102159ad93SMark Brown break; 10112159ad93SMark Brown } 10122159ad93SMark Brown 10132159ad93SMark Brown return ret; 10142159ad93SMark Brown } 10154e08d50dSCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_event); 1016973838a0SMark Brown 10170fe1daa6SKuninori Morimoto int wm_adsp2_component_probe(struct wm_adsp *dsp, struct snd_soc_component *component) 1018f5e2ce92SRichard Fitzgerald { 1019af813a6fSCharles Keepax char preload[32]; 1020af813a6fSCharles Keepax 1021e1468202SSimon Trimmer snprintf(preload, ARRAY_SIZE(preload), "%s Preload", dsp->cs_dsp.name); 102295a594d0SCharles Keepax snd_soc_component_disable_pin(component, preload); 1023685f51a5SRichard Fitzgerald 1024e1468202SSimon Trimmer cs_dsp_init_debugfs(&dsp->cs_dsp, component->debugfs_root); 1025f9f55e31SRichard Fitzgerald 10260fe1daa6SKuninori Morimoto dsp->component = component; 1027af813a6fSCharles Keepax 10280a047f07SRichard Fitzgerald return 0; 1029f5e2ce92SRichard Fitzgerald } 10300fe1daa6SKuninori Morimoto EXPORT_SYMBOL_GPL(wm_adsp2_component_probe); 1031f5e2ce92SRichard Fitzgerald 10320fe1daa6SKuninori Morimoto int wm_adsp2_component_remove(struct wm_adsp *dsp, struct snd_soc_component *component) 1033f5e2ce92SRichard Fitzgerald { 1034e1468202SSimon Trimmer cs_dsp_cleanup_debugfs(&dsp->cs_dsp); 1035f9f55e31SRichard Fitzgerald 1036f5e2ce92SRichard Fitzgerald return 0; 1037f5e2ce92SRichard Fitzgerald } 10380fe1daa6SKuninori Morimoto EXPORT_SYMBOL_GPL(wm_adsp2_component_remove); 1039f5e2ce92SRichard Fitzgerald 104025ca837bSSimon Trimmer int wm_adsp2_init(struct wm_adsp *dsp) 104125ca837bSSimon Trimmer { 1042e1468202SSimon Trimmer int ret; 1043e1468202SSimon Trimmer 10444e08d50dSCharles Keepax INIT_WORK(&dsp->boot_work, wm_adsp_boot_work); 10456ab2b7b4SDimitris Papastamos 10466092be2dSCharles Keepax dsp->sys_config_size = sizeof(struct wm_adsp_system_config_xm_hdr); 10472dd04464SSimon Trimmer dsp->cs_dsp.client_ops = &wm_adsp2_client_ops; 10486092be2dSCharles Keepax 1049e1468202SSimon Trimmer ret = cs_dsp_adsp2_init(&dsp->cs_dsp); 1050e1468202SSimon Trimmer if (ret) 1051e1468202SSimon Trimmer return ret; 105225ca837bSSimon Trimmer 1053e1468202SSimon Trimmer return wm_adsp_common_init(dsp); 1054973838a0SMark Brown } 1055973838a0SMark Brown EXPORT_SYMBOL_GPL(wm_adsp2_init); 10560a37c6efSPraveen Diwakar 105725ca837bSSimon Trimmer int wm_halo_init(struct wm_adsp *dsp) 105825ca837bSSimon Trimmer { 1059e1468202SSimon Trimmer int ret; 1060e1468202SSimon Trimmer 1061170b1e12SWen Shi INIT_WORK(&dsp->boot_work, wm_adsp_boot_work); 1062170b1e12SWen Shi 10636092be2dSCharles Keepax dsp->sys_config_size = sizeof(struct wm_halo_system_config_xm_hdr); 10642dd04464SSimon Trimmer dsp->cs_dsp.client_ops = &wm_adsp2_client_ops; 10656092be2dSCharles Keepax 1066e1468202SSimon Trimmer ret = cs_dsp_halo_init(&dsp->cs_dsp); 1067e1468202SSimon Trimmer if (ret) 1068e1468202SSimon Trimmer return ret; 106925ca837bSSimon Trimmer 1070e1468202SSimon Trimmer return wm_adsp_common_init(dsp); 1071170b1e12SWen Shi } 1072170b1e12SWen Shi EXPORT_SYMBOL_GPL(wm_halo_init); 1073170b1e12SWen Shi 107425ca837bSSimon Trimmer void wm_adsp2_remove(struct wm_adsp *dsp) 107525ca837bSSimon Trimmer { 1076e1468202SSimon Trimmer cs_dsp_remove(&dsp->cs_dsp); 107725ca837bSSimon Trimmer } 107866225e98SRichard Fitzgerald EXPORT_SYMBOL_GPL(wm_adsp2_remove); 107966225e98SRichard Fitzgerald 1080edd71350SCharles Keepax static inline int wm_adsp_compr_attached(struct wm_adsp_compr *compr) 1081edd71350SCharles Keepax { 1082edd71350SCharles Keepax return compr->buf != NULL; 1083edd71350SCharles Keepax } 1084edd71350SCharles Keepax 1085edd71350SCharles Keepax static int wm_adsp_compr_attach(struct wm_adsp_compr *compr) 1086edd71350SCharles Keepax { 10874f2d4eabSStuart Henderson struct wm_adsp_compr_buf *buf = NULL, *tmp; 10884f2d4eabSStuart Henderson 1089a2bcbc1bSCharles Keepax if (compr->dsp->fatal_error) 1090a2bcbc1bSCharles Keepax return -EINVAL; 1091a2bcbc1bSCharles Keepax 10924f2d4eabSStuart Henderson list_for_each_entry(tmp, &compr->dsp->buffer_list, list) { 10934f2d4eabSStuart Henderson if (!tmp->name || !strcmp(compr->name, tmp->name)) { 10944f2d4eabSStuart Henderson buf = tmp; 10954f2d4eabSStuart Henderson break; 10964f2d4eabSStuart Henderson } 10974f2d4eabSStuart Henderson } 10984f2d4eabSStuart Henderson 10994f2d4eabSStuart Henderson if (!buf) 1100edd71350SCharles Keepax return -EINVAL; 1101edd71350SCharles Keepax 11024f2d4eabSStuart Henderson compr->buf = buf; 1103789b930aSCharles Keepax buf->compr = compr; 1104edd71350SCharles Keepax 1105edd71350SCharles Keepax return 0; 1106edd71350SCharles Keepax } 1107edd71350SCharles Keepax 1108721be3beSCharles Keepax static void wm_adsp_compr_detach(struct wm_adsp_compr *compr) 1109721be3beSCharles Keepax { 1110721be3beSCharles Keepax if (!compr) 1111721be3beSCharles Keepax return; 1112721be3beSCharles Keepax 1113721be3beSCharles Keepax /* Wake the poll so it can see buffer is no longer attached */ 1114721be3beSCharles Keepax if (compr->stream) 1115721be3beSCharles Keepax snd_compr_fragment_elapsed(compr->stream); 1116721be3beSCharles Keepax 1117721be3beSCharles Keepax if (wm_adsp_compr_attached(compr)) { 1118721be3beSCharles Keepax compr->buf->compr = NULL; 1119721be3beSCharles Keepax compr->buf = NULL; 1120721be3beSCharles Keepax } 1121721be3beSCharles Keepax } 1122721be3beSCharles Keepax 1123406abc95SCharles Keepax int wm_adsp_compr_open(struct wm_adsp *dsp, struct snd_compr_stream *stream) 1124406abc95SCharles Keepax { 11254f2d4eabSStuart Henderson struct wm_adsp_compr *compr, *tmp; 11264f2d4eabSStuart Henderson struct snd_soc_pcm_runtime *rtd = stream->private_data; 1127406abc95SCharles Keepax int ret = 0; 1128406abc95SCharles Keepax 1129e1468202SSimon Trimmer mutex_lock(&dsp->cs_dsp.pwr_lock); 1130406abc95SCharles Keepax 1131406abc95SCharles Keepax if (wm_adsp_fw[dsp->fw].num_caps == 0) { 11320d3fba3eSCharles Keepax adsp_err(dsp, "%s: Firmware does not support compressed API\n", 1133b5cb8558SKuninori Morimoto asoc_rtd_to_codec(rtd, 0)->name); 1134406abc95SCharles Keepax ret = -ENXIO; 1135406abc95SCharles Keepax goto out; 1136406abc95SCharles Keepax } 1137406abc95SCharles Keepax 1138406abc95SCharles Keepax if (wm_adsp_fw[dsp->fw].compr_direction != stream->direction) { 11390d3fba3eSCharles Keepax adsp_err(dsp, "%s: Firmware does not support stream direction\n", 1140b5cb8558SKuninori Morimoto asoc_rtd_to_codec(rtd, 0)->name); 1141406abc95SCharles Keepax ret = -EINVAL; 1142406abc95SCharles Keepax goto out; 1143406abc95SCharles Keepax } 1144406abc95SCharles Keepax 11454f2d4eabSStuart Henderson list_for_each_entry(tmp, &dsp->compr_list, list) { 1146b5cb8558SKuninori Morimoto if (!strcmp(tmp->name, asoc_rtd_to_codec(rtd, 0)->name)) { 11470d3fba3eSCharles Keepax adsp_err(dsp, "%s: Only a single stream supported per dai\n", 1148b5cb8558SKuninori Morimoto asoc_rtd_to_codec(rtd, 0)->name); 114995fe9597SCharles Keepax ret = -EBUSY; 115095fe9597SCharles Keepax goto out; 115195fe9597SCharles Keepax } 11524f2d4eabSStuart Henderson } 115395fe9597SCharles Keepax 1154406abc95SCharles Keepax compr = kzalloc(sizeof(*compr), GFP_KERNEL); 1155406abc95SCharles Keepax if (!compr) { 1156406abc95SCharles Keepax ret = -ENOMEM; 1157406abc95SCharles Keepax goto out; 1158406abc95SCharles Keepax } 1159406abc95SCharles Keepax 1160406abc95SCharles Keepax compr->dsp = dsp; 1161406abc95SCharles Keepax compr->stream = stream; 1162b5cb8558SKuninori Morimoto compr->name = asoc_rtd_to_codec(rtd, 0)->name; 1163406abc95SCharles Keepax 11644f2d4eabSStuart Henderson list_add_tail(&compr->list, &dsp->compr_list); 1165406abc95SCharles Keepax 1166406abc95SCharles Keepax stream->runtime->private_data = compr; 1167406abc95SCharles Keepax 1168406abc95SCharles Keepax out: 1169e1468202SSimon Trimmer mutex_unlock(&dsp->cs_dsp.pwr_lock); 1170406abc95SCharles Keepax 1171406abc95SCharles Keepax return ret; 1172406abc95SCharles Keepax } 1173406abc95SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_open); 1174406abc95SCharles Keepax 11753a5ccf25SKuninori Morimoto int wm_adsp_compr_free(struct snd_soc_component *component, 11763a5ccf25SKuninori Morimoto struct snd_compr_stream *stream) 1177406abc95SCharles Keepax { 1178406abc95SCharles Keepax struct wm_adsp_compr *compr = stream->runtime->private_data; 1179406abc95SCharles Keepax struct wm_adsp *dsp = compr->dsp; 1180406abc95SCharles Keepax 1181e1468202SSimon Trimmer mutex_lock(&dsp->cs_dsp.pwr_lock); 1182406abc95SCharles Keepax 1183721be3beSCharles Keepax wm_adsp_compr_detach(compr); 11844f2d4eabSStuart Henderson list_del(&compr->list); 1185406abc95SCharles Keepax 118683a40ce9SCharles Keepax kfree(compr->raw_buf); 1187406abc95SCharles Keepax kfree(compr); 1188406abc95SCharles Keepax 1189e1468202SSimon Trimmer mutex_unlock(&dsp->cs_dsp.pwr_lock); 1190406abc95SCharles Keepax 1191406abc95SCharles Keepax return 0; 1192406abc95SCharles Keepax } 1193406abc95SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_free); 1194406abc95SCharles Keepax 1195406abc95SCharles Keepax static int wm_adsp_compr_check_params(struct snd_compr_stream *stream, 1196406abc95SCharles Keepax struct snd_compr_params *params) 1197406abc95SCharles Keepax { 1198406abc95SCharles Keepax struct wm_adsp_compr *compr = stream->runtime->private_data; 1199406abc95SCharles Keepax struct wm_adsp *dsp = compr->dsp; 1200406abc95SCharles Keepax const struct wm_adsp_fw_caps *caps; 1201406abc95SCharles Keepax const struct snd_codec_desc *desc; 1202406abc95SCharles Keepax int i, j; 1203406abc95SCharles Keepax 1204406abc95SCharles Keepax if (params->buffer.fragment_size < WM_ADSP_MIN_FRAGMENT_SIZE || 1205406abc95SCharles Keepax params->buffer.fragment_size > WM_ADSP_MAX_FRAGMENT_SIZE || 1206406abc95SCharles Keepax params->buffer.fragments < WM_ADSP_MIN_FRAGMENTS || 1207406abc95SCharles Keepax params->buffer.fragments > WM_ADSP_MAX_FRAGMENTS || 12085beb8eeaSSimon Trimmer params->buffer.fragment_size % CS_DSP_DATA_WORD_SIZE) { 12090d3fba3eSCharles Keepax compr_err(compr, "Invalid buffer fragsize=%d fragments=%d\n", 1210406abc95SCharles Keepax params->buffer.fragment_size, 1211406abc95SCharles Keepax params->buffer.fragments); 1212406abc95SCharles Keepax 1213406abc95SCharles Keepax return -EINVAL; 1214406abc95SCharles Keepax } 1215406abc95SCharles Keepax 1216406abc95SCharles Keepax for (i = 0; i < wm_adsp_fw[dsp->fw].num_caps; i++) { 1217406abc95SCharles Keepax caps = &wm_adsp_fw[dsp->fw].caps[i]; 1218406abc95SCharles Keepax desc = &caps->desc; 1219406abc95SCharles Keepax 1220406abc95SCharles Keepax if (caps->id != params->codec.id) 1221406abc95SCharles Keepax continue; 1222406abc95SCharles Keepax 1223406abc95SCharles Keepax if (stream->direction == SND_COMPRESS_PLAYBACK) { 1224406abc95SCharles Keepax if (desc->max_ch < params->codec.ch_out) 1225406abc95SCharles Keepax continue; 1226406abc95SCharles Keepax } else { 1227406abc95SCharles Keepax if (desc->max_ch < params->codec.ch_in) 1228406abc95SCharles Keepax continue; 1229406abc95SCharles Keepax } 1230406abc95SCharles Keepax 1231406abc95SCharles Keepax if (!(desc->formats & (1 << params->codec.format))) 1232406abc95SCharles Keepax continue; 1233406abc95SCharles Keepax 1234406abc95SCharles Keepax for (j = 0; j < desc->num_sample_rates; ++j) 1235406abc95SCharles Keepax if (desc->sample_rates[j] == params->codec.sample_rate) 1236406abc95SCharles Keepax return 0; 1237406abc95SCharles Keepax } 1238406abc95SCharles Keepax 12390d3fba3eSCharles Keepax compr_err(compr, "Invalid params id=%u ch=%u,%u rate=%u fmt=%u\n", 1240406abc95SCharles Keepax params->codec.id, params->codec.ch_in, params->codec.ch_out, 1241406abc95SCharles Keepax params->codec.sample_rate, params->codec.format); 1242406abc95SCharles Keepax return -EINVAL; 1243406abc95SCharles Keepax } 1244406abc95SCharles Keepax 1245565ace46SCharles Keepax static inline unsigned int wm_adsp_compr_frag_words(struct wm_adsp_compr *compr) 1246565ace46SCharles Keepax { 12475beb8eeaSSimon Trimmer return compr->size.fragment_size / CS_DSP_DATA_WORD_SIZE; 1248565ace46SCharles Keepax } 1249565ace46SCharles Keepax 12503a5ccf25SKuninori Morimoto int wm_adsp_compr_set_params(struct snd_soc_component *component, 12513a5ccf25SKuninori Morimoto struct snd_compr_stream *stream, 1252406abc95SCharles Keepax struct snd_compr_params *params) 1253406abc95SCharles Keepax { 1254406abc95SCharles Keepax struct wm_adsp_compr *compr = stream->runtime->private_data; 125583a40ce9SCharles Keepax unsigned int size; 1256406abc95SCharles Keepax int ret; 1257406abc95SCharles Keepax 1258406abc95SCharles Keepax ret = wm_adsp_compr_check_params(stream, params); 1259406abc95SCharles Keepax if (ret) 1260406abc95SCharles Keepax return ret; 1261406abc95SCharles Keepax 1262406abc95SCharles Keepax compr->size = params->buffer; 1263406abc95SCharles Keepax 12640d3fba3eSCharles Keepax compr_dbg(compr, "fragment_size=%d fragments=%d\n", 1265406abc95SCharles Keepax compr->size.fragment_size, compr->size.fragments); 1266406abc95SCharles Keepax 126783a40ce9SCharles Keepax size = wm_adsp_compr_frag_words(compr) * sizeof(*compr->raw_buf); 126883a40ce9SCharles Keepax compr->raw_buf = kmalloc(size, GFP_DMA | GFP_KERNEL); 126983a40ce9SCharles Keepax if (!compr->raw_buf) 127083a40ce9SCharles Keepax return -ENOMEM; 127183a40ce9SCharles Keepax 1272da2b3358SCharles Keepax compr->sample_rate = params->codec.sample_rate; 1273da2b3358SCharles Keepax 1274406abc95SCharles Keepax return 0; 1275406abc95SCharles Keepax } 1276406abc95SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_set_params); 1277406abc95SCharles Keepax 12783a5ccf25SKuninori Morimoto int wm_adsp_compr_get_caps(struct snd_soc_component *component, 12793a5ccf25SKuninori Morimoto struct snd_compr_stream *stream, 1280406abc95SCharles Keepax struct snd_compr_caps *caps) 1281406abc95SCharles Keepax { 1282406abc95SCharles Keepax struct wm_adsp_compr *compr = stream->runtime->private_data; 1283406abc95SCharles Keepax int fw = compr->dsp->fw; 1284406abc95SCharles Keepax int i; 1285406abc95SCharles Keepax 1286406abc95SCharles Keepax if (wm_adsp_fw[fw].caps) { 1287406abc95SCharles Keepax for (i = 0; i < wm_adsp_fw[fw].num_caps; i++) 1288406abc95SCharles Keepax caps->codecs[i] = wm_adsp_fw[fw].caps[i].id; 1289406abc95SCharles Keepax 1290406abc95SCharles Keepax caps->num_codecs = i; 1291406abc95SCharles Keepax caps->direction = wm_adsp_fw[fw].compr_direction; 1292406abc95SCharles Keepax 1293406abc95SCharles Keepax caps->min_fragment_size = WM_ADSP_MIN_FRAGMENT_SIZE; 1294406abc95SCharles Keepax caps->max_fragment_size = WM_ADSP_MAX_FRAGMENT_SIZE; 1295406abc95SCharles Keepax caps->min_fragments = WM_ADSP_MIN_FRAGMENTS; 1296406abc95SCharles Keepax caps->max_fragments = WM_ADSP_MAX_FRAGMENTS; 1297406abc95SCharles Keepax } 1298406abc95SCharles Keepax 1299406abc95SCharles Keepax return 0; 1300406abc95SCharles Keepax } 1301406abc95SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_get_caps); 1302406abc95SCharles Keepax 13032cd19bdbSCharles Keepax static inline int wm_adsp_buffer_read(struct wm_adsp_compr_buf *buf, 13042cd19bdbSCharles Keepax unsigned int field_offset, u32 *data) 13052cd19bdbSCharles Keepax { 1306e1468202SSimon Trimmer return cs_dsp_read_data_word(&buf->dsp->cs_dsp, buf->host_buf_mem_type, 13072cd19bdbSCharles Keepax buf->host_buf_ptr + field_offset, data); 13082cd19bdbSCharles Keepax } 13092cd19bdbSCharles Keepax 13102cd19bdbSCharles Keepax static inline int wm_adsp_buffer_write(struct wm_adsp_compr_buf *buf, 13112cd19bdbSCharles Keepax unsigned int field_offset, u32 data) 13122cd19bdbSCharles Keepax { 1313e1468202SSimon Trimmer return cs_dsp_write_data_word(&buf->dsp->cs_dsp, buf->host_buf_mem_type, 1314e1468202SSimon Trimmer buf->host_buf_ptr + field_offset, 1315e1468202SSimon Trimmer data); 13162cd19bdbSCharles Keepax } 13172cd19bdbSCharles Keepax 13181e38f069SCharles Keepax static int wm_adsp_buffer_populate(struct wm_adsp_compr_buf *buf) 13191e38f069SCharles Keepax { 13201e38f069SCharles Keepax const struct wm_adsp_fw_caps *caps = wm_adsp_fw[buf->dsp->fw].caps; 13211e38f069SCharles Keepax struct wm_adsp_buffer_region *region; 13221e38f069SCharles Keepax u32 offset = 0; 13231e38f069SCharles Keepax int i, ret; 13241e38f069SCharles Keepax 1325a792af69SCharles Keepax buf->regions = kcalloc(caps->num_regions, sizeof(*buf->regions), 1326a792af69SCharles Keepax GFP_KERNEL); 1327a792af69SCharles Keepax if (!buf->regions) 1328a792af69SCharles Keepax return -ENOMEM; 1329a792af69SCharles Keepax 13301e38f069SCharles Keepax for (i = 0; i < caps->num_regions; ++i) { 13311e38f069SCharles Keepax region = &buf->regions[i]; 13321e38f069SCharles Keepax 13331e38f069SCharles Keepax region->offset = offset; 13341e38f069SCharles Keepax region->mem_type = caps->region_defs[i].mem_type; 13351e38f069SCharles Keepax 13361e38f069SCharles Keepax ret = wm_adsp_buffer_read(buf, caps->region_defs[i].base_offset, 13371e38f069SCharles Keepax ®ion->base_addr); 13381e38f069SCharles Keepax if (ret < 0) 13391e38f069SCharles Keepax return ret; 13401e38f069SCharles Keepax 13411e38f069SCharles Keepax ret = wm_adsp_buffer_read(buf, caps->region_defs[i].size_offset, 13421e38f069SCharles Keepax &offset); 13431e38f069SCharles Keepax if (ret < 0) 13441e38f069SCharles Keepax return ret; 13451e38f069SCharles Keepax 13461e38f069SCharles Keepax region->cumulative_size = offset; 13471e38f069SCharles Keepax 13480d3fba3eSCharles Keepax compr_dbg(buf, 13491e38f069SCharles Keepax "region=%d type=%d base=%08x off=%08x size=%08x\n", 13501e38f069SCharles Keepax i, region->mem_type, region->base_addr, 13511e38f069SCharles Keepax region->offset, region->cumulative_size); 13521e38f069SCharles Keepax } 13531e38f069SCharles Keepax 13541e38f069SCharles Keepax return 0; 13551e38f069SCharles Keepax } 13561e38f069SCharles Keepax 13571e38f069SCharles Keepax static void wm_adsp_buffer_clear(struct wm_adsp_compr_buf *buf) 13581e38f069SCharles Keepax { 13591e38f069SCharles Keepax buf->irq_count = 0xFFFFFFFF; 13601e38f069SCharles Keepax buf->read_index = -1; 13611e38f069SCharles Keepax buf->avail = 0; 13621e38f069SCharles Keepax } 13631e38f069SCharles Keepax 1364a792af69SCharles Keepax static struct wm_adsp_compr_buf *wm_adsp_buffer_alloc(struct wm_adsp *dsp) 1365a792af69SCharles Keepax { 1366a792af69SCharles Keepax struct wm_adsp_compr_buf *buf; 1367a792af69SCharles Keepax 1368a792af69SCharles Keepax buf = kzalloc(sizeof(*buf), GFP_KERNEL); 1369a792af69SCharles Keepax if (!buf) 1370a792af69SCharles Keepax return NULL; 1371a792af69SCharles Keepax 1372a792af69SCharles Keepax buf->dsp = dsp; 1373a792af69SCharles Keepax 1374a792af69SCharles Keepax wm_adsp_buffer_clear(buf); 1375a792af69SCharles Keepax 13764f2d4eabSStuart Henderson list_add_tail(&buf->list, &dsp->buffer_list); 1377a792af69SCharles Keepax 1378a792af69SCharles Keepax return buf; 1379a792af69SCharles Keepax } 1380a792af69SCharles Keepax 1381a792af69SCharles Keepax static int wm_adsp_buffer_parse_legacy(struct wm_adsp *dsp) 13822cd19bdbSCharles Keepax { 13835beb8eeaSSimon Trimmer struct cs_dsp_alg_region *alg_region; 1384a792af69SCharles Keepax struct wm_adsp_compr_buf *buf; 13852cd19bdbSCharles Keepax u32 xmalg, addr, magic; 13862cd19bdbSCharles Keepax int i, ret; 13872cd19bdbSCharles Keepax 1388e1468202SSimon Trimmer alg_region = cs_dsp_find_alg_region(&dsp->cs_dsp, WMFW_ADSP2_XM, dsp->cs_dsp.fw_id); 13899daf4fd0SLi Xu if (!alg_region) { 13909daf4fd0SLi Xu adsp_err(dsp, "No algorithm region found\n"); 13919daf4fd0SLi Xu return -EINVAL; 13929daf4fd0SLi Xu } 13939daf4fd0SLi Xu 1394a792af69SCharles Keepax buf = wm_adsp_buffer_alloc(dsp); 1395a792af69SCharles Keepax if (!buf) 1396a792af69SCharles Keepax return -ENOMEM; 1397a792af69SCharles Keepax 13986092be2dSCharles Keepax xmalg = dsp->sys_config_size / sizeof(__be32); 13992cd19bdbSCharles Keepax 14002cd19bdbSCharles Keepax addr = alg_region->base + xmalg + ALG_XM_FIELD(magic); 1401e1468202SSimon Trimmer ret = cs_dsp_read_data_word(&dsp->cs_dsp, WMFW_ADSP2_XM, addr, &magic); 14022cd19bdbSCharles Keepax if (ret < 0) 14032cd19bdbSCharles Keepax return ret; 14042cd19bdbSCharles Keepax 14052cd19bdbSCharles Keepax if (magic != WM_ADSP_ALG_XM_STRUCT_MAGIC) 1406a792af69SCharles Keepax return -ENODEV; 14072cd19bdbSCharles Keepax 14082cd19bdbSCharles Keepax addr = alg_region->base + xmalg + ALG_XM_FIELD(host_buf_ptr); 14092cd19bdbSCharles Keepax for (i = 0; i < 5; ++i) { 1410e1468202SSimon Trimmer ret = cs_dsp_read_data_word(&dsp->cs_dsp, WMFW_ADSP2_XM, addr, 14112cd19bdbSCharles Keepax &buf->host_buf_ptr); 14122cd19bdbSCharles Keepax if (ret < 0) 14132cd19bdbSCharles Keepax return ret; 14142cd19bdbSCharles Keepax 14152cd19bdbSCharles Keepax if (buf->host_buf_ptr) 14162cd19bdbSCharles Keepax break; 14172cd19bdbSCharles Keepax 14182cd19bdbSCharles Keepax usleep_range(1000, 2000); 14192cd19bdbSCharles Keepax } 14202cd19bdbSCharles Keepax 14212cd19bdbSCharles Keepax if (!buf->host_buf_ptr) 14222cd19bdbSCharles Keepax return -EIO; 14232cd19bdbSCharles Keepax 1424fb13f19dSAndrew Ford buf->host_buf_mem_type = WMFW_ADSP2_XM; 1425fb13f19dSAndrew Ford 1426a792af69SCharles Keepax ret = wm_adsp_buffer_populate(buf); 1427a792af69SCharles Keepax if (ret < 0) 1428a792af69SCharles Keepax return ret; 1429a792af69SCharles Keepax 14300d3fba3eSCharles Keepax compr_dbg(buf, "legacy host_buf_ptr=%x\n", buf->host_buf_ptr); 14312cd19bdbSCharles Keepax 14322cd19bdbSCharles Keepax return 0; 14332cd19bdbSCharles Keepax } 14342cd19bdbSCharles Keepax 14350700bc2fSSimon Trimmer static int wm_adsp_buffer_parse_coeff(struct cs_dsp_coeff_ctl *cs_ctl) 1436d52ed4b0SRichard Fitzgerald { 14374f2d4eabSStuart Henderson struct wm_adsp_host_buf_coeff_v1 coeff_v1; 1438a792af69SCharles Keepax struct wm_adsp_compr_buf *buf; 1439e1468202SSimon Trimmer struct wm_adsp *dsp = container_of(cs_ctl->dsp, struct wm_adsp, cs_dsp); 144004ae0859SCharles Keepax unsigned int version; 1441a792af69SCharles Keepax int ret, i; 1442d52ed4b0SRichard Fitzgerald 1443d52ed4b0SRichard Fitzgerald for (i = 0; i < 5; ++i) { 1444a887f9c7SCharles Keepax ret = cs_dsp_coeff_read_ctrl(cs_ctl, 0, &coeff_v1, 1445a887f9c7SCharles Keepax min(cs_ctl->len, sizeof(coeff_v1))); 1446d52ed4b0SRichard Fitzgerald if (ret < 0) 1447d52ed4b0SRichard Fitzgerald return ret; 1448d52ed4b0SRichard Fitzgerald 144904ae0859SCharles Keepax if (coeff_v1.host_buf_ptr) 1450d52ed4b0SRichard Fitzgerald break; 1451d52ed4b0SRichard Fitzgerald 1452d52ed4b0SRichard Fitzgerald usleep_range(1000, 2000); 1453d52ed4b0SRichard Fitzgerald } 1454d52ed4b0SRichard Fitzgerald 145504ae0859SCharles Keepax if (!coeff_v1.host_buf_ptr) { 14560700bc2fSSimon Trimmer adsp_err(dsp, "Failed to acquire host buffer\n"); 1457d52ed4b0SRichard Fitzgerald return -EIO; 1458d52ed4b0SRichard Fitzgerald } 1459d52ed4b0SRichard Fitzgerald 14600700bc2fSSimon Trimmer buf = wm_adsp_buffer_alloc(dsp); 14612cd19bdbSCharles Keepax if (!buf) 14622cd19bdbSCharles Keepax return -ENOMEM; 14632cd19bdbSCharles Keepax 14640700bc2fSSimon Trimmer buf->host_buf_mem_type = cs_ctl->alg_region.type; 146504ae0859SCharles Keepax buf->host_buf_ptr = be32_to_cpu(coeff_v1.host_buf_ptr); 14662cd19bdbSCharles Keepax 14672cd19bdbSCharles Keepax ret = wm_adsp_buffer_populate(buf); 1468a792af69SCharles Keepax if (ret < 0) 1469a792af69SCharles Keepax return ret; 1470a792af69SCharles Keepax 14714f2d4eabSStuart Henderson /* 14724f2d4eabSStuart Henderson * v0 host_buffer coefficients didn't have versioning, so if the 14734f2d4eabSStuart Henderson * control is one word, assume version 0. 14744f2d4eabSStuart Henderson */ 14750700bc2fSSimon Trimmer if (cs_ctl->len == 4) { 14760d3fba3eSCharles Keepax compr_dbg(buf, "host_buf_ptr=%x\n", buf->host_buf_ptr); 1477a792af69SCharles Keepax return 0; 14782cd19bdbSCharles Keepax } 14792cd19bdbSCharles Keepax 1480a0b653e8SRichard Fitzgerald version = be32_to_cpu(coeff_v1.versions) & HOST_BUF_COEFF_COMPAT_VER_MASK; 1481a0b653e8SRichard Fitzgerald version >>= HOST_BUF_COEFF_COMPAT_VER_SHIFT; 14824f2d4eabSStuart Henderson 1483a0b653e8SRichard Fitzgerald if (version > HOST_BUF_COEFF_SUPPORTED_COMPAT_VER) { 14840700bc2fSSimon Trimmer adsp_err(dsp, 14854f2d4eabSStuart Henderson "Host buffer coeff ver %u > supported version %u\n", 1486a0b653e8SRichard Fitzgerald version, HOST_BUF_COEFF_SUPPORTED_COMPAT_VER); 14874f2d4eabSStuart Henderson return -EINVAL; 14884f2d4eabSStuart Henderson } 14894f2d4eabSStuart Henderson 14905beb8eeaSSimon Trimmer cs_dsp_remove_padding((u32 *)&coeff_v1.name, ARRAY_SIZE(coeff_v1.name)); 14914f2d4eabSStuart Henderson 14920700bc2fSSimon Trimmer buf->name = kasprintf(GFP_KERNEL, "%s-dsp-%s", dsp->part, 14934f2d4eabSStuart Henderson (char *)&coeff_v1.name); 14944f2d4eabSStuart Henderson 14950d3fba3eSCharles Keepax compr_dbg(buf, "host_buf_ptr=%x coeff version %u\n", 1496a0b653e8SRichard Fitzgerald buf->host_buf_ptr, version); 14974f2d4eabSStuart Henderson 1498a0b653e8SRichard Fitzgerald return version; 14994f2d4eabSStuart Henderson } 15004f2d4eabSStuart Henderson 1501a792af69SCharles Keepax static int wm_adsp_buffer_init(struct wm_adsp *dsp) 1502a792af69SCharles Keepax { 15030700bc2fSSimon Trimmer struct cs_dsp_coeff_ctl *cs_ctl; 1504a792af69SCharles Keepax int ret; 1505a792af69SCharles Keepax 1506e1468202SSimon Trimmer list_for_each_entry(cs_ctl, &dsp->cs_dsp.ctl_list, list) { 15070700bc2fSSimon Trimmer if (cs_ctl->type != WMFW_CTL_TYPE_HOST_BUFFER) 1508a792af69SCharles Keepax continue; 1509a792af69SCharles Keepax 15100700bc2fSSimon Trimmer if (!cs_ctl->enabled) 1511a792af69SCharles Keepax continue; 1512a792af69SCharles Keepax 15130700bc2fSSimon Trimmer ret = wm_adsp_buffer_parse_coeff(cs_ctl); 1514a792af69SCharles Keepax if (ret < 0) { 1515a792af69SCharles Keepax adsp_err(dsp, "Failed to parse coeff: %d\n", ret); 1516a792af69SCharles Keepax goto error; 15174f2d4eabSStuart Henderson } else if (ret == 0) { 15184f2d4eabSStuart Henderson /* Only one buffer supported for version 0 */ 1519a792af69SCharles Keepax return 0; 1520a792af69SCharles Keepax } 15214f2d4eabSStuart Henderson } 1522a792af69SCharles Keepax 15234f2d4eabSStuart Henderson if (list_empty(&dsp->buffer_list)) { 1524a792af69SCharles Keepax /* Fall back to legacy support */ 1525a792af69SCharles Keepax ret = wm_adsp_buffer_parse_legacy(dsp); 1526a792af69SCharles Keepax if (ret) { 1527a792af69SCharles Keepax adsp_err(dsp, "Failed to parse legacy: %d\n", ret); 1528a792af69SCharles Keepax goto error; 1529a792af69SCharles Keepax } 1530a792af69SCharles Keepax } 15312cd19bdbSCharles Keepax 15322cd19bdbSCharles Keepax return 0; 15332cd19bdbSCharles Keepax 1534a792af69SCharles Keepax error: 1535a792af69SCharles Keepax wm_adsp_buffer_free(dsp); 15362cd19bdbSCharles Keepax return ret; 15372cd19bdbSCharles Keepax } 15382cd19bdbSCharles Keepax 15392cd19bdbSCharles Keepax static int wm_adsp_buffer_free(struct wm_adsp *dsp) 15402cd19bdbSCharles Keepax { 15414f2d4eabSStuart Henderson struct wm_adsp_compr_buf *buf, *tmp; 1542721be3beSCharles Keepax 15434f2d4eabSStuart Henderson list_for_each_entry_safe(buf, tmp, &dsp->buffer_list, list) { 15444f2d4eabSStuart Henderson wm_adsp_compr_detach(buf->compr); 15452cd19bdbSCharles Keepax 15464f2d4eabSStuart Henderson kfree(buf->name); 15474f2d4eabSStuart Henderson kfree(buf->regions); 15484f2d4eabSStuart Henderson list_del(&buf->list); 15494f2d4eabSStuart Henderson kfree(buf); 15502cd19bdbSCharles Keepax } 15512cd19bdbSCharles Keepax 15522cd19bdbSCharles Keepax return 0; 15532cd19bdbSCharles Keepax } 15542cd19bdbSCharles Keepax 1555f938f348SStuart Henderson static int wm_adsp_buffer_get_error(struct wm_adsp_compr_buf *buf) 1556f938f348SStuart Henderson { 1557f938f348SStuart Henderson int ret; 1558f938f348SStuart Henderson 1559f938f348SStuart Henderson ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(error), &buf->error); 1560f938f348SStuart Henderson if (ret < 0) { 156148ead31cSCharles Keepax compr_err(buf, "Failed to check buffer error: %d\n", ret); 1562f938f348SStuart Henderson return ret; 1563f938f348SStuart Henderson } 1564f938f348SStuart Henderson if (buf->error != 0) { 156548ead31cSCharles Keepax compr_err(buf, "Buffer error occurred: %d\n", buf->error); 1566f938f348SStuart Henderson return -EIO; 1567f938f348SStuart Henderson } 1568f938f348SStuart Henderson 1569f938f348SStuart Henderson return 0; 1570f938f348SStuart Henderson } 1571f938f348SStuart Henderson 15723a5ccf25SKuninori Morimoto int wm_adsp_compr_trigger(struct snd_soc_component *component, 15733a5ccf25SKuninori Morimoto struct snd_compr_stream *stream, int cmd) 157495fe9597SCharles Keepax { 157595fe9597SCharles Keepax struct wm_adsp_compr *compr = stream->runtime->private_data; 157695fe9597SCharles Keepax struct wm_adsp *dsp = compr->dsp; 157795fe9597SCharles Keepax int ret = 0; 157895fe9597SCharles Keepax 15790d3fba3eSCharles Keepax compr_dbg(compr, "Trigger: %d\n", cmd); 158095fe9597SCharles Keepax 1581e1468202SSimon Trimmer mutex_lock(&dsp->cs_dsp.pwr_lock); 158295fe9597SCharles Keepax 158395fe9597SCharles Keepax switch (cmd) { 158495fe9597SCharles Keepax case SNDRV_PCM_TRIGGER_START: 158561fc060cSCharles Keepax if (!wm_adsp_compr_attached(compr)) { 158695fe9597SCharles Keepax ret = wm_adsp_compr_attach(compr); 158795fe9597SCharles Keepax if (ret < 0) { 15880d3fba3eSCharles Keepax compr_err(compr, "Failed to link buffer and stream: %d\n", 158995fe9597SCharles Keepax ret); 159095fe9597SCharles Keepax break; 159195fe9597SCharles Keepax } 159261fc060cSCharles Keepax } 159361fc060cSCharles Keepax 1594f938f348SStuart Henderson ret = wm_adsp_buffer_get_error(compr->buf); 1595f938f348SStuart Henderson if (ret < 0) 1596f938f348SStuart Henderson break; 1597f938f348SStuart Henderson 1598565ace46SCharles Keepax /* Trigger the IRQ at one fragment of data */ 1599565ace46SCharles Keepax ret = wm_adsp_buffer_write(compr->buf, 1600565ace46SCharles Keepax HOST_BUFFER_FIELD(high_water_mark), 1601565ace46SCharles Keepax wm_adsp_compr_frag_words(compr)); 1602565ace46SCharles Keepax if (ret < 0) { 16030d3fba3eSCharles Keepax compr_err(compr, "Failed to set high water mark: %d\n", 1604565ace46SCharles Keepax ret); 1605565ace46SCharles Keepax break; 1606565ace46SCharles Keepax } 160795fe9597SCharles Keepax break; 160895fe9597SCharles Keepax case SNDRV_PCM_TRIGGER_STOP: 160943d147beSCharles Keepax if (wm_adsp_compr_attached(compr)) 1610639e5eb3SCharles Keepax wm_adsp_buffer_clear(compr->buf); 161195fe9597SCharles Keepax break; 161295fe9597SCharles Keepax default: 161395fe9597SCharles Keepax ret = -EINVAL; 161495fe9597SCharles Keepax break; 161595fe9597SCharles Keepax } 161695fe9597SCharles Keepax 1617e1468202SSimon Trimmer mutex_unlock(&dsp->cs_dsp.pwr_lock); 161895fe9597SCharles Keepax 161995fe9597SCharles Keepax return ret; 162095fe9597SCharles Keepax } 162195fe9597SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_trigger); 162295fe9597SCharles Keepax 1623565ace46SCharles Keepax static inline int wm_adsp_buffer_size(struct wm_adsp_compr_buf *buf) 1624565ace46SCharles Keepax { 1625565ace46SCharles Keepax int last_region = wm_adsp_fw[buf->dsp->fw].caps->num_regions - 1; 1626565ace46SCharles Keepax 1627565ace46SCharles Keepax return buf->regions[last_region].cumulative_size; 1628565ace46SCharles Keepax } 1629565ace46SCharles Keepax 1630565ace46SCharles Keepax static int wm_adsp_buffer_update_avail(struct wm_adsp_compr_buf *buf) 1631565ace46SCharles Keepax { 1632565ace46SCharles Keepax u32 next_read_index, next_write_index; 1633565ace46SCharles Keepax int write_index, read_index, avail; 1634565ace46SCharles Keepax int ret; 1635565ace46SCharles Keepax 1636565ace46SCharles Keepax /* Only sync read index if we haven't already read a valid index */ 1637565ace46SCharles Keepax if (buf->read_index < 0) { 1638565ace46SCharles Keepax ret = wm_adsp_buffer_read(buf, 1639565ace46SCharles Keepax HOST_BUFFER_FIELD(next_read_index), 1640565ace46SCharles Keepax &next_read_index); 1641565ace46SCharles Keepax if (ret < 0) 1642565ace46SCharles Keepax return ret; 1643565ace46SCharles Keepax 1644565ace46SCharles Keepax read_index = sign_extend32(next_read_index, 23); 1645565ace46SCharles Keepax 1646565ace46SCharles Keepax if (read_index < 0) { 16470d3fba3eSCharles Keepax compr_dbg(buf, "Avail check on unstarted stream\n"); 1648565ace46SCharles Keepax return 0; 1649565ace46SCharles Keepax } 1650565ace46SCharles Keepax 1651565ace46SCharles Keepax buf->read_index = read_index; 1652565ace46SCharles Keepax } 1653565ace46SCharles Keepax 1654565ace46SCharles Keepax ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(next_write_index), 1655565ace46SCharles Keepax &next_write_index); 1656565ace46SCharles Keepax if (ret < 0) 1657565ace46SCharles Keepax return ret; 1658565ace46SCharles Keepax 1659565ace46SCharles Keepax write_index = sign_extend32(next_write_index, 23); 1660565ace46SCharles Keepax 1661565ace46SCharles Keepax avail = write_index - buf->read_index; 1662565ace46SCharles Keepax if (avail < 0) 1663565ace46SCharles Keepax avail += wm_adsp_buffer_size(buf); 1664565ace46SCharles Keepax 16650d3fba3eSCharles Keepax compr_dbg(buf, "readindex=0x%x, writeindex=0x%x, avail=%d\n", 16665beb8eeaSSimon Trimmer buf->read_index, write_index, avail * CS_DSP_DATA_WORD_SIZE); 1667565ace46SCharles Keepax 1668565ace46SCharles Keepax buf->avail = avail; 1669565ace46SCharles Keepax 1670565ace46SCharles Keepax return 0; 1671565ace46SCharles Keepax } 1672565ace46SCharles Keepax 1673565ace46SCharles Keepax int wm_adsp_compr_handle_irq(struct wm_adsp *dsp) 1674565ace46SCharles Keepax { 1675612047f0SCharles Keepax struct wm_adsp_compr_buf *buf; 1676612047f0SCharles Keepax struct wm_adsp_compr *compr; 1677565ace46SCharles Keepax int ret = 0; 1678565ace46SCharles Keepax 1679e1468202SSimon Trimmer mutex_lock(&dsp->cs_dsp.pwr_lock); 1680565ace46SCharles Keepax 16814f2d4eabSStuart Henderson if (list_empty(&dsp->buffer_list)) { 1682565ace46SCharles Keepax ret = -ENODEV; 1683565ace46SCharles Keepax goto out; 1684565ace46SCharles Keepax } 16850d3fba3eSCharles Keepax 1686565ace46SCharles Keepax adsp_dbg(dsp, "Handling buffer IRQ\n"); 1687565ace46SCharles Keepax 16884f2d4eabSStuart Henderson list_for_each_entry(buf, &dsp->buffer_list, list) { 16894f2d4eabSStuart Henderson compr = buf->compr; 16904f2d4eabSStuart Henderson 16919771b18aSCharles Keepax ret = wm_adsp_buffer_get_error(buf); 16929771b18aSCharles Keepax if (ret < 0) 16935847609eSCharles Keepax goto out_notify; /* Wake poll to report error */ 1694565ace46SCharles Keepax 1695565ace46SCharles Keepax ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(irq_count), 1696565ace46SCharles Keepax &buf->irq_count); 1697565ace46SCharles Keepax if (ret < 0) { 16980d3fba3eSCharles Keepax compr_err(buf, "Failed to get irq_count: %d\n", ret); 1699565ace46SCharles Keepax goto out; 1700565ace46SCharles Keepax } 1701565ace46SCharles Keepax 1702565ace46SCharles Keepax ret = wm_adsp_buffer_update_avail(buf); 1703565ace46SCharles Keepax if (ret < 0) { 17040d3fba3eSCharles Keepax compr_err(buf, "Error reading avail: %d\n", ret); 1705565ace46SCharles Keepax goto out; 1706565ace46SCharles Keepax } 1707565ace46SCharles Keepax 170820b7f7c5SCharles Keepax if (wm_adsp_fw[dsp->fw].voice_trigger && buf->irq_count == 2) 170920b7f7c5SCharles Keepax ret = WM_ADSP_COMPR_VOICE_TRIGGER; 171020b7f7c5SCharles Keepax 17115847609eSCharles Keepax out_notify: 1712c7dae7c4SCharles Keepax if (compr && compr->stream) 171383a40ce9SCharles Keepax snd_compr_fragment_elapsed(compr->stream); 17144f2d4eabSStuart Henderson } 171583a40ce9SCharles Keepax 1716565ace46SCharles Keepax out: 1717e1468202SSimon Trimmer mutex_unlock(&dsp->cs_dsp.pwr_lock); 1718565ace46SCharles Keepax 1719565ace46SCharles Keepax return ret; 1720565ace46SCharles Keepax } 1721565ace46SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_handle_irq); 1722565ace46SCharles Keepax 1723565ace46SCharles Keepax static int wm_adsp_buffer_reenable_irq(struct wm_adsp_compr_buf *buf) 1724565ace46SCharles Keepax { 1725565ace46SCharles Keepax if (buf->irq_count & 0x01) 1726565ace46SCharles Keepax return 0; 1727565ace46SCharles Keepax 17280d3fba3eSCharles Keepax compr_dbg(buf, "Enable IRQ(0x%x) for next fragment\n", buf->irq_count); 1729565ace46SCharles Keepax 1730565ace46SCharles Keepax buf->irq_count |= 0x01; 1731565ace46SCharles Keepax 1732565ace46SCharles Keepax return wm_adsp_buffer_write(buf, HOST_BUFFER_FIELD(irq_ack), 1733565ace46SCharles Keepax buf->irq_count); 1734565ace46SCharles Keepax } 1735565ace46SCharles Keepax 17363a5ccf25SKuninori Morimoto int wm_adsp_compr_pointer(struct snd_soc_component *component, 17373a5ccf25SKuninori Morimoto struct snd_compr_stream *stream, 1738565ace46SCharles Keepax struct snd_compr_tstamp *tstamp) 1739565ace46SCharles Keepax { 1740565ace46SCharles Keepax struct wm_adsp_compr *compr = stream->runtime->private_data; 1741565ace46SCharles Keepax struct wm_adsp *dsp = compr->dsp; 1742612047f0SCharles Keepax struct wm_adsp_compr_buf *buf; 1743565ace46SCharles Keepax int ret = 0; 1744565ace46SCharles Keepax 17450d3fba3eSCharles Keepax compr_dbg(compr, "Pointer request\n"); 1746565ace46SCharles Keepax 1747e1468202SSimon Trimmer mutex_lock(&dsp->cs_dsp.pwr_lock); 1748565ace46SCharles Keepax 1749612047f0SCharles Keepax buf = compr->buf; 1750612047f0SCharles Keepax 1751aa612f2bSCharles Keepax if (dsp->fatal_error || !buf || buf->error) { 17528d280664SCharles Keepax snd_compr_stop_error(stream, SNDRV_PCM_STATE_XRUN); 1753565ace46SCharles Keepax ret = -EIO; 1754565ace46SCharles Keepax goto out; 1755565ace46SCharles Keepax } 1756565ace46SCharles Keepax 1757565ace46SCharles Keepax if (buf->avail < wm_adsp_compr_frag_words(compr)) { 1758565ace46SCharles Keepax ret = wm_adsp_buffer_update_avail(buf); 1759565ace46SCharles Keepax if (ret < 0) { 17600d3fba3eSCharles Keepax compr_err(compr, "Error reading avail: %d\n", ret); 1761565ace46SCharles Keepax goto out; 1762565ace46SCharles Keepax } 1763565ace46SCharles Keepax 1764565ace46SCharles Keepax /* 1765565ace46SCharles Keepax * If we really have less than 1 fragment available tell the 1766565ace46SCharles Keepax * DSP to inform us once a whole fragment is available. 1767565ace46SCharles Keepax */ 1768565ace46SCharles Keepax if (buf->avail < wm_adsp_compr_frag_words(compr)) { 17695847609eSCharles Keepax ret = wm_adsp_buffer_get_error(buf); 17708d280664SCharles Keepax if (ret < 0) { 1771789b930aSCharles Keepax if (buf->error) 17728d280664SCharles Keepax snd_compr_stop_error(stream, 17738d280664SCharles Keepax SNDRV_PCM_STATE_XRUN); 17745847609eSCharles Keepax goto out; 17758d280664SCharles Keepax } 17765847609eSCharles Keepax 1777565ace46SCharles Keepax ret = wm_adsp_buffer_reenable_irq(buf); 1778565ace46SCharles Keepax if (ret < 0) { 17790d3fba3eSCharles Keepax compr_err(compr, "Failed to re-enable buffer IRQ: %d\n", 1780565ace46SCharles Keepax ret); 1781565ace46SCharles Keepax goto out; 1782565ace46SCharles Keepax } 1783565ace46SCharles Keepax } 1784565ace46SCharles Keepax } 1785565ace46SCharles Keepax 1786565ace46SCharles Keepax tstamp->copied_total = compr->copied_total; 17875beb8eeaSSimon Trimmer tstamp->copied_total += buf->avail * CS_DSP_DATA_WORD_SIZE; 1788da2b3358SCharles Keepax tstamp->sampling_rate = compr->sample_rate; 1789565ace46SCharles Keepax 1790565ace46SCharles Keepax out: 1791e1468202SSimon Trimmer mutex_unlock(&dsp->cs_dsp.pwr_lock); 1792565ace46SCharles Keepax 1793565ace46SCharles Keepax return ret; 1794565ace46SCharles Keepax } 1795565ace46SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_pointer); 1796565ace46SCharles Keepax 179783a40ce9SCharles Keepax static int wm_adsp_buffer_capture_block(struct wm_adsp_compr *compr, int target) 179883a40ce9SCharles Keepax { 179983a40ce9SCharles Keepax struct wm_adsp_compr_buf *buf = compr->buf; 180083a40ce9SCharles Keepax unsigned int adsp_addr; 180183a40ce9SCharles Keepax int mem_type, nwords, max_read; 1802cc7d6ce9SCharles Keepax int i, ret; 180383a40ce9SCharles Keepax 180483a40ce9SCharles Keepax /* Calculate read parameters */ 180583a40ce9SCharles Keepax for (i = 0; i < wm_adsp_fw[buf->dsp->fw].caps->num_regions; ++i) 180683a40ce9SCharles Keepax if (buf->read_index < buf->regions[i].cumulative_size) 180783a40ce9SCharles Keepax break; 180883a40ce9SCharles Keepax 180983a40ce9SCharles Keepax if (i == wm_adsp_fw[buf->dsp->fw].caps->num_regions) 181083a40ce9SCharles Keepax return -EINVAL; 181183a40ce9SCharles Keepax 181283a40ce9SCharles Keepax mem_type = buf->regions[i].mem_type; 181383a40ce9SCharles Keepax adsp_addr = buf->regions[i].base_addr + 181483a40ce9SCharles Keepax (buf->read_index - buf->regions[i].offset); 181583a40ce9SCharles Keepax 181683a40ce9SCharles Keepax max_read = wm_adsp_compr_frag_words(compr); 181783a40ce9SCharles Keepax nwords = buf->regions[i].cumulative_size - buf->read_index; 181883a40ce9SCharles Keepax 181983a40ce9SCharles Keepax if (nwords > target) 182083a40ce9SCharles Keepax nwords = target; 182183a40ce9SCharles Keepax if (nwords > buf->avail) 182283a40ce9SCharles Keepax nwords = buf->avail; 182383a40ce9SCharles Keepax if (nwords > max_read) 182483a40ce9SCharles Keepax nwords = max_read; 182583a40ce9SCharles Keepax if (!nwords) 182683a40ce9SCharles Keepax return 0; 182783a40ce9SCharles Keepax 182883a40ce9SCharles Keepax /* Read data from DSP */ 1829e1468202SSimon Trimmer ret = cs_dsp_read_raw_data_block(&buf->dsp->cs_dsp, mem_type, adsp_addr, 1830a0b653e8SRichard Fitzgerald nwords, (__be32 *)compr->raw_buf); 183183a40ce9SCharles Keepax if (ret < 0) 183283a40ce9SCharles Keepax return ret; 183383a40ce9SCharles Keepax 18345beb8eeaSSimon Trimmer cs_dsp_remove_padding(compr->raw_buf, nwords); 183583a40ce9SCharles Keepax 183683a40ce9SCharles Keepax /* update read index to account for words read */ 183783a40ce9SCharles Keepax buf->read_index += nwords; 183883a40ce9SCharles Keepax if (buf->read_index == wm_adsp_buffer_size(buf)) 183983a40ce9SCharles Keepax buf->read_index = 0; 184083a40ce9SCharles Keepax 184183a40ce9SCharles Keepax ret = wm_adsp_buffer_write(buf, HOST_BUFFER_FIELD(next_read_index), 184283a40ce9SCharles Keepax buf->read_index); 184383a40ce9SCharles Keepax if (ret < 0) 184483a40ce9SCharles Keepax return ret; 184583a40ce9SCharles Keepax 184683a40ce9SCharles Keepax /* update avail to account for words read */ 184783a40ce9SCharles Keepax buf->avail -= nwords; 184883a40ce9SCharles Keepax 184983a40ce9SCharles Keepax return nwords; 185083a40ce9SCharles Keepax } 185183a40ce9SCharles Keepax 185283a40ce9SCharles Keepax static int wm_adsp_compr_read(struct wm_adsp_compr *compr, 185383a40ce9SCharles Keepax char __user *buf, size_t count) 185483a40ce9SCharles Keepax { 1855aa612f2bSCharles Keepax struct wm_adsp *dsp = compr->dsp; 185683a40ce9SCharles Keepax int ntotal = 0; 185783a40ce9SCharles Keepax int nwords, nbytes; 185883a40ce9SCharles Keepax 18590d3fba3eSCharles Keepax compr_dbg(compr, "Requested read of %zu bytes\n", count); 186083a40ce9SCharles Keepax 1861aa612f2bSCharles Keepax if (dsp->fatal_error || !compr->buf || compr->buf->error) { 18628d280664SCharles Keepax snd_compr_stop_error(compr->stream, SNDRV_PCM_STATE_XRUN); 186383a40ce9SCharles Keepax return -EIO; 18648d280664SCharles Keepax } 186583a40ce9SCharles Keepax 18665beb8eeaSSimon Trimmer count /= CS_DSP_DATA_WORD_SIZE; 186783a40ce9SCharles Keepax 186883a40ce9SCharles Keepax do { 186983a40ce9SCharles Keepax nwords = wm_adsp_buffer_capture_block(compr, count); 187083a40ce9SCharles Keepax if (nwords < 0) { 18710d3fba3eSCharles Keepax compr_err(compr, "Failed to capture block: %d\n", 18720d3fba3eSCharles Keepax nwords); 187383a40ce9SCharles Keepax return nwords; 187483a40ce9SCharles Keepax } 187583a40ce9SCharles Keepax 18765beb8eeaSSimon Trimmer nbytes = nwords * CS_DSP_DATA_WORD_SIZE; 187783a40ce9SCharles Keepax 18780d3fba3eSCharles Keepax compr_dbg(compr, "Read %d bytes\n", nbytes); 187983a40ce9SCharles Keepax 188083a40ce9SCharles Keepax if (copy_to_user(buf + ntotal, compr->raw_buf, nbytes)) { 18810d3fba3eSCharles Keepax compr_err(compr, "Failed to copy data to user: %d, %d\n", 188283a40ce9SCharles Keepax ntotal, nbytes); 188383a40ce9SCharles Keepax return -EFAULT; 188483a40ce9SCharles Keepax } 188583a40ce9SCharles Keepax 188683a40ce9SCharles Keepax count -= nwords; 188783a40ce9SCharles Keepax ntotal += nbytes; 188883a40ce9SCharles Keepax } while (nwords > 0 && count > 0); 188983a40ce9SCharles Keepax 189083a40ce9SCharles Keepax compr->copied_total += ntotal; 189183a40ce9SCharles Keepax 189283a40ce9SCharles Keepax return ntotal; 189383a40ce9SCharles Keepax } 189483a40ce9SCharles Keepax 18953a5ccf25SKuninori Morimoto int wm_adsp_compr_copy(struct snd_soc_component *component, 18963a5ccf25SKuninori Morimoto struct snd_compr_stream *stream, char __user *buf, 189783a40ce9SCharles Keepax size_t count) 189883a40ce9SCharles Keepax { 189983a40ce9SCharles Keepax struct wm_adsp_compr *compr = stream->runtime->private_data; 190083a40ce9SCharles Keepax struct wm_adsp *dsp = compr->dsp; 190183a40ce9SCharles Keepax int ret; 190283a40ce9SCharles Keepax 1903e1468202SSimon Trimmer mutex_lock(&dsp->cs_dsp.pwr_lock); 190483a40ce9SCharles Keepax 190583a40ce9SCharles Keepax if (stream->direction == SND_COMPRESS_CAPTURE) 190683a40ce9SCharles Keepax ret = wm_adsp_compr_read(compr, buf, count); 190783a40ce9SCharles Keepax else 190883a40ce9SCharles Keepax ret = -ENOTSUPP; 190983a40ce9SCharles Keepax 1910e1468202SSimon Trimmer mutex_unlock(&dsp->cs_dsp.pwr_lock); 191183a40ce9SCharles Keepax 191283a40ce9SCharles Keepax return ret; 191383a40ce9SCharles Keepax } 191483a40ce9SCharles Keepax EXPORT_SYMBOL_GPL(wm_adsp_compr_copy); 191583a40ce9SCharles Keepax 1916e1468202SSimon Trimmer static void wm_adsp_fatal_error(struct cs_dsp *cs_dsp) 1917a2bcbc1bSCharles Keepax { 1918e1468202SSimon Trimmer struct wm_adsp *dsp = container_of(cs_dsp, struct wm_adsp, cs_dsp); 1919a2bcbc1bSCharles Keepax struct wm_adsp_compr *compr; 1920a2bcbc1bSCharles Keepax 1921a2bcbc1bSCharles Keepax dsp->fatal_error = true; 1922a2bcbc1bSCharles Keepax 1923a2bcbc1bSCharles Keepax list_for_each_entry(compr, &dsp->compr_list, list) { 1924aa612f2bSCharles Keepax if (compr->stream) 1925a2bcbc1bSCharles Keepax snd_compr_fragment_elapsed(compr->stream); 1926a2bcbc1bSCharles Keepax } 1927a2bcbc1bSCharles Keepax } 1928a2bcbc1bSCharles Keepax 192925ca837bSSimon Trimmer irqreturn_t wm_adsp2_bus_error(int irq, void *data) 193025ca837bSSimon Trimmer { 193125ca837bSSimon Trimmer struct wm_adsp *dsp = (struct wm_adsp *)data; 193225ca837bSSimon Trimmer 1933e1468202SSimon Trimmer cs_dsp_adsp2_bus_error(&dsp->cs_dsp); 1934a2225a6dSCharles Keepax 193551a2c944SMayuresh Kulkarni return IRQ_HANDLED; 193651a2c944SMayuresh Kulkarni } 193751a2c944SMayuresh Kulkarni EXPORT_SYMBOL_GPL(wm_adsp2_bus_error); 193851a2c944SMayuresh Kulkarni 193925ca837bSSimon Trimmer irqreturn_t wm_halo_bus_error(int irq, void *data) 194025ca837bSSimon Trimmer { 194125ca837bSSimon Trimmer struct wm_adsp *dsp = (struct wm_adsp *)data; 194225ca837bSSimon Trimmer 1943e1468202SSimon Trimmer cs_dsp_halo_bus_error(&dsp->cs_dsp); 19442ae58138SRichard Fitzgerald 19452ae58138SRichard Fitzgerald return IRQ_HANDLED; 19462ae58138SRichard Fitzgerald } 19472ae58138SRichard Fitzgerald EXPORT_SYMBOL_GPL(wm_halo_bus_error); 19482ae58138SRichard Fitzgerald 194925ca837bSSimon Trimmer irqreturn_t wm_halo_wdt_expire(int irq, void *data) 195025ca837bSSimon Trimmer { 195125ca837bSSimon Trimmer struct wm_adsp *dsp = data; 195225ca837bSSimon Trimmer 1953e1468202SSimon Trimmer cs_dsp_halo_wdt_expire(&dsp->cs_dsp); 19548bc144f9SStuart Henderson 19558bc144f9SStuart Henderson return IRQ_HANDLED; 19568bc144f9SStuart Henderson } 19578bc144f9SStuart Henderson EXPORT_SYMBOL_GPL(wm_halo_wdt_expire); 19588bc144f9SStuart Henderson 19592dd04464SSimon Trimmer static const struct cs_dsp_client_ops wm_adsp1_client_ops = { 19602dd04464SSimon Trimmer .control_add = wm_adsp_control_add, 19612dd04464SSimon Trimmer .control_remove = wm_adsp_control_remove, 19622dd04464SSimon Trimmer }; 19632dd04464SSimon Trimmer 19642dd04464SSimon Trimmer static const struct cs_dsp_client_ops wm_adsp2_client_ops = { 19652dd04464SSimon Trimmer .control_add = wm_adsp_control_add, 19662dd04464SSimon Trimmer .control_remove = wm_adsp_control_remove, 19672dd04464SSimon Trimmer .post_run = wm_adsp_event_post_run, 19682dd04464SSimon Trimmer .post_stop = wm_adsp_event_post_stop, 19692dd04464SSimon Trimmer .watchdog_expired = wm_adsp_fatal_error, 19702dd04464SSimon Trimmer }; 19712dd04464SSimon Trimmer 19720a37c6efSPraveen Diwakar MODULE_LICENSE("GPL v2"); 1973