diff options
Diffstat (limited to 'sound/pci/hda/cs35l41_hda.c')
-rw-r--r-- | sound/pci/hda/cs35l41_hda.c | 423 |
1 files changed, 293 insertions, 130 deletions
diff --git a/sound/pci/hda/cs35l41_hda.c b/sound/pci/hda/cs35l41_hda.c index d3fa6e1367..031703f010 100644 --- a/sound/pci/hda/cs35l41_hda.c +++ b/sound/pci/hda/cs35l41_hda.c @@ -13,6 +13,7 @@ #include <sound/soc.h> #include <linux/pm_runtime.h> #include <linux/spi/spi.h> +#include <linux/vmalloc.h> #include "hda_local.h" #include "hda_auto_parser.h" #include "hda_jack.h" @@ -22,7 +23,6 @@ #include "hda_cs_dsp_ctl.h" #include "cs35l41_hda_property.h" -#define CS35L41_FIRMWARE_ROOT "cirrus/" #define CS35L41_PART "cs35l41" #define HALO_STATE_DSP_CTL_NAME "HALO_STATE" @@ -37,6 +37,42 @@ #define CS35L41_UUID "50d90cdc-3de4-4f18-b528-c7fe3b71f40d" #define CS35L41_DSM_GET_MUTE 5 #define CS35L41_NOTIFY_EVENT 0x91 +#define CS35L41_TUNING_SIG 0x109A4A35 + +enum cs35l41_tuning_param_types { + TUNING_PARAM_GAIN, +}; + +struct cs35l41_tuning_param_hdr { + __le32 tuning_index; + __le32 type; + __le32 size; +} __packed; + +struct cs35l41_tuning_param { + struct cs35l41_tuning_param_hdr hdr; + union { + __le32 gain; + }; +} __packed; + +struct cs35l41_tuning_params { + __le32 signature; + __le32 version; + __le32 size; + __le32 num_entries; + u8 data[]; +} __packed; + +/* Firmware calibration controls */ +static const struct cirrus_amp_cal_controls cs35l41_calibration_controls = { + .alg_id = CAL_DSP_CTL_ALG, + .mem_region = CAL_DSP_CTL_TYPE, + .ambient = CAL_AMBIENT_DSP_CTL_NAME, + .calr = CAL_R_DSP_CTL_NAME, + .status = CAL_STATUS_DSP_CTL_NAME, + .checksum = CAL_CHECKSUM_DSP_CTL_NAME, +}; static bool firmware_autostart = 1; module_param(firmware_autostart, bool, 0444); @@ -75,7 +111,7 @@ static const struct reg_sequence cs35l41_hda_config_dsp[] = { { CS35L41_SP_HIZ_CTRL, 0x00000003 }, // Hi-Z unused/disabled { CS35L41_SP_TX_WL, 0x00000018 }, // 24 cycles/slot { CS35L41_SP_RX_WL, 0x00000018 }, // 24 cycles/slot - { CS35L41_DAC_PCM1_SRC, 0x00000032 }, // DACPCM1_SRC = ERR_VOL + { CS35L41_DAC_PCM1_SRC, 0x00000032 }, // DACPCM1_SRC = DSP1TX1 { CS35L41_ASP_TX1_SRC, 0x00000018 }, // ASPTX1 SRC = VMON { CS35L41_ASP_TX2_SRC, 0x00000019 }, // ASPTX2 SRC = IMON { CS35L41_ASP_TX3_SRC, 0x00000028 }, // ASPTX3 SRC = VPMON @@ -84,7 +120,7 @@ static const struct reg_sequence cs35l41_hda_config_dsp[] = { { CS35L41_DSP1_RX2_SRC, 0x00000008 }, // DSP1RX2 SRC = ASPRX1 { CS35L41_DSP1_RX3_SRC, 0x00000018 }, // DSP1RX3 SRC = VMON { CS35L41_DSP1_RX4_SRC, 0x00000019 }, // DSP1RX4 SRC = IMON - { CS35L41_DSP1_RX5_SRC, 0x00000029 }, // DSP1RX5 SRC = VBSTMON + { CS35L41_DSP1_RX6_SRC, 0x00000029 }, // DSP1RX6 SRC = VBSTMON }; static const struct reg_sequence cs35l41_hda_unmute[] = { @@ -92,11 +128,6 @@ static const struct reg_sequence cs35l41_hda_unmute[] = { { CS35L41_AMP_GAIN_CTRL, 0x00000084 }, // AMP_GAIN_PCM 4.5 dB }; -static const struct reg_sequence cs35l41_hda_unmute_dsp[] = { - { CS35L41_AMP_DIG_VOL_CTRL, 0x00008000 }, // AMP_HPF_PCM_EN = 1, AMP_VOL_PCM 0.0 dB - { CS35L41_AMP_GAIN_CTRL, 0x00000233 }, // AMP_GAIN_PCM = 17.5dB AMP_GAIN_PDM = 19.5dB -}; - static const struct reg_sequence cs35l41_hda_mute[] = { { CS35L41_AMP_GAIN_CTRL, 0x00000000 }, // AMP_GAIN_PCM 0.5 dB { CS35L41_AMP_DIG_VOL_CTRL, 0x0000A678 }, // AMP_HPF_PCM_EN = 1, AMP_VOL_PCM Mute @@ -117,9 +148,30 @@ static const struct cs_dsp_client_ops client_ops = { .control_remove = hda_cs_dsp_control_remove, }; +static int cs35l41_request_tuning_param_file(struct cs35l41_hda *cs35l41, char *tuning_filename, + const struct firmware **firmware, char **filename, + const char *ssid) +{ + int ret = 0; + + /* Filename is the same as the tuning file with "cfg" suffix */ + *filename = kasprintf(GFP_KERNEL, "%scfg", tuning_filename); + if (*filename == NULL) + return -ENOMEM; + + ret = firmware_request_nowarn(firmware, *filename, cs35l41->dev); + if (ret != 0) { + dev_dbg(cs35l41->dev, "Failed to request '%s'\n", *filename); + kfree(*filename); + *filename = NULL; + } + + return ret; +} + static int cs35l41_request_firmware_file(struct cs35l41_hda *cs35l41, const struct firmware **firmware, char **filename, - const char *dir, const char *ssid, const char *amp_name, + const char *ssid, const char *amp_name, int spkid, const char *filetype) { const char * const dsp_name = cs35l41->cs_dsp.name; @@ -127,23 +179,23 @@ static int cs35l41_request_firmware_file(struct cs35l41_hda *cs35l41, int ret = 0; if (spkid > -1 && ssid && amp_name) - *filename = kasprintf(GFP_KERNEL, "%s%s-%s-%s-%s-spkid%d-%s.%s", dir, CS35L41_PART, + *filename = kasprintf(GFP_KERNEL, "cirrus/%s-%s-%s-%s-spkid%d-%s.%s", CS35L41_PART, dsp_name, hda_cs_dsp_fw_ids[cs35l41->firmware_type], ssid, spkid, amp_name, filetype); else if (spkid > -1 && ssid) - *filename = kasprintf(GFP_KERNEL, "%s%s-%s-%s-%s-spkid%d.%s", dir, CS35L41_PART, + *filename = kasprintf(GFP_KERNEL, "cirrus/%s-%s-%s-%s-spkid%d.%s", CS35L41_PART, dsp_name, hda_cs_dsp_fw_ids[cs35l41->firmware_type], ssid, spkid, filetype); else if (ssid && amp_name) - *filename = kasprintf(GFP_KERNEL, "%s%s-%s-%s-%s-%s.%s", dir, CS35L41_PART, + *filename = kasprintf(GFP_KERNEL, "cirrus/%s-%s-%s-%s-%s.%s", CS35L41_PART, dsp_name, hda_cs_dsp_fw_ids[cs35l41->firmware_type], ssid, amp_name, filetype); else if (ssid) - *filename = kasprintf(GFP_KERNEL, "%s%s-%s-%s-%s.%s", dir, CS35L41_PART, + *filename = kasprintf(GFP_KERNEL, "cirrus/%s-%s-%s-%s.%s", CS35L41_PART, dsp_name, hda_cs_dsp_fw_ids[cs35l41->firmware_type], ssid, filetype); else - *filename = kasprintf(GFP_KERNEL, "%s%s-%s-%s.%s", dir, CS35L41_PART, + *filename = kasprintf(GFP_KERNEL, "cirrus/%s-%s-%s.%s", CS35L41_PART, dsp_name, hda_cs_dsp_fw_ids[cs35l41->firmware_type], filetype); @@ -184,13 +236,11 @@ static int cs35l41_request_firmware_files_spkid(struct cs35l41_hda *cs35l41, /* try cirrus/part-dspN-fwtype-sub<-spkidN><-ampname>.wmfw */ ret = cs35l41_request_firmware_file(cs35l41, wmfw_firmware, wmfw_filename, - CS35L41_FIRMWARE_ROOT, cs35l41->acpi_subsystem_id, cs35l41->amp_name, cs35l41->speaker_id, "wmfw"); if (!ret) { /* try cirrus/part-dspN-fwtype-sub<-spkidN><-ampname>.bin */ ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename, - CS35L41_FIRMWARE_ROOT, cs35l41->acpi_subsystem_id, cs35l41->amp_name, cs35l41->speaker_id, "bin"); if (ret) @@ -201,12 +251,11 @@ static int cs35l41_request_firmware_files_spkid(struct cs35l41_hda *cs35l41, /* try cirrus/part-dspN-fwtype-sub<-ampname>.wmfw */ ret = cs35l41_request_firmware_file(cs35l41, wmfw_firmware, wmfw_filename, - CS35L41_FIRMWARE_ROOT, cs35l41->acpi_subsystem_id, + cs35l41->acpi_subsystem_id, cs35l41->amp_name, -1, "wmfw"); if (!ret) { /* try cirrus/part-dspN-fwtype-sub<-spkidN><-ampname>.bin */ ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename, - CS35L41_FIRMWARE_ROOT, cs35l41->acpi_subsystem_id, cs35l41->amp_name, cs35l41->speaker_id, "bin"); if (ret) @@ -217,18 +266,17 @@ static int cs35l41_request_firmware_files_spkid(struct cs35l41_hda *cs35l41, /* try cirrus/part-dspN-fwtype-sub<-spkidN>.wmfw */ ret = cs35l41_request_firmware_file(cs35l41, wmfw_firmware, wmfw_filename, - CS35L41_FIRMWARE_ROOT, cs35l41->acpi_subsystem_id, + cs35l41->acpi_subsystem_id, NULL, cs35l41->speaker_id, "wmfw"); if (!ret) { /* try cirrus/part-dspN-fwtype-sub<-spkidN><-ampname>.bin */ ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename, - CS35L41_FIRMWARE_ROOT, cs35l41->acpi_subsystem_id, cs35l41->amp_name, cs35l41->speaker_id, "bin"); if (ret) /* try cirrus/part-dspN-fwtype-sub<-spkidN>.bin */ ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, - coeff_filename, CS35L41_FIRMWARE_ROOT, + coeff_filename, cs35l41->acpi_subsystem_id, NULL, cs35l41->speaker_id, "bin"); if (ret) @@ -239,18 +287,17 @@ static int cs35l41_request_firmware_files_spkid(struct cs35l41_hda *cs35l41, /* try cirrus/part-dspN-fwtype-sub.wmfw */ ret = cs35l41_request_firmware_file(cs35l41, wmfw_firmware, wmfw_filename, - CS35L41_FIRMWARE_ROOT, cs35l41->acpi_subsystem_id, + cs35l41->acpi_subsystem_id, NULL, -1, "wmfw"); if (!ret) { /* try cirrus/part-dspN-fwtype-sub<-spkidN><-ampname>.bin */ ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename, - CS35L41_FIRMWARE_ROOT, cs35l41->acpi_subsystem_id, cs35l41->amp_name, cs35l41->speaker_id, "bin"); if (ret) /* try cirrus/part-dspN-fwtype-sub<-spkidN>.bin */ ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, - coeff_filename, CS35L41_FIRMWARE_ROOT, + coeff_filename, cs35l41->acpi_subsystem_id, NULL, cs35l41->speaker_id, "bin"); if (ret) @@ -277,13 +324,13 @@ static int cs35l41_fallback_firmware_file(struct cs35l41_hda *cs35l41, /* fallback try cirrus/part-dspN-fwtype.wmfw */ ret = cs35l41_request_firmware_file(cs35l41, wmfw_firmware, wmfw_filename, - CS35L41_FIRMWARE_ROOT, NULL, NULL, -1, "wmfw"); + NULL, NULL, -1, "wmfw"); if (ret) goto err; /* fallback try cirrus/part-dspN-fwtype.bin */ ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename, - CS35L41_FIRMWARE_ROOT, NULL, NULL, -1, "bin"); + NULL, NULL, -1, "bin"); if (ret) { release_firmware(*wmfw_firmware); kfree(*wmfw_filename); @@ -312,12 +359,11 @@ static int cs35l41_request_firmware_files(struct cs35l41_hda *cs35l41, /* try cirrus/part-dspN-fwtype-sub<-ampname>.wmfw */ ret = cs35l41_request_firmware_file(cs35l41, wmfw_firmware, wmfw_filename, - CS35L41_FIRMWARE_ROOT, cs35l41->acpi_subsystem_id, + cs35l41->acpi_subsystem_id, cs35l41->amp_name, -1, "wmfw"); if (!ret) { /* try cirrus/part-dspN-fwtype-sub<-ampname>.bin */ ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename, - CS35L41_FIRMWARE_ROOT, cs35l41->acpi_subsystem_id, cs35l41->amp_name, -1, "bin"); if (ret) @@ -328,18 +374,16 @@ static int cs35l41_request_firmware_files(struct cs35l41_hda *cs35l41, /* try cirrus/part-dspN-fwtype-sub.wmfw */ ret = cs35l41_request_firmware_file(cs35l41, wmfw_firmware, wmfw_filename, - CS35L41_FIRMWARE_ROOT, cs35l41->acpi_subsystem_id, + cs35l41->acpi_subsystem_id, NULL, -1, "wmfw"); if (!ret) { /* try cirrus/part-dspN-fwtype-sub<-ampname>.bin */ ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename, - CS35L41_FIRMWARE_ROOT, cs35l41->acpi_subsystem_id, cs35l41->amp_name, -1, "bin"); if (ret) /* try cirrus/part-dspN-fwtype-sub.bin */ ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename, - CS35L41_FIRMWARE_ROOT, cs35l41->acpi_subsystem_id, NULL, -1, "bin"); if (ret) @@ -361,95 +405,162 @@ fallback: coeff_firmware, coeff_filename); } -#if IS_ENABLED(CONFIG_EFI) -static int cs35l41_apply_calibration(struct cs35l41_hda *cs35l41, __be32 ambient, __be32 r0, - __be32 status, __be32 checksum) + +static void cs35l41_hda_apply_calibration(struct cs35l41_hda *cs35l41) +{ + int ret; + + if (!cs35l41->cal_data_valid) + return; + + ret = cs_amp_write_cal_coeffs(&cs35l41->cs_dsp, &cs35l41_calibration_controls, + &cs35l41->cal_data); + if (ret < 0) + dev_warn(cs35l41->dev, "Failed to apply calibration: %d\n", ret); + else + dev_info(cs35l41->dev, "Calibration applied: R0=%d\n", cs35l41->cal_data.calR); +} + +static int cs35l41_read_silicon_uid(struct cs35l41_hda *cs35l41, u64 *uid) { + u32 tmp; int ret; - ret = hda_cs_dsp_write_ctl(&cs35l41->cs_dsp, CAL_AMBIENT_DSP_CTL_NAME, CAL_DSP_CTL_TYPE, - CAL_DSP_CTL_ALG, &ambient, 4); + ret = regmap_read(cs35l41->regmap, CS35L41_DIE_STS2, &tmp); if (ret) { - dev_err(cs35l41->dev, "Cannot Write Control: %s - %d\n", CAL_AMBIENT_DSP_CTL_NAME, - ret); + dev_err(cs35l41->dev, "Cannot obtain CS35L41_DIE_STS2: %d\n", ret); return ret; } - ret = hda_cs_dsp_write_ctl(&cs35l41->cs_dsp, CAL_R_DSP_CTL_NAME, CAL_DSP_CTL_TYPE, - CAL_DSP_CTL_ALG, &r0, 4); + + *uid = tmp; + *uid <<= 32; + + ret = regmap_read(cs35l41->regmap, CS35L41_DIE_STS1, &tmp); if (ret) { - dev_err(cs35l41->dev, "Cannot Write Control: %s - %d\n", CAL_R_DSP_CTL_NAME, ret); + dev_err(cs35l41->dev, "Cannot obtain CS35L41_DIE_STS1: %d\n", ret); return ret; } - ret = hda_cs_dsp_write_ctl(&cs35l41->cs_dsp, CAL_STATUS_DSP_CTL_NAME, CAL_DSP_CTL_TYPE, - CAL_DSP_CTL_ALG, &status, 4); - if (ret) { - dev_err(cs35l41->dev, "Cannot Write Control: %s - %d\n", CAL_STATUS_DSP_CTL_NAME, - ret); + + *uid |= tmp; + + dev_dbg(cs35l41->dev, "UniqueID = %#llx\n", *uid); + + return 0; +} + +static int cs35l41_get_calibration(struct cs35l41_hda *cs35l41) +{ + u64 silicon_uid; + int ret; + + ret = cs35l41_read_silicon_uid(cs35l41, &silicon_uid); + if (ret < 0) return ret; - } - ret = hda_cs_dsp_write_ctl(&cs35l41->cs_dsp, CAL_CHECKSUM_DSP_CTL_NAME, CAL_DSP_CTL_TYPE, - CAL_DSP_CTL_ALG, &checksum, 4); - if (ret) { - dev_err(cs35l41->dev, "Cannot Write Control: %s - %d\n", CAL_CHECKSUM_DSP_CTL_NAME, - ret); + + ret = cs_amp_get_efi_calibration_data(cs35l41->dev, silicon_uid, + cs35l41->index, + &cs35l41->cal_data); + + /* Only return an error status if probe should be aborted */ + if ((ret == -ENOENT) || (ret == -EOVERFLOW)) + return 0; + + if (ret < 0) return ret; - } + + cs35l41->cal_data_valid = true; return 0; } -static int cs35l41_save_calibration(struct cs35l41_hda *cs35l41) + +static void cs35l41_set_default_tuning_params(struct cs35l41_hda *cs35l41) { - static efi_guid_t efi_guid = EFI_GUID(0x02f9af02, 0x7734, 0x4233, 0xb4, 0x3d, 0x93, 0xfe, - 0x5a, 0xa3, 0x5d, 0xb3); - static efi_char16_t efi_name[] = L"CirrusSmartAmpCalibrationData"; - const struct cs35l41_amp_efi_data *efi_data; - const struct cs35l41_amp_cal_data *cl; - unsigned long data_size = 0; - efi_status_t status; - int ret = 0; - u8 *data = NULL; - u32 attr; + cs35l41->tuning_gain = DEFAULT_AMP_GAIN_PCM; +} - /* Get real size of UEFI variable */ - status = efi.get_variable(efi_name, &efi_guid, &attr, &data_size, data); - if (status == EFI_BUFFER_TOO_SMALL) { - ret = -ENODEV; - /* Allocate data buffer of data_size bytes */ - data = vmalloc(data_size); - if (!data) - return -ENOMEM; - /* Get variable contents into buffer */ - status = efi.get_variable(efi_name, &efi_guid, &attr, &data_size, data); - if (status == EFI_SUCCESS) { - efi_data = (struct cs35l41_amp_efi_data *)data; - dev_dbg(cs35l41->dev, "Calibration: Size=%d, Amp Count=%d\n", - efi_data->size, efi_data->count); - if (efi_data->count > cs35l41->index) { - cl = &efi_data->data[cs35l41->index]; - dev_dbg(cs35l41->dev, - "Calibration: Ambient=%02x, Status=%02x, R0=%d\n", - cl->calAmbient, cl->calStatus, cl->calR); - - /* Calibration can only be applied whilst the DSP is not running */ - ret = cs35l41_apply_calibration(cs35l41, - cpu_to_be32(cl->calAmbient), - cpu_to_be32(cl->calR), - cpu_to_be32(cl->calStatus), - cpu_to_be32(cl->calR + 1)); - } +static int cs35l41_read_tuning_params(struct cs35l41_hda *cs35l41, const struct firmware *firmware) +{ + struct cs35l41_tuning_params *params; + unsigned int offset = 0; + unsigned int end; + int i; + + params = (void *)&firmware->data[0]; + + if (le32_to_cpu(params->size) != firmware->size) { + dev_err(cs35l41->dev, "Wrong Size for Tuning Param file. Expected %d got %zu\n", + le32_to_cpu(params->size), firmware->size); + return -EINVAL; + } + + if (le32_to_cpu(params->version) != 1) { + dev_err(cs35l41->dev, "Unsupported Tuning Param Version: %d\n", + le32_to_cpu(params->version)); + return -EINVAL; + } + + if (le32_to_cpu(params->signature) != CS35L41_TUNING_SIG) { + dev_err(cs35l41->dev, + "Mismatched Signature for Tuning Param file. Expected %#x got %#x\n", + CS35L41_TUNING_SIG, le32_to_cpu(params->signature)); + return -EINVAL; + } + + end = firmware->size - sizeof(struct cs35l41_tuning_params); + + for (i = 0; i < le32_to_cpu(params->num_entries); i++) { + struct cs35l41_tuning_param *param; + + if ((offset >= end) || ((offset + sizeof(struct cs35l41_tuning_param_hdr)) >= end)) + return -EFAULT; + + param = (void *)¶ms->data[offset]; + offset += le32_to_cpu(param->hdr.size); + + if (offset > end) + return -EFAULT; + + switch (le32_to_cpu(param->hdr.type)) { + case TUNING_PARAM_GAIN: + cs35l41->tuning_gain = le32_to_cpu(param->gain); + dev_dbg(cs35l41->dev, "Applying Gain: %d\n", cs35l41->tuning_gain); + break; + default: + break; } - vfree(data); } - return ret; + + return 0; } -#else -static int cs35l41_save_calibration(struct cs35l41_hda *cs35l41) + +static int cs35l41_load_tuning_params(struct cs35l41_hda *cs35l41, char *tuning_filename) { - dev_warn(cs35l41->dev, "Calibration not supported without EFI support.\n"); - return 0; + const struct firmware *tuning_param_file = NULL; + char *tuning_param_filename = NULL; + int ret; + + ret = cs35l41_request_tuning_param_file(cs35l41, tuning_filename, &tuning_param_file, + &tuning_param_filename, cs35l41->acpi_subsystem_id); + if (ret) { + dev_dbg(cs35l41->dev, "Missing Tuning Param for file: %s: %d\n", tuning_filename, + ret); + return 0; + } + + ret = cs35l41_read_tuning_params(cs35l41, tuning_param_file); + if (ret) { + dev_err(cs35l41->dev, "Error reading Tuning Params from file: %s: %d\n", + tuning_param_filename, ret); + /* Reset to default Tuning Parameters */ + cs35l41_set_default_tuning_params(cs35l41); + } + + release_firmware(tuning_param_file); + kfree(tuning_param_filename); + + return ret; } -#endif static int cs35l41_init_dsp(struct cs35l41_hda *cs35l41) { @@ -470,27 +581,35 @@ static int cs35l41_init_dsp(struct cs35l41_hda *cs35l41) cs35l41->halo_initialized = true; } + cs35l41_set_default_tuning_params(cs35l41); + ret = cs35l41_request_firmware_files(cs35l41, &wmfw_firmware, &wmfw_filename, &coeff_firmware, &coeff_filename); if (ret < 0) return ret; dev_dbg(cs35l41->dev, "Loading WMFW Firmware: %s\n", wmfw_filename); - if (coeff_filename) + if (coeff_filename) { dev_dbg(cs35l41->dev, "Loading Coefficient File: %s\n", coeff_filename); - else + ret = cs35l41_load_tuning_params(cs35l41, coeff_filename); + if (ret) + dev_warn(cs35l41->dev, "Unable to load Tuning Parameters: %d\n", ret); + } else { dev_warn(cs35l41->dev, "No Coefficient File available.\n"); + } ret = cs_dsp_power_up(dsp, wmfw_firmware, wmfw_filename, coeff_firmware, coeff_filename, hda_cs_dsp_fw_ids[cs35l41->firmware_type]); if (ret) - goto err_release; + goto err; cs35l41_add_controls(cs35l41); - ret = cs35l41_save_calibration(cs35l41); + cs35l41_hda_apply_calibration(cs35l41); -err_release: +err: + if (ret) + cs35l41_set_default_tuning_params(cs35l41); release_firmware(wmfw_firmware); release_firmware(coeff_firmware); kfree(wmfw_filename); @@ -503,6 +622,7 @@ static void cs35l41_shutdown_dsp(struct cs35l41_hda *cs35l41) { struct cs_dsp *dsp = &cs35l41->cs_dsp; + cs35l41_set_default_tuning_params(cs35l41); cs_dsp_stop(dsp); cs_dsp_power_down(dsp); dev_dbg(cs35l41->dev, "Unloaded Firmware\n"); @@ -553,6 +673,10 @@ static void cs35l41_hda_play_start(struct device *dev) if (cs35l41->cs_dsp.running) { regmap_multi_reg_write(reg, cs35l41_hda_config_dsp, ARRAY_SIZE(cs35l41_hda_config_dsp)); + if (cs35l41->hw_cfg.bst_type == CS35L41_INT_BOOST) + regmap_write(reg, CS35L41_DSP1_RX5_SRC, CS35L41_INPUT_SRC_VPMON); + else + regmap_write(reg, CS35L41_DSP1_RX5_SRC, CS35L41_INPUT_SRC_VBSTMON); regmap_update_bits(reg, CS35L41_PWR_CTRL2, CS35L41_VMON_EN_MASK | CS35L41_IMON_EN_MASK, 1 << CS35L41_VMON_EN_SHIFT | 1 << CS35L41_IMON_EN_SHIFT); @@ -570,6 +694,7 @@ static void cs35l41_mute(struct device *dev, bool mute) { struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev); struct regmap *reg = cs35l41->regmap; + unsigned int amp_gain; dev_dbg(dev, "Mute(%d:%d) Playback Started: %d\n", mute, cs35l41->mute_override, cs35l41->playback_started); @@ -581,8 +706,13 @@ static void cs35l41_mute(struct device *dev, bool mute) } else { dev_dbg(dev, "Unmuting\n"); if (cs35l41->cs_dsp.running) { - regmap_multi_reg_write(reg, cs35l41_hda_unmute_dsp, - ARRAY_SIZE(cs35l41_hda_unmute_dsp)); + dev_dbg(dev, "Using Tuned Gain: %d\n", cs35l41->tuning_gain); + amp_gain = (cs35l41->tuning_gain << CS35L41_AMP_GAIN_PCM_SHIFT) | + (DEFAULT_AMP_GAIN_PDM << CS35L41_AMP_GAIN_PDM_SHIFT); + + /* AMP_HPF_PCM_EN = 1, AMP_VOL_PCM 0.0 dB */ + regmap_write(reg, CS35L41_AMP_DIG_VOL_CTRL, 0x00008000); + regmap_write(reg, CS35L41_AMP_GAIN_CTRL, amp_gain); } else { regmap_multi_reg_write(reg, cs35l41_hda_unmute, ARRAY_SIZE(cs35l41_hda_unmute)); @@ -1056,6 +1186,9 @@ static int cs35l41_smart_amp(struct cs35l41_hda *cs35l41) goto clean_dsp; } + dev_info(cs35l41->dev, "Firmware Loaded - Type: %s, Gain: %d\n", + hda_cs_dsp_fw_ids[cs35l41->firmware_type], cs35l41->tuning_gain); + return 0; clean_dsp: @@ -1362,7 +1495,7 @@ static void cs35l41_hda_unbind(struct device *dev, struct device *master, void * if (comps[cs35l41->index].dev == dev) { memset(&comps[cs35l41->index], 0, sizeof(*comps)); sleep_flags = lock_system_sleep(); - device_link_remove(&comps->codec->core.dev, cs35l41->dev); + device_link_remove(&cs35l41->codec->core.dev, cs35l41->dev); unlock_system_sleep(sleep_flags); } } @@ -1461,13 +1594,56 @@ static struct regmap_irq_chip cs35l41_regmap_irq_chip = { .runtime_pm = true, }; +static void cs35l41_configure_interrupt(struct cs35l41_hda *cs35l41, int irq_pol) +{ + int irq; + int ret; + int i; + + if (!cs35l41->irq) { + dev_warn(cs35l41->dev, "No Interrupt Found"); + goto err; + } + + ret = devm_regmap_add_irq_chip(cs35l41->dev, cs35l41->regmap, cs35l41->irq, + IRQF_ONESHOT | IRQF_SHARED | irq_pol, + 0, &cs35l41_regmap_irq_chip, &cs35l41->irq_data); + if (ret) { + dev_dbg(cs35l41->dev, "Unable to add IRQ Chip: %d.", ret); + goto err; + } + + for (i = 0; i < ARRAY_SIZE(cs35l41_irqs); i++) { + irq = regmap_irq_get_virq(cs35l41->irq_data, cs35l41_irqs[i].irq); + if (irq < 0) { + ret = irq; + dev_dbg(cs35l41->dev, "Unable to map IRQ %s: %d.", cs35l41_irqs[i].name, + ret); + goto err; + } + + ret = devm_request_threaded_irq(cs35l41->dev, irq, NULL, + cs35l41_irqs[i].handler, + IRQF_ONESHOT | IRQF_SHARED | irq_pol, + cs35l41_irqs[i].name, cs35l41); + if (ret) { + dev_dbg(cs35l41->dev, "Unable to allocate IRQ %s:: %d.", + cs35l41_irqs[i].name, ret); + goto err; + } + } + return; +err: + dev_warn(cs35l41->dev, + "IRQ Config Failed. Amp errors may not be recoverable without reboot."); +} + static int cs35l41_hda_apply_properties(struct cs35l41_hda *cs35l41) { struct cs35l41_hw_cfg *hw_cfg = &cs35l41->hw_cfg; bool using_irq = false; - int irq, irq_pol; + int irq_pol; int ret; - int i; if (!cs35l41->hw_cfg.valid) return -EINVAL; @@ -1510,26 +1686,8 @@ static int cs35l41_hda_apply_properties(struct cs35l41_hda *cs35l41) irq_pol = cs35l41_gpio_config(cs35l41->regmap, hw_cfg); - if (cs35l41->irq && using_irq) { - ret = devm_regmap_add_irq_chip(cs35l41->dev, cs35l41->regmap, cs35l41->irq, - IRQF_ONESHOT | IRQF_SHARED | irq_pol, - 0, &cs35l41_regmap_irq_chip, &cs35l41->irq_data); - if (ret) - return ret; - - for (i = 0; i < ARRAY_SIZE(cs35l41_irqs); i++) { - irq = regmap_irq_get_virq(cs35l41->irq_data, cs35l41_irqs[i].irq); - if (irq < 0) - return irq; - - ret = devm_request_threaded_irq(cs35l41->dev, irq, NULL, - cs35l41_irqs[i].handler, - IRQF_ONESHOT | IRQF_SHARED | irq_pol, - cs35l41_irqs[i].name, cs35l41); - if (ret) - return ret; - } - } + if (using_irq) + cs35l41_configure_interrupt(cs35l41, irq_pol); return cs35l41_hda_channel_map(cs35l41->dev, 0, NULL, 1, &hw_cfg->spk_pos); } @@ -1808,6 +1966,10 @@ int cs35l41_hda_probe(struct device *dev, const char *device_name, int id, int i if (ret) goto err; + ret = cs35l41_get_calibration(cs35l41); + if (ret && ret != -ENOENT) + goto err; + cs35l41_mute(cs35l41->dev, true); INIT_WORK(&cs35l41->fw_load_work, cs35l41_fw_load_work); @@ -1857,6 +2019,8 @@ void cs35l41_hda_remove(struct device *dev) { struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev); + component_del(cs35l41->dev, &cs35l41_hda_comp_ops); + pm_runtime_get_sync(cs35l41->dev); pm_runtime_dont_use_autosuspend(cs35l41->dev); pm_runtime_disable(cs35l41->dev); @@ -1864,8 +2028,6 @@ void cs35l41_hda_remove(struct device *dev) if (cs35l41->halo_initialized) cs35l41_remove_dsp(cs35l41); - component_del(cs35l41->dev, &cs35l41_hda_comp_ops); - acpi_dev_put(cs35l41->dacpi); pm_runtime_put_noidle(cs35l41->dev); @@ -1888,6 +2050,7 @@ EXPORT_SYMBOL_NS_GPL(cs35l41_hda_pm_ops, SND_HDA_SCODEC_CS35L41); MODULE_DESCRIPTION("CS35L41 HDA Driver"); MODULE_IMPORT_NS(SND_HDA_CS_DSP_CONTROLS); +MODULE_IMPORT_NS(SND_SOC_CS_AMP_LIB); MODULE_AUTHOR("Lucas Tanure, Cirrus Logic Inc, <tanureal@opensource.cirrus.com>"); MODULE_LICENSE("GPL"); MODULE_IMPORT_NS(FW_CS_DSP); |