Unverified Commit 0c4ebb28 authored by Mark Brown's avatar Mark Brown
Browse files

ALSA: cs35l56: Apply calibration from EFI

Merge series from Richard Fitzgerald <rf@opensource.cirrus.com>:

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
parents c06a7a8e cfa43aaa
Loading
Loading
Loading
Loading
+52 −0
Original line number Diff line number Diff line
/* 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 −0
Original line number Diff line number Diff line
@@ -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
@@ -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
@@ -262,6 +266,9 @@ 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;
};

@@ -269,6 +276,8 @@ 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];

@@ -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);
+2 −0
Original line number Diff line number Diff line
@@ -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.
@@ -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.
+32 −7
Original line number Diff line number Diff line
@@ -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"
@@ -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;
@@ -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 (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);
@@ -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);
@@ -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;

@@ -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");
@@ -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 <rf@opensource.cirrus.com>");
MODULE_AUTHOR("Simon Trimmer <simont@opensource.cirrus.com>");
MODULE_LICENSE("GPL");
MODULE_IMPORT_NS(FW_CS_DSP);
+4 −0
Original line number Diff line number Diff line
@@ -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
@@ -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
Loading