Skip to content

Commit

Permalink
ALSA: cs35l56: Apply calibration from EFI
Browse files Browse the repository at this point in the history
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 (thesofproject#5) depends on the ASoC patches #2 and #3
  • Loading branch information
broonie committed Feb 24, 2024
2 parents c06a7a8 + cfa43aa commit 0c4ebb2
Show file tree
Hide file tree
Showing 12 changed files with 529 additions and 19 deletions.
52 changes: 52 additions & 0 deletions include/sound/cs-amp-lib.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) 2024 Cirrus Logic, Inc. and
* Cirrus Logic International Semiconductor Ltd.
*/

#ifndef CS_AMP_LIB_H
#define CS_AMP_LIB_H

#include <linux/efi.h>
#include <linux/types.h>

struct cs_dsp;

struct cirrus_amp_cal_data {
u32 calTarget[2];
u32 calTime[2];
s8 calAmbient;
u8 calStatus;
u16 calR;
} __packed;

struct cirrus_amp_efi_data {
u32 size;
u32 count;
struct cirrus_amp_cal_data data[];
} __packed;

/**
* struct cirrus_amp_cal_controls - definition of firmware calibration controls
* @alg_id: ID of algorithm containing the controls.
* @mem_region: DSP memory region containing the controls.
* @ambient: Name of control for calAmbient value.
* @calr: Name of control for calR value.
* @status: Name of control for calStatus value.
* @checksum: Name of control for checksum value.
*/
struct cirrus_amp_cal_controls {
unsigned int alg_id;
int mem_region;
const char *ambient;
const char *calr;
const char *status;
const char *checksum;
};

int cs_amp_write_cal_coeffs(struct cs_dsp *dsp,
const struct cirrus_amp_cal_controls *controls,
const struct cirrus_amp_cal_data *data);
int cs_amp_get_efi_calibration_data(struct device *dev, u64 target_uid, int amp_index,
struct cirrus_amp_cal_data *out_data);
#endif /* CS_AMP_LIB_H */
10 changes: 10 additions & 0 deletions include/sound/cs35l56.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include <linux/firmware/cirrus/cs_dsp.h>
#include <linux/regulator/consumer.h>
#include <linux/regmap.h>
#include <sound/cs-amp-lib.h>

#define CS35L56_DEVID 0x0000000
#define CS35L56_REVID 0x0000004
Expand All @@ -23,6 +24,9 @@
#define CS35L56_BLOCK_ENABLES2 0x000201C
#define CS35L56_REFCLK_INPUT 0x0002C04
#define CS35L56_GLOBAL_SAMPLE_RATE 0x0002C0C
#define CS35L56_OTP_MEM_53 0x00300D4
#define CS35L56_OTP_MEM_54 0x00300D8
#define CS35L56_OTP_MEM_55 0x00300DC
#define CS35L56_ASP1_ENABLES1 0x0004800
#define CS35L56_ASP1_CONTROL1 0x0004804
#define CS35L56_ASP1_CONTROL2 0x0004808
Expand Down Expand Up @@ -262,13 +266,18 @@ struct cs35l56_base {
bool fw_patched;
bool secured;
bool can_hibernate;
bool cal_data_valid;
s8 cal_index;
struct cirrus_amp_cal_data cal_data;
struct gpio_desc *reset_gpio;
};

extern struct regmap_config cs35l56_regmap_i2c;
extern struct regmap_config cs35l56_regmap_spi;
extern struct regmap_config cs35l56_regmap_sdw;

extern const struct cirrus_amp_cal_controls cs35l56_calibration_controls;

extern const char * const cs35l56_tx_input_texts[CS35L56_NUM_INPUT_SRC];
extern const unsigned int cs35l56_tx_input_values[CS35L56_NUM_INPUT_SRC];

Expand All @@ -286,6 +295,7 @@ int cs35l56_is_fw_reload_needed(struct cs35l56_base *cs35l56_base);
int cs35l56_runtime_suspend_common(struct cs35l56_base *cs35l56_base);
int cs35l56_runtime_resume_common(struct cs35l56_base *cs35l56_base, bool is_soundwire);
void cs35l56_init_cs_dsp(struct cs35l56_base *cs35l56_base, struct cs_dsp *cs_dsp);
int cs35l56_get_calibration(struct cs35l56_base *cs35l56_base);
int cs35l56_read_prot_status(struct cs35l56_base *cs35l56_base,
bool *fw_missing, unsigned int *fw_version);
int cs35l56_hw_init(struct cs35l56_base *cs35l56_base);
Expand Down
2 changes: 2 additions & 0 deletions sound/pci/hda/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ config SND_HDA_SCODEC_CS35L56_I2C
select SND_HDA_SCODEC_CS35L56
select SND_HDA_CIRRUS_SCODEC
select SND_HDA_CS_DSP_CONTROLS
select SND_SOC_CS_AMP_LIB
help
Say Y or M here to include CS35L56 amplifier support with
I2C control.
Expand All @@ -177,6 +178,7 @@ config SND_HDA_SCODEC_CS35L56_SPI
select SND_HDA_SCODEC_CS35L56
select SND_HDA_CIRRUS_SCODEC
select SND_HDA_CS_DSP_CONTROLS
select SND_SOC_CS_AMP_LIB
help
Say Y or M here to include CS35L56 amplifier support with
SPI control.
Expand Down
39 changes: 32 additions & 7 deletions sound/pci/hda/cs35l56_hda.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include <linux/regmap.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/cs-amp-lib.h>
#include <sound/hda_codec.h>
#include <sound/tlv.h>
#include "cirrus_scodec.h"
Expand Down Expand Up @@ -547,6 +548,22 @@ static void cs35l56_hda_add_dsp_controls(struct cs35l56_hda *cs35l56)
hda_cs_dsp_add_controls(&cs35l56->cs_dsp, &info);
}

static void cs35l56_hda_apply_calibration(struct cs35l56_hda *cs35l56)
{
int ret;

if (!cs35l56->base.cal_data_valid || cs35l56->base.secured)
return;

ret = cs_amp_write_cal_coeffs(&cs35l56->cs_dsp,
&cs35l56_calibration_controls,
&cs35l56->base.cal_data);
if (ret < 0)
dev_warn(cs35l56->base.dev, "Failed to write calibration: %d\n", ret);
else
dev_info(cs35l56->base.dev, "Calibration applied\n");
}

static int cs35l56_hda_fw_load(struct cs35l56_hda *cs35l56)
{
const struct firmware *coeff_firmware = NULL;
Expand Down Expand Up @@ -618,12 +635,8 @@ static int cs35l56_hda_fw_load(struct cs35l56_hda *cs35l56)
if (coeff_filename)
dev_dbg(cs35l56->base.dev, "Loaded Coefficients: %s\n", coeff_filename);

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

cs35l56_hda_apply_calibration(cs35l56);
ret = cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_AUDIO_REINIT);
if (ret)
cs_dsp_stop(&cs35l56->cs_dsp);

err_powered_up:
if (!cs35l56->base.fw_patched)
cs_dsp_power_down(&cs35l56->cs_dsp);
Expand Down Expand Up @@ -953,6 +971,8 @@ int cs35l56_hda_common_probe(struct cs35l56_hda *cs35l56, int id)
goto err;
}

cs35l56->base.cal_index = cs35l56->index;

cs35l56_init_cs_dsp(&cs35l56->base, &cs35l56->cs_dsp);
cs35l56->cs_dsp.client_ops = &cs35l56_hda_client_ops;

Expand Down Expand Up @@ -990,6 +1010,10 @@ int cs35l56_hda_common_probe(struct cs35l56_hda *cs35l56, int id)
if (ret)
goto err;

ret = cs35l56_get_calibration(&cs35l56->base);
if (ret)
goto err;

ret = cs_dsp_halo_init(&cs35l56->cs_dsp);
if (ret) {
dev_err_probe(cs35l56->base.dev, ret, "cs_dsp_halo_init failed\n");
Expand Down Expand Up @@ -1064,10 +1088,11 @@ const struct dev_pm_ops cs35l56_hda_pm_ops = {
EXPORT_SYMBOL_NS_GPL(cs35l56_hda_pm_ops, SND_HDA_SCODEC_CS35L56);

MODULE_DESCRIPTION("CS35L56 HDA Driver");
MODULE_IMPORT_NS(FW_CS_DSP);
MODULE_IMPORT_NS(SND_HDA_CIRRUS_SCODEC);
MODULE_IMPORT_NS(SND_HDA_CS_DSP_CONTROLS);
MODULE_IMPORT_NS(SND_SOC_CS35L56_SHARED);
MODULE_IMPORT_NS(SND_SOC_CS_AMP_LIB);
MODULE_AUTHOR("Richard Fitzgerald <[email protected]>");
MODULE_AUTHOR("Simon Trimmer <[email protected]>");
MODULE_LICENSE("GPL");
MODULE_IMPORT_NS(FW_CS_DSP);
4 changes: 4 additions & 0 deletions sound/soc/codecs/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -729,6 +729,9 @@ config SND_SOC_CROS_EC_CODEC
If you say yes here you will get support for the
ChromeOS Embedded Controller's Audio Codec.

config SND_SOC_CS_AMP_LIB
tristate

config SND_SOC_CS35L32
tristate "Cirrus Logic CS35L32 CODEC"
depends on I2C
Expand Down Expand Up @@ -797,6 +800,7 @@ config SND_SOC_CS35L56
tristate

config SND_SOC_CS35L56_SHARED
select SND_SOC_CS_AMP_LIB
tristate

config SND_SOC_CS35L56_I2C
Expand Down
2 changes: 2 additions & 0 deletions sound/soc/codecs/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ snd-soc-chv3-codec-objs := chv3-codec.o
snd-soc-cpcap-objs := cpcap.o
snd-soc-cq93vc-objs := cq93vc.o
snd-soc-cros-ec-codec-objs := cros_ec_codec.o
snd-soc-cs-amp-lib-objs := cs-amp-lib.o
snd-soc-cs35l32-objs := cs35l32.o
snd-soc-cs35l33-objs := cs35l33.o
snd-soc-cs35l34-objs := cs35l34.o
Expand Down Expand Up @@ -452,6 +453,7 @@ obj-$(CONFIG_SND_SOC_CHV3_CODEC) += snd-soc-chv3-codec.o
obj-$(CONFIG_SND_SOC_CQ0093VC) += snd-soc-cq93vc.o
obj-$(CONFIG_SND_SOC_CPCAP) += snd-soc-cpcap.o
obj-$(CONFIG_SND_SOC_CROS_EC_CODEC) += snd-soc-cros-ec-codec.o
obj-$(CONFIG_SND_SOC_CS_AMP_LIB) += snd-soc-cs-amp-lib.o
obj-$(CONFIG_SND_SOC_CS35L32) += snd-soc-cs35l32.o
obj-$(CONFIG_SND_SOC_CS35L33) += snd-soc-cs35l33.o
obj-$(CONFIG_SND_SOC_CS35L34) += snd-soc-cs35l34.o
Expand Down
Loading

0 comments on commit 0c4ebb2

Please sign in to comment.