summaryrefslogtreecommitdiff
path: root/sound/hda/codecs/side-codecs
diff options
context:
space:
mode:
Diffstat (limited to 'sound/hda/codecs/side-codecs')
-rw-r--r--sound/hda/codecs/side-codecs/Kconfig15
-rw-r--r--sound/hda/codecs/side-codecs/cs35l41_hda.c2
-rw-r--r--sound/hda/codecs/side-codecs/cs35l56_hda.c116
-rw-r--r--sound/hda/codecs/side-codecs/cs35l56_hda.h6
-rw-r--r--sound/hda/codecs/side-codecs/hda_component.c4
-rw-r--r--sound/hda/codecs/side-codecs/tas2781_hda_i2c.c45
6 files changed, 163 insertions, 25 deletions
diff --git a/sound/hda/codecs/side-codecs/Kconfig b/sound/hda/codecs/side-codecs/Kconfig
index cbf1847896bc..f674e9a9c7d7 100644
--- a/sound/hda/codecs/side-codecs/Kconfig
+++ b/sound/hda/codecs/side-codecs/Kconfig
@@ -88,6 +88,21 @@ config SND_HDA_SCODEC_CS35L56_SPI
Say Y or M here to include CS35L56 amplifier support with
SPI control.
+menu "CS35L56 driver options"
+ depends on SND_HDA_SCODEC_CS35L56
+
+config SND_HDA_SCODEC_CS35L56_CAL_DEBUGFS
+ bool "CS35L56 create debugfs for factory calibration"
+ default N
+ depends on DEBUG_FS
+ select SND_SOC_CS35L56_CAL_DEBUGFS_COMMON
+ help
+ Create debugfs entries used during factory-line manufacture
+ for factory calibration.
+
+ If unsure select "N".
+endmenu
+
config SND_HDA_SCODEC_TAS2781
tristate
select SND_HDA_GENERIC
diff --git a/sound/hda/codecs/side-codecs/cs35l41_hda.c b/sound/hda/codecs/side-codecs/cs35l41_hda.c
index c04208e685a0..c0f2a3ff77a1 100644
--- a/sound/hda/codecs/side-codecs/cs35l41_hda.c
+++ b/sound/hda/codecs/side-codecs/cs35l41_hda.c
@@ -1410,6 +1410,8 @@ static int cs35l41_get_acpi_mute_state(struct cs35l41_hda *cs35l41, acpi_handle
if (cs35l41_dsm_supported(handle, CS35L41_DSM_GET_MUTE)) {
ret = acpi_evaluate_dsm(handle, &guid, 0, CS35L41_DSM_GET_MUTE, NULL);
+ if (!ret)
+ return -EINVAL;
mute = *ret->buffer.pointer;
dev_dbg(cs35l41->dev, "CS35L41_DSM_GET_MUTE: %d\n", mute);
}
diff --git a/sound/hda/codecs/side-codecs/cs35l56_hda.c b/sound/hda/codecs/side-codecs/cs35l56_hda.c
index 5bb1c4ebeaf3..f7ba92e11957 100644
--- a/sound/hda/codecs/side-codecs/cs35l56_hda.c
+++ b/sound/hda/codecs/side-codecs/cs35l56_hda.c
@@ -548,20 +548,24 @@ static void cs35l56_hda_release_firmware_files(const struct firmware *wmfw_firmw
kfree(coeff_filename);
}
-static void cs35l56_hda_apply_calibration(struct cs35l56_hda *cs35l56)
+static int cs35l56_hda_apply_calibration(struct cs35l56_hda *cs35l56)
{
int ret;
if (!cs35l56->base.cal_data_valid || cs35l56->base.secured)
- return;
+ return -EACCES;
ret = cs_amp_write_cal_coeffs(&cs35l56->cs_dsp,
&cs35l56_calibration_controls,
&cs35l56->base.cal_data);
- if (ret < 0)
+ if (ret < 0) {
dev_warn(cs35l56->base.dev, "Failed to write calibration: %d\n", ret);
- else
- dev_info(cs35l56->base.dev, "Calibration applied\n");
+ return ret;
+ }
+
+ dev_info(cs35l56->base.dev, "Calibration applied\n");
+
+ return 0;
}
static void cs35l56_hda_fw_load(struct cs35l56_hda *cs35l56)
@@ -669,7 +673,9 @@ static void cs35l56_hda_fw_load(struct cs35l56_hda *cs35l56)
if (ret)
dev_dbg(cs35l56->base.dev, "%s: cs_dsp_run ret %d\n", __func__, ret);
+ /* Don't need to check return code, it's not fatal if this fails */
cs35l56_hda_apply_calibration(cs35l56);
+
ret = cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_AUDIO_REINIT);
if (ret)
cs_dsp_stop(&cs35l56->cs_dsp);
@@ -695,6 +701,100 @@ static void cs35l56_hda_dsp_work(struct work_struct *work)
cs35l56_hda_fw_load(cs35l56);
}
+static ssize_t cs35l56_hda_debugfs_calibrate_write(struct file *file,
+ const char __user *from,
+ size_t count, loff_t *ppos)
+{
+ struct cs35l56_base *cs35l56_base = file->private_data;
+ ssize_t ret;
+
+ ret = pm_runtime_resume_and_get(cs35l56_base->dev);
+ if (ret)
+ return ret;
+
+ ret = cs35l56_calibrate_debugfs_write(cs35l56_base, from, count, ppos);
+ pm_runtime_autosuspend(cs35l56_base->dev);
+
+ return ret;
+}
+
+static ssize_t cs35l56_hda_debugfs_cal_temperature_write(struct file *file,
+ const char __user *from,
+ size_t count, loff_t *ppos)
+{
+ struct cs35l56_base *cs35l56_base = file->private_data;
+ ssize_t ret;
+
+ ret = pm_runtime_resume_and_get(cs35l56_base->dev);
+ if (ret)
+ return ret;
+
+ ret = cs35l56_cal_ambient_debugfs_write(cs35l56_base, from, count, ppos);
+ pm_runtime_autosuspend(cs35l56_base->dev);
+
+ return ret;
+}
+
+static ssize_t cs35l56_hda_debugfs_cal_data_read(struct file *file,
+ char __user *to,
+ size_t count, loff_t *ppos)
+{
+ struct cs35l56_base *cs35l56_base = file->private_data;
+ ssize_t ret;
+
+ ret = pm_runtime_resume_and_get(cs35l56_base->dev);
+ if (ret)
+ return ret;
+
+ ret = cs35l56_cal_data_debugfs_read(cs35l56_base, to, count, ppos);
+ pm_runtime_autosuspend(cs35l56_base->dev);
+
+ return ret;
+}
+
+static ssize_t cs35l56_hda_debugfs_cal_data_write(struct file *file,
+ const char __user *from,
+ size_t count, loff_t *ppos)
+{
+ struct cs35l56_base *cs35l56_base = file->private_data;
+ struct cs35l56_hda *cs35l56 = cs35l56_hda_from_base(cs35l56_base);
+ ssize_t ret;
+
+ ret = cs35l56_cal_data_debugfs_write(cs35l56_base, from, count, ppos);
+ if (ret == -ENODATA)
+ return count; /* Ignore writes of empty cal blobs */
+
+ if (ret < 0)
+ return ret;
+
+ ret = pm_runtime_resume_and_get(cs35l56_base->dev);
+ if (ret)
+ return ret;
+
+ ret = cs35l56_hda_apply_calibration(cs35l56);
+ if (ret == 0)
+ cs35l56_mbox_send(cs35l56_base, CS35L56_MBOX_CMD_AUDIO_REINIT);
+ else
+ count = -EIO;
+
+ pm_runtime_autosuspend(cs35l56_base->dev);
+
+ return count;
+}
+
+static const struct cs35l56_cal_debugfs_fops cs35l56_hda_cal_debugfs_fops = {
+ .calibrate = {
+ .write = cs35l56_hda_debugfs_calibrate_write,
+ },
+ .cal_temperature = {
+ .write = cs35l56_hda_debugfs_cal_temperature_write,
+ },
+ .cal_data = {
+ .read = cs35l56_hda_debugfs_cal_data_read,
+ .write = cs35l56_hda_debugfs_cal_data_write,
+ },
+};
+
static int cs35l56_hda_bind(struct device *dev, struct device *master, void *master_data)
{
struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev);
@@ -722,6 +822,9 @@ static int cs35l56_hda_bind(struct device *dev, struct device *master, void *mas
cs_dsp_init_debugfs(&cs35l56->cs_dsp, cs35l56->debugfs_root);
#endif
+ if (IS_ENABLED(CONFIG_SND_HDA_SCODEC_CS35L56_CAL_DEBUGFS))
+ cs35l56_create_cal_debugfs(&cs35l56->base, &cs35l56_hda_cal_debugfs_fops);
+
dev_dbg(cs35l56->base.dev, "Bound\n");
return 0;
@@ -735,6 +838,7 @@ static void cs35l56_hda_unbind(struct device *dev, struct device *master, void *
cancel_work_sync(&cs35l56->dsp_work);
+ cs35l56_remove_cal_debugfs(&cs35l56->base);
cs35l56_hda_remove_controls(cs35l56);
#if IS_ENABLED(CONFIG_SND_DEBUG)
@@ -1050,7 +1154,7 @@ int cs35l56_hda_common_probe(struct cs35l56_hda *cs35l56, int hid, int id)
}
cs35l56->base.type = hid & 0xff;
- cs35l56->base.cal_index = -1;
+ cs35l56->base.cal_index = cs35l56->index;
cs35l56_init_cs_dsp(&cs35l56->base, &cs35l56->cs_dsp);
cs35l56->cs_dsp.client_ops = &cs35l56_hda_client_ops;
diff --git a/sound/hda/codecs/side-codecs/cs35l56_hda.h b/sound/hda/codecs/side-codecs/cs35l56_hda.h
index 38d94fb213a5..cb4b5e7356a3 100644
--- a/sound/hda/codecs/side-codecs/cs35l56_hda.h
+++ b/sound/hda/codecs/side-codecs/cs35l56_hda.h
@@ -9,6 +9,7 @@
#ifndef __CS35L56_HDA_H__
#define __CS35L56_HDA_H__
+#include <linux/container_of.h>
#include <linux/device.h>
#include <linux/gpio/consumer.h>
#include <linux/firmware/cirrus/cs_dsp.h>
@@ -42,6 +43,11 @@ struct cs35l56_hda {
#endif
};
+static inline struct cs35l56_hda *cs35l56_hda_from_base(struct cs35l56_base *cs35l56_base)
+{
+ return container_of(cs35l56_base, struct cs35l56_hda, base);
+}
+
extern const struct dev_pm_ops cs35l56_hda_pm_ops;
int cs35l56_hda_common_probe(struct cs35l56_hda *cs35l56, int hid, int id);
diff --git a/sound/hda/codecs/side-codecs/hda_component.c b/sound/hda/codecs/side-codecs/hda_component.c
index bcf47a301697..8a2a200600a7 100644
--- a/sound/hda/codecs/side-codecs/hda_component.c
+++ b/sound/hda/codecs/side-codecs/hda_component.c
@@ -174,6 +174,10 @@ int hda_component_manager_init(struct hda_codec *cdc,
sm->match_str = match_str;
sm->index = i;
component_match_add(dev, &match, hda_comp_match_dev_name, sm);
+ if (IS_ERR(match)) {
+ codec_err(cdc, "Fail to add component %ld\n", PTR_ERR(match));
+ return PTR_ERR(match);
+ }
}
ret = component_master_add_with_match(dev, ops, match);
diff --git a/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c b/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c
index a126f04c3ed7..c8619995b1d7 100644
--- a/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c
+++ b/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c
@@ -87,6 +87,7 @@ static const struct acpi_gpio_mapping tas2781_speaker_id_gpios[] = {
static int tas2781_read_acpi(struct tasdevice_priv *p, const char *hid)
{
+ struct gpio_desc *speaker_id;
struct acpi_device *adev;
struct device *physdev;
LIST_HEAD(resources);
@@ -119,19 +120,31 @@ static int tas2781_read_acpi(struct tasdevice_priv *p, const char *hid)
/* Speaker id was needed for ASUS projects. */
ret = kstrtou32(sub, 16, &subid);
if (!ret && upper_16_bits(subid) == PCI_VENDOR_ID_ASUSTEK) {
- ret = devm_acpi_dev_add_driver_gpios(p->dev,
- tas2781_speaker_id_gpios);
- if (ret < 0)
+ ret = acpi_dev_add_driver_gpios(adev, tas2781_speaker_id_gpios);
+ if (ret < 0) {
dev_err(p->dev, "Failed to add driver gpio %d.\n",
ret);
- p->speaker_id = devm_gpiod_get(p->dev, "speakerid", GPIOD_IN);
- if (IS_ERR(p->speaker_id)) {
- dev_err(p->dev, "Failed to get Speaker id.\n");
- ret = PTR_ERR(p->speaker_id);
- goto err;
+ p->speaker_id = -1;
+ goto end_2563;
+ }
+
+ speaker_id = fwnode_gpiod_get_index(acpi_fwnode_handle(adev),
+ "speakerid", 0, GPIOD_IN, NULL);
+ if (!IS_ERR(speaker_id)) {
+ p->speaker_id = gpiod_get_value_cansleep(speaker_id);
+ dev_dbg(p->dev, "Got speaker id gpio from ACPI: %d.\n",
+ p->speaker_id);
+ gpiod_put(speaker_id);
+ } else {
+ p->speaker_id = -1;
+ ret = PTR_ERR(speaker_id);
+ dev_err(p->dev, "Get speaker id gpio failed %d.\n",
+ ret);
}
+
+ acpi_dev_remove_driver_gpios(adev);
} else {
- p->speaker_id = NULL;
+ p->speaker_id = -1;
}
end_2563:
@@ -432,23 +445,16 @@ static void tasdevice_dspfw_init(void *context)
struct tas2781_hda *tas_hda = dev_get_drvdata(tas_priv->dev);
struct tas2781_hda_i2c_priv *hda_priv = tas_hda->hda_priv;
struct hda_codec *codec = tas_priv->codec;
- int ret, spk_id;
+ int ret;
tasdevice_dsp_remove(tas_priv);
tas_priv->fw_state = TASDEVICE_DSP_FW_PENDING;
- if (tas_priv->speaker_id != NULL) {
- // Speaker id need to be checked for ASUS only.
- spk_id = gpiod_get_value(tas_priv->speaker_id);
- if (spk_id < 0) {
- // Speaker id is not valid, use default.
- dev_dbg(tas_priv->dev, "Wrong spk_id = %d\n", spk_id);
- spk_id = 0;
- }
+ if (tas_priv->speaker_id >= 0) {
snprintf(tas_priv->coef_binaryname,
sizeof(tas_priv->coef_binaryname),
"TAS2XXX%04X%d.bin",
lower_16_bits(codec->core.subsystem_id),
- spk_id);
+ tas_priv->speaker_id);
} else {
snprintf(tas_priv->coef_binaryname,
sizeof(tas_priv->coef_binaryname),
@@ -669,6 +675,7 @@ static int tas2781_hda_i2c_probe(struct i2c_client *clt)
*/
device_name = "TXNW5825";
hda_priv->hda_chip_id = HDA_TAS5825;
+ tas_hda->priv->chip_id = TAS5825;
} else {
return -ENODEV;
}