brintos

brintos / linux-shallow public Read only

0
0
Text · 51.7 KiB · e692831 Raw
2102 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * wm_adsp.c  --  Wolfson ADSP support4 *5 * Copyright 2012 Wolfson Microelectronics plc6 *7 * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>8 */9 10#include <linux/array_size.h>11#include <linux/ctype.h>12#include <linux/module.h>13#include <linux/moduleparam.h>14#include <linux/init.h>15#include <linux/delay.h>16#include <linux/firmware.h>17#include <linux/list.h>18#include <linux/pm.h>19#include <linux/regmap.h>20#include <linux/regulator/consumer.h>21#include <linux/slab.h>22#include <linux/vmalloc.h>23#include <linux/workqueue.h>24#include <linux/debugfs.h>25#include <sound/core.h>26#include <sound/pcm.h>27#include <sound/pcm_params.h>28#include <sound/soc.h>29#include <sound/jack.h>30#include <sound/initval.h>31#include <sound/tlv.h>32 33#include "wm_adsp.h"34 35#define adsp_crit(_dsp, fmt, ...) \36	dev_crit(_dsp->cs_dsp.dev, "%s: " fmt, _dsp->cs_dsp.name, ##__VA_ARGS__)37#define adsp_err(_dsp, fmt, ...) \38	dev_err(_dsp->cs_dsp.dev, "%s: " fmt, _dsp->cs_dsp.name, ##__VA_ARGS__)39#define adsp_warn(_dsp, fmt, ...) \40	dev_warn(_dsp->cs_dsp.dev, "%s: " fmt, _dsp->cs_dsp.name, ##__VA_ARGS__)41#define adsp_info(_dsp, fmt, ...) \42	dev_info(_dsp->cs_dsp.dev, "%s: " fmt, _dsp->cs_dsp.name, ##__VA_ARGS__)43#define adsp_dbg(_dsp, fmt, ...) \44	dev_dbg(_dsp->cs_dsp.dev, "%s: " fmt, _dsp->cs_dsp.name, ##__VA_ARGS__)45 46#define compr_err(_obj, fmt, ...) \47	adsp_err(_obj->dsp, "%s: " fmt, _obj->name ? _obj->name : "legacy", \48		 ##__VA_ARGS__)49#define compr_dbg(_obj, fmt, ...) \50	adsp_dbg(_obj->dsp, "%s: " fmt, _obj->name ? _obj->name : "legacy", \51		 ##__VA_ARGS__)52 53#define ADSP_MAX_STD_CTRL_SIZE               51254 55static const struct cs_dsp_client_ops wm_adsp1_client_ops;56static const struct cs_dsp_client_ops wm_adsp2_client_ops;57 58#define WM_ADSP_FW_MBC_VSS  059#define WM_ADSP_FW_HIFI     160#define WM_ADSP_FW_TX       261#define WM_ADSP_FW_TX_SPK   362#define WM_ADSP_FW_RX       463#define WM_ADSP_FW_RX_ANC   564#define WM_ADSP_FW_CTRL     665#define WM_ADSP_FW_ASR      766#define WM_ADSP_FW_TRACE    867#define WM_ADSP_FW_SPK_PROT 968#define WM_ADSP_FW_SPK_CALI 1069#define WM_ADSP_FW_SPK_DIAG 1170#define WM_ADSP_FW_MISC     1271 72#define WM_ADSP_NUM_FW      1373 74static const char *wm_adsp_fw_text[WM_ADSP_NUM_FW] = {75	[WM_ADSP_FW_MBC_VSS] =  "MBC/VSS",76	[WM_ADSP_FW_HIFI] =     "MasterHiFi",77	[WM_ADSP_FW_TX] =       "Tx",78	[WM_ADSP_FW_TX_SPK] =   "Tx Speaker",79	[WM_ADSP_FW_RX] =       "Rx",80	[WM_ADSP_FW_RX_ANC] =   "Rx ANC",81	[WM_ADSP_FW_CTRL] =     "Voice Ctrl",82	[WM_ADSP_FW_ASR] =      "ASR Assist",83	[WM_ADSP_FW_TRACE] =    "Dbg Trace",84	[WM_ADSP_FW_SPK_PROT] = "Protection",85	[WM_ADSP_FW_SPK_CALI] = "Calibration",86	[WM_ADSP_FW_SPK_DIAG] = "Diagnostic",87	[WM_ADSP_FW_MISC] =     "Misc",88};89 90struct wm_adsp_system_config_xm_hdr {91	__be32 sys_enable;92	__be32 fw_id;93	__be32 fw_rev;94	__be32 boot_status;95	__be32 watchdog;96	__be32 dma_buffer_size;97	__be32 rdma[6];98	__be32 wdma[8];99	__be32 build_job_name[3];100	__be32 build_job_number;101} __packed;102 103struct wm_halo_system_config_xm_hdr {104	__be32 halo_heartbeat;105	__be32 build_job_name[3];106	__be32 build_job_number;107} __packed;108 109struct wm_adsp_alg_xm_struct {110	__be32 magic;111	__be32 smoothing;112	__be32 threshold;113	__be32 host_buf_ptr;114	__be32 start_seq;115	__be32 high_water_mark;116	__be32 low_water_mark;117	__be64 smoothed_power;118} __packed;119 120struct wm_adsp_host_buf_coeff_v1 {121	__be32 host_buf_ptr;		/* Host buffer pointer */122	__be32 versions;		/* Version numbers */123	__be32 name[4];			/* The buffer name */124} __packed;125 126struct wm_adsp_buffer {127	__be32 buf1_base;		/* Base addr of first buffer area */128	__be32 buf1_size;		/* Size of buf1 area in DSP words */129	__be32 buf2_base;		/* Base addr of 2nd buffer area */130	__be32 buf1_buf2_size;		/* Size of buf1+buf2 in DSP words */131	__be32 buf3_base;		/* Base addr of buf3 area */132	__be32 buf_total_size;		/* Size of buf1+buf2+buf3 in DSP words */133	__be32 high_water_mark;		/* Point at which IRQ is asserted */134	__be32 irq_count;		/* bits 1-31 count IRQ assertions */135	__be32 irq_ack;			/* acked IRQ count, bit 0 enables IRQ */136	__be32 next_write_index;	/* word index of next write */137	__be32 next_read_index;		/* word index of next read */138	__be32 error;			/* error if any */139	__be32 oldest_block_index;	/* word index of oldest surviving */140	__be32 requested_rewind;	/* how many blocks rewind was done */141	__be32 reserved_space;		/* internal */142	__be32 min_free;		/* min free space since stream start */143	__be32 blocks_written[2];	/* total blocks written (64 bit) */144	__be32 words_written[2];	/* total words written (64 bit) */145} __packed;146 147struct wm_adsp_compr;148 149struct wm_adsp_compr_buf {150	struct list_head list;151	struct wm_adsp *dsp;152	struct wm_adsp_compr *compr;153 154	struct wm_adsp_buffer_region *regions;155	u32 host_buf_ptr;156 157	u32 error;158	u32 irq_count;159	int read_index;160	int avail;161	int host_buf_mem_type;162 163	char *name;164};165 166struct wm_adsp_compr {167	struct list_head list;168	struct wm_adsp *dsp;169	struct wm_adsp_compr_buf *buf;170 171	struct snd_compr_stream *stream;172	struct snd_compressed_buffer size;173 174	u32 *raw_buf;175	unsigned int copied_total;176 177	unsigned int sample_rate;178 179	const char *name;180};181 182#define WM_ADSP_MIN_FRAGMENTS          1183#define WM_ADSP_MAX_FRAGMENTS          256184#define WM_ADSP_MIN_FRAGMENT_SIZE      (16 * CS_DSP_DATA_WORD_SIZE)185#define WM_ADSP_MAX_FRAGMENT_SIZE      (4096 * CS_DSP_DATA_WORD_SIZE)186 187#define WM_ADSP_ALG_XM_STRUCT_MAGIC    0x49aec7188 189#define HOST_BUFFER_FIELD(field) \190	(offsetof(struct wm_adsp_buffer, field) / sizeof(__be32))191 192#define ALG_XM_FIELD(field) \193	(offsetof(struct wm_adsp_alg_xm_struct, field) / sizeof(__be32))194 195#define HOST_BUF_COEFF_SUPPORTED_COMPAT_VER	1196 197#define HOST_BUF_COEFF_COMPAT_VER_MASK		0xFF00198#define HOST_BUF_COEFF_COMPAT_VER_SHIFT		8199 200static int wm_adsp_buffer_init(struct wm_adsp *dsp);201static int wm_adsp_buffer_free(struct wm_adsp *dsp);202 203struct wm_adsp_buffer_region {204	unsigned int offset;205	unsigned int cumulative_size;206	unsigned int mem_type;207	unsigned int base_addr;208};209 210struct wm_adsp_buffer_region_def {211	unsigned int mem_type;212	unsigned int base_offset;213	unsigned int size_offset;214};215 216static const struct wm_adsp_buffer_region_def default_regions[] = {217	{218		.mem_type = WMFW_ADSP2_XM,219		.base_offset = HOST_BUFFER_FIELD(buf1_base),220		.size_offset = HOST_BUFFER_FIELD(buf1_size),221	},222	{223		.mem_type = WMFW_ADSP2_XM,224		.base_offset = HOST_BUFFER_FIELD(buf2_base),225		.size_offset = HOST_BUFFER_FIELD(buf1_buf2_size),226	},227	{228		.mem_type = WMFW_ADSP2_YM,229		.base_offset = HOST_BUFFER_FIELD(buf3_base),230		.size_offset = HOST_BUFFER_FIELD(buf_total_size),231	},232};233 234struct wm_adsp_fw_caps {235	u32 id;236	struct snd_codec_desc desc;237	int num_regions;238	const struct wm_adsp_buffer_region_def *region_defs;239};240 241static const struct wm_adsp_fw_caps ctrl_caps[] = {242	{243		.id = SND_AUDIOCODEC_BESPOKE,244		.desc = {245			.max_ch = 8,246			.sample_rates = { 16000 },247			.num_sample_rates = 1,248			.formats = SNDRV_PCM_FMTBIT_S16_LE,249		},250		.num_regions = ARRAY_SIZE(default_regions),251		.region_defs = default_regions,252	},253};254 255static const struct wm_adsp_fw_caps trace_caps[] = {256	{257		.id = SND_AUDIOCODEC_BESPOKE,258		.desc = {259			.max_ch = 8,260			.sample_rates = {261				4000, 8000, 11025, 12000, 16000, 22050,262				24000, 32000, 44100, 48000, 64000, 88200,263				96000, 176400, 192000264			},265			.num_sample_rates = 15,266			.formats = SNDRV_PCM_FMTBIT_S16_LE,267		},268		.num_regions = ARRAY_SIZE(default_regions),269		.region_defs = default_regions,270	},271};272 273static const struct {274	const char *file;275	int compr_direction;276	int num_caps;277	const struct wm_adsp_fw_caps *caps;278	bool voice_trigger;279} wm_adsp_fw[WM_ADSP_NUM_FW] = {280	[WM_ADSP_FW_MBC_VSS] =  { .file = "mbc-vss" },281	[WM_ADSP_FW_HIFI] =     { .file = "hifi" },282	[WM_ADSP_FW_TX] =       { .file = "tx" },283	[WM_ADSP_FW_TX_SPK] =   { .file = "tx-spk" },284	[WM_ADSP_FW_RX] =       { .file = "rx" },285	[WM_ADSP_FW_RX_ANC] =   { .file = "rx-anc" },286	[WM_ADSP_FW_CTRL] =     {287		.file = "ctrl",288		.compr_direction = SND_COMPRESS_CAPTURE,289		.num_caps = ARRAY_SIZE(ctrl_caps),290		.caps = ctrl_caps,291		.voice_trigger = true,292	},293	[WM_ADSP_FW_ASR] =      { .file = "asr" },294	[WM_ADSP_FW_TRACE] =    {295		.file = "trace",296		.compr_direction = SND_COMPRESS_CAPTURE,297		.num_caps = ARRAY_SIZE(trace_caps),298		.caps = trace_caps,299	},300	[WM_ADSP_FW_SPK_PROT] = {301		.file = "spk-prot",302		.compr_direction = SND_COMPRESS_CAPTURE,303		.num_caps = ARRAY_SIZE(trace_caps),304		.caps = trace_caps,305	},306	[WM_ADSP_FW_SPK_CALI] = { .file = "spk-cali" },307	[WM_ADSP_FW_SPK_DIAG] = { .file = "spk-diag" },308	[WM_ADSP_FW_MISC] =     { .file = "misc" },309};310 311struct wm_coeff_ctl {312	const char *name;313	struct cs_dsp_coeff_ctl *cs_ctl;314	struct soc_bytes_ext bytes_ext;315	struct work_struct work;316};317 318int wm_adsp_fw_get(struct snd_kcontrol *kcontrol,319		   struct snd_ctl_elem_value *ucontrol)320{321	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);322	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;323	struct wm_adsp *dsp = snd_soc_component_get_drvdata(component);324 325	ucontrol->value.enumerated.item[0] = dsp[e->shift_l].fw;326 327	return 0;328}329EXPORT_SYMBOL_GPL(wm_adsp_fw_get);330 331int wm_adsp_fw_put(struct snd_kcontrol *kcontrol,332		   struct snd_ctl_elem_value *ucontrol)333{334	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);335	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;336	struct wm_adsp *dsp = snd_soc_component_get_drvdata(component);337	int ret = 1;338 339	if (ucontrol->value.enumerated.item[0] == dsp[e->shift_l].fw)340		return 0;341 342	if (ucontrol->value.enumerated.item[0] >= WM_ADSP_NUM_FW)343		return -EINVAL;344 345	mutex_lock(&dsp[e->shift_l].cs_dsp.pwr_lock);346 347	if (dsp[e->shift_l].cs_dsp.booted || !list_empty(&dsp[e->shift_l].compr_list))348		ret = -EBUSY;349	else350		dsp[e->shift_l].fw = ucontrol->value.enumerated.item[0];351 352	mutex_unlock(&dsp[e->shift_l].cs_dsp.pwr_lock);353 354	return ret;355}356EXPORT_SYMBOL_GPL(wm_adsp_fw_put);357 358const struct soc_enum wm_adsp_fw_enum[] = {359	SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),360	SOC_ENUM_SINGLE(0, 1, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),361	SOC_ENUM_SINGLE(0, 2, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),362	SOC_ENUM_SINGLE(0, 3, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),363	SOC_ENUM_SINGLE(0, 4, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),364	SOC_ENUM_SINGLE(0, 5, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),365	SOC_ENUM_SINGLE(0, 6, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),366};367EXPORT_SYMBOL_GPL(wm_adsp_fw_enum);368 369static inline struct wm_coeff_ctl *bytes_ext_to_ctl(struct soc_bytes_ext *ext)370{371	return container_of(ext, struct wm_coeff_ctl, bytes_ext);372}373 374static int wm_coeff_info(struct snd_kcontrol *kctl,375			 struct snd_ctl_elem_info *uinfo)376{377	struct soc_bytes_ext *bytes_ext =378		(struct soc_bytes_ext *)kctl->private_value;379	struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);380	struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl;381 382	switch (cs_ctl->type) {383	case WMFW_CTL_TYPE_ACKED:384		uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;385		uinfo->value.integer.min = CS_DSP_ACKED_CTL_MIN_VALUE;386		uinfo->value.integer.max = CS_DSP_ACKED_CTL_MAX_VALUE;387		uinfo->value.integer.step = 1;388		uinfo->count = 1;389		break;390	default:391		uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;392		uinfo->count = cs_ctl->len;393		break;394	}395 396	return 0;397}398 399static int wm_coeff_put(struct snd_kcontrol *kctl,400			struct snd_ctl_elem_value *ucontrol)401{402	struct soc_bytes_ext *bytes_ext =403		(struct soc_bytes_ext *)kctl->private_value;404	struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);405	struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl;406	char *p = ucontrol->value.bytes.data;407 408	return cs_dsp_coeff_lock_and_write_ctrl(cs_ctl, 0, p, cs_ctl->len);409}410 411static int wm_coeff_tlv_put(struct snd_kcontrol *kctl,412			    const unsigned int __user *bytes, unsigned int size)413{414	struct soc_bytes_ext *bytes_ext =415		(struct soc_bytes_ext *)kctl->private_value;416	struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);417	struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl;418	void *scratch;419	int ret = 0;420 421	scratch = vmalloc(size);422	if (!scratch)423		return -ENOMEM;424 425	if (copy_from_user(scratch, bytes, size))426		ret = -EFAULT;427	else428		ret = cs_dsp_coeff_lock_and_write_ctrl(cs_ctl, 0, scratch, size);429 430	vfree(scratch);431 432	return ret;433}434 435static int wm_coeff_put_acked(struct snd_kcontrol *kctl,436			      struct snd_ctl_elem_value *ucontrol)437{438	struct soc_bytes_ext *bytes_ext =439		(struct soc_bytes_ext *)kctl->private_value;440	struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);441	struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl;442	unsigned int val = ucontrol->value.integer.value[0];443	int ret;444 445	if (val == 0)446		return 0;	/* 0 means no event */447 448	mutex_lock(&cs_ctl->dsp->pwr_lock);449 450	if (cs_ctl->enabled)451		ret = cs_dsp_coeff_write_acked_control(cs_ctl, val);452	else453		ret = -EPERM;454 455	mutex_unlock(&cs_ctl->dsp->pwr_lock);456 457	if (ret < 0)458		return ret;459 460	return 1;461}462 463static int wm_coeff_get(struct snd_kcontrol *kctl,464			struct snd_ctl_elem_value *ucontrol)465{466	struct soc_bytes_ext *bytes_ext =467		(struct soc_bytes_ext *)kctl->private_value;468	struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);469	struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl;470	char *p = ucontrol->value.bytes.data;471 472	return cs_dsp_coeff_lock_and_read_ctrl(cs_ctl, 0, p, cs_ctl->len);473}474 475static int wm_coeff_tlv_get(struct snd_kcontrol *kctl,476			    unsigned int __user *bytes, unsigned int size)477{478	struct soc_bytes_ext *bytes_ext =479		(struct soc_bytes_ext *)kctl->private_value;480	struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);481	struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl;482	int ret = 0;483 484	mutex_lock(&cs_ctl->dsp->pwr_lock);485 486	ret = cs_dsp_coeff_read_ctrl(cs_ctl, 0, cs_ctl->cache, size);487 488	if (!ret && copy_to_user(bytes, cs_ctl->cache, size))489		ret = -EFAULT;490 491	mutex_unlock(&cs_ctl->dsp->pwr_lock);492 493	return ret;494}495 496static int wm_coeff_get_acked(struct snd_kcontrol *kcontrol,497			      struct snd_ctl_elem_value *ucontrol)498{499	/*500	 * Although it's not useful to read an acked control, we must satisfy501	 * user-side assumptions that all controls are readable and that a502	 * write of the same value should be filtered out (it's valid to send503	 * the same event number again to the firmware). We therefore return 0,504	 * meaning "no event" so valid event numbers will always be a change505	 */506	ucontrol->value.integer.value[0] = 0;507 508	return 0;509}510 511static unsigned int wmfw_convert_flags(unsigned int in, unsigned int len)512{513	unsigned int out, rd, wr, vol;514 515	if (len > ADSP_MAX_STD_CTRL_SIZE) {516		rd = SNDRV_CTL_ELEM_ACCESS_TLV_READ;517		wr = SNDRV_CTL_ELEM_ACCESS_TLV_WRITE;518		vol = SNDRV_CTL_ELEM_ACCESS_VOLATILE;519 520		out = SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;521	} else {522		rd = SNDRV_CTL_ELEM_ACCESS_READ;523		wr = SNDRV_CTL_ELEM_ACCESS_WRITE;524		vol = SNDRV_CTL_ELEM_ACCESS_VOLATILE;525 526		out = 0;527	}528 529	if (in) {530		out |= rd;531		if (in & WMFW_CTL_FLAG_WRITEABLE)532			out |= wr;533		if (in & WMFW_CTL_FLAG_VOLATILE)534			out |= vol;535	} else {536		out |= rd | wr | vol;537	}538 539	return out;540}541 542static void wm_adsp_ctl_work(struct work_struct *work)543{544	struct wm_coeff_ctl *ctl = container_of(work,545						struct wm_coeff_ctl,546						work);547	struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl;548	struct wm_adsp *dsp = container_of(cs_ctl->dsp,549					   struct wm_adsp,550					   cs_dsp);551	struct snd_kcontrol_new *kcontrol;552 553	kcontrol = kzalloc(sizeof(*kcontrol), GFP_KERNEL);554	if (!kcontrol)555		return;556 557	kcontrol->name = ctl->name;558	kcontrol->info = wm_coeff_info;559	kcontrol->iface = SNDRV_CTL_ELEM_IFACE_MIXER;560	kcontrol->tlv.c = snd_soc_bytes_tlv_callback;561	kcontrol->private_value = (unsigned long)&ctl->bytes_ext;562	kcontrol->access = wmfw_convert_flags(cs_ctl->flags, cs_ctl->len);563 564	switch (cs_ctl->type) {565	case WMFW_CTL_TYPE_ACKED:566		kcontrol->get = wm_coeff_get_acked;567		kcontrol->put = wm_coeff_put_acked;568		break;569	default:570		if (kcontrol->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) {571			ctl->bytes_ext.max = cs_ctl->len;572			ctl->bytes_ext.get = wm_coeff_tlv_get;573			ctl->bytes_ext.put = wm_coeff_tlv_put;574		} else {575			kcontrol->get = wm_coeff_get;576			kcontrol->put = wm_coeff_put;577		}578		break;579	}580 581	snd_soc_add_component_controls(dsp->component, kcontrol, 1);582 583	kfree(kcontrol);584}585 586int wm_adsp_control_add(struct cs_dsp_coeff_ctl *cs_ctl)587{588	struct wm_adsp *dsp = container_of(cs_ctl->dsp, struct wm_adsp, cs_dsp);589	struct cs_dsp *cs_dsp = &dsp->cs_dsp;590	struct wm_coeff_ctl *ctl;591	char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];592	const char *region_name;593	int ret;594 595	if (cs_ctl->flags & WMFW_CTL_FLAG_SYS)596		return 0;597 598	region_name = cs_dsp_mem_region_name(cs_ctl->alg_region.type);599	if (!region_name) {600		adsp_err(dsp, "Unknown region type: %d\n", cs_ctl->alg_region.type);601		return -EINVAL;602	}603 604	switch (cs_dsp->wmfw_ver) {605	case 0:606	case 1:607		ret = scnprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN,608				"%s %s %x", cs_dsp->name, region_name,609				cs_ctl->alg_region.alg);610		break;611	case 2:612		ret = scnprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN,613				"%s%c %.12s %x", cs_dsp->name, *region_name,614				wm_adsp_fw_text[dsp->fw], cs_ctl->alg_region.alg);615		break;616	default:617		ret = scnprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN,618				"%s %.12s %x", cs_dsp->name,619				wm_adsp_fw_text[dsp->fw], cs_ctl->alg_region.alg);620		break;621	}622 623	if (cs_ctl->subname) {624		int avail = SNDRV_CTL_ELEM_ID_NAME_MAXLEN - ret - 2;625		int skip = 0;626 627		if (dsp->component->name_prefix)628			avail -= strlen(dsp->component->name_prefix) + 1;629 630		/* Truncate the subname from the start if it is too long */631		if (cs_ctl->subname_len > avail)632			skip = cs_ctl->subname_len - avail;633 634		snprintf(name + ret, SNDRV_CTL_ELEM_ID_NAME_MAXLEN - ret,635			 " %.*s", cs_ctl->subname_len - skip, cs_ctl->subname + skip);636	}637 638	ctl = kzalloc(sizeof(*ctl), GFP_KERNEL);639	if (!ctl)640		return -ENOMEM;641	ctl->cs_ctl = cs_ctl;642 643	ctl->name = kmemdup(name, strlen(name) + 1, GFP_KERNEL);644	if (!ctl->name) {645		ret = -ENOMEM;646		goto err_ctl;647	}648 649	cs_ctl->priv = ctl;650 651	INIT_WORK(&ctl->work, wm_adsp_ctl_work);652	schedule_work(&ctl->work);653 654	return 0;655 656err_ctl:657	kfree(ctl);658 659	return ret;660}661EXPORT_SYMBOL_GPL(wm_adsp_control_add);662 663static int wm_adsp_control_add_cb(struct cs_dsp_coeff_ctl *cs_ctl)664{665	struct wm_adsp *dsp = container_of(cs_ctl->dsp, struct wm_adsp, cs_dsp);666 667	if (dsp->control_add)668		return (dsp->control_add)(dsp, cs_ctl);669	else670		return wm_adsp_control_add(cs_ctl);671}672 673static void wm_adsp_control_remove(struct cs_dsp_coeff_ctl *cs_ctl)674{675	struct wm_coeff_ctl *ctl = cs_ctl->priv;676 677	cancel_work_sync(&ctl->work);678 679	kfree(ctl->name);680	kfree(ctl);681}682 683int wm_adsp_write_ctl(struct wm_adsp *dsp, const char *name, int type,684		      unsigned int alg, void *buf, size_t len)685{686	struct cs_dsp_coeff_ctl *cs_ctl;687	int ret;688 689	mutex_lock(&dsp->cs_dsp.pwr_lock);690	cs_ctl = cs_dsp_get_ctl(&dsp->cs_dsp, name, type, alg);691	ret = cs_dsp_coeff_write_ctrl(cs_ctl, 0, buf, len);692	mutex_unlock(&dsp->cs_dsp.pwr_lock);693 694	if (ret < 0)695		return ret;696 697	return 0;698}699EXPORT_SYMBOL_GPL(wm_adsp_write_ctl);700 701int wm_adsp_read_ctl(struct wm_adsp *dsp, const char *name, int type,702		     unsigned int alg, void *buf, size_t len)703{704	int ret;705 706	mutex_lock(&dsp->cs_dsp.pwr_lock);707	ret = cs_dsp_coeff_read_ctrl(cs_dsp_get_ctl(&dsp->cs_dsp, name, type, alg),708				     0, buf, len);709	mutex_unlock(&dsp->cs_dsp.pwr_lock);710 711	return ret;712}713EXPORT_SYMBOL_GPL(wm_adsp_read_ctl);714 715static void wm_adsp_release_firmware_files(struct wm_adsp *dsp,716					   const struct firmware *wmfw_firmware,717					   char *wmfw_filename,718					   const struct firmware *coeff_firmware,719					   char *coeff_filename)720{721	if (wmfw_firmware)722		release_firmware(wmfw_firmware);723	kfree(wmfw_filename);724 725	if (coeff_firmware)726		release_firmware(coeff_firmware);727	kfree(coeff_filename);728}729 730static int wm_adsp_request_firmware_file(struct wm_adsp *dsp,731					 const struct firmware **firmware, char **filename,732					 const char *dir, const char *system_name,733					 const char *asoc_component_prefix,734					 const char *filetype)735{736	struct cs_dsp *cs_dsp = &dsp->cs_dsp;737	const char *fwf;738	char *s, c;739	int ret = 0;740 741	if (dsp->fwf_name)742		fwf = dsp->fwf_name;743	else744		fwf = dsp->cs_dsp.name;745 746	if (system_name && asoc_component_prefix)747		*filename = kasprintf(GFP_KERNEL, "%s%s-%s-%s-%s-%s.%s", dir, dsp->part,748				      fwf, wm_adsp_fw[dsp->fw].file, system_name,749				      asoc_component_prefix, filetype);750	else if (system_name)751		*filename = kasprintf(GFP_KERNEL, "%s%s-%s-%s-%s.%s", dir, dsp->part,752				      fwf, wm_adsp_fw[dsp->fw].file, system_name,753				      filetype);754	else755		*filename = kasprintf(GFP_KERNEL, "%s%s-%s-%s.%s", dir, dsp->part, fwf,756				      wm_adsp_fw[dsp->fw].file, filetype);757 758	if (*filename == NULL)759		return -ENOMEM;760 761	/*762	 * Make sure that filename is lower-case and any non alpha-numeric763	 * characters except full stop and forward slash are replaced with764	 * hyphens.765	 */766	s = *filename;767	while (*s) {768		c = *s;769		if (isalnum(c))770			*s = tolower(c);771		else if ((c != '.') && (c != '/'))772			*s = '-';773		s++;774	}775 776	ret = firmware_request_nowarn(firmware, *filename, cs_dsp->dev);777	if (ret != 0) {778		adsp_dbg(dsp, "Failed to request '%s'\n", *filename);779		kfree(*filename);780		*filename = NULL;781	} else {782		adsp_dbg(dsp, "Found '%s'\n", *filename);783	}784 785	return ret;786}787 788static const char *cirrus_dir = "cirrus/";789static int wm_adsp_request_firmware_files(struct wm_adsp *dsp,790					  const struct firmware **wmfw_firmware,791					  char **wmfw_filename,792					  const struct firmware **coeff_firmware,793					  char **coeff_filename)794{795	const char *system_name = dsp->system_name;796	const char *asoc_component_prefix = dsp->component->name_prefix;797	int ret = 0;798 799	if (system_name && asoc_component_prefix) {800		if (!wm_adsp_request_firmware_file(dsp, wmfw_firmware, wmfw_filename,801						   cirrus_dir, system_name,802						   asoc_component_prefix, "wmfw")) {803			wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename,804						      cirrus_dir, system_name,805						      asoc_component_prefix, "bin");806			return 0;807		}808	}809 810	if (system_name) {811		if (!wm_adsp_request_firmware_file(dsp, wmfw_firmware, wmfw_filename,812						   cirrus_dir, system_name,813						   NULL, "wmfw")) {814			if (asoc_component_prefix)815				wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename,816							      cirrus_dir, system_name,817							      asoc_component_prefix, "bin");818 819			if (!*coeff_firmware)820				wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename,821							      cirrus_dir, system_name,822							      NULL, "bin");823			return 0;824		}825	}826 827	/* Check system-specific bin without wmfw before falling back to generic */828	if (dsp->wmfw_optional && system_name) {829		if (asoc_component_prefix)830			wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename,831						      cirrus_dir, system_name,832						      asoc_component_prefix, "bin");833 834		if (!*coeff_firmware)835			wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename,836						      cirrus_dir, system_name,837						      NULL, "bin");838 839		if (*coeff_firmware)840			return 0;841	}842 843	/* Check legacy location */844	if (!wm_adsp_request_firmware_file(dsp, wmfw_firmware, wmfw_filename,845					   "", NULL, NULL, "wmfw")) {846		wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename,847					      "", NULL, NULL, "bin");848		return 0;849	}850 851	/* Fall back to generic wmfw and optional matching bin */852	ret = wm_adsp_request_firmware_file(dsp, wmfw_firmware, wmfw_filename,853					    cirrus_dir, NULL, NULL, "wmfw");854	if (!ret || dsp->wmfw_optional) {855		wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename,856					      cirrus_dir, NULL, NULL, "bin");857		return 0;858	}859 860	adsp_err(dsp, "Failed to request firmware <%s>%s-%s-%s<-%s<%s>>.wmfw\n",861		 cirrus_dir, dsp->part,862		 dsp->fwf_name ? dsp->fwf_name : dsp->cs_dsp.name,863		 wm_adsp_fw[dsp->fw].file, system_name, asoc_component_prefix);864 865	return -ENOENT;866}867 868static int wm_adsp_common_init(struct wm_adsp *dsp)869{870	INIT_LIST_HEAD(&dsp->compr_list);871	INIT_LIST_HEAD(&dsp->buffer_list);872 873	return 0;874}875 876int wm_adsp1_init(struct wm_adsp *dsp)877{878	int ret;879 880	dsp->cs_dsp.client_ops = &wm_adsp1_client_ops;881 882	ret = cs_dsp_adsp1_init(&dsp->cs_dsp);883	if (ret)884		return ret;885 886	return wm_adsp_common_init(dsp);887}888EXPORT_SYMBOL_GPL(wm_adsp1_init);889 890int wm_adsp1_event(struct snd_soc_dapm_widget *w,891		   struct snd_kcontrol *kcontrol,892		   int event)893{894	struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);895	struct wm_adsp *dsps = snd_soc_component_get_drvdata(component);896	struct wm_adsp *dsp = &dsps[w->shift];897	int ret = 0;898	char *wmfw_filename = NULL;899	const struct firmware *wmfw_firmware = NULL;900	char *coeff_filename = NULL;901	const struct firmware *coeff_firmware = NULL;902 903	dsp->component = component;904 905	switch (event) {906	case SND_SOC_DAPM_POST_PMU:907		ret = wm_adsp_request_firmware_files(dsp,908						     &wmfw_firmware, &wmfw_filename,909						     &coeff_firmware, &coeff_filename);910		if (ret)911			break;912 913		ret = cs_dsp_adsp1_power_up(&dsp->cs_dsp,914					    wmfw_firmware, wmfw_filename,915					    coeff_firmware, coeff_filename,916					    wm_adsp_fw_text[dsp->fw]);917 918		wm_adsp_release_firmware_files(dsp,919					       wmfw_firmware, wmfw_filename,920					       coeff_firmware, coeff_filename);921		break;922	case SND_SOC_DAPM_PRE_PMD:923		cs_dsp_adsp1_power_down(&dsp->cs_dsp);924		break;925	default:926		break;927	}928 929	return ret;930}931EXPORT_SYMBOL_GPL(wm_adsp1_event);932 933int wm_adsp2_set_dspclk(struct snd_soc_dapm_widget *w, unsigned int freq)934{935	struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);936	struct wm_adsp *dsps = snd_soc_component_get_drvdata(component);937	struct wm_adsp *dsp = &dsps[w->shift];938 939	return cs_dsp_set_dspclk(&dsp->cs_dsp, freq);940}941EXPORT_SYMBOL_GPL(wm_adsp2_set_dspclk);942 943int wm_adsp2_preloader_get(struct snd_kcontrol *kcontrol,944			   struct snd_ctl_elem_value *ucontrol)945{946	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);947	struct wm_adsp *dsps = snd_soc_component_get_drvdata(component);948	struct soc_mixer_control *mc =949		(struct soc_mixer_control *)kcontrol->private_value;950	struct wm_adsp *dsp = &dsps[mc->shift - 1];951 952	ucontrol->value.integer.value[0] = dsp->preloaded;953 954	return 0;955}956EXPORT_SYMBOL_GPL(wm_adsp2_preloader_get);957 958int wm_adsp2_preloader_put(struct snd_kcontrol *kcontrol,959			   struct snd_ctl_elem_value *ucontrol)960{961	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);962	struct wm_adsp *dsps = snd_soc_component_get_drvdata(component);963	struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);964	struct soc_mixer_control *mc =965		(struct soc_mixer_control *)kcontrol->private_value;966	struct wm_adsp *dsp = &dsps[mc->shift - 1];967	char preload[32];968 969	if (dsp->preloaded == ucontrol->value.integer.value[0])970		return 0;971 972	snprintf(preload, ARRAY_SIZE(preload), "%s Preload", dsp->cs_dsp.name);973 974	if (ucontrol->value.integer.value[0] || dsp->toggle_preload)975		snd_soc_component_force_enable_pin(component, preload);976	else977		snd_soc_component_disable_pin(component, preload);978 979	snd_soc_dapm_sync(dapm);980 981	flush_work(&dsp->boot_work);982 983	dsp->preloaded = ucontrol->value.integer.value[0];984 985	if (dsp->toggle_preload) {986		snd_soc_component_disable_pin(component, preload);987		snd_soc_dapm_sync(dapm);988	}989 990	return 1;991}992EXPORT_SYMBOL_GPL(wm_adsp2_preloader_put);993 994int wm_adsp_power_up(struct wm_adsp *dsp, bool load_firmware)995{996	int ret = 0;997	char *wmfw_filename = NULL;998	const struct firmware *wmfw_firmware = NULL;999	char *coeff_filename = NULL;1000	const struct firmware *coeff_firmware = NULL;1001 1002	if (load_firmware) {1003		ret = wm_adsp_request_firmware_files(dsp,1004						     &wmfw_firmware, &wmfw_filename,1005						     &coeff_firmware, &coeff_filename);1006		if (ret)1007			return ret;1008	}1009 1010	ret = cs_dsp_power_up(&dsp->cs_dsp,1011			      wmfw_firmware, wmfw_filename,1012			      coeff_firmware, coeff_filename,1013			      wm_adsp_fw_text[dsp->fw]);1014 1015	wm_adsp_release_firmware_files(dsp,1016				       wmfw_firmware, wmfw_filename,1017				       coeff_firmware, coeff_filename);1018 1019	return ret;1020}1021EXPORT_SYMBOL_GPL(wm_adsp_power_up);1022 1023void wm_adsp_power_down(struct wm_adsp *dsp)1024{1025	cs_dsp_power_down(&dsp->cs_dsp);1026}1027EXPORT_SYMBOL_GPL(wm_adsp_power_down);1028 1029static void wm_adsp_boot_work(struct work_struct *work)1030{1031	struct wm_adsp *dsp = container_of(work,1032					   struct wm_adsp,1033					   boot_work);1034 1035	wm_adsp_power_up(dsp, true);1036}1037 1038int wm_adsp_early_event(struct snd_soc_dapm_widget *w,1039			struct snd_kcontrol *kcontrol, int event)1040{1041	struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);1042	struct wm_adsp *dsps = snd_soc_component_get_drvdata(component);1043	struct wm_adsp *dsp = &dsps[w->shift];1044 1045	switch (event) {1046	case SND_SOC_DAPM_PRE_PMU:1047		queue_work(system_unbound_wq, &dsp->boot_work);1048		break;1049	case SND_SOC_DAPM_PRE_PMD:1050		wm_adsp_power_down(dsp);1051		break;1052	default:1053		break;1054	}1055 1056	return 0;1057}1058EXPORT_SYMBOL_GPL(wm_adsp_early_event);1059 1060static int wm_adsp_pre_run(struct cs_dsp *cs_dsp)1061{1062	struct wm_adsp *dsp = container_of(cs_dsp, struct wm_adsp, cs_dsp);1063 1064	if (!dsp->pre_run)1065		return 0;1066 1067	return (*dsp->pre_run)(dsp);1068}1069 1070static int wm_adsp_event_post_run(struct cs_dsp *cs_dsp)1071{1072	struct wm_adsp *dsp = container_of(cs_dsp, struct wm_adsp, cs_dsp);1073 1074	if (wm_adsp_fw[dsp->fw].num_caps != 0)1075		return wm_adsp_buffer_init(dsp);1076 1077	return 0;1078}1079 1080static void wm_adsp_event_post_stop(struct cs_dsp *cs_dsp)1081{1082	struct wm_adsp *dsp = container_of(cs_dsp, struct wm_adsp, cs_dsp);1083 1084	if (wm_adsp_fw[dsp->fw].num_caps != 0)1085		wm_adsp_buffer_free(dsp);1086 1087	dsp->fatal_error = false;1088}1089 1090int wm_adsp_run(struct wm_adsp *dsp)1091{1092	flush_work(&dsp->boot_work);1093 1094	return cs_dsp_run(&dsp->cs_dsp);1095}1096EXPORT_SYMBOL_GPL(wm_adsp_run);1097 1098void wm_adsp_stop(struct wm_adsp *dsp)1099{1100	cs_dsp_stop(&dsp->cs_dsp);1101}1102EXPORT_SYMBOL_GPL(wm_adsp_stop);1103 1104int wm_adsp_event(struct snd_soc_dapm_widget *w,1105		  struct snd_kcontrol *kcontrol, int event)1106{1107	struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);1108	struct wm_adsp *dsps = snd_soc_component_get_drvdata(component);1109	struct wm_adsp *dsp = &dsps[w->shift];1110 1111	switch (event) {1112	case SND_SOC_DAPM_POST_PMU:1113		return wm_adsp_run(dsp);1114	case SND_SOC_DAPM_PRE_PMD:1115		wm_adsp_stop(dsp);1116		return 0;1117	default:1118		return 0;1119	}1120}1121EXPORT_SYMBOL_GPL(wm_adsp_event);1122 1123int wm_adsp2_component_probe(struct wm_adsp *dsp, struct snd_soc_component *component)1124{1125	char preload[32];1126 1127	if (!dsp->cs_dsp.no_core_startstop) {1128		snprintf(preload, ARRAY_SIZE(preload), "%s Preload", dsp->cs_dsp.name);1129		snd_soc_component_disable_pin(component, preload);1130	}1131 1132	cs_dsp_init_debugfs(&dsp->cs_dsp, component->debugfs_root);1133 1134	dsp->component = component;1135 1136	return 0;1137}1138EXPORT_SYMBOL_GPL(wm_adsp2_component_probe);1139 1140int wm_adsp2_component_remove(struct wm_adsp *dsp, struct snd_soc_component *component)1141{1142	cs_dsp_cleanup_debugfs(&dsp->cs_dsp);1143 1144	return 0;1145}1146EXPORT_SYMBOL_GPL(wm_adsp2_component_remove);1147 1148int wm_adsp2_init(struct wm_adsp *dsp)1149{1150	int ret;1151 1152	INIT_WORK(&dsp->boot_work, wm_adsp_boot_work);1153 1154	dsp->sys_config_size = sizeof(struct wm_adsp_system_config_xm_hdr);1155	dsp->cs_dsp.client_ops = &wm_adsp2_client_ops;1156 1157	ret = cs_dsp_adsp2_init(&dsp->cs_dsp);1158	if (ret)1159		return ret;1160 1161	return wm_adsp_common_init(dsp);1162}1163EXPORT_SYMBOL_GPL(wm_adsp2_init);1164 1165int wm_halo_init(struct wm_adsp *dsp)1166{1167	int ret;1168 1169	INIT_WORK(&dsp->boot_work, wm_adsp_boot_work);1170 1171	dsp->sys_config_size = sizeof(struct wm_halo_system_config_xm_hdr);1172	dsp->cs_dsp.client_ops = &wm_adsp2_client_ops;1173 1174	ret = cs_dsp_halo_init(&dsp->cs_dsp);1175	if (ret)1176		return ret;1177 1178	return wm_adsp_common_init(dsp);1179}1180EXPORT_SYMBOL_GPL(wm_halo_init);1181 1182void wm_adsp2_remove(struct wm_adsp *dsp)1183{1184	cs_dsp_remove(&dsp->cs_dsp);1185}1186EXPORT_SYMBOL_GPL(wm_adsp2_remove);1187 1188static inline int wm_adsp_compr_attached(struct wm_adsp_compr *compr)1189{1190	return compr->buf != NULL;1191}1192 1193static int wm_adsp_compr_attach(struct wm_adsp_compr *compr)1194{1195	struct wm_adsp_compr_buf *buf = NULL, *tmp;1196 1197	if (compr->dsp->fatal_error)1198		return -EINVAL;1199 1200	list_for_each_entry(tmp, &compr->dsp->buffer_list, list) {1201		if (!tmp->name || !strcmp(compr->name, tmp->name)) {1202			buf = tmp;1203			break;1204		}1205	}1206 1207	if (!buf)1208		return -EINVAL;1209 1210	compr->buf = buf;1211	buf->compr = compr;1212 1213	return 0;1214}1215 1216static void wm_adsp_compr_detach(struct wm_adsp_compr *compr)1217{1218	if (!compr)1219		return;1220 1221	/* Wake the poll so it can see buffer is no longer attached */1222	if (compr->stream)1223		snd_compr_fragment_elapsed(compr->stream);1224 1225	if (wm_adsp_compr_attached(compr)) {1226		compr->buf->compr = NULL;1227		compr->buf = NULL;1228	}1229}1230 1231int wm_adsp_compr_open(struct wm_adsp *dsp, struct snd_compr_stream *stream)1232{1233	struct wm_adsp_compr *compr, *tmp;1234	struct snd_soc_pcm_runtime *rtd = stream->private_data;1235	int ret = 0;1236 1237	mutex_lock(&dsp->cs_dsp.pwr_lock);1238 1239	if (wm_adsp_fw[dsp->fw].num_caps == 0) {1240		adsp_err(dsp, "%s: Firmware does not support compressed API\n",1241			 snd_soc_rtd_to_codec(rtd, 0)->name);1242		ret = -ENXIO;1243		goto out;1244	}1245 1246	if (wm_adsp_fw[dsp->fw].compr_direction != stream->direction) {1247		adsp_err(dsp, "%s: Firmware does not support stream direction\n",1248			 snd_soc_rtd_to_codec(rtd, 0)->name);1249		ret = -EINVAL;1250		goto out;1251	}1252 1253	list_for_each_entry(tmp, &dsp->compr_list, list) {1254		if (!strcmp(tmp->name, snd_soc_rtd_to_codec(rtd, 0)->name)) {1255			adsp_err(dsp, "%s: Only a single stream supported per dai\n",1256				 snd_soc_rtd_to_codec(rtd, 0)->name);1257			ret = -EBUSY;1258			goto out;1259		}1260	}1261 1262	compr = kzalloc(sizeof(*compr), GFP_KERNEL);1263	if (!compr) {1264		ret = -ENOMEM;1265		goto out;1266	}1267 1268	compr->dsp = dsp;1269	compr->stream = stream;1270	compr->name = snd_soc_rtd_to_codec(rtd, 0)->name;1271 1272	list_add_tail(&compr->list, &dsp->compr_list);1273 1274	stream->runtime->private_data = compr;1275 1276out:1277	mutex_unlock(&dsp->cs_dsp.pwr_lock);1278 1279	return ret;1280}1281EXPORT_SYMBOL_GPL(wm_adsp_compr_open);1282 1283int wm_adsp_compr_free(struct snd_soc_component *component,1284		       struct snd_compr_stream *stream)1285{1286	struct wm_adsp_compr *compr = stream->runtime->private_data;1287	struct wm_adsp *dsp = compr->dsp;1288 1289	mutex_lock(&dsp->cs_dsp.pwr_lock);1290 1291	wm_adsp_compr_detach(compr);1292	list_del(&compr->list);1293 1294	kfree(compr->raw_buf);1295	kfree(compr);1296 1297	mutex_unlock(&dsp->cs_dsp.pwr_lock);1298 1299	return 0;1300}1301EXPORT_SYMBOL_GPL(wm_adsp_compr_free);1302 1303static int wm_adsp_compr_check_params(struct snd_compr_stream *stream,1304				      struct snd_compr_params *params)1305{1306	struct wm_adsp_compr *compr = stream->runtime->private_data;1307	struct wm_adsp *dsp = compr->dsp;1308	const struct wm_adsp_fw_caps *caps;1309	const struct snd_codec_desc *desc;1310	int i, j;1311 1312	if (params->buffer.fragment_size < WM_ADSP_MIN_FRAGMENT_SIZE ||1313	    params->buffer.fragment_size > WM_ADSP_MAX_FRAGMENT_SIZE ||1314	    params->buffer.fragments < WM_ADSP_MIN_FRAGMENTS ||1315	    params->buffer.fragments > WM_ADSP_MAX_FRAGMENTS ||1316	    params->buffer.fragment_size % CS_DSP_DATA_WORD_SIZE) {1317		compr_err(compr, "Invalid buffer fragsize=%d fragments=%d\n",1318			  params->buffer.fragment_size,1319			  params->buffer.fragments);1320 1321		return -EINVAL;1322	}1323 1324	for (i = 0; i < wm_adsp_fw[dsp->fw].num_caps; i++) {1325		caps = &wm_adsp_fw[dsp->fw].caps[i];1326		desc = &caps->desc;1327 1328		if (caps->id != params->codec.id)1329			continue;1330 1331		if (stream->direction == SND_COMPRESS_PLAYBACK) {1332			if (desc->max_ch < params->codec.ch_out)1333				continue;1334		} else {1335			if (desc->max_ch < params->codec.ch_in)1336				continue;1337		}1338 1339		if (!(desc->formats & (1 << params->codec.format)))1340			continue;1341 1342		for (j = 0; j < desc->num_sample_rates; ++j)1343			if (desc->sample_rates[j] == params->codec.sample_rate)1344				return 0;1345	}1346 1347	compr_err(compr, "Invalid params id=%u ch=%u,%u rate=%u fmt=%u\n",1348		  params->codec.id, params->codec.ch_in, params->codec.ch_out,1349		  params->codec.sample_rate, params->codec.format);1350	return -EINVAL;1351}1352 1353static inline unsigned int wm_adsp_compr_frag_words(struct wm_adsp_compr *compr)1354{1355	return compr->size.fragment_size / CS_DSP_DATA_WORD_SIZE;1356}1357 1358int wm_adsp_compr_set_params(struct snd_soc_component *component,1359			     struct snd_compr_stream *stream,1360			     struct snd_compr_params *params)1361{1362	struct wm_adsp_compr *compr = stream->runtime->private_data;1363	unsigned int size;1364	int ret;1365 1366	ret = wm_adsp_compr_check_params(stream, params);1367	if (ret)1368		return ret;1369 1370	compr->size = params->buffer;1371 1372	compr_dbg(compr, "fragment_size=%d fragments=%d\n",1373		  compr->size.fragment_size, compr->size.fragments);1374 1375	size = wm_adsp_compr_frag_words(compr) * sizeof(*compr->raw_buf);1376	compr->raw_buf = kmalloc(size, GFP_DMA | GFP_KERNEL);1377	if (!compr->raw_buf)1378		return -ENOMEM;1379 1380	compr->sample_rate = params->codec.sample_rate;1381 1382	return 0;1383}1384EXPORT_SYMBOL_GPL(wm_adsp_compr_set_params);1385 1386int wm_adsp_compr_get_caps(struct snd_soc_component *component,1387			   struct snd_compr_stream *stream,1388			   struct snd_compr_caps *caps)1389{1390	struct wm_adsp_compr *compr = stream->runtime->private_data;1391	int fw = compr->dsp->fw;1392	int i;1393 1394	if (wm_adsp_fw[fw].caps) {1395		for (i = 0; i < wm_adsp_fw[fw].num_caps; i++)1396			caps->codecs[i] = wm_adsp_fw[fw].caps[i].id;1397 1398		caps->num_codecs = i;1399		caps->direction = wm_adsp_fw[fw].compr_direction;1400 1401		caps->min_fragment_size = WM_ADSP_MIN_FRAGMENT_SIZE;1402		caps->max_fragment_size = WM_ADSP_MAX_FRAGMENT_SIZE;1403		caps->min_fragments = WM_ADSP_MIN_FRAGMENTS;1404		caps->max_fragments = WM_ADSP_MAX_FRAGMENTS;1405	}1406 1407	return 0;1408}1409EXPORT_SYMBOL_GPL(wm_adsp_compr_get_caps);1410 1411static inline int wm_adsp_buffer_read(struct wm_adsp_compr_buf *buf,1412				      unsigned int field_offset, u32 *data)1413{1414	return cs_dsp_read_data_word(&buf->dsp->cs_dsp, buf->host_buf_mem_type,1415				     buf->host_buf_ptr + field_offset, data);1416}1417 1418static inline int wm_adsp_buffer_write(struct wm_adsp_compr_buf *buf,1419				       unsigned int field_offset, u32 data)1420{1421	return cs_dsp_write_data_word(&buf->dsp->cs_dsp, buf->host_buf_mem_type,1422				      buf->host_buf_ptr + field_offset,1423				      data);1424}1425 1426static int wm_adsp_buffer_populate(struct wm_adsp_compr_buf *buf)1427{1428	const struct wm_adsp_fw_caps *caps = wm_adsp_fw[buf->dsp->fw].caps;1429	struct wm_adsp_buffer_region *region;1430	u32 offset = 0;1431	int i, ret;1432 1433	buf->regions = kcalloc(caps->num_regions, sizeof(*buf->regions),1434			       GFP_KERNEL);1435	if (!buf->regions)1436		return -ENOMEM;1437 1438	for (i = 0; i < caps->num_regions; ++i) {1439		region = &buf->regions[i];1440 1441		region->offset = offset;1442		region->mem_type = caps->region_defs[i].mem_type;1443 1444		ret = wm_adsp_buffer_read(buf, caps->region_defs[i].base_offset,1445					  &region->base_addr);1446		if (ret < 0)1447			goto err;1448 1449		ret = wm_adsp_buffer_read(buf, caps->region_defs[i].size_offset,1450					  &offset);1451		if (ret < 0)1452			goto err;1453 1454		region->cumulative_size = offset;1455 1456		compr_dbg(buf,1457			  "region=%d type=%d base=%08x off=%08x size=%08x\n",1458			  i, region->mem_type, region->base_addr,1459			  region->offset, region->cumulative_size);1460	}1461 1462	return 0;1463 1464err:1465	kfree(buf->regions);1466	return ret;1467}1468 1469static void wm_adsp_buffer_clear(struct wm_adsp_compr_buf *buf)1470{1471	buf->irq_count = 0xFFFFFFFF;1472	buf->read_index = -1;1473	buf->avail = 0;1474}1475 1476static struct wm_adsp_compr_buf *wm_adsp_buffer_alloc(struct wm_adsp *dsp)1477{1478	struct wm_adsp_compr_buf *buf;1479 1480	buf = kzalloc(sizeof(*buf), GFP_KERNEL);1481	if (!buf)1482		return NULL;1483 1484	buf->dsp = dsp;1485 1486	wm_adsp_buffer_clear(buf);1487 1488	return buf;1489}1490 1491static int wm_adsp_buffer_parse_legacy(struct wm_adsp *dsp)1492{1493	struct cs_dsp_alg_region *alg_region;1494	struct wm_adsp_compr_buf *buf;1495	u32 xmalg, addr, magic;1496	int i, ret;1497 1498	alg_region = cs_dsp_find_alg_region(&dsp->cs_dsp, WMFW_ADSP2_XM, dsp->cs_dsp.fw_id);1499	if (!alg_region) {1500		adsp_err(dsp, "No algorithm region found\n");1501		return -EINVAL;1502	}1503 1504	xmalg = dsp->sys_config_size / sizeof(__be32);1505 1506	addr = alg_region->base + xmalg + ALG_XM_FIELD(magic);1507	ret = cs_dsp_read_data_word(&dsp->cs_dsp, WMFW_ADSP2_XM, addr, &magic);1508	if (ret < 0)1509		return ret;1510 1511	if (magic != WM_ADSP_ALG_XM_STRUCT_MAGIC)1512		return -ENODEV;1513 1514	buf = wm_adsp_buffer_alloc(dsp);1515	if (!buf)1516		return -ENOMEM;1517 1518	addr = alg_region->base + xmalg + ALG_XM_FIELD(host_buf_ptr);1519	for (i = 0; i < 5; ++i) {1520		ret = cs_dsp_read_data_word(&dsp->cs_dsp, WMFW_ADSP2_XM, addr,1521					    &buf->host_buf_ptr);1522		if (ret < 0)1523			goto err;1524 1525		if (buf->host_buf_ptr)1526			break;1527 1528		usleep_range(1000, 2000);1529	}1530 1531	if (!buf->host_buf_ptr) {1532		ret = -EIO;1533		goto err;1534	}1535 1536	buf->host_buf_mem_type = WMFW_ADSP2_XM;1537 1538	ret = wm_adsp_buffer_populate(buf);1539	if (ret < 0)1540		goto err;1541 1542	list_add_tail(&buf->list, &dsp->buffer_list);1543 1544	compr_dbg(buf, "legacy host_buf_ptr=%x\n", buf->host_buf_ptr);1545 1546	return 0;1547 1548err:1549	kfree(buf);1550 1551	return ret;1552}1553 1554static int wm_adsp_buffer_parse_coeff(struct cs_dsp_coeff_ctl *cs_ctl)1555{1556	struct wm_adsp_host_buf_coeff_v1 coeff_v1;1557	struct wm_adsp_compr_buf *buf;1558	struct wm_adsp *dsp = container_of(cs_ctl->dsp, struct wm_adsp, cs_dsp);1559	unsigned int version = 0;1560	int ret, i;1561 1562	for (i = 0; i < 5; ++i) {1563		ret = cs_dsp_coeff_read_ctrl(cs_ctl, 0, &coeff_v1,1564					     min(cs_ctl->len, sizeof(coeff_v1)));1565		if (ret < 0)1566			return ret;1567 1568		if (coeff_v1.host_buf_ptr)1569			break;1570 1571		usleep_range(1000, 2000);1572	}1573 1574	if (!coeff_v1.host_buf_ptr) {1575		adsp_err(dsp, "Failed to acquire host buffer\n");1576		return -EIO;1577	}1578 1579	buf = wm_adsp_buffer_alloc(dsp);1580	if (!buf)1581		return -ENOMEM;1582 1583	buf->host_buf_mem_type = cs_ctl->alg_region.type;1584	buf->host_buf_ptr = be32_to_cpu(coeff_v1.host_buf_ptr);1585 1586	ret = wm_adsp_buffer_populate(buf);1587	if (ret < 0)1588		goto err;1589 1590	/*1591	 * v0 host_buffer coefficients didn't have versioning, so if the1592	 * control is one word, assume version 0.1593	 */1594	if (cs_ctl->len == 4)1595		goto done;1596 1597	version = be32_to_cpu(coeff_v1.versions) & HOST_BUF_COEFF_COMPAT_VER_MASK;1598	version >>= HOST_BUF_COEFF_COMPAT_VER_SHIFT;1599 1600	if (version > HOST_BUF_COEFF_SUPPORTED_COMPAT_VER) {1601		adsp_err(dsp,1602			 "Host buffer coeff ver %u > supported version %u\n",1603			 version, HOST_BUF_COEFF_SUPPORTED_COMPAT_VER);1604		ret = -EINVAL;1605		goto err;1606	}1607 1608	cs_dsp_remove_padding((u32 *)&coeff_v1.name, ARRAY_SIZE(coeff_v1.name));1609 1610	buf->name = kasprintf(GFP_KERNEL, "%s-dsp-%s", dsp->part,1611			      (char *)&coeff_v1.name);1612 1613done:1614	list_add_tail(&buf->list, &dsp->buffer_list);1615 1616	compr_dbg(buf, "host_buf_ptr=%x coeff version %u\n",1617		  buf->host_buf_ptr, version);1618 1619	return version;1620 1621err:1622	kfree(buf);1623 1624	return ret;1625}1626 1627static int wm_adsp_buffer_init(struct wm_adsp *dsp)1628{1629	struct cs_dsp_coeff_ctl *cs_ctl;1630	int ret;1631 1632	list_for_each_entry(cs_ctl, &dsp->cs_dsp.ctl_list, list) {1633		if (cs_ctl->type != WMFW_CTL_TYPE_HOST_BUFFER)1634			continue;1635 1636		if (!cs_ctl->enabled)1637			continue;1638 1639		ret = wm_adsp_buffer_parse_coeff(cs_ctl);1640		if (ret < 0) {1641			adsp_err(dsp, "Failed to parse coeff: %d\n", ret);1642			goto error;1643		} else if (ret == 0) {1644			/* Only one buffer supported for version 0 */1645			return 0;1646		}1647	}1648 1649	if (list_empty(&dsp->buffer_list)) {1650		/* Fall back to legacy support */1651		ret = wm_adsp_buffer_parse_legacy(dsp);1652		if (ret == -ENODEV)1653			adsp_info(dsp, "Legacy support not available\n");1654		else if (ret)1655			adsp_warn(dsp, "Failed to parse legacy: %d\n", ret);1656	}1657 1658	return 0;1659 1660error:1661	wm_adsp_buffer_free(dsp);1662	return ret;1663}1664 1665static int wm_adsp_buffer_free(struct wm_adsp *dsp)1666{1667	struct wm_adsp_compr_buf *buf, *tmp;1668 1669	list_for_each_entry_safe(buf, tmp, &dsp->buffer_list, list) {1670		wm_adsp_compr_detach(buf->compr);1671 1672		kfree(buf->name);1673		kfree(buf->regions);1674		list_del(&buf->list);1675		kfree(buf);1676	}1677 1678	return 0;1679}1680 1681static int wm_adsp_buffer_get_error(struct wm_adsp_compr_buf *buf)1682{1683	int ret;1684 1685	ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(error), &buf->error);1686	if (ret < 0) {1687		compr_err(buf, "Failed to check buffer error: %d\n", ret);1688		return ret;1689	}1690	if (buf->error != 0) {1691		compr_err(buf, "Buffer error occurred: %d\n", buf->error);1692		return -EIO;1693	}1694 1695	return 0;1696}1697 1698int wm_adsp_compr_trigger(struct snd_soc_component *component,1699			  struct snd_compr_stream *stream, int cmd)1700{1701	struct wm_adsp_compr *compr = stream->runtime->private_data;1702	struct wm_adsp *dsp = compr->dsp;1703	int ret = 0;1704 1705	compr_dbg(compr, "Trigger: %d\n", cmd);1706 1707	mutex_lock(&dsp->cs_dsp.pwr_lock);1708 1709	switch (cmd) {1710	case SNDRV_PCM_TRIGGER_START:1711		if (!wm_adsp_compr_attached(compr)) {1712			ret = wm_adsp_compr_attach(compr);1713			if (ret < 0) {1714				compr_err(compr, "Failed to link buffer and stream: %d\n",1715					  ret);1716				break;1717			}1718		}1719 1720		ret = wm_adsp_buffer_get_error(compr->buf);1721		if (ret < 0)1722			break;1723 1724		/* Trigger the IRQ at one fragment of data */1725		ret = wm_adsp_buffer_write(compr->buf,1726					   HOST_BUFFER_FIELD(high_water_mark),1727					   wm_adsp_compr_frag_words(compr));1728		if (ret < 0) {1729			compr_err(compr, "Failed to set high water mark: %d\n",1730				  ret);1731			break;1732		}1733		break;1734	case SNDRV_PCM_TRIGGER_STOP:1735		if (wm_adsp_compr_attached(compr))1736			wm_adsp_buffer_clear(compr->buf);1737		break;1738	default:1739		ret = -EINVAL;1740		break;1741	}1742 1743	mutex_unlock(&dsp->cs_dsp.pwr_lock);1744 1745	return ret;1746}1747EXPORT_SYMBOL_GPL(wm_adsp_compr_trigger);1748 1749static inline int wm_adsp_buffer_size(struct wm_adsp_compr_buf *buf)1750{1751	int last_region = wm_adsp_fw[buf->dsp->fw].caps->num_regions - 1;1752 1753	return buf->regions[last_region].cumulative_size;1754}1755 1756static int wm_adsp_buffer_update_avail(struct wm_adsp_compr_buf *buf)1757{1758	u32 next_read_index, next_write_index;1759	int write_index, read_index, avail;1760	int ret;1761 1762	/* Only sync read index if we haven't already read a valid index */1763	if (buf->read_index < 0) {1764		ret = wm_adsp_buffer_read(buf,1765				HOST_BUFFER_FIELD(next_read_index),1766				&next_read_index);1767		if (ret < 0)1768			return ret;1769 1770		read_index = sign_extend32(next_read_index, 23);1771 1772		if (read_index < 0) {1773			compr_dbg(buf, "Avail check on unstarted stream\n");1774			return 0;1775		}1776 1777		buf->read_index = read_index;1778	}1779 1780	ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(next_write_index),1781			&next_write_index);1782	if (ret < 0)1783		return ret;1784 1785	write_index = sign_extend32(next_write_index, 23);1786 1787	avail = write_index - buf->read_index;1788	if (avail < 0)1789		avail += wm_adsp_buffer_size(buf);1790 1791	compr_dbg(buf, "readindex=0x%x, writeindex=0x%x, avail=%d\n",1792		  buf->read_index, write_index, avail * CS_DSP_DATA_WORD_SIZE);1793 1794	buf->avail = avail;1795 1796	return 0;1797}1798 1799int wm_adsp_compr_handle_irq(struct wm_adsp *dsp)1800{1801	struct wm_adsp_compr_buf *buf;1802	struct wm_adsp_compr *compr;1803	int ret = 0;1804 1805	mutex_lock(&dsp->cs_dsp.pwr_lock);1806 1807	if (list_empty(&dsp->buffer_list)) {1808		ret = -ENODEV;1809		goto out;1810	}1811 1812	adsp_dbg(dsp, "Handling buffer IRQ\n");1813 1814	list_for_each_entry(buf, &dsp->buffer_list, list) {1815		compr = buf->compr;1816 1817		ret = wm_adsp_buffer_get_error(buf);1818		if (ret < 0)1819			goto out_notify; /* Wake poll to report error */1820 1821		ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(irq_count),1822					  &buf->irq_count);1823		if (ret < 0) {1824			compr_err(buf, "Failed to get irq_count: %d\n", ret);1825			goto out;1826		}1827 1828		ret = wm_adsp_buffer_update_avail(buf);1829		if (ret < 0) {1830			compr_err(buf, "Error reading avail: %d\n", ret);1831			goto out;1832		}1833 1834		if (wm_adsp_fw[dsp->fw].voice_trigger && buf->irq_count == 2)1835			ret = WM_ADSP_COMPR_VOICE_TRIGGER;1836 1837out_notify:1838		if (compr && compr->stream)1839			snd_compr_fragment_elapsed(compr->stream);1840	}1841 1842out:1843	mutex_unlock(&dsp->cs_dsp.pwr_lock);1844 1845	return ret;1846}1847EXPORT_SYMBOL_GPL(wm_adsp_compr_handle_irq);1848 1849static int wm_adsp_buffer_reenable_irq(struct wm_adsp_compr_buf *buf)1850{1851	if (buf->irq_count & 0x01)1852		return 0;1853 1854	compr_dbg(buf, "Enable IRQ(0x%x) for next fragment\n", buf->irq_count);1855 1856	buf->irq_count |= 0x01;1857 1858	return wm_adsp_buffer_write(buf, HOST_BUFFER_FIELD(irq_ack),1859				    buf->irq_count);1860}1861 1862int wm_adsp_compr_pointer(struct snd_soc_component *component,1863			  struct snd_compr_stream *stream,1864			  struct snd_compr_tstamp *tstamp)1865{1866	struct wm_adsp_compr *compr = stream->runtime->private_data;1867	struct wm_adsp *dsp = compr->dsp;1868	struct wm_adsp_compr_buf *buf;1869	int ret = 0;1870 1871	compr_dbg(compr, "Pointer request\n");1872 1873	mutex_lock(&dsp->cs_dsp.pwr_lock);1874 1875	buf = compr->buf;1876 1877	if (dsp->fatal_error || !buf || buf->error) {1878		snd_compr_stop_error(stream, SNDRV_PCM_STATE_XRUN);1879		ret = -EIO;1880		goto out;1881	}1882 1883	if (buf->avail < wm_adsp_compr_frag_words(compr)) {1884		ret = wm_adsp_buffer_update_avail(buf);1885		if (ret < 0) {1886			compr_err(compr, "Error reading avail: %d\n", ret);1887			goto out;1888		}1889 1890		/*1891		 * If we really have less than 1 fragment available tell the1892		 * DSP to inform us once a whole fragment is available.1893		 */1894		if (buf->avail < wm_adsp_compr_frag_words(compr)) {1895			ret = wm_adsp_buffer_get_error(buf);1896			if (ret < 0) {1897				if (buf->error)1898					snd_compr_stop_error(stream,1899							SNDRV_PCM_STATE_XRUN);1900				goto out;1901			}1902 1903			ret = wm_adsp_buffer_reenable_irq(buf);1904			if (ret < 0) {1905				compr_err(compr, "Failed to re-enable buffer IRQ: %d\n",1906					  ret);1907				goto out;1908			}1909		}1910	}1911 1912	tstamp->copied_total = compr->copied_total;1913	tstamp->copied_total += buf->avail * CS_DSP_DATA_WORD_SIZE;1914	tstamp->sampling_rate = compr->sample_rate;1915 1916out:1917	mutex_unlock(&dsp->cs_dsp.pwr_lock);1918 1919	return ret;1920}1921EXPORT_SYMBOL_GPL(wm_adsp_compr_pointer);1922 1923static int wm_adsp_buffer_capture_block(struct wm_adsp_compr *compr, int target)1924{1925	struct wm_adsp_compr_buf *buf = compr->buf;1926	unsigned int adsp_addr;1927	int mem_type, nwords, max_read;1928	int i, ret;1929 1930	/* Calculate read parameters */1931	for (i = 0; i < wm_adsp_fw[buf->dsp->fw].caps->num_regions; ++i)1932		if (buf->read_index < buf->regions[i].cumulative_size)1933			break;1934 1935	if (i == wm_adsp_fw[buf->dsp->fw].caps->num_regions)1936		return -EINVAL;1937 1938	mem_type = buf->regions[i].mem_type;1939	adsp_addr = buf->regions[i].base_addr +1940		    (buf->read_index - buf->regions[i].offset);1941 1942	max_read = wm_adsp_compr_frag_words(compr);1943	nwords = buf->regions[i].cumulative_size - buf->read_index;1944 1945	if (nwords > target)1946		nwords = target;1947	if (nwords > buf->avail)1948		nwords = buf->avail;1949	if (nwords > max_read)1950		nwords = max_read;1951	if (!nwords)1952		return 0;1953 1954	/* Read data from DSP */1955	ret = cs_dsp_read_raw_data_block(&buf->dsp->cs_dsp, mem_type, adsp_addr,1956					 nwords, (__be32 *)compr->raw_buf);1957	if (ret < 0)1958		return ret;1959 1960	cs_dsp_remove_padding(compr->raw_buf, nwords);1961 1962	/* update read index to account for words read */1963	buf->read_index += nwords;1964	if (buf->read_index == wm_adsp_buffer_size(buf))1965		buf->read_index = 0;1966 1967	ret = wm_adsp_buffer_write(buf, HOST_BUFFER_FIELD(next_read_index),1968				   buf->read_index);1969	if (ret < 0)1970		return ret;1971 1972	/* update avail to account for words read */1973	buf->avail -= nwords;1974 1975	return nwords;1976}1977 1978static int wm_adsp_compr_read(struct wm_adsp_compr *compr,1979			      char __user *buf, size_t count)1980{1981	struct wm_adsp *dsp = compr->dsp;1982	int ntotal = 0;1983	int nwords, nbytes;1984 1985	compr_dbg(compr, "Requested read of %zu bytes\n", count);1986 1987	if (dsp->fatal_error || !compr->buf || compr->buf->error) {1988		snd_compr_stop_error(compr->stream, SNDRV_PCM_STATE_XRUN);1989		return -EIO;1990	}1991 1992	count /= CS_DSP_DATA_WORD_SIZE;1993 1994	do {1995		nwords = wm_adsp_buffer_capture_block(compr, count);1996		if (nwords < 0) {1997			compr_err(compr, "Failed to capture block: %d\n",1998				  nwords);1999			return nwords;2000		}2001 2002		nbytes = nwords * CS_DSP_DATA_WORD_SIZE;2003 2004		compr_dbg(compr, "Read %d bytes\n", nbytes);2005 2006		if (copy_to_user(buf + ntotal, compr->raw_buf, nbytes)) {2007			compr_err(compr, "Failed to copy data to user: %d, %d\n",2008				  ntotal, nbytes);2009			return -EFAULT;2010		}2011 2012		count -= nwords;2013		ntotal += nbytes;2014	} while (nwords > 0 && count > 0);2015 2016	compr->copied_total += ntotal;2017 2018	return ntotal;2019}2020 2021int wm_adsp_compr_copy(struct snd_soc_component *component,2022		       struct snd_compr_stream *stream, char __user *buf,2023		       size_t count)2024{2025	struct wm_adsp_compr *compr = stream->runtime->private_data;2026	struct wm_adsp *dsp = compr->dsp;2027	int ret;2028 2029	mutex_lock(&dsp->cs_dsp.pwr_lock);2030 2031	if (stream->direction == SND_COMPRESS_CAPTURE)2032		ret = wm_adsp_compr_read(compr, buf, count);2033	else2034		ret = -ENOTSUPP;2035 2036	mutex_unlock(&dsp->cs_dsp.pwr_lock);2037 2038	return ret;2039}2040EXPORT_SYMBOL_GPL(wm_adsp_compr_copy);2041 2042static void wm_adsp_fatal_error(struct cs_dsp *cs_dsp)2043{2044	struct wm_adsp *dsp = container_of(cs_dsp, struct wm_adsp, cs_dsp);2045	struct wm_adsp_compr *compr;2046 2047	dsp->fatal_error = true;2048 2049	list_for_each_entry(compr, &dsp->compr_list, list) {2050		if (compr->stream)2051			snd_compr_fragment_elapsed(compr->stream);2052	}2053}2054 2055irqreturn_t wm_adsp2_bus_error(int irq, void *data)2056{2057	struct wm_adsp *dsp = (struct wm_adsp *)data;2058 2059	cs_dsp_adsp2_bus_error(&dsp->cs_dsp);2060 2061	return IRQ_HANDLED;2062}2063EXPORT_SYMBOL_GPL(wm_adsp2_bus_error);2064 2065irqreturn_t wm_halo_bus_error(int irq, void *data)2066{2067	struct wm_adsp *dsp = (struct wm_adsp *)data;2068 2069	cs_dsp_halo_bus_error(&dsp->cs_dsp);2070 2071	return IRQ_HANDLED;2072}2073EXPORT_SYMBOL_GPL(wm_halo_bus_error);2074 2075irqreturn_t wm_halo_wdt_expire(int irq, void *data)2076{2077	struct wm_adsp *dsp = data;2078 2079	cs_dsp_halo_wdt_expire(&dsp->cs_dsp);2080 2081	return IRQ_HANDLED;2082}2083EXPORT_SYMBOL_GPL(wm_halo_wdt_expire);2084 2085static const struct cs_dsp_client_ops wm_adsp1_client_ops = {2086	.control_add = wm_adsp_control_add_cb,2087	.control_remove = wm_adsp_control_remove,2088};2089 2090static const struct cs_dsp_client_ops wm_adsp2_client_ops = {2091	.control_add = wm_adsp_control_add_cb,2092	.control_remove = wm_adsp_control_remove,2093	.pre_run = wm_adsp_pre_run,2094	.post_run = wm_adsp_event_post_run,2095	.post_stop = wm_adsp_event_post_stop,2096	.watchdog_expired = wm_adsp_fatal_error,2097};2098 2099MODULE_DESCRIPTION("Cirrus Logic ASoC DSP Support");2100MODULE_LICENSE("GPL v2");2101MODULE_IMPORT_NS(FW_CS_DSP);2102