mirror of
https://github.com/torvalds/linux.git
synced 2026-05-28 17:13:52 +02:00
ASoC: cs35l56: Support for restoring calibration on
Merge series from Richard Fitzgerald <rf@opensource.cirrus.com>: These two patches add ALSA controls to support restoring factory calibration during OS boot on ChromeOS. ChromeOS applies calibration during boot using a process that has restricted access permissions. This process needs ALSA controls for all settings that it must restore.
This commit is contained in:
commit
2b0d5d9b39
|
|
@ -16,6 +16,8 @@
|
|||
#include <linux/spi/spi.h>
|
||||
#include <sound/cs-amp-lib.h>
|
||||
|
||||
struct snd_ctl_elem_value;
|
||||
|
||||
#define CS35L56_DEVID 0x0000000
|
||||
#define CS35L56_REVID 0x0000004
|
||||
#define CS35L56_RELID 0x000000C
|
||||
|
|
@ -268,6 +270,10 @@
|
|||
#define CS35L56_CAL_STATUS_SUCCESS 1
|
||||
#define CS35L56_CAL_STATUS_OUT_OF_RANGE 3
|
||||
|
||||
#define CS35L56_CAL_SET_STATUS_UNKNOWN 0
|
||||
#define CS35L56_CAL_SET_STATUS_DEFAULT 1
|
||||
#define CS35L56_CAL_SET_STATUS_SET 2
|
||||
|
||||
#define CS35L56_CONTROL_PORT_READY_US 2200
|
||||
#define CS35L56_HALO_STATE_POLL_US 1000
|
||||
#define CS35L56_HALO_STATE_TIMEOUT_US 250000
|
||||
|
|
@ -363,6 +369,7 @@ extern const struct regmap_config cs35l63_regmap_i2c;
|
|||
extern const struct regmap_config cs35l63_regmap_sdw;
|
||||
|
||||
extern const struct cirrus_amp_cal_controls cs35l56_calibration_controls;
|
||||
extern const char * const cs35l56_cal_set_status_text[3];
|
||||
|
||||
extern const char * const cs35l56_tx_input_texts[CS35L56_NUM_INPUT_SRC];
|
||||
extern const unsigned int cs35l56_tx_input_values[CS35L56_NUM_INPUT_SRC];
|
||||
|
|
@ -381,6 +388,8 @@ 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_stash_calibration(struct cs35l56_base *cs35l56_base,
|
||||
const struct cirrus_amp_cal_data *data);
|
||||
ssize_t cs35l56_calibrate_debugfs_write(struct cs35l56_base *cs35l56_base,
|
||||
const char __user *from, size_t count,
|
||||
loff_t *ppos);
|
||||
|
|
@ -396,6 +405,8 @@ ssize_t cs35l56_cal_data_debugfs_write(struct cs35l56_base *cs35l56_base,
|
|||
void cs35l56_create_cal_debugfs(struct cs35l56_base *cs35l56_base,
|
||||
const struct cs35l56_cal_debugfs_fops *fops);
|
||||
void cs35l56_remove_cal_debugfs(struct cs35l56_base *cs35l56_base);
|
||||
int cs35l56_cal_set_status_get(struct cs35l56_base *cs35l56_base,
|
||||
struct snd_ctl_elem_value *uvalue);
|
||||
int cs35l56_read_prot_status(struct cs35l56_base *cs35l56_base,
|
||||
bool *fw_missing, unsigned int *fw_version);
|
||||
void cs35l56_log_tuning(struct cs35l56_base *cs35l56_base, struct cs_dsp *cs_dsp);
|
||||
|
|
|
|||
|
|
@ -912,6 +912,20 @@ config SND_SOC_CS35L56_CAL_DEBUGFS
|
|||
Create debugfs entries used during factory-line manufacture
|
||||
for factory calibration.
|
||||
|
||||
If unsure select "N".
|
||||
|
||||
config SND_SOC_CS35L56_CAL_SET_CTRL
|
||||
bool "CS35L56 ALSA control to restore factory calibration"
|
||||
default N
|
||||
select SND_SOC_CS35L56_CAL_SYSFS_COMMON
|
||||
help
|
||||
Allow restoring factory calibration data through an ALSA
|
||||
control. This is only needed on platforms without UEFI or
|
||||
some other method of non-volatile storage that the driver
|
||||
can access directly.
|
||||
|
||||
On most platforms this is not needed.
|
||||
|
||||
If unsure select "N".
|
||||
endmenu
|
||||
|
||||
|
|
|
|||
|
|
@ -962,8 +962,8 @@ int cs35l56_get_calibration(struct cs35l56_base *cs35l56_base)
|
|||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs35l56_get_calibration, "SND_SOC_CS35L56_SHARED");
|
||||
|
||||
static int cs35l56_stash_calibration(struct cs35l56_base *cs35l56_base,
|
||||
const struct cirrus_amp_cal_data *data)
|
||||
int cs35l56_stash_calibration(struct cs35l56_base *cs35l56_base,
|
||||
const struct cirrus_amp_cal_data *data)
|
||||
{
|
||||
|
||||
/* Ignore if it is empty */
|
||||
|
|
@ -980,6 +980,7 @@ static int cs35l56_stash_calibration(struct cs35l56_base *cs35l56_base,
|
|||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs35l56_stash_calibration, "SND_SOC_CS35L56_SHARED");
|
||||
|
||||
static int cs35l56_perform_calibration(struct cs35l56_base *cs35l56_base)
|
||||
{
|
||||
|
|
@ -1262,6 +1263,54 @@ void cs35l56_remove_cal_debugfs(struct cs35l56_base *cs35l56_base)
|
|||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs35l56_remove_cal_debugfs, "SND_SOC_CS35L56_SHARED");
|
||||
|
||||
const char * const cs35l56_cal_set_status_text[] = {
|
||||
"Unknown", "Default", "Set",
|
||||
};
|
||||
EXPORT_SYMBOL_NS_GPL(cs35l56_cal_set_status_text, "SND_SOC_CS35L56_SHARED");
|
||||
|
||||
int cs35l56_cal_set_status_get(struct cs35l56_base *cs35l56_base,
|
||||
struct snd_ctl_elem_value *uvalue)
|
||||
{
|
||||
struct cs_dsp *dsp = cs35l56_base->dsp;
|
||||
__be32 cal_set_status_be;
|
||||
int alg_id;
|
||||
int ret;
|
||||
|
||||
switch (cs35l56_base->type) {
|
||||
case 0x54:
|
||||
case 0x56:
|
||||
case 0x57:
|
||||
alg_id = 0x9f210;
|
||||
break;
|
||||
default:
|
||||
alg_id = 0xbf210;
|
||||
break;
|
||||
}
|
||||
|
||||
scoped_guard(mutex, &dsp->pwr_lock) {
|
||||
ret = cs_dsp_coeff_read_ctrl(cs_dsp_get_ctl(dsp,
|
||||
"CAL_SET_STATUS",
|
||||
WMFW_ADSP2_YM, alg_id),
|
||||
0, &cal_set_status_be,
|
||||
sizeof(cal_set_status_be));
|
||||
}
|
||||
if (ret) {
|
||||
uvalue->value.enumerated.item[0] = CS35L56_CAL_SET_STATUS_UNKNOWN;
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (be32_to_cpu(cal_set_status_be)) {
|
||||
case CS35L56_CAL_SET_STATUS_DEFAULT:
|
||||
case CS35L56_CAL_SET_STATUS_SET:
|
||||
uvalue->value.enumerated.item[0] = be32_to_cpu(cal_set_status_be);
|
||||
return 0;
|
||||
default:
|
||||
uvalue->value.enumerated.item[0] = CS35L56_CAL_SET_STATUS_UNKNOWN;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs35l56_cal_set_status_get, "SND_SOC_CS35L56_SHARED");
|
||||
|
||||
int cs35l56_read_prot_status(struct cs35l56_base *cs35l56_base,
|
||||
bool *fw_missing, unsigned int *fw_version)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -66,6 +66,18 @@ static int cs35l56_dspwait_put_volsw(struct snd_kcontrol *kcontrol,
|
|||
|
||||
static DECLARE_TLV_DB_SCALE(vol_tlv, -10000, 25, 0);
|
||||
|
||||
static SOC_ENUM_SINGLE_DECL(cs35l56_cal_set_status_enum, SND_SOC_NOPM, 0,
|
||||
cs35l56_cal_set_status_text);
|
||||
|
||||
static int cs35l56_cal_set_status_ctl_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
|
||||
struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component);
|
||||
|
||||
return cs35l56_cal_set_status_get(&cs35l56->base, ucontrol);
|
||||
}
|
||||
|
||||
static const struct snd_kcontrol_new cs35l56_controls[] = {
|
||||
SOC_SINGLE_EXT("Speaker Switch",
|
||||
CS35L56_MAIN_RENDER_USER_MUTE, 0, 1, 1,
|
||||
|
|
@ -83,6 +95,8 @@ static const struct snd_kcontrol_new cs35l56_controls[] = {
|
|||
SOC_SINGLE_EXT("Posture Number", CS35L56_MAIN_POSTURE_NUMBER,
|
||||
0, 255, 0,
|
||||
cs35l56_dspwait_get_volsw, cs35l56_dspwait_put_volsw),
|
||||
SOC_ENUM_EXT("CAL_SET_STATUS", cs35l56_cal_set_status_enum,
|
||||
cs35l56_cal_set_status_ctl_get, NULL),
|
||||
};
|
||||
|
||||
static const struct snd_kcontrol_new cs35l63_controls[] = {
|
||||
|
|
@ -102,6 +116,8 @@ static const struct snd_kcontrol_new cs35l63_controls[] = {
|
|||
SOC_SINGLE_EXT("Posture Number", CS35L63_MAIN_POSTURE_NUMBER,
|
||||
0, 255, 0,
|
||||
cs35l56_dspwait_get_volsw, cs35l56_dspwait_put_volsw),
|
||||
SOC_ENUM_EXT("CAL_SET_STATUS", cs35l56_cal_set_status_enum,
|
||||
cs35l56_cal_set_status_ctl_get, NULL),
|
||||
};
|
||||
|
||||
static SOC_VALUE_ENUM_SINGLE_DECL(cs35l56_asp1tx1_enum,
|
||||
|
|
@ -1024,6 +1040,67 @@ static const struct cs35l56_cal_debugfs_fops cs35l56_cal_debugfs_fops = {
|
|||
},
|
||||
};
|
||||
|
||||
static int cs35l56_cal_data_rb_ctl_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
|
||||
struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component);
|
||||
|
||||
if (!cs35l56->base.cal_data_valid)
|
||||
return -ENODATA;
|
||||
|
||||
memcpy(ucontrol->value.bytes.data, &cs35l56->base.cal_data,
|
||||
sizeof(cs35l56->base.cal_data));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cs35l56_cal_data_ctl_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
|
||||
struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component);
|
||||
|
||||
/*
|
||||
* This control is write-only but mixer libraries often try to read
|
||||
* a control before writing it. So we have to implement read.
|
||||
* Return zeros so a write of valid data will always be a change
|
||||
* from its "current value".
|
||||
*/
|
||||
memset(ucontrol->value.bytes.data, 0, sizeof(cs35l56->base.cal_data));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cs35l56_cal_data_ctl_set(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
|
||||
struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component);
|
||||
const struct cirrus_amp_cal_data *cal_data = (const void *)ucontrol->value.bytes.data;
|
||||
int ret;
|
||||
|
||||
if (cs35l56->base.cal_data_valid)
|
||||
return -EACCES;
|
||||
|
||||
ret = cs35l56_stash_calibration(&cs35l56->base, cal_data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = cs35l56_new_cal_data_apply(cs35l56);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const struct snd_kcontrol_new cs35l56_cal_data_restore_controls[] = {
|
||||
SND_SOC_BYTES_E("CAL_DATA", 0, sizeof(struct cirrus_amp_cal_data) / sizeof(u32),
|
||||
cs35l56_cal_data_ctl_get, cs35l56_cal_data_ctl_set),
|
||||
SND_SOC_BYTES_E("CAL_DATA_RB", 0, sizeof(struct cirrus_amp_cal_data) / sizeof(u32),
|
||||
cs35l56_cal_data_rb_ctl_get, NULL),
|
||||
};
|
||||
|
||||
static int cs35l56_set_fw_suffix(struct cs35l56_private *cs35l56)
|
||||
{
|
||||
if (cs35l56->dsp.fwf_suffix)
|
||||
|
|
@ -1118,6 +1195,12 @@ static int cs35l56_component_probe(struct snd_soc_component *component)
|
|||
break;
|
||||
}
|
||||
|
||||
if (!ret && IS_ENABLED(CONFIG_SND_SOC_CS35L56_CAL_SET_CTRL)) {
|
||||
ret = snd_soc_add_component_controls(component,
|
||||
cs35l56_cal_data_restore_controls,
|
||||
ARRAY_SIZE(cs35l56_cal_data_restore_controls));
|
||||
}
|
||||
|
||||
if (ret)
|
||||
return dev_err_probe(cs35l56->base.dev, ret, "unable to add controls\n");
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user