File Index
Files and directories
Sign in
FILE

cs-amp-lib.c

File preview with syntax highlighting.

linux-6.19.14/sound/soc/codecs/cs-amp-lib.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2//
  3// Common code for Cirrus Logic Smart Amplifiers
  4//
  5// Copyright (C) 2024 Cirrus Logic, Inc. and
  6//               Cirrus Logic International Semiconductor Ltd.
  7
  8#include <asm/byteorder.h>
  9#include <kunit/static_stub.h>
 10#include <linux/debugfs.h>
 11#include <linux/dev_printk.h>
 12#include <linux/efi.h>
 13#include <linux/firmware/cirrus/cs_dsp.h>
 14#include <linux/math64.h>
 15#include <linux/module.h>
 16#include <linux/mutex.h>
 17#include <linux/overflow.h>
 18#include <linux/slab.h>
 19#include <linux/timekeeping.h>
 20#include <linux/types.h>
 21#include <sound/cs-amp-lib.h>
 22
 23#define CIRRUS_LOGIC_CALIBRATION_EFI_NAME L"CirrusSmartAmpCalibrationData"
 24#define CIRRUS_LOGIC_CALIBRATION_EFI_GUID \
 25	EFI_GUID(0x02f9af02, 0x7734, 0x4233, 0xb4, 0x3d, 0x93, 0xfe, 0x5a, 0xa3, 0x5d, 0xb3)
 26
 27#define LENOVO_SPEAKER_ID_EFI_NAME L"SdwSpeaker"
 28#define LENOVO_SPEAKER_ID_EFI_GUID \
 29	EFI_GUID(0x48df970e, 0xe27f, 0x460a, 0xb5, 0x86, 0x77, 0x19, 0x80, 0x1d, 0x92, 0x82)
 30
 31#define HP_SPEAKER_ID_EFI_NAME L"HPSpeakerID"
 32#define HP_SPEAKER_ID_EFI_GUID \
 33	EFI_GUID(0xc49593a4, 0xd099, 0x419b, 0xa2, 0xc3, 0x67, 0xe9, 0x80, 0xe6, 0x1d, 0x1e)
 34
 35#define HP_CALIBRATION_EFI_NAME L"SmartAmpCalibrationData"
 36#define HP_CALIBRATION_EFI_GUID \
 37	EFI_GUID(0x53559579, 0x8753, 0x4f5c, 0x91, 0x30, 0xe8, 0x2a, 0xcf, 0xb8, 0xd8, 0x93)
 38
 39static const struct cs_amp_lib_cal_efivar {
 40	efi_char16_t *name;
 41	efi_guid_t *guid;
 42} cs_amp_lib_cal_efivars[] = {
 43	{
 44		.name = HP_CALIBRATION_EFI_NAME,
 45		.guid = &HP_CALIBRATION_EFI_GUID,
 46	},
 47	{
 48		.name = CIRRUS_LOGIC_CALIBRATION_EFI_NAME,
 49		.guid = &CIRRUS_LOGIC_CALIBRATION_EFI_GUID,
 50	},
 51};
 52
 53#define CS_AMP_CAL_DEFAULT_EFI_ATTR			\
 54		(EFI_VARIABLE_NON_VOLATILE |		\
 55		 EFI_VARIABLE_BOOTSERVICE_ACCESS |	\
 56		 EFI_VARIABLE_RUNTIME_ACCESS)
 57
 58/* Offset from Unix time to Windows time (100ns since 1 Jan 1601) */
 59#define UNIX_TIME_TO_WINDOWS_TIME_OFFSET	116444736000000000ULL
 60
 61static DEFINE_MUTEX(cs_amp_efi_cal_write_lock);
 62
 63static u64 cs_amp_time_now_in_windows_time(void)
 64{
 65	u64 time_in_100ns = div_u64(ktime_get_real_ns(), 100);
 66
 67	return time_in_100ns + UNIX_TIME_TO_WINDOWS_TIME_OFFSET;
 68}
 69
 70static int cs_amp_write_cal_coeff(struct cs_dsp *dsp,
 71				  const struct cirrus_amp_cal_controls *controls,
 72				  const char *ctl_name, u32 val)
 73{
 74	struct cs_dsp_coeff_ctl *cs_ctl;
 75	__be32 beval = cpu_to_be32(val);
 76	int ret;
 77
 78	KUNIT_STATIC_STUB_REDIRECT(cs_amp_write_cal_coeff, dsp, controls, ctl_name, val);
 79
 80	if (IS_REACHABLE(CONFIG_FW_CS_DSP)) {
 81		mutex_lock(&dsp->pwr_lock);
 82		cs_ctl = cs_dsp_get_ctl(dsp, ctl_name, controls->mem_region, controls->alg_id);
 83		ret = cs_dsp_coeff_write_ctrl(cs_ctl, 0, &beval, sizeof(beval));
 84		mutex_unlock(&dsp->pwr_lock);
 85
 86		if (ret < 0) {
 87			dev_err(dsp->dev, "Failed to write to '%s': %d\n", ctl_name, ret);
 88			return ret;
 89		}
 90
 91		return 0;
 92	}
 93
 94	return -ENODEV;
 95}
 96
 97static int cs_amp_read_cal_coeff(struct cs_dsp *dsp,
 98				 const struct cirrus_amp_cal_controls *controls,
 99				 const char *ctl_name, u32 *val)
100{
101	struct cs_dsp_coeff_ctl *cs_ctl;
102	__be32 beval;
103	int ret;
104
105	KUNIT_STATIC_STUB_REDIRECT(cs_amp_read_cal_coeff, dsp, controls, ctl_name, val);
106
107	if (!IS_REACHABLE(CONFIG_FW_CS_DSP))
108		return -ENODEV;
109
110	scoped_guard(mutex, &dsp->pwr_lock) {
111		cs_ctl = cs_dsp_get_ctl(dsp, ctl_name, controls->mem_region, controls->alg_id);
112		ret = cs_dsp_coeff_read_ctrl(cs_ctl, 0, &beval, sizeof(beval));
113	}
114
115	if (ret < 0) {
116		dev_err(dsp->dev, "Failed to write to '%s': %d\n", ctl_name, ret);
117		return ret;
118	}
119
120	*val = be32_to_cpu(beval);
121
122	return 0;
123}
124
125static int _cs_amp_write_cal_coeffs(struct cs_dsp *dsp,
126				    const struct cirrus_amp_cal_controls *controls,
127				    const struct cirrus_amp_cal_data *data)
128{
129	int ret;
130
131	dev_dbg(dsp->dev, "Calibration: Ambient=%#x, Status=%#x, CalR=%d\n",
132		data->calAmbient, data->calStatus, data->calR);
133
134	if (list_empty(&dsp->ctl_list)) {
135		dev_info(dsp->dev, "Calibration disabled due to missing firmware controls\n");
136		return -ENOENT;
137	}
138
139	ret = cs_amp_write_cal_coeff(dsp, controls, controls->ambient, data->calAmbient);
140	if (ret)
141		return ret;
142
143	ret = cs_amp_write_cal_coeff(dsp, controls, controls->calr, data->calR);
144	if (ret)
145		return ret;
146
147	ret = cs_amp_write_cal_coeff(dsp, controls, controls->status, data->calStatus);
148	if (ret)
149		return ret;
150
151	ret = cs_amp_write_cal_coeff(dsp, controls, controls->checksum, data->calR + 1);
152	if (ret)
153		return ret;
154
155	return 0;
156}
157
158static int _cs_amp_read_cal_coeffs(struct cs_dsp *dsp,
159				    const struct cirrus_amp_cal_controls *controls,
160				    struct cirrus_amp_cal_data *data)
161{
162	u64 time;
163	u32 val;
164	int ret;
165
166	if (list_empty(&dsp->ctl_list)) {
167		dev_info(dsp->dev, "Calibration disabled due to missing firmware controls\n");
168		return -ENOENT;
169	}
170
171	ret = cs_amp_read_cal_coeff(dsp, controls, controls->ambient, &val);
172	if (ret)
173		return ret;
174
175	data->calAmbient = (s8)val;
176
177	ret = cs_amp_read_cal_coeff(dsp, controls, controls->calr, &val);
178	if (ret)
179		return ret;
180
181	data->calR = (u16)val;
182
183	ret = cs_amp_read_cal_coeff(dsp, controls, controls->status, &val);
184	if (ret)
185		return ret;
186
187	data->calStatus = (u8)val;
188
189	/* Fill in timestamp */
190	time = cs_amp_time_now_in_windows_time();
191	data->calTime[0] = (u32)time;
192	data->calTime[1] = (u32)(time >> 32);
193
194	return 0;
195}
196
197/**
198 * cs_amp_write_cal_coeffs - Write calibration data to firmware controls.
199 * @dsp:	Pointer to struct cs_dsp.
200 * @controls:	Pointer to definition of firmware controls to be written.
201 * @data:	Pointer to calibration data.
202 *
203 * Returns: 0 on success, else negative error value.
204 */
205int cs_amp_write_cal_coeffs(struct cs_dsp *dsp,
206			    const struct cirrus_amp_cal_controls *controls,
207			    const struct cirrus_amp_cal_data *data)
208{
209	if (IS_REACHABLE(CONFIG_FW_CS_DSP) || IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST))
210		return _cs_amp_write_cal_coeffs(dsp, controls, data);
211	else
212		return -ENODEV;
213}
214EXPORT_SYMBOL_NS_GPL(cs_amp_write_cal_coeffs, "SND_SOC_CS_AMP_LIB");
215
216/**
217 * cs_amp_read_cal_coeffs - Read calibration data from firmware controls.
218 * @dsp:	Pointer to struct cs_dsp.
219 * @controls:	Pointer to definition of firmware controls to be read.
220 * @data:	Pointer to calibration data where results will be written.
221 *
222 * Returns: 0 on success, else negative error value.
223 */
224int cs_amp_read_cal_coeffs(struct cs_dsp *dsp,
225			   const struct cirrus_amp_cal_controls *controls,
226			   struct cirrus_amp_cal_data *data)
227{
228	if (IS_REACHABLE(CONFIG_FW_CS_DSP) || IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST))
229		return _cs_amp_read_cal_coeffs(dsp, controls, data);
230	else
231		return -ENODEV;
232}
233EXPORT_SYMBOL_NS_GPL(cs_amp_read_cal_coeffs, "SND_SOC_CS_AMP_LIB");
234
235/**
236 * cs_amp_write_ambient_temp - write value to calibration ambient temperature
237 * @dsp:	Pointer to struct cs_dsp.
238 * @controls:	Pointer to definition of firmware controls to be read.
239 * @temp:	Temperature in degrees celcius.
240 *
241 * Returns: 0 on success, else negative error value.
242 */
243int cs_amp_write_ambient_temp(struct cs_dsp *dsp,
244			      const struct cirrus_amp_cal_controls *controls,
245			      u32 temp)
246{
247	if (IS_REACHABLE(CONFIG_FW_CS_DSP) || IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST))
248		return cs_amp_write_cal_coeff(dsp, controls, controls->ambient, temp);
249	else
250		return -ENODEV;
251}
252EXPORT_SYMBOL_NS_GPL(cs_amp_write_ambient_temp, "SND_SOC_CS_AMP_LIB");
253
254static efi_status_t cs_amp_get_efi_variable(efi_char16_t *name,
255					    efi_guid_t *guid,
256					    u32 *returned_attr,
257					    unsigned long *size,
258					    void *buf)
259{
260	u32 attr;
261
262	if (!returned_attr)
263		returned_attr = &attr;
264
265	KUNIT_STATIC_STUB_REDIRECT(cs_amp_get_efi_variable, name, guid,
266				   returned_attr, size, buf);
267
268	if (efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE))
269		return efi.get_variable(name, guid, returned_attr, size, buf);
270
271	return EFI_NOT_FOUND;
272}
273
274static efi_status_t cs_amp_set_efi_variable(efi_char16_t *name,
275					    efi_guid_t *guid,
276					    u32 attr,
277					    unsigned long size,
278					    void *buf)
279{
280	KUNIT_STATIC_STUB_REDIRECT(cs_amp_set_efi_variable, name, guid, attr, size, buf);
281
282	if (!efi_rt_services_supported(EFI_RT_SUPPORTED_SET_VARIABLE))
283		return EFI_NOT_FOUND;
284
285	return efi.set_variable(name, guid, attr, size, buf);
286}
287
288static int cs_amp_convert_efi_status(efi_status_t status)
289{
290	switch (status) {
291	case EFI_SUCCESS:
292		return 0;
293	case EFI_NOT_FOUND:
294		return -ENOENT;
295	case EFI_BUFFER_TOO_SMALL:
296		return -EFBIG;
297	case EFI_WRITE_PROTECTED:
298	case EFI_UNSUPPORTED:
299	case EFI_ACCESS_DENIED:
300	case EFI_SECURITY_VIOLATION:
301		return -EACCES;
302	default:
303		return -EIO;
304	}
305}
306
307static struct cirrus_amp_efi_data *cs_amp_get_cal_efi_buffer(struct device *dev,
308							     efi_char16_t **name,
309							     efi_guid_t **guid,
310							     u32 *attr)
311{
312	struct cirrus_amp_efi_data *efi_data;
313	unsigned long data_size = 0;
314	u8 *data;
315	efi_status_t status;
316	int i, ret;
317
318	/* Find EFI variable and get size */
319	for (i = 0; i < ARRAY_SIZE(cs_amp_lib_cal_efivars); i++) {
320		status = cs_amp_get_efi_variable(cs_amp_lib_cal_efivars[i].name,
321						 cs_amp_lib_cal_efivars[i].guid,
322						 attr, &data_size, NULL);
323		if (status == EFI_BUFFER_TOO_SMALL)
324			break;
325	}
326
327	if (status != EFI_BUFFER_TOO_SMALL)
328		return ERR_PTR(-ENOENT);
329
330	if (name)
331		*name = cs_amp_lib_cal_efivars[i].name;
332
333	if (guid)
334		*guid = cs_amp_lib_cal_efivars[i].guid;
335
336	if (data_size < sizeof(*efi_data)) {
337		dev_err(dev, "EFI cal variable truncated\n");
338		return ERR_PTR(-EOVERFLOW);
339	}
340
341	/* Get variable contents into buffer */
342	data = kmalloc(data_size, GFP_KERNEL);
343	if (!data)
344		return ERR_PTR(-ENOMEM);
345
346	status = cs_amp_get_efi_variable(cs_amp_lib_cal_efivars[i].name,
347					 cs_amp_lib_cal_efivars[i].guid,
348					 attr, &data_size, data);
349	if (status != EFI_SUCCESS) {
350		ret = -EINVAL;
351		goto err;
352	}
353
354	efi_data = (struct cirrus_amp_efi_data *)data;
355	dev_dbg(dev, "Calibration: Size=%d, Amp Count=%d\n", efi_data->size, efi_data->count);
356
357	if ((efi_data->count > 128) ||
358	    struct_size(efi_data, data, efi_data->count) > data_size) {
359		dev_err(dev, "EFI cal variable truncated\n");
360		ret = -EOVERFLOW;
361		goto err;
362	}
363
364	/* This could be zero-filled space pre-allocated by the BIOS */
365	if (efi_data->size == 0)
366		efi_data->size = data_size;
367
368	return efi_data;
369
370err:
371	kfree(data);
372	dev_err(dev, "Failed to read calibration data from EFI: %d\n", ret);
373
374	return ERR_PTR(ret);
375}
376
377static int cs_amp_set_cal_efi_buffer(struct device *dev,
378				     efi_char16_t *name,
379				     efi_guid_t *guid,
380				     u32 attr,
381				     struct cirrus_amp_efi_data *data)
382{
383	efi_status_t status;
384
385	status = cs_amp_set_efi_variable(name, guid, attr,
386					 struct_size(data, data, data->count), data);
387
388	return cs_amp_convert_efi_status(status);
389}
390
391static int _cs_amp_get_efi_calibration_data(struct device *dev, u64 target_uid, int amp_index,
392					    struct cirrus_amp_cal_data *out_data)
393{
394	struct cirrus_amp_efi_data *efi_data;
395	struct cirrus_amp_cal_data *cal = NULL;
396	int i, ret;
397
398	efi_data = cs_amp_get_cal_efi_buffer(dev, NULL, NULL, NULL);
399	if (IS_ERR(efi_data))
400		return PTR_ERR(efi_data);
401
402	if (target_uid) {
403		for (i = 0; i < efi_data->count; ++i) {
404			u64 cal_target = cs_amp_cal_target_u64(&efi_data->data[i]);
405
406			/* Skip empty entries */
407			if (!efi_data->data[i].calTime[0] && !efi_data->data[i].calTime[1])
408				continue;
409
410			/* Skip entries with unpopulated silicon ID */
411			if (cal_target == 0)
412				continue;
413
414			if (cal_target == target_uid) {
415				cal = &efi_data->data[i];
416				break;
417			}
418		}
419	}
420
421	if (!cal && (amp_index >= 0) && (amp_index < efi_data->count) &&
422	    (efi_data->data[amp_index].calTime[0] || efi_data->data[amp_index].calTime[1])) {
423		u64 cal_target = cs_amp_cal_target_u64(&efi_data->data[amp_index]);
424
425		/*
426		 * Treat unpopulated cal_target as a wildcard.
427		 * If target_uid != 0 we can only get here if cal_target == 0
428		 * or it didn't match any cal_target value.
429		 * If target_uid == 0 it is a wildcard.
430		 */
431		if ((cal_target == 0) || (target_uid == 0))
432			cal = &efi_data->data[amp_index];
433		else
434			dev_warn(dev, "Calibration entry %d does not match silicon ID", amp_index);
435	}
436
437	if (cal) {
438		memcpy(out_data, cal, sizeof(*out_data));
439		ret = 0;
440	} else {
441		dev_warn(dev, "No calibration for silicon ID %#llx\n", target_uid);
442		ret = -ENOENT;
443	}
444
445	kfree(efi_data);
446
447	return ret;
448}
449
450static int _cs_amp_set_efi_calibration_data(struct device *dev, int amp_index, int num_amps,
451					    const struct cirrus_amp_cal_data *in_data)
452{
453	u64 cal_target = cs_amp_cal_target_u64(in_data);
454	unsigned long num_entries;
455	struct cirrus_amp_efi_data *data __free(kfree) = NULL;
456	efi_char16_t *name = CIRRUS_LOGIC_CALIBRATION_EFI_NAME;
457	efi_guid_t *guid = &CIRRUS_LOGIC_CALIBRATION_EFI_GUID;
458	u32 attr = CS_AMP_CAL_DEFAULT_EFI_ATTR;
459	int i, ret;
460
461	if (cal_target == 0)
462		return -EINVAL;
463
464	data = cs_amp_get_cal_efi_buffer(dev, &name, &guid, &attr);
465	ret = PTR_ERR_OR_ZERO(data);
466	if (ret == -ENOENT) {
467		data = NULL;
468		goto alloc_new;
469	} else if (ret) {
470		return ret;
471	}
472
473	/*
474	 * If the EFI variable is just zero-filled reserved space the count
475	 * must be set.
476	 */
477	if (data->count == 0)
478		data->count = (data->size - sizeof(data)) / sizeof(data->data[0]);
479
480	if (amp_index < 0) {
481		/* Is there already a slot for this target? */
482		for (amp_index = 0; amp_index < data->count; amp_index++) {
483			if (cs_amp_cal_target_u64(&data->data[amp_index]) == cal_target)
484				break;
485		}
486
487		/* Else find an empty slot */
488		if (amp_index >= data->count) {
489			for (amp_index = 0; amp_index < data->count; amp_index++) {
490				if ((data->data[amp_index].calTime[0] == 0) &&
491				    (data->data[amp_index].calTime[1] == 0))
492					break;
493			}
494		}
495	} else {
496		/*
497		 * If the index is forced there could be another active
498		 * slot with the same calTarget. So deduplicate.
499		 */
500		for (i = 0; i < data->count; i++) {
501			if (i == amp_index)
502				continue;
503
504			if ((data->data[i].calTime[0] == 0) && (data->data[i].calTime[1] == 0))
505				continue;
506
507			if (cs_amp_cal_target_u64(&data->data[i]) == cal_target)
508				memset(data->data[i].calTime, 0, sizeof(data->data[i].calTime));
509		}
510	}
511
512alloc_new:
513	if (amp_index < 0)
514		amp_index = 0;
515
516	num_entries = max(num_amps, amp_index + 1);
517	if (!data || (data->count < num_entries)) {
518		struct cirrus_amp_efi_data *old_data __free(kfree) = no_free_ptr(data);
519		unsigned int new_data_size = struct_size(data, data, num_entries);
520
521		data = kzalloc(new_data_size, GFP_KERNEL);
522		if (!data)
523			return -ENOMEM;
524
525		if (old_data)
526			memcpy(data, old_data, struct_size(old_data, data, old_data->count));
527
528		data->count = num_entries;
529		data->size = new_data_size;
530	}
531
532	data->data[amp_index] = *in_data;
533	ret = cs_amp_set_cal_efi_buffer(dev, name, guid, attr, data);
534	if (ret) {
535		dev_err(dev, "Failed writing calibration to EFI: %d\n", ret);
536		return ret;
537	}
538
539	return 0;
540}
541
542/**
543 * cs_amp_get_efi_calibration_data - get an entry from calibration data in EFI.
544 * @dev:	struct device of the caller.
545 * @target_uid:	UID to match, or zero to ignore UID matching.
546 * @amp_index:	Entry index to use, or -1 to prevent lookup by index.
547 * @out_data:	struct cirrus_amp_cal_data where the entry will be copied.
548 *
549 * This function can perform 3 types of lookup:
550 *
551 * (target_uid > 0, amp_index >= 0)
552 *	UID search with fallback to using the array index.
553 *	Search the calibration data for a non-zero calTarget that matches
554 *	target_uid, and if found return that entry. Else, if the entry at
555 *	[amp_index] has calTarget == 0, return that entry. Else fail.
556 *
557 * (target_uid > 0, amp_index < 0)
558 *	UID search only.
559 *	Search the calibration data for a non-zero calTarget that matches
560 *	target_uid, and if found return that entry. Else fail.
561 *
562 * (target_uid == 0, amp_index >= 0)
563 *	Array index fetch only.
564 *	Return the entry at [amp_index].
565 *
566 * An array lookup will be skipped if amp_index exceeds the number of
567 * entries in the calibration array, and in this case the return will
568 * be -ENOENT. An out-of-range amp_index does not prevent matching by
569 * target_uid - it has the same effect as passing amp_index < 0.
570 *
571 * If the EFI data is too short to be a valid entry, or the entry count
572 * in the EFI data overflows the actual length of the data, this function
573 * returns -EOVERFLOW.
574 *
575 * Return: 0 if the entry was found, -ENOENT if no entry was found,
576 *	   -EOVERFLOW if the EFI file is corrupt, else other error value.
577 */
578int cs_amp_get_efi_calibration_data(struct device *dev, u64 target_uid, int amp_index,
579				    struct cirrus_amp_cal_data *out_data)
580{
581	if (IS_ENABLED(CONFIG_EFI) || IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST))
582		return _cs_amp_get_efi_calibration_data(dev, target_uid, amp_index, out_data);
583	else
584		return -ENOENT;
585}
586EXPORT_SYMBOL_NS_GPL(cs_amp_get_efi_calibration_data, "SND_SOC_CS_AMP_LIB");
587
588/**
589 * cs_amp_set_efi_calibration_data - write a calibration data entry to EFI.
590 * @dev:	struct device of the caller.
591 * @amp_index:	Entry index to use, or -1 to use any available slot.
592 * @num_amps:	Maximum number of amps to reserve slots for, or -1 to ignore.
593 * @in_data:	struct cirrus_amp_cal_data entry to be written to EFI.
594 *
595 * If a Vendor-specific variable exists it will be updated,
596 * else if the Cirrus variable exists it will be updated
597 * else the Cirrus variable will be created.
598 *
599 * If amp_index >= 0 the data will be placed in this entry of the calibration
600 * data array, overwriting what was in that entry. Any other entries with the
601 * same calTarget will be marked empty.
602 *
603 * If amp_index < 0 and in_data->calTarget matches any existing entry, that
604 * entry will be overwritten. Else the first available free entry will be used,
605 * extending the size of the EFI variable if there are no free entries.
606 *
607 * If num_amps > 0 the EFI variable will be sized to contain at least this
608 * many calibration entries, with any new entries marked empty.
609 *
610 * Return: 0 if the write was successful, -EFBIG if space could not be made in
611 *	   the EFI file to add the entry, -EACCES if it was not possible to
612 *	   read or write the EFI variable.
613 */
614int cs_amp_set_efi_calibration_data(struct device *dev, int amp_index, int num_amps,
615				    const struct cirrus_amp_cal_data *in_data)
616{
617	if (IS_ENABLED(CONFIG_EFI) || IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST)) {
618		scoped_guard(mutex, &cs_amp_efi_cal_write_lock) {
619			return _cs_amp_set_efi_calibration_data(dev, amp_index,
620								num_amps, in_data);
621		}
622	}
623
624	return -ENOENT;
625}
626EXPORT_SYMBOL_NS_GPL(cs_amp_set_efi_calibration_data, "SND_SOC_CS_AMP_LIB");
627
628struct cs_amp_spkid_efi {
629	efi_char16_t *name;
630	efi_guid_t *guid;
631	u8 values[2];
632};
633
634static int cs_amp_get_efi_byte_spkid(struct device *dev, const struct cs_amp_spkid_efi *info)
635{
636	efi_status_t status;
637	unsigned long size;
638	u8 spkid;
639	int i, ret;
640
641	size = sizeof(spkid);
642	status = cs_amp_get_efi_variable(info->name, info->guid, NULL, &size, &spkid);
643	ret = cs_amp_convert_efi_status(status);
644	if (ret < 0)
645		return ret;
646
647	if (size == 0)
648		return -ENOENT;
649
650	for (i = 0; i < ARRAY_SIZE(info->values); i++) {
651		if (info->values[i] == spkid)
652			return i;
653	}
654
655	dev_err(dev, "EFI speaker ID bad value %#x\n", spkid);
656
657	return -EINVAL;
658}
659
660static const struct cs_amp_spkid_efi cs_amp_spkid_byte_types[] = {
661	{
662		.name = LENOVO_SPEAKER_ID_EFI_NAME,
663		.guid = &LENOVO_SPEAKER_ID_EFI_GUID,
664		.values = { 0xd0, 0xd1 },
665	},
666	{
667		.name = HP_SPEAKER_ID_EFI_NAME,
668		.guid = &HP_SPEAKER_ID_EFI_GUID,
669		.values = { 0x30, 0x31 },
670	},
671};
672
673/**
674 * cs_amp_get_vendor_spkid - get a speaker ID from vendor-specific storage
675 * @dev:	pointer to struct device
676 *
677 * Known vendor-specific methods of speaker ID are checked and if one is
678 * found its speaker ID value is returned.
679 *
680 * Return: >=0 is a valid speaker ID. -ENOENT if a vendor-specific method
681 *	   was not found. -EACCES if the vendor-specific storage could not
682 *	   be read. Other error values indicate that the data from the
683 *	   vendor-specific storage was found but could not be understood.
684 */
685int cs_amp_get_vendor_spkid(struct device *dev)
686{
687	int i, ret;
688
689	if (!efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE) &&
690	    !IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST))
691		return -ENOENT;
692
693	for (i = 0; i < ARRAY_SIZE(cs_amp_spkid_byte_types); i++) {
694		ret = cs_amp_get_efi_byte_spkid(dev, &cs_amp_spkid_byte_types[i]);
695		if (ret != -ENOENT)
696			return ret;
697	}
698
699	return -ENOENT;
700}
701EXPORT_SYMBOL_NS_GPL(cs_amp_get_vendor_spkid, "SND_SOC_CS_AMP_LIB");
702
703/**
704 * cs_amp_create_debugfs - create a debugfs directory for a device
705 *
706 * @dev: pointer to struct device
707 *
708 * Creates a node under "cirrus_logic" in the root of the debugfs filesystem.
709 * This is for Cirrus-specific debugfs functionality to be grouped in a
710 * defined way, independently of the debugfs provided by ALSA/ASoC.
711 * The general ALSA/ASoC debugfs may not be enabled, and does not necessarily
712 * have a stable layout or naming convention.
713 *
714 * Return: Pointer to the dentry for the created directory, or -ENODEV.
715 */
716struct dentry *cs_amp_create_debugfs(struct device *dev)
717{
718	struct dentry *dir;
719
720	dir = debugfs_lookup("cirrus_logic", NULL);
721	if (!dir)
722		dir = debugfs_create_dir("cirrus_logic", NULL);
723
724	return debugfs_create_dir(dev_name(dev), dir);
725}
726EXPORT_SYMBOL_NS_GPL(cs_amp_create_debugfs, "SND_SOC_CS_AMP_LIB");
727
728static const struct cs_amp_test_hooks cs_amp_test_hook_ptrs = {
729	.get_efi_variable = cs_amp_get_efi_variable,
730	.set_efi_variable = cs_amp_set_efi_variable,
731	.write_cal_coeff = cs_amp_write_cal_coeff,
732	.read_cal_coeff = cs_amp_read_cal_coeff,
733};
734
735const struct cs_amp_test_hooks * const cs_amp_test_hooks =
736	PTR_IF(IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST), &cs_amp_test_hook_ptrs);
737EXPORT_SYMBOL_NS_GPL(cs_amp_test_hooks, "SND_SOC_CS_AMP_LIB");
738
739MODULE_DESCRIPTION("Cirrus Logic amplifier library");
740MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
741MODULE_LICENSE("GPL");
742MODULE_IMPORT_NS("FW_CS_DSP");
~/ Read-only file preview.