Skip to content

Commit 0c4ebb2

Browse files
committed
ALSA: cs35l56: Apply calibration from EFI
Merge series from Richard Fitzgerald <[email protected]>: Factory calibration of the speakers stores the calibration information into an EFI variable. This set of patches adds support for applying speaker calibration data from that EFI variable. The HDA patch (#5) depends on the ASoC patches #2 and #3
2 parents c06a7a8 + cfa43aa commit 0c4ebb2

File tree

12 files changed

+529
-19
lines changed

12 files changed

+529
-19
lines changed

include/sound/cs-amp-lib.h

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/* SPDX-License-Identifier: GPL-2.0-only */
2+
/*
3+
* Copyright (C) 2024 Cirrus Logic, Inc. and
4+
* Cirrus Logic International Semiconductor Ltd.
5+
*/
6+
7+
#ifndef CS_AMP_LIB_H
8+
#define CS_AMP_LIB_H
9+
10+
#include <linux/efi.h>
11+
#include <linux/types.h>
12+
13+
struct cs_dsp;
14+
15+
struct cirrus_amp_cal_data {
16+
u32 calTarget[2];
17+
u32 calTime[2];
18+
s8 calAmbient;
19+
u8 calStatus;
20+
u16 calR;
21+
} __packed;
22+
23+
struct cirrus_amp_efi_data {
24+
u32 size;
25+
u32 count;
26+
struct cirrus_amp_cal_data data[];
27+
} __packed;
28+
29+
/**
30+
* struct cirrus_amp_cal_controls - definition of firmware calibration controls
31+
* @alg_id: ID of algorithm containing the controls.
32+
* @mem_region: DSP memory region containing the controls.
33+
* @ambient: Name of control for calAmbient value.
34+
* @calr: Name of control for calR value.
35+
* @status: Name of control for calStatus value.
36+
* @checksum: Name of control for checksum value.
37+
*/
38+
struct cirrus_amp_cal_controls {
39+
unsigned int alg_id;
40+
int mem_region;
41+
const char *ambient;
42+
const char *calr;
43+
const char *status;
44+
const char *checksum;
45+
};
46+
47+
int cs_amp_write_cal_coeffs(struct cs_dsp *dsp,
48+
const struct cirrus_amp_cal_controls *controls,
49+
const struct cirrus_amp_cal_data *data);
50+
int cs_amp_get_efi_calibration_data(struct device *dev, u64 target_uid, int amp_index,
51+
struct cirrus_amp_cal_data *out_data);
52+
#endif /* CS_AMP_LIB_H */

include/sound/cs35l56.h

+10
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <linux/firmware/cirrus/cs_dsp.h>
1313
#include <linux/regulator/consumer.h>
1414
#include <linux/regmap.h>
15+
#include <sound/cs-amp-lib.h>
1516

1617
#define CS35L56_DEVID 0x0000000
1718
#define CS35L56_REVID 0x0000004
@@ -23,6 +24,9 @@
2324
#define CS35L56_BLOCK_ENABLES2 0x000201C
2425
#define CS35L56_REFCLK_INPUT 0x0002C04
2526
#define CS35L56_GLOBAL_SAMPLE_RATE 0x0002C0C
27+
#define CS35L56_OTP_MEM_53 0x00300D4
28+
#define CS35L56_OTP_MEM_54 0x00300D8
29+
#define CS35L56_OTP_MEM_55 0x00300DC
2630
#define CS35L56_ASP1_ENABLES1 0x0004800
2731
#define CS35L56_ASP1_CONTROL1 0x0004804
2832
#define CS35L56_ASP1_CONTROL2 0x0004808
@@ -262,13 +266,18 @@ struct cs35l56_base {
262266
bool fw_patched;
263267
bool secured;
264268
bool can_hibernate;
269+
bool cal_data_valid;
270+
s8 cal_index;
271+
struct cirrus_amp_cal_data cal_data;
265272
struct gpio_desc *reset_gpio;
266273
};
267274

268275
extern struct regmap_config cs35l56_regmap_i2c;
269276
extern struct regmap_config cs35l56_regmap_spi;
270277
extern struct regmap_config cs35l56_regmap_sdw;
271278

279+
extern const struct cirrus_amp_cal_controls cs35l56_calibration_controls;
280+
272281
extern const char * const cs35l56_tx_input_texts[CS35L56_NUM_INPUT_SRC];
273282
extern const unsigned int cs35l56_tx_input_values[CS35L56_NUM_INPUT_SRC];
274283

@@ -286,6 +295,7 @@ int cs35l56_is_fw_reload_needed(struct cs35l56_base *cs35l56_base);
286295
int cs35l56_runtime_suspend_common(struct cs35l56_base *cs35l56_base);
287296
int cs35l56_runtime_resume_common(struct cs35l56_base *cs35l56_base, bool is_soundwire);
288297
void cs35l56_init_cs_dsp(struct cs35l56_base *cs35l56_base, struct cs_dsp *cs_dsp);
298+
int cs35l56_get_calibration(struct cs35l56_base *cs35l56_base);
289299
int cs35l56_read_prot_status(struct cs35l56_base *cs35l56_base,
290300
bool *fw_missing, unsigned int *fw_version);
291301
int cs35l56_hw_init(struct cs35l56_base *cs35l56_base);

sound/pci/hda/Kconfig

+2
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@ config SND_HDA_SCODEC_CS35L56_I2C
162162
select SND_HDA_SCODEC_CS35L56
163163
select SND_HDA_CIRRUS_SCODEC
164164
select SND_HDA_CS_DSP_CONTROLS
165+
select SND_SOC_CS_AMP_LIB
165166
help
166167
Say Y or M here to include CS35L56 amplifier support with
167168
I2C control.
@@ -177,6 +178,7 @@ config SND_HDA_SCODEC_CS35L56_SPI
177178
select SND_HDA_SCODEC_CS35L56
178179
select SND_HDA_CIRRUS_SCODEC
179180
select SND_HDA_CS_DSP_CONTROLS
181+
select SND_SOC_CS_AMP_LIB
180182
help
181183
Say Y or M here to include CS35L56 amplifier support with
182184
SPI control.

sound/pci/hda/cs35l56_hda.c

+32-7
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include <linux/regmap.h>
1515
#include <linux/slab.h>
1616
#include <sound/core.h>
17+
#include <sound/cs-amp-lib.h>
1718
#include <sound/hda_codec.h>
1819
#include <sound/tlv.h>
1920
#include "cirrus_scodec.h"
@@ -547,6 +548,22 @@ static void cs35l56_hda_add_dsp_controls(struct cs35l56_hda *cs35l56)
547548
hda_cs_dsp_add_controls(&cs35l56->cs_dsp, &info);
548549
}
549550

551+
static void cs35l56_hda_apply_calibration(struct cs35l56_hda *cs35l56)
552+
{
553+
int ret;
554+
555+
if (!cs35l56->base.cal_data_valid || cs35l56->base.secured)
556+
return;
557+
558+
ret = cs_amp_write_cal_coeffs(&cs35l56->cs_dsp,
559+
&cs35l56_calibration_controls,
560+
&cs35l56->base.cal_data);
561+
if (ret < 0)
562+
dev_warn(cs35l56->base.dev, "Failed to write calibration: %d\n", ret);
563+
else
564+
dev_info(cs35l56->base.dev, "Calibration applied\n");
565+
}
566+
550567
static int cs35l56_hda_fw_load(struct cs35l56_hda *cs35l56)
551568
{
552569
const struct firmware *coeff_firmware = NULL;
@@ -618,12 +635,8 @@ static int cs35l56_hda_fw_load(struct cs35l56_hda *cs35l56)
618635
if (coeff_filename)
619636
dev_dbg(cs35l56->base.dev, "Loaded Coefficients: %s\n", coeff_filename);
620637

621-
if (!firmware_missing) {
622-
ret = cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_AUDIO_REINIT);
623-
if (ret)
624-
goto err_powered_up;
625-
} else if (wmfw_firmware || coeff_firmware) {
626-
/* If we downloaded firmware, reset the device and wait for it to boot */
638+
/* If we downloaded firmware, reset the device and wait for it to boot */
639+
if (firmware_missing && (wmfw_firmware || coeff_firmware)) {
627640
cs35l56_system_reset(&cs35l56->base, false);
628641
regcache_mark_dirty(cs35l56->base.regmap);
629642
ret = cs35l56_wait_for_firmware_boot(&cs35l56->base);
@@ -646,6 +659,11 @@ static int cs35l56_hda_fw_load(struct cs35l56_hda *cs35l56)
646659
if (ret)
647660
dev_dbg(cs35l56->base.dev, "%s: cs_dsp_run ret %d\n", __func__, ret);
648661

662+
cs35l56_hda_apply_calibration(cs35l56);
663+
ret = cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_AUDIO_REINIT);
664+
if (ret)
665+
cs_dsp_stop(&cs35l56->cs_dsp);
666+
649667
err_powered_up:
650668
if (!cs35l56->base.fw_patched)
651669
cs_dsp_power_down(&cs35l56->cs_dsp);
@@ -953,6 +971,8 @@ int cs35l56_hda_common_probe(struct cs35l56_hda *cs35l56, int id)
953971
goto err;
954972
}
955973

974+
cs35l56->base.cal_index = cs35l56->index;
975+
956976
cs35l56_init_cs_dsp(&cs35l56->base, &cs35l56->cs_dsp);
957977
cs35l56->cs_dsp.client_ops = &cs35l56_hda_client_ops;
958978

@@ -990,6 +1010,10 @@ int cs35l56_hda_common_probe(struct cs35l56_hda *cs35l56, int id)
9901010
if (ret)
9911011
goto err;
9921012

1013+
ret = cs35l56_get_calibration(&cs35l56->base);
1014+
if (ret)
1015+
goto err;
1016+
9931017
ret = cs_dsp_halo_init(&cs35l56->cs_dsp);
9941018
if (ret) {
9951019
dev_err_probe(cs35l56->base.dev, ret, "cs_dsp_halo_init failed\n");
@@ -1064,10 +1088,11 @@ const struct dev_pm_ops cs35l56_hda_pm_ops = {
10641088
EXPORT_SYMBOL_NS_GPL(cs35l56_hda_pm_ops, SND_HDA_SCODEC_CS35L56);
10651089

10661090
MODULE_DESCRIPTION("CS35L56 HDA Driver");
1091+
MODULE_IMPORT_NS(FW_CS_DSP);
10671092
MODULE_IMPORT_NS(SND_HDA_CIRRUS_SCODEC);
10681093
MODULE_IMPORT_NS(SND_HDA_CS_DSP_CONTROLS);
10691094
MODULE_IMPORT_NS(SND_SOC_CS35L56_SHARED);
1095+
MODULE_IMPORT_NS(SND_SOC_CS_AMP_LIB);
10701096
MODULE_AUTHOR("Richard Fitzgerald <[email protected]>");
10711097
MODULE_AUTHOR("Simon Trimmer <[email protected]>");
10721098
MODULE_LICENSE("GPL");
1073-
MODULE_IMPORT_NS(FW_CS_DSP);

sound/soc/codecs/Kconfig

+4
Original file line numberDiff line numberDiff line change
@@ -729,6 +729,9 @@ config SND_SOC_CROS_EC_CODEC
729729
If you say yes here you will get support for the
730730
ChromeOS Embedded Controller's Audio Codec.
731731

732+
config SND_SOC_CS_AMP_LIB
733+
tristate
734+
732735
config SND_SOC_CS35L32
733736
tristate "Cirrus Logic CS35L32 CODEC"
734737
depends on I2C
@@ -797,6 +800,7 @@ config SND_SOC_CS35L56
797800
tristate
798801

799802
config SND_SOC_CS35L56_SHARED
803+
select SND_SOC_CS_AMP_LIB
800804
tristate
801805

802806
config SND_SOC_CS35L56_I2C

sound/soc/codecs/Makefile

+2
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ snd-soc-chv3-codec-objs := chv3-codec.o
5959
snd-soc-cpcap-objs := cpcap.o
6060
snd-soc-cq93vc-objs := cq93vc.o
6161
snd-soc-cros-ec-codec-objs := cros_ec_codec.o
62+
snd-soc-cs-amp-lib-objs := cs-amp-lib.o
6263
snd-soc-cs35l32-objs := cs35l32.o
6364
snd-soc-cs35l33-objs := cs35l33.o
6465
snd-soc-cs35l34-objs := cs35l34.o
@@ -452,6 +453,7 @@ obj-$(CONFIG_SND_SOC_CHV3_CODEC) += snd-soc-chv3-codec.o
452453
obj-$(CONFIG_SND_SOC_CQ0093VC) += snd-soc-cq93vc.o
453454
obj-$(CONFIG_SND_SOC_CPCAP) += snd-soc-cpcap.o
454455
obj-$(CONFIG_SND_SOC_CROS_EC_CODEC) += snd-soc-cros-ec-codec.o
456+
obj-$(CONFIG_SND_SOC_CS_AMP_LIB) += snd-soc-cs-amp-lib.o
455457
obj-$(CONFIG_SND_SOC_CS35L32) += snd-soc-cs35l32.o
456458
obj-$(CONFIG_SND_SOC_CS35L33) += snd-soc-cs35l33.o
457459
obj-$(CONFIG_SND_SOC_CS35L34) += snd-soc-cs35l34.o

0 commit comments

Comments
 (0)