162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci//
362306a36Sopenharmony_ci// CS35l41 ALSA HDA audio driver
462306a36Sopenharmony_ci//
562306a36Sopenharmony_ci// Copyright 2021 Cirrus Logic, Inc.
662306a36Sopenharmony_ci//
762306a36Sopenharmony_ci// Author: Lucas Tanure <tanureal@opensource.cirrus.com>
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/acpi.h>
1062306a36Sopenharmony_ci#include <linux/module.h>
1162306a36Sopenharmony_ci#include <linux/moduleparam.h>
1262306a36Sopenharmony_ci#include <sound/hda_codec.h>
1362306a36Sopenharmony_ci#include <sound/soc.h>
1462306a36Sopenharmony_ci#include <linux/pm_runtime.h>
1562306a36Sopenharmony_ci#include "hda_local.h"
1662306a36Sopenharmony_ci#include "hda_auto_parser.h"
1762306a36Sopenharmony_ci#include "hda_jack.h"
1862306a36Sopenharmony_ci#include "hda_generic.h"
1962306a36Sopenharmony_ci#include "hda_component.h"
2062306a36Sopenharmony_ci#include "cs35l41_hda.h"
2162306a36Sopenharmony_ci#include "hda_cs_dsp_ctl.h"
2262306a36Sopenharmony_ci#include "cs35l41_hda_property.h"
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#define CS35L41_FIRMWARE_ROOT "cirrus/"
2562306a36Sopenharmony_ci#define CS35L41_PART "cs35l41"
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci#define HALO_STATE_DSP_CTL_NAME		"HALO_STATE"
2862306a36Sopenharmony_ci#define HALO_STATE_DSP_CTL_TYPE		5
2962306a36Sopenharmony_ci#define HALO_STATE_DSP_CTL_ALG		262308
3062306a36Sopenharmony_ci#define CAL_R_DSP_CTL_NAME		"CAL_R"
3162306a36Sopenharmony_ci#define CAL_STATUS_DSP_CTL_NAME		"CAL_STATUS"
3262306a36Sopenharmony_ci#define CAL_CHECKSUM_DSP_CTL_NAME	"CAL_CHECKSUM"
3362306a36Sopenharmony_ci#define CAL_AMBIENT_DSP_CTL_NAME	"CAL_AMBIENT"
3462306a36Sopenharmony_ci#define CAL_DSP_CTL_TYPE		5
3562306a36Sopenharmony_ci#define CAL_DSP_CTL_ALG			205
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_cistatic bool firmware_autostart = 1;
3862306a36Sopenharmony_cimodule_param(firmware_autostart, bool, 0444);
3962306a36Sopenharmony_ciMODULE_PARM_DESC(firmware_autostart, "Allow automatic firmware download on boot"
4062306a36Sopenharmony_ci			     "(0=Disable, 1=Enable) (default=1); ");
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_cistatic const struct reg_sequence cs35l41_hda_config[] = {
4362306a36Sopenharmony_ci	{ CS35L41_PLL_CLK_CTRL,		0x00000430 }, // 3072000Hz, BCLK Input, PLL_REFCLK_EN = 1
4462306a36Sopenharmony_ci	{ CS35L41_DSP_CLK_CTRL,		0x00000003 }, // DSP CLK EN
4562306a36Sopenharmony_ci	{ CS35L41_GLOBAL_CLK_CTRL,	0x00000003 }, // GLOBAL_FS = 48 kHz
4662306a36Sopenharmony_ci	{ CS35L41_SP_ENABLES,		0x00010000 }, // ASP_RX1_EN = 1
4762306a36Sopenharmony_ci	{ CS35L41_SP_RATE_CTRL,		0x00000021 }, // ASP_BCLK_FREQ = 3.072 MHz
4862306a36Sopenharmony_ci	{ CS35L41_SP_FORMAT,		0x20200200 }, // 32 bits RX/TX slots, I2S, clk consumer
4962306a36Sopenharmony_ci	{ CS35L41_SP_HIZ_CTRL,		0x00000002 }, // Hi-Z unused
5062306a36Sopenharmony_ci	{ CS35L41_SP_TX_WL,		0x00000018 }, // 24 cycles/slot
5162306a36Sopenharmony_ci	{ CS35L41_SP_RX_WL,		0x00000018 }, // 24 cycles/slot
5262306a36Sopenharmony_ci	{ CS35L41_DAC_PCM1_SRC,		0x00000008 }, // DACPCM1_SRC = ASPRX1
5362306a36Sopenharmony_ci	{ CS35L41_ASP_TX1_SRC,		0x00000018 }, // ASPTX1 SRC = VMON
5462306a36Sopenharmony_ci	{ CS35L41_ASP_TX2_SRC,		0x00000019 }, // ASPTX2 SRC = IMON
5562306a36Sopenharmony_ci	{ CS35L41_ASP_TX3_SRC,		0x00000032 }, // ASPTX3 SRC = ERRVOL
5662306a36Sopenharmony_ci	{ CS35L41_ASP_TX4_SRC,		0x00000033 }, // ASPTX4 SRC = CLASSH_TGT
5762306a36Sopenharmony_ci	{ CS35L41_DSP1_RX1_SRC,		0x00000008 }, // DSP1RX1 SRC = ASPRX1
5862306a36Sopenharmony_ci	{ CS35L41_DSP1_RX2_SRC,		0x00000009 }, // DSP1RX2 SRC = ASPRX2
5962306a36Sopenharmony_ci	{ CS35L41_DSP1_RX3_SRC,         0x00000018 }, // DSP1RX3 SRC = VMON
6062306a36Sopenharmony_ci	{ CS35L41_DSP1_RX4_SRC,         0x00000019 }, // DSP1RX4 SRC = IMON
6162306a36Sopenharmony_ci	{ CS35L41_DSP1_RX5_SRC,         0x00000020 }, // DSP1RX5 SRC = ERRVOL
6262306a36Sopenharmony_ci};
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_cistatic const struct reg_sequence cs35l41_hda_config_dsp[] = {
6562306a36Sopenharmony_ci	{ CS35L41_PLL_CLK_CTRL,		0x00000430 }, // 3072000Hz, BCLK Input, PLL_REFCLK_EN = 1
6662306a36Sopenharmony_ci	{ CS35L41_DSP_CLK_CTRL,		0x00000003 }, // DSP CLK EN
6762306a36Sopenharmony_ci	{ CS35L41_GLOBAL_CLK_CTRL,	0x00000003 }, // GLOBAL_FS = 48 kHz
6862306a36Sopenharmony_ci	{ CS35L41_SP_ENABLES,		0x00010001 }, // ASP_RX1_EN = 1, ASP_TX1_EN = 1
6962306a36Sopenharmony_ci	{ CS35L41_SP_RATE_CTRL,		0x00000021 }, // ASP_BCLK_FREQ = 3.072 MHz
7062306a36Sopenharmony_ci	{ CS35L41_SP_FORMAT,		0x20200200 }, // 32 bits RX/TX slots, I2S, clk consumer
7162306a36Sopenharmony_ci	{ CS35L41_SP_HIZ_CTRL,		0x00000003 }, // Hi-Z unused/disabled
7262306a36Sopenharmony_ci	{ CS35L41_SP_TX_WL,		0x00000018 }, // 24 cycles/slot
7362306a36Sopenharmony_ci	{ CS35L41_SP_RX_WL,		0x00000018 }, // 24 cycles/slot
7462306a36Sopenharmony_ci	{ CS35L41_DAC_PCM1_SRC,		0x00000032 }, // DACPCM1_SRC = ERR_VOL
7562306a36Sopenharmony_ci	{ CS35L41_ASP_TX1_SRC,		0x00000018 }, // ASPTX1 SRC = VMON
7662306a36Sopenharmony_ci	{ CS35L41_ASP_TX2_SRC,		0x00000019 }, // ASPTX2 SRC = IMON
7762306a36Sopenharmony_ci	{ CS35L41_ASP_TX3_SRC,		0x00000028 }, // ASPTX3 SRC = VPMON
7862306a36Sopenharmony_ci	{ CS35L41_ASP_TX4_SRC,		0x00000029 }, // ASPTX4 SRC = VBSTMON
7962306a36Sopenharmony_ci	{ CS35L41_DSP1_RX1_SRC,		0x00000008 }, // DSP1RX1 SRC = ASPRX1
8062306a36Sopenharmony_ci	{ CS35L41_DSP1_RX2_SRC,		0x00000008 }, // DSP1RX2 SRC = ASPRX1
8162306a36Sopenharmony_ci	{ CS35L41_DSP1_RX3_SRC,         0x00000018 }, // DSP1RX3 SRC = VMON
8262306a36Sopenharmony_ci	{ CS35L41_DSP1_RX4_SRC,         0x00000019 }, // DSP1RX4 SRC = IMON
8362306a36Sopenharmony_ci	{ CS35L41_DSP1_RX5_SRC,         0x00000029 }, // DSP1RX5 SRC = VBSTMON
8462306a36Sopenharmony_ci};
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_cistatic const struct reg_sequence cs35l41_hda_unmute[] = {
8762306a36Sopenharmony_ci	{ CS35L41_AMP_DIG_VOL_CTRL,	0x00008000 }, // AMP_HPF_PCM_EN = 1, AMP_VOL_PCM  0.0 dB
8862306a36Sopenharmony_ci	{ CS35L41_AMP_GAIN_CTRL,	0x00000084 }, // AMP_GAIN_PCM 4.5 dB
8962306a36Sopenharmony_ci};
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_cistatic const struct reg_sequence cs35l41_hda_unmute_dsp[] = {
9262306a36Sopenharmony_ci	{ CS35L41_AMP_DIG_VOL_CTRL,	0x00008000 }, // AMP_HPF_PCM_EN = 1, AMP_VOL_PCM  0.0 dB
9362306a36Sopenharmony_ci	{ CS35L41_AMP_GAIN_CTRL,	0x00000233 }, // AMP_GAIN_PCM = 17.5dB AMP_GAIN_PDM = 19.5dB
9462306a36Sopenharmony_ci};
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_cistatic const struct reg_sequence cs35l41_hda_mute[] = {
9762306a36Sopenharmony_ci	{ CS35L41_AMP_GAIN_CTRL,	0x00000000 }, // AMP_GAIN_PCM 0.5 dB
9862306a36Sopenharmony_ci	{ CS35L41_AMP_DIG_VOL_CTRL,	0x0000A678 }, // AMP_HPF_PCM_EN = 1, AMP_VOL_PCM Mute
9962306a36Sopenharmony_ci};
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_cistatic void cs35l41_add_controls(struct cs35l41_hda *cs35l41)
10262306a36Sopenharmony_ci{
10362306a36Sopenharmony_ci	struct hda_cs_dsp_ctl_info info;
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	info.device_name = cs35l41->amp_name;
10662306a36Sopenharmony_ci	info.fw_type = cs35l41->firmware_type;
10762306a36Sopenharmony_ci	info.card = cs35l41->codec->card;
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	hda_cs_dsp_add_controls(&cs35l41->cs_dsp, &info);
11062306a36Sopenharmony_ci}
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_cistatic const struct cs_dsp_client_ops client_ops = {
11362306a36Sopenharmony_ci	.control_remove = hda_cs_dsp_control_remove,
11462306a36Sopenharmony_ci};
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_cistatic int cs35l41_request_firmware_file(struct cs35l41_hda *cs35l41,
11762306a36Sopenharmony_ci					 const struct firmware **firmware, char **filename,
11862306a36Sopenharmony_ci					 const char *dir, const char *ssid, const char *amp_name,
11962306a36Sopenharmony_ci					 int spkid, const char *filetype)
12062306a36Sopenharmony_ci{
12162306a36Sopenharmony_ci	const char * const dsp_name = cs35l41->cs_dsp.name;
12262306a36Sopenharmony_ci	char *s, c;
12362306a36Sopenharmony_ci	int ret = 0;
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	if (spkid > -1 && ssid && amp_name)
12662306a36Sopenharmony_ci		*filename = kasprintf(GFP_KERNEL, "%s%s-%s-%s-%s-spkid%d-%s.%s", dir, CS35L41_PART,
12762306a36Sopenharmony_ci				      dsp_name, hda_cs_dsp_fw_ids[cs35l41->firmware_type],
12862306a36Sopenharmony_ci				      ssid, spkid, amp_name, filetype);
12962306a36Sopenharmony_ci	else if (spkid > -1 && ssid)
13062306a36Sopenharmony_ci		*filename = kasprintf(GFP_KERNEL, "%s%s-%s-%s-%s-spkid%d.%s", dir, CS35L41_PART,
13162306a36Sopenharmony_ci				      dsp_name, hda_cs_dsp_fw_ids[cs35l41->firmware_type],
13262306a36Sopenharmony_ci				      ssid, spkid, filetype);
13362306a36Sopenharmony_ci	else if (ssid && amp_name)
13462306a36Sopenharmony_ci		*filename = kasprintf(GFP_KERNEL, "%s%s-%s-%s-%s-%s.%s", dir, CS35L41_PART,
13562306a36Sopenharmony_ci				      dsp_name, hda_cs_dsp_fw_ids[cs35l41->firmware_type],
13662306a36Sopenharmony_ci				      ssid, amp_name, filetype);
13762306a36Sopenharmony_ci	else if (ssid)
13862306a36Sopenharmony_ci		*filename = kasprintf(GFP_KERNEL, "%s%s-%s-%s-%s.%s", dir, CS35L41_PART,
13962306a36Sopenharmony_ci				      dsp_name, hda_cs_dsp_fw_ids[cs35l41->firmware_type],
14062306a36Sopenharmony_ci				      ssid, filetype);
14162306a36Sopenharmony_ci	else
14262306a36Sopenharmony_ci		*filename = kasprintf(GFP_KERNEL, "%s%s-%s-%s.%s", dir, CS35L41_PART,
14362306a36Sopenharmony_ci				      dsp_name, hda_cs_dsp_fw_ids[cs35l41->firmware_type],
14462306a36Sopenharmony_ci				      filetype);
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	if (*filename == NULL)
14762306a36Sopenharmony_ci		return -ENOMEM;
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	/*
15062306a36Sopenharmony_ci	 * Make sure that filename is lower-case and any non alpha-numeric
15162306a36Sopenharmony_ci	 * characters except full stop and '/' are replaced with hyphens.
15262306a36Sopenharmony_ci	 */
15362306a36Sopenharmony_ci	s = *filename;
15462306a36Sopenharmony_ci	while (*s) {
15562306a36Sopenharmony_ci		c = *s;
15662306a36Sopenharmony_ci		if (isalnum(c))
15762306a36Sopenharmony_ci			*s = tolower(c);
15862306a36Sopenharmony_ci		else if (c != '.' && c != '/')
15962306a36Sopenharmony_ci			*s = '-';
16062306a36Sopenharmony_ci		s++;
16162306a36Sopenharmony_ci	}
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	ret = firmware_request_nowarn(firmware, *filename, cs35l41->dev);
16462306a36Sopenharmony_ci	if (ret != 0) {
16562306a36Sopenharmony_ci		dev_dbg(cs35l41->dev, "Failed to request '%s'\n", *filename);
16662306a36Sopenharmony_ci		kfree(*filename);
16762306a36Sopenharmony_ci		*filename = NULL;
16862306a36Sopenharmony_ci	}
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	return ret;
17162306a36Sopenharmony_ci}
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_cistatic int cs35l41_request_firmware_files_spkid(struct cs35l41_hda *cs35l41,
17462306a36Sopenharmony_ci						const struct firmware **wmfw_firmware,
17562306a36Sopenharmony_ci						char **wmfw_filename,
17662306a36Sopenharmony_ci						const struct firmware **coeff_firmware,
17762306a36Sopenharmony_ci						char **coeff_filename)
17862306a36Sopenharmony_ci{
17962306a36Sopenharmony_ci	int ret;
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	/* try cirrus/part-dspN-fwtype-sub<-spkidN><-ampname>.wmfw */
18262306a36Sopenharmony_ci	ret = cs35l41_request_firmware_file(cs35l41, wmfw_firmware, wmfw_filename,
18362306a36Sopenharmony_ci					    CS35L41_FIRMWARE_ROOT,
18462306a36Sopenharmony_ci					    cs35l41->acpi_subsystem_id, cs35l41->amp_name,
18562306a36Sopenharmony_ci					    cs35l41->speaker_id, "wmfw");
18662306a36Sopenharmony_ci	if (!ret) {
18762306a36Sopenharmony_ci		/* try cirrus/part-dspN-fwtype-sub<-spkidN><-ampname>.bin */
18862306a36Sopenharmony_ci		ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename,
18962306a36Sopenharmony_ci						    CS35L41_FIRMWARE_ROOT,
19062306a36Sopenharmony_ci						    cs35l41->acpi_subsystem_id, cs35l41->amp_name,
19162306a36Sopenharmony_ci						    cs35l41->speaker_id, "bin");
19262306a36Sopenharmony_ci		if (ret)
19362306a36Sopenharmony_ci			goto coeff_err;
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci		return 0;
19662306a36Sopenharmony_ci	}
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	/* try cirrus/part-dspN-fwtype-sub<-ampname>.wmfw */
19962306a36Sopenharmony_ci	ret = cs35l41_request_firmware_file(cs35l41, wmfw_firmware, wmfw_filename,
20062306a36Sopenharmony_ci					    CS35L41_FIRMWARE_ROOT, cs35l41->acpi_subsystem_id,
20162306a36Sopenharmony_ci					    cs35l41->amp_name, -1, "wmfw");
20262306a36Sopenharmony_ci	if (!ret) {
20362306a36Sopenharmony_ci		/* try cirrus/part-dspN-fwtype-sub<-spkidN><-ampname>.bin */
20462306a36Sopenharmony_ci		ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename,
20562306a36Sopenharmony_ci						    CS35L41_FIRMWARE_ROOT,
20662306a36Sopenharmony_ci						    cs35l41->acpi_subsystem_id, cs35l41->amp_name,
20762306a36Sopenharmony_ci						    cs35l41->speaker_id, "bin");
20862306a36Sopenharmony_ci		if (ret)
20962306a36Sopenharmony_ci			goto coeff_err;
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci		return 0;
21262306a36Sopenharmony_ci	}
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	/* try cirrus/part-dspN-fwtype-sub<-spkidN>.wmfw */
21562306a36Sopenharmony_ci	ret = cs35l41_request_firmware_file(cs35l41, wmfw_firmware, wmfw_filename,
21662306a36Sopenharmony_ci					    CS35L41_FIRMWARE_ROOT, cs35l41->acpi_subsystem_id,
21762306a36Sopenharmony_ci					    NULL, cs35l41->speaker_id, "wmfw");
21862306a36Sopenharmony_ci	if (!ret) {
21962306a36Sopenharmony_ci		/* try cirrus/part-dspN-fwtype-sub<-spkidN><-ampname>.bin */
22062306a36Sopenharmony_ci		ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename,
22162306a36Sopenharmony_ci						    CS35L41_FIRMWARE_ROOT,
22262306a36Sopenharmony_ci						    cs35l41->acpi_subsystem_id,
22362306a36Sopenharmony_ci						    cs35l41->amp_name, cs35l41->speaker_id, "bin");
22462306a36Sopenharmony_ci		if (ret)
22562306a36Sopenharmony_ci			/* try cirrus/part-dspN-fwtype-sub<-spkidN>.bin */
22662306a36Sopenharmony_ci			ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware,
22762306a36Sopenharmony_ci							    coeff_filename, CS35L41_FIRMWARE_ROOT,
22862306a36Sopenharmony_ci							    cs35l41->acpi_subsystem_id, NULL,
22962306a36Sopenharmony_ci							    cs35l41->speaker_id, "bin");
23062306a36Sopenharmony_ci		if (ret)
23162306a36Sopenharmony_ci			goto coeff_err;
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci		return 0;
23462306a36Sopenharmony_ci	}
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	/* try cirrus/part-dspN-fwtype-sub.wmfw */
23762306a36Sopenharmony_ci	ret = cs35l41_request_firmware_file(cs35l41, wmfw_firmware, wmfw_filename,
23862306a36Sopenharmony_ci					    CS35L41_FIRMWARE_ROOT, cs35l41->acpi_subsystem_id,
23962306a36Sopenharmony_ci					    NULL, -1, "wmfw");
24062306a36Sopenharmony_ci	if (!ret) {
24162306a36Sopenharmony_ci		/* try cirrus/part-dspN-fwtype-sub<-spkidN><-ampname>.bin */
24262306a36Sopenharmony_ci		ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename,
24362306a36Sopenharmony_ci						    CS35L41_FIRMWARE_ROOT,
24462306a36Sopenharmony_ci						    cs35l41->acpi_subsystem_id, cs35l41->amp_name,
24562306a36Sopenharmony_ci						    cs35l41->speaker_id, "bin");
24662306a36Sopenharmony_ci		if (ret)
24762306a36Sopenharmony_ci			/* try cirrus/part-dspN-fwtype-sub<-spkidN>.bin */
24862306a36Sopenharmony_ci			ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware,
24962306a36Sopenharmony_ci							    coeff_filename, CS35L41_FIRMWARE_ROOT,
25062306a36Sopenharmony_ci							    cs35l41->acpi_subsystem_id, NULL,
25162306a36Sopenharmony_ci							    cs35l41->speaker_id, "bin");
25262306a36Sopenharmony_ci		if (ret)
25362306a36Sopenharmony_ci			goto coeff_err;
25462306a36Sopenharmony_ci	}
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	return ret;
25762306a36Sopenharmony_cicoeff_err:
25862306a36Sopenharmony_ci	release_firmware(*wmfw_firmware);
25962306a36Sopenharmony_ci	kfree(*wmfw_filename);
26062306a36Sopenharmony_ci	return ret;
26162306a36Sopenharmony_ci}
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_cistatic int cs35l41_fallback_firmware_file(struct cs35l41_hda *cs35l41,
26462306a36Sopenharmony_ci					  const struct firmware **wmfw_firmware,
26562306a36Sopenharmony_ci					  char **wmfw_filename,
26662306a36Sopenharmony_ci					  const struct firmware **coeff_firmware,
26762306a36Sopenharmony_ci					  char **coeff_filename)
26862306a36Sopenharmony_ci{
26962306a36Sopenharmony_ci	int ret;
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	/* Handle fallback */
27262306a36Sopenharmony_ci	dev_warn(cs35l41->dev, "Falling back to default firmware.\n");
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	/* fallback try cirrus/part-dspN-fwtype.wmfw */
27562306a36Sopenharmony_ci	ret = cs35l41_request_firmware_file(cs35l41, wmfw_firmware, wmfw_filename,
27662306a36Sopenharmony_ci					    CS35L41_FIRMWARE_ROOT, NULL, NULL, -1, "wmfw");
27762306a36Sopenharmony_ci	if (ret)
27862306a36Sopenharmony_ci		goto err;
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	/* fallback try cirrus/part-dspN-fwtype.bin */
28162306a36Sopenharmony_ci	ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename,
28262306a36Sopenharmony_ci					    CS35L41_FIRMWARE_ROOT, NULL, NULL, -1, "bin");
28362306a36Sopenharmony_ci	if (ret) {
28462306a36Sopenharmony_ci		release_firmware(*wmfw_firmware);
28562306a36Sopenharmony_ci		kfree(*wmfw_filename);
28662306a36Sopenharmony_ci		goto err;
28762306a36Sopenharmony_ci	}
28862306a36Sopenharmony_ci	return 0;
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_cierr:
29162306a36Sopenharmony_ci	dev_warn(cs35l41->dev, "Unable to find firmware and tuning\n");
29262306a36Sopenharmony_ci	return ret;
29362306a36Sopenharmony_ci}
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_cistatic int cs35l41_request_firmware_files(struct cs35l41_hda *cs35l41,
29662306a36Sopenharmony_ci					  const struct firmware **wmfw_firmware,
29762306a36Sopenharmony_ci					  char **wmfw_filename,
29862306a36Sopenharmony_ci					  const struct firmware **coeff_firmware,
29962306a36Sopenharmony_ci					  char **coeff_filename)
30062306a36Sopenharmony_ci{
30162306a36Sopenharmony_ci	int ret;
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	if (cs35l41->speaker_id > -1) {
30462306a36Sopenharmony_ci		ret = cs35l41_request_firmware_files_spkid(cs35l41, wmfw_firmware, wmfw_filename,
30562306a36Sopenharmony_ci							   coeff_firmware, coeff_filename);
30662306a36Sopenharmony_ci		goto out;
30762306a36Sopenharmony_ci	}
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	/* try cirrus/part-dspN-fwtype-sub<-ampname>.wmfw */
31062306a36Sopenharmony_ci	ret = cs35l41_request_firmware_file(cs35l41, wmfw_firmware, wmfw_filename,
31162306a36Sopenharmony_ci					    CS35L41_FIRMWARE_ROOT, cs35l41->acpi_subsystem_id,
31262306a36Sopenharmony_ci					    cs35l41->amp_name, -1, "wmfw");
31362306a36Sopenharmony_ci	if (!ret) {
31462306a36Sopenharmony_ci		/* try cirrus/part-dspN-fwtype-sub<-ampname>.bin */
31562306a36Sopenharmony_ci		ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename,
31662306a36Sopenharmony_ci						    CS35L41_FIRMWARE_ROOT,
31762306a36Sopenharmony_ci						    cs35l41->acpi_subsystem_id, cs35l41->amp_name,
31862306a36Sopenharmony_ci						    -1, "bin");
31962306a36Sopenharmony_ci		if (ret)
32062306a36Sopenharmony_ci			goto coeff_err;
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci		goto out;
32362306a36Sopenharmony_ci	}
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	/* try cirrus/part-dspN-fwtype-sub.wmfw */
32662306a36Sopenharmony_ci	ret = cs35l41_request_firmware_file(cs35l41, wmfw_firmware, wmfw_filename,
32762306a36Sopenharmony_ci					    CS35L41_FIRMWARE_ROOT, cs35l41->acpi_subsystem_id,
32862306a36Sopenharmony_ci					    NULL, -1, "wmfw");
32962306a36Sopenharmony_ci	if (!ret) {
33062306a36Sopenharmony_ci		/* try cirrus/part-dspN-fwtype-sub<-ampname>.bin */
33162306a36Sopenharmony_ci		ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename,
33262306a36Sopenharmony_ci						    CS35L41_FIRMWARE_ROOT,
33362306a36Sopenharmony_ci						    cs35l41->acpi_subsystem_id,
33462306a36Sopenharmony_ci						    cs35l41->amp_name, -1, "bin");
33562306a36Sopenharmony_ci		if (ret)
33662306a36Sopenharmony_ci			/* try cirrus/part-dspN-fwtype-sub.bin */
33762306a36Sopenharmony_ci			ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename,
33862306a36Sopenharmony_ci							    CS35L41_FIRMWARE_ROOT,
33962306a36Sopenharmony_ci							    cs35l41->acpi_subsystem_id, NULL, -1,
34062306a36Sopenharmony_ci							    "bin");
34162306a36Sopenharmony_ci		if (ret)
34262306a36Sopenharmony_ci			goto coeff_err;
34362306a36Sopenharmony_ci	}
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ciout:
34662306a36Sopenharmony_ci	if (ret)
34762306a36Sopenharmony_ci		/* if all attempts at finding firmware fail, try fallback */
34862306a36Sopenharmony_ci		goto fallback;
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	return 0;
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_cicoeff_err:
35362306a36Sopenharmony_ci	release_firmware(*wmfw_firmware);
35462306a36Sopenharmony_ci	kfree(*wmfw_filename);
35562306a36Sopenharmony_cifallback:
35662306a36Sopenharmony_ci	return cs35l41_fallback_firmware_file(cs35l41, wmfw_firmware, wmfw_filename,
35762306a36Sopenharmony_ci					      coeff_firmware, coeff_filename);
35862306a36Sopenharmony_ci}
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_EFI)
36162306a36Sopenharmony_cistatic int cs35l41_apply_calibration(struct cs35l41_hda *cs35l41, __be32 ambient, __be32 r0,
36262306a36Sopenharmony_ci				     __be32 status, __be32 checksum)
36362306a36Sopenharmony_ci{
36462306a36Sopenharmony_ci	int ret;
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	ret = hda_cs_dsp_write_ctl(&cs35l41->cs_dsp, CAL_AMBIENT_DSP_CTL_NAME, CAL_DSP_CTL_TYPE,
36762306a36Sopenharmony_ci				   CAL_DSP_CTL_ALG, &ambient, 4);
36862306a36Sopenharmony_ci	if (ret) {
36962306a36Sopenharmony_ci		dev_err(cs35l41->dev, "Cannot Write Control: %s - %d\n", CAL_AMBIENT_DSP_CTL_NAME,
37062306a36Sopenharmony_ci			ret);
37162306a36Sopenharmony_ci		return ret;
37262306a36Sopenharmony_ci	}
37362306a36Sopenharmony_ci	ret = hda_cs_dsp_write_ctl(&cs35l41->cs_dsp, CAL_R_DSP_CTL_NAME, CAL_DSP_CTL_TYPE,
37462306a36Sopenharmony_ci				   CAL_DSP_CTL_ALG, &r0, 4);
37562306a36Sopenharmony_ci	if (ret) {
37662306a36Sopenharmony_ci		dev_err(cs35l41->dev, "Cannot Write Control: %s - %d\n", CAL_R_DSP_CTL_NAME, ret);
37762306a36Sopenharmony_ci		return ret;
37862306a36Sopenharmony_ci	}
37962306a36Sopenharmony_ci	ret = hda_cs_dsp_write_ctl(&cs35l41->cs_dsp, CAL_STATUS_DSP_CTL_NAME, CAL_DSP_CTL_TYPE,
38062306a36Sopenharmony_ci				   CAL_DSP_CTL_ALG, &status, 4);
38162306a36Sopenharmony_ci	if (ret) {
38262306a36Sopenharmony_ci		dev_err(cs35l41->dev, "Cannot Write Control: %s - %d\n", CAL_STATUS_DSP_CTL_NAME,
38362306a36Sopenharmony_ci			ret);
38462306a36Sopenharmony_ci		return ret;
38562306a36Sopenharmony_ci	}
38662306a36Sopenharmony_ci	ret = hda_cs_dsp_write_ctl(&cs35l41->cs_dsp, CAL_CHECKSUM_DSP_CTL_NAME, CAL_DSP_CTL_TYPE,
38762306a36Sopenharmony_ci				   CAL_DSP_CTL_ALG, &checksum, 4);
38862306a36Sopenharmony_ci	if (ret) {
38962306a36Sopenharmony_ci		dev_err(cs35l41->dev, "Cannot Write Control: %s - %d\n", CAL_CHECKSUM_DSP_CTL_NAME,
39062306a36Sopenharmony_ci			ret);
39162306a36Sopenharmony_ci		return ret;
39262306a36Sopenharmony_ci	}
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	return 0;
39562306a36Sopenharmony_ci}
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_cistatic int cs35l41_save_calibration(struct cs35l41_hda *cs35l41)
39862306a36Sopenharmony_ci{
39962306a36Sopenharmony_ci	static efi_guid_t efi_guid = EFI_GUID(0x02f9af02, 0x7734, 0x4233, 0xb4, 0x3d, 0x93, 0xfe,
40062306a36Sopenharmony_ci					      0x5a, 0xa3, 0x5d, 0xb3);
40162306a36Sopenharmony_ci	static efi_char16_t efi_name[] = L"CirrusSmartAmpCalibrationData";
40262306a36Sopenharmony_ci	const struct cs35l41_amp_efi_data *efi_data;
40362306a36Sopenharmony_ci	const struct cs35l41_amp_cal_data *cl;
40462306a36Sopenharmony_ci	unsigned long data_size = 0;
40562306a36Sopenharmony_ci	efi_status_t status;
40662306a36Sopenharmony_ci	int ret = 0;
40762306a36Sopenharmony_ci	u8 *data = NULL;
40862306a36Sopenharmony_ci	u32 attr;
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	/* Get real size of UEFI variable */
41162306a36Sopenharmony_ci	status = efi.get_variable(efi_name, &efi_guid, &attr, &data_size, data);
41262306a36Sopenharmony_ci	if (status == EFI_BUFFER_TOO_SMALL) {
41362306a36Sopenharmony_ci		ret = -ENODEV;
41462306a36Sopenharmony_ci		/* Allocate data buffer of data_size bytes */
41562306a36Sopenharmony_ci		data = vmalloc(data_size);
41662306a36Sopenharmony_ci		if (!data)
41762306a36Sopenharmony_ci			return -ENOMEM;
41862306a36Sopenharmony_ci		/* Get variable contents into buffer */
41962306a36Sopenharmony_ci		status = efi.get_variable(efi_name, &efi_guid, &attr, &data_size, data);
42062306a36Sopenharmony_ci		if (status == EFI_SUCCESS) {
42162306a36Sopenharmony_ci			efi_data = (struct cs35l41_amp_efi_data *)data;
42262306a36Sopenharmony_ci			dev_dbg(cs35l41->dev, "Calibration: Size=%d, Amp Count=%d\n",
42362306a36Sopenharmony_ci				efi_data->size, efi_data->count);
42462306a36Sopenharmony_ci			if (efi_data->count > cs35l41->index) {
42562306a36Sopenharmony_ci				cl = &efi_data->data[cs35l41->index];
42662306a36Sopenharmony_ci				dev_dbg(cs35l41->dev,
42762306a36Sopenharmony_ci					"Calibration: Ambient=%02x, Status=%02x, R0=%d\n",
42862306a36Sopenharmony_ci					cl->calAmbient, cl->calStatus, cl->calR);
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci				/* Calibration can only be applied whilst the DSP is not running */
43162306a36Sopenharmony_ci				ret = cs35l41_apply_calibration(cs35l41,
43262306a36Sopenharmony_ci								cpu_to_be32(cl->calAmbient),
43362306a36Sopenharmony_ci								cpu_to_be32(cl->calR),
43462306a36Sopenharmony_ci								cpu_to_be32(cl->calStatus),
43562306a36Sopenharmony_ci								cpu_to_be32(cl->calR + 1));
43662306a36Sopenharmony_ci			}
43762306a36Sopenharmony_ci		}
43862306a36Sopenharmony_ci		vfree(data);
43962306a36Sopenharmony_ci	}
44062306a36Sopenharmony_ci	return ret;
44162306a36Sopenharmony_ci}
44262306a36Sopenharmony_ci#else
44362306a36Sopenharmony_cistatic int cs35l41_save_calibration(struct cs35l41_hda *cs35l41)
44462306a36Sopenharmony_ci{
44562306a36Sopenharmony_ci	dev_warn(cs35l41->dev, "Calibration not supported without EFI support.\n");
44662306a36Sopenharmony_ci	return 0;
44762306a36Sopenharmony_ci}
44862306a36Sopenharmony_ci#endif
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_cistatic int cs35l41_init_dsp(struct cs35l41_hda *cs35l41)
45162306a36Sopenharmony_ci{
45262306a36Sopenharmony_ci	const struct firmware *coeff_firmware = NULL;
45362306a36Sopenharmony_ci	const struct firmware *wmfw_firmware = NULL;
45462306a36Sopenharmony_ci	struct cs_dsp *dsp = &cs35l41->cs_dsp;
45562306a36Sopenharmony_ci	char *coeff_filename = NULL;
45662306a36Sopenharmony_ci	char *wmfw_filename = NULL;
45762306a36Sopenharmony_ci	int ret;
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	if (!cs35l41->halo_initialized) {
46062306a36Sopenharmony_ci		cs35l41_configure_cs_dsp(cs35l41->dev, cs35l41->regmap, dsp);
46162306a36Sopenharmony_ci		dsp->client_ops = &client_ops;
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci		ret = cs_dsp_halo_init(&cs35l41->cs_dsp);
46462306a36Sopenharmony_ci		if (ret)
46562306a36Sopenharmony_ci			return ret;
46662306a36Sopenharmony_ci		cs35l41->halo_initialized = true;
46762306a36Sopenharmony_ci	}
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	ret = cs35l41_request_firmware_files(cs35l41, &wmfw_firmware, &wmfw_filename,
47062306a36Sopenharmony_ci					     &coeff_firmware, &coeff_filename);
47162306a36Sopenharmony_ci	if (ret < 0)
47262306a36Sopenharmony_ci		return ret;
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci	dev_dbg(cs35l41->dev, "Loading WMFW Firmware: %s\n", wmfw_filename);
47562306a36Sopenharmony_ci	if (coeff_filename)
47662306a36Sopenharmony_ci		dev_dbg(cs35l41->dev, "Loading Coefficient File: %s\n", coeff_filename);
47762306a36Sopenharmony_ci	else
47862306a36Sopenharmony_ci		dev_warn(cs35l41->dev, "No Coefficient File available.\n");
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	ret = cs_dsp_power_up(dsp, wmfw_firmware, wmfw_filename, coeff_firmware, coeff_filename,
48162306a36Sopenharmony_ci			      hda_cs_dsp_fw_ids[cs35l41->firmware_type]);
48262306a36Sopenharmony_ci	if (ret)
48362306a36Sopenharmony_ci		goto err_release;
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci	cs35l41_add_controls(cs35l41);
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	ret = cs35l41_save_calibration(cs35l41);
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_cierr_release:
49062306a36Sopenharmony_ci	release_firmware(wmfw_firmware);
49162306a36Sopenharmony_ci	release_firmware(coeff_firmware);
49262306a36Sopenharmony_ci	kfree(wmfw_filename);
49362306a36Sopenharmony_ci	kfree(coeff_filename);
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci	return ret;
49662306a36Sopenharmony_ci}
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_cistatic void cs35l41_shutdown_dsp(struct cs35l41_hda *cs35l41)
49962306a36Sopenharmony_ci{
50062306a36Sopenharmony_ci	struct cs_dsp *dsp = &cs35l41->cs_dsp;
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	cs_dsp_stop(dsp);
50362306a36Sopenharmony_ci	cs_dsp_power_down(dsp);
50462306a36Sopenharmony_ci	cs35l41->firmware_running = false;
50562306a36Sopenharmony_ci	dev_dbg(cs35l41->dev, "Unloaded Firmware\n");
50662306a36Sopenharmony_ci}
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_cistatic void cs35l41_remove_dsp(struct cs35l41_hda *cs35l41)
50962306a36Sopenharmony_ci{
51062306a36Sopenharmony_ci	struct cs_dsp *dsp = &cs35l41->cs_dsp;
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci	cancel_work_sync(&cs35l41->fw_load_work);
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	mutex_lock(&cs35l41->fw_mutex);
51562306a36Sopenharmony_ci	cs35l41_shutdown_dsp(cs35l41);
51662306a36Sopenharmony_ci	cs_dsp_remove(dsp);
51762306a36Sopenharmony_ci	cs35l41->halo_initialized = false;
51862306a36Sopenharmony_ci	mutex_unlock(&cs35l41->fw_mutex);
51962306a36Sopenharmony_ci}
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci/* Protection release cycle to get the speaker out of Safe-Mode */
52262306a36Sopenharmony_cistatic void cs35l41_error_release(struct device *dev, struct regmap *regmap, unsigned int mask)
52362306a36Sopenharmony_ci{
52462306a36Sopenharmony_ci	regmap_write(regmap, CS35L41_PROTECT_REL_ERR_IGN, 0);
52562306a36Sopenharmony_ci	regmap_set_bits(regmap, CS35L41_PROTECT_REL_ERR_IGN, mask);
52662306a36Sopenharmony_ci	regmap_clear_bits(regmap, CS35L41_PROTECT_REL_ERR_IGN, mask);
52762306a36Sopenharmony_ci}
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci/* Clear all errors to release safe mode. Global Enable must be cleared first. */
53062306a36Sopenharmony_cistatic void cs35l41_irq_release(struct cs35l41_hda *cs35l41)
53162306a36Sopenharmony_ci{
53262306a36Sopenharmony_ci	cs35l41_error_release(cs35l41->dev, cs35l41->regmap, cs35l41->irq_errors);
53362306a36Sopenharmony_ci	cs35l41->irq_errors = 0;
53462306a36Sopenharmony_ci}
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_cistatic void cs35l41_hda_play_start(struct device *dev)
53762306a36Sopenharmony_ci{
53862306a36Sopenharmony_ci	struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev);
53962306a36Sopenharmony_ci	struct regmap *reg = cs35l41->regmap;
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci	dev_dbg(dev, "Play (Start)\n");
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	if (cs35l41->playback_started) {
54462306a36Sopenharmony_ci		dev_dbg(dev, "Playback already started.");
54562306a36Sopenharmony_ci		return;
54662306a36Sopenharmony_ci	}
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci	cs35l41->playback_started = true;
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci	if (cs35l41->firmware_running) {
55162306a36Sopenharmony_ci		regmap_multi_reg_write(reg, cs35l41_hda_config_dsp,
55262306a36Sopenharmony_ci				       ARRAY_SIZE(cs35l41_hda_config_dsp));
55362306a36Sopenharmony_ci		regmap_update_bits(reg, CS35L41_PWR_CTRL2,
55462306a36Sopenharmony_ci				   CS35L41_VMON_EN_MASK | CS35L41_IMON_EN_MASK,
55562306a36Sopenharmony_ci				   1 << CS35L41_VMON_EN_SHIFT | 1 << CS35L41_IMON_EN_SHIFT);
55662306a36Sopenharmony_ci		cs35l41_set_cspl_mbox_cmd(cs35l41->dev, reg, CSPL_MBOX_CMD_RESUME);
55762306a36Sopenharmony_ci	} else {
55862306a36Sopenharmony_ci		regmap_multi_reg_write(reg, cs35l41_hda_config, ARRAY_SIZE(cs35l41_hda_config));
55962306a36Sopenharmony_ci	}
56062306a36Sopenharmony_ci	regmap_update_bits(reg, CS35L41_PWR_CTRL2, CS35L41_AMP_EN_MASK, 1 << CS35L41_AMP_EN_SHIFT);
56162306a36Sopenharmony_ci	if (cs35l41->hw_cfg.bst_type == CS35L41_EXT_BOOST)
56262306a36Sopenharmony_ci		regmap_write(reg, CS35L41_GPIO1_CTRL1, 0x00008001);
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci}
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_cistatic void cs35l41_hda_play_done(struct device *dev)
56762306a36Sopenharmony_ci{
56862306a36Sopenharmony_ci	struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev);
56962306a36Sopenharmony_ci	struct regmap *reg = cs35l41->regmap;
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci	dev_dbg(dev, "Play (Complete)\n");
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci	cs35l41_global_enable(dev, reg, cs35l41->hw_cfg.bst_type, 1,
57462306a36Sopenharmony_ci			      cs35l41->firmware_running);
57562306a36Sopenharmony_ci	if (cs35l41->firmware_running) {
57662306a36Sopenharmony_ci		regmap_multi_reg_write(reg, cs35l41_hda_unmute_dsp,
57762306a36Sopenharmony_ci				       ARRAY_SIZE(cs35l41_hda_unmute_dsp));
57862306a36Sopenharmony_ci	} else {
57962306a36Sopenharmony_ci		regmap_multi_reg_write(reg, cs35l41_hda_unmute,
58062306a36Sopenharmony_ci				       ARRAY_SIZE(cs35l41_hda_unmute));
58162306a36Sopenharmony_ci	}
58262306a36Sopenharmony_ci}
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_cistatic void cs35l41_hda_pause_start(struct device *dev)
58562306a36Sopenharmony_ci{
58662306a36Sopenharmony_ci	struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev);
58762306a36Sopenharmony_ci	struct regmap *reg = cs35l41->regmap;
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci	dev_dbg(dev, "Pause (Start)\n");
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci	regmap_multi_reg_write(reg, cs35l41_hda_mute, ARRAY_SIZE(cs35l41_hda_mute));
59262306a36Sopenharmony_ci	cs35l41_global_enable(dev, reg, cs35l41->hw_cfg.bst_type, 0,
59362306a36Sopenharmony_ci			      cs35l41->firmware_running);
59462306a36Sopenharmony_ci}
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_cistatic void cs35l41_hda_pause_done(struct device *dev)
59762306a36Sopenharmony_ci{
59862306a36Sopenharmony_ci	struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev);
59962306a36Sopenharmony_ci	struct regmap *reg = cs35l41->regmap;
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci	dev_dbg(dev, "Pause (Complete)\n");
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci	regmap_update_bits(reg, CS35L41_PWR_CTRL2, CS35L41_AMP_EN_MASK, 0 << CS35L41_AMP_EN_SHIFT);
60462306a36Sopenharmony_ci	if (cs35l41->hw_cfg.bst_type == CS35L41_EXT_BOOST)
60562306a36Sopenharmony_ci		regmap_write(reg, CS35L41_GPIO1_CTRL1, 0x00000001);
60662306a36Sopenharmony_ci	if (cs35l41->firmware_running) {
60762306a36Sopenharmony_ci		cs35l41_set_cspl_mbox_cmd(dev, reg, CSPL_MBOX_CMD_PAUSE);
60862306a36Sopenharmony_ci		regmap_update_bits(reg, CS35L41_PWR_CTRL2,
60962306a36Sopenharmony_ci				   CS35L41_VMON_EN_MASK | CS35L41_IMON_EN_MASK,
61062306a36Sopenharmony_ci				   0 << CS35L41_VMON_EN_SHIFT | 0 << CS35L41_IMON_EN_SHIFT);
61162306a36Sopenharmony_ci	}
61262306a36Sopenharmony_ci	cs35l41_irq_release(cs35l41);
61362306a36Sopenharmony_ci	cs35l41->playback_started = false;
61462306a36Sopenharmony_ci}
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_cistatic void cs35l41_hda_pre_playback_hook(struct device *dev, int action)
61762306a36Sopenharmony_ci{
61862306a36Sopenharmony_ci	struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev);
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci	switch (action) {
62162306a36Sopenharmony_ci	case HDA_GEN_PCM_ACT_CLEANUP:
62262306a36Sopenharmony_ci		mutex_lock(&cs35l41->fw_mutex);
62362306a36Sopenharmony_ci		cs35l41_hda_pause_start(dev);
62462306a36Sopenharmony_ci		mutex_unlock(&cs35l41->fw_mutex);
62562306a36Sopenharmony_ci		break;
62662306a36Sopenharmony_ci	default:
62762306a36Sopenharmony_ci		break;
62862306a36Sopenharmony_ci	}
62962306a36Sopenharmony_ci}
63062306a36Sopenharmony_cistatic void cs35l41_hda_playback_hook(struct device *dev, int action)
63162306a36Sopenharmony_ci{
63262306a36Sopenharmony_ci	struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev);
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci	switch (action) {
63562306a36Sopenharmony_ci	case HDA_GEN_PCM_ACT_OPEN:
63662306a36Sopenharmony_ci		/*
63762306a36Sopenharmony_ci		 * All amps must be resumed before we can start playing back.
63862306a36Sopenharmony_ci		 * This ensures, for external boost, that all amps are in AMP_SAFE mode.
63962306a36Sopenharmony_ci		 * Do this in HDA_GEN_PCM_ACT_OPEN, since this is run prior to any of the
64062306a36Sopenharmony_ci		 * other actions.
64162306a36Sopenharmony_ci		 */
64262306a36Sopenharmony_ci		pm_runtime_get_sync(dev);
64362306a36Sopenharmony_ci		break;
64462306a36Sopenharmony_ci	case HDA_GEN_PCM_ACT_PREPARE:
64562306a36Sopenharmony_ci		mutex_lock(&cs35l41->fw_mutex);
64662306a36Sopenharmony_ci		cs35l41_hda_play_start(dev);
64762306a36Sopenharmony_ci		mutex_unlock(&cs35l41->fw_mutex);
64862306a36Sopenharmony_ci		break;
64962306a36Sopenharmony_ci	case HDA_GEN_PCM_ACT_CLEANUP:
65062306a36Sopenharmony_ci		mutex_lock(&cs35l41->fw_mutex);
65162306a36Sopenharmony_ci		cs35l41_hda_pause_done(dev);
65262306a36Sopenharmony_ci		mutex_unlock(&cs35l41->fw_mutex);
65362306a36Sopenharmony_ci		break;
65462306a36Sopenharmony_ci	case HDA_GEN_PCM_ACT_CLOSE:
65562306a36Sopenharmony_ci		mutex_lock(&cs35l41->fw_mutex);
65662306a36Sopenharmony_ci		if (!cs35l41->firmware_running && cs35l41->request_fw_load &&
65762306a36Sopenharmony_ci		    !cs35l41->fw_request_ongoing) {
65862306a36Sopenharmony_ci			dev_info(dev, "Requesting Firmware Load after HDA_GEN_PCM_ACT_CLOSE\n");
65962306a36Sopenharmony_ci			cs35l41->fw_request_ongoing = true;
66062306a36Sopenharmony_ci			schedule_work(&cs35l41->fw_load_work);
66162306a36Sopenharmony_ci		}
66262306a36Sopenharmony_ci		mutex_unlock(&cs35l41->fw_mutex);
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_ci		/*
66562306a36Sopenharmony_ci		 * Playback must be finished for all amps before we start runtime suspend.
66662306a36Sopenharmony_ci		 * This ensures no amps are playing back when we start putting them to sleep.
66762306a36Sopenharmony_ci		 */
66862306a36Sopenharmony_ci		pm_runtime_mark_last_busy(dev);
66962306a36Sopenharmony_ci		pm_runtime_put_autosuspend(dev);
67062306a36Sopenharmony_ci		break;
67162306a36Sopenharmony_ci	default:
67262306a36Sopenharmony_ci		break;
67362306a36Sopenharmony_ci	}
67462306a36Sopenharmony_ci}
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_cistatic void cs35l41_hda_post_playback_hook(struct device *dev, int action)
67762306a36Sopenharmony_ci{
67862306a36Sopenharmony_ci	struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev);
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci	switch (action) {
68162306a36Sopenharmony_ci	case HDA_GEN_PCM_ACT_PREPARE:
68262306a36Sopenharmony_ci		mutex_lock(&cs35l41->fw_mutex);
68362306a36Sopenharmony_ci		cs35l41_hda_play_done(dev);
68462306a36Sopenharmony_ci		mutex_unlock(&cs35l41->fw_mutex);
68562306a36Sopenharmony_ci		break;
68662306a36Sopenharmony_ci	default:
68762306a36Sopenharmony_ci		break;
68862306a36Sopenharmony_ci	}
68962306a36Sopenharmony_ci}
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_cistatic int cs35l41_hda_channel_map(struct device *dev, unsigned int tx_num, unsigned int *tx_slot,
69262306a36Sopenharmony_ci				    unsigned int rx_num, unsigned int *rx_slot)
69362306a36Sopenharmony_ci{
69462306a36Sopenharmony_ci	struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev);
69562306a36Sopenharmony_ci	static const char * const channel_name[] = { "L", "R" };
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ci	if (!cs35l41->amp_name) {
69862306a36Sopenharmony_ci		if (*rx_slot >= ARRAY_SIZE(channel_name))
69962306a36Sopenharmony_ci			return -EINVAL;
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_ci		cs35l41->amp_name = devm_kasprintf(cs35l41->dev, GFP_KERNEL, "%s%d",
70262306a36Sopenharmony_ci						   channel_name[*rx_slot], cs35l41->channel_index);
70362306a36Sopenharmony_ci		if (!cs35l41->amp_name)
70462306a36Sopenharmony_ci			return -ENOMEM;
70562306a36Sopenharmony_ci	}
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_ci	return cs35l41_set_channels(cs35l41->dev, cs35l41->regmap, tx_num, tx_slot, rx_num,
70862306a36Sopenharmony_ci				    rx_slot);
70962306a36Sopenharmony_ci}
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_cistatic int cs35l41_ready_for_reset(struct cs35l41_hda *cs35l41)
71262306a36Sopenharmony_ci{
71362306a36Sopenharmony_ci	int ret = 0;
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ci	mutex_lock(&cs35l41->fw_mutex);
71662306a36Sopenharmony_ci	if (cs35l41->firmware_running) {
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci		regcache_cache_only(cs35l41->regmap, false);
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci		ret = cs35l41_exit_hibernate(cs35l41->dev, cs35l41->regmap);
72162306a36Sopenharmony_ci		if (ret) {
72262306a36Sopenharmony_ci			dev_warn(cs35l41->dev, "Unable to exit Hibernate.");
72362306a36Sopenharmony_ci			goto err;
72462306a36Sopenharmony_ci		}
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci		/* Test key needs to be unlocked to allow the OTP settings to re-apply */
72762306a36Sopenharmony_ci		cs35l41_test_key_unlock(cs35l41->dev, cs35l41->regmap);
72862306a36Sopenharmony_ci		ret = regcache_sync(cs35l41->regmap);
72962306a36Sopenharmony_ci		cs35l41_test_key_lock(cs35l41->dev, cs35l41->regmap);
73062306a36Sopenharmony_ci		if (ret) {
73162306a36Sopenharmony_ci			dev_err(cs35l41->dev, "Failed to restore register cache: %d\n", ret);
73262306a36Sopenharmony_ci			goto err;
73362306a36Sopenharmony_ci		}
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_ci		if (cs35l41->hw_cfg.bst_type == CS35L41_EXT_BOOST)
73662306a36Sopenharmony_ci			cs35l41_init_boost(cs35l41->dev, cs35l41->regmap, &cs35l41->hw_cfg);
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_ci		cs35l41_shutdown_dsp(cs35l41);
73962306a36Sopenharmony_ci		cs35l41_safe_reset(cs35l41->regmap, cs35l41->hw_cfg.bst_type);
74062306a36Sopenharmony_ci	}
74162306a36Sopenharmony_cierr:
74262306a36Sopenharmony_ci	regcache_cache_only(cs35l41->regmap, true);
74362306a36Sopenharmony_ci	regcache_mark_dirty(cs35l41->regmap);
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_ci	mutex_unlock(&cs35l41->fw_mutex);
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ci	return ret;
74862306a36Sopenharmony_ci}
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_cistatic int cs35l41_system_suspend_prep(struct device *dev)
75162306a36Sopenharmony_ci{
75262306a36Sopenharmony_ci	struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev);
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci	dev_dbg(cs35l41->dev, "System Suspend Prepare\n");
75562306a36Sopenharmony_ci
75662306a36Sopenharmony_ci	if (cs35l41->hw_cfg.bst_type == CS35L41_EXT_BOOST_NO_VSPK_SWITCH) {
75762306a36Sopenharmony_ci		dev_err_once(cs35l41->dev, "System Suspend not supported\n");
75862306a36Sopenharmony_ci		return 0; /* don't block the whole system suspend */
75962306a36Sopenharmony_ci	}
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ci	mutex_lock(&cs35l41->fw_mutex);
76262306a36Sopenharmony_ci	if (cs35l41->playback_started)
76362306a36Sopenharmony_ci		cs35l41_hda_pause_start(dev);
76462306a36Sopenharmony_ci	mutex_unlock(&cs35l41->fw_mutex);
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci	return 0;
76762306a36Sopenharmony_ci}
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_cistatic int cs35l41_system_suspend(struct device *dev)
77062306a36Sopenharmony_ci{
77162306a36Sopenharmony_ci	struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev);
77262306a36Sopenharmony_ci	int ret;
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_ci	dev_dbg(cs35l41->dev, "System Suspend\n");
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_ci	if (cs35l41->hw_cfg.bst_type == CS35L41_EXT_BOOST_NO_VSPK_SWITCH) {
77762306a36Sopenharmony_ci		dev_err_once(cs35l41->dev, "System Suspend not supported\n");
77862306a36Sopenharmony_ci		return 0; /* don't block the whole system suspend */
77962306a36Sopenharmony_ci	}
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_ci	mutex_lock(&cs35l41->fw_mutex);
78262306a36Sopenharmony_ci	if (cs35l41->playback_started)
78362306a36Sopenharmony_ci		cs35l41_hda_pause_done(dev);
78462306a36Sopenharmony_ci	mutex_unlock(&cs35l41->fw_mutex);
78562306a36Sopenharmony_ci
78662306a36Sopenharmony_ci	ret = pm_runtime_force_suspend(dev);
78762306a36Sopenharmony_ci	if (ret) {
78862306a36Sopenharmony_ci		dev_err(dev, "System Suspend Failed, unable to runtime suspend: %d\n", ret);
78962306a36Sopenharmony_ci		return ret;
79062306a36Sopenharmony_ci	}
79162306a36Sopenharmony_ci
79262306a36Sopenharmony_ci	/* Shutdown DSP before system suspend */
79362306a36Sopenharmony_ci	ret = cs35l41_ready_for_reset(cs35l41);
79462306a36Sopenharmony_ci
79562306a36Sopenharmony_ci	if (ret)
79662306a36Sopenharmony_ci		dev_err(dev, "System Suspend Failed, not ready for Reset: %d\n", ret);
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_ci	/*
79962306a36Sopenharmony_ci	 * Reset GPIO may be shared, so cannot reset here.
80062306a36Sopenharmony_ci	 * However beyond this point, amps may be powered down.
80162306a36Sopenharmony_ci	 */
80262306a36Sopenharmony_ci	return ret;
80362306a36Sopenharmony_ci}
80462306a36Sopenharmony_ci
80562306a36Sopenharmony_cistatic int cs35l41_system_resume(struct device *dev)
80662306a36Sopenharmony_ci{
80762306a36Sopenharmony_ci	struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev);
80862306a36Sopenharmony_ci	int ret;
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ci	dev_dbg(cs35l41->dev, "System Resume\n");
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_ci	if (cs35l41->hw_cfg.bst_type == CS35L41_EXT_BOOST_NO_VSPK_SWITCH) {
81362306a36Sopenharmony_ci		dev_err_once(cs35l41->dev, "System Resume not supported\n");
81462306a36Sopenharmony_ci		return 0; /* don't block the whole system resume */
81562306a36Sopenharmony_ci	}
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_ci	if (cs35l41->reset_gpio) {
81862306a36Sopenharmony_ci		usleep_range(2000, 2100);
81962306a36Sopenharmony_ci		gpiod_set_value_cansleep(cs35l41->reset_gpio, 1);
82062306a36Sopenharmony_ci	}
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_ci	usleep_range(2000, 2100);
82362306a36Sopenharmony_ci
82462306a36Sopenharmony_ci	ret = pm_runtime_force_resume(dev);
82562306a36Sopenharmony_ci	if (ret) {
82662306a36Sopenharmony_ci		dev_err(dev, "System Resume Failed: Unable to runtime resume: %d\n", ret);
82762306a36Sopenharmony_ci		return ret;
82862306a36Sopenharmony_ci	}
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_ci	mutex_lock(&cs35l41->fw_mutex);
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_ci	if (cs35l41->request_fw_load && !cs35l41->fw_request_ongoing) {
83362306a36Sopenharmony_ci		cs35l41->fw_request_ongoing = true;
83462306a36Sopenharmony_ci		schedule_work(&cs35l41->fw_load_work);
83562306a36Sopenharmony_ci	}
83662306a36Sopenharmony_ci	mutex_unlock(&cs35l41->fw_mutex);
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_ci	return ret;
83962306a36Sopenharmony_ci}
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_cistatic int cs35l41_runtime_idle(struct device *dev)
84262306a36Sopenharmony_ci{
84362306a36Sopenharmony_ci	struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev);
84462306a36Sopenharmony_ci
84562306a36Sopenharmony_ci	if (cs35l41->hw_cfg.bst_type == CS35L41_EXT_BOOST_NO_VSPK_SWITCH)
84662306a36Sopenharmony_ci		return -EBUSY; /* suspend not supported yet on this model */
84762306a36Sopenharmony_ci	return 0;
84862306a36Sopenharmony_ci}
84962306a36Sopenharmony_ci
85062306a36Sopenharmony_cistatic int cs35l41_runtime_suspend(struct device *dev)
85162306a36Sopenharmony_ci{
85262306a36Sopenharmony_ci	struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev);
85362306a36Sopenharmony_ci	int ret = 0;
85462306a36Sopenharmony_ci
85562306a36Sopenharmony_ci	dev_dbg(cs35l41->dev, "Runtime Suspend\n");
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_ci	if (cs35l41->hw_cfg.bst_type == CS35L41_EXT_BOOST_NO_VSPK_SWITCH) {
85862306a36Sopenharmony_ci		dev_dbg(cs35l41->dev, "Runtime Suspend not supported\n");
85962306a36Sopenharmony_ci		return 0;
86062306a36Sopenharmony_ci	}
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_ci	mutex_lock(&cs35l41->fw_mutex);
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_ci	if (cs35l41->firmware_running) {
86562306a36Sopenharmony_ci		ret = cs35l41_enter_hibernate(cs35l41->dev, cs35l41->regmap,
86662306a36Sopenharmony_ci					      cs35l41->hw_cfg.bst_type);
86762306a36Sopenharmony_ci		if (ret)
86862306a36Sopenharmony_ci			goto err;
86962306a36Sopenharmony_ci	} else {
87062306a36Sopenharmony_ci		cs35l41_safe_reset(cs35l41->regmap, cs35l41->hw_cfg.bst_type);
87162306a36Sopenharmony_ci	}
87262306a36Sopenharmony_ci
87362306a36Sopenharmony_ci	regcache_cache_only(cs35l41->regmap, true);
87462306a36Sopenharmony_ci	regcache_mark_dirty(cs35l41->regmap);
87562306a36Sopenharmony_ci
87662306a36Sopenharmony_cierr:
87762306a36Sopenharmony_ci	mutex_unlock(&cs35l41->fw_mutex);
87862306a36Sopenharmony_ci
87962306a36Sopenharmony_ci	return ret;
88062306a36Sopenharmony_ci}
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_cistatic int cs35l41_runtime_resume(struct device *dev)
88362306a36Sopenharmony_ci{
88462306a36Sopenharmony_ci	struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev);
88562306a36Sopenharmony_ci	int ret = 0;
88662306a36Sopenharmony_ci
88762306a36Sopenharmony_ci	dev_dbg(cs35l41->dev, "Runtime Resume\n");
88862306a36Sopenharmony_ci
88962306a36Sopenharmony_ci	if (cs35l41->hw_cfg.bst_type == CS35L41_EXT_BOOST_NO_VSPK_SWITCH) {
89062306a36Sopenharmony_ci		dev_dbg(cs35l41->dev, "Runtime Resume not supported\n");
89162306a36Sopenharmony_ci		return 0;
89262306a36Sopenharmony_ci	}
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_ci	mutex_lock(&cs35l41->fw_mutex);
89562306a36Sopenharmony_ci
89662306a36Sopenharmony_ci	regcache_cache_only(cs35l41->regmap, false);
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_ci	if (cs35l41->firmware_running)	{
89962306a36Sopenharmony_ci		ret = cs35l41_exit_hibernate(cs35l41->dev, cs35l41->regmap);
90062306a36Sopenharmony_ci		if (ret) {
90162306a36Sopenharmony_ci			dev_warn(cs35l41->dev, "Unable to exit Hibernate.");
90262306a36Sopenharmony_ci			goto err;
90362306a36Sopenharmony_ci		}
90462306a36Sopenharmony_ci	}
90562306a36Sopenharmony_ci
90662306a36Sopenharmony_ci	/* Test key needs to be unlocked to allow the OTP settings to re-apply */
90762306a36Sopenharmony_ci	cs35l41_test_key_unlock(cs35l41->dev, cs35l41->regmap);
90862306a36Sopenharmony_ci	ret = regcache_sync(cs35l41->regmap);
90962306a36Sopenharmony_ci	cs35l41_test_key_lock(cs35l41->dev, cs35l41->regmap);
91062306a36Sopenharmony_ci	if (ret) {
91162306a36Sopenharmony_ci		dev_err(cs35l41->dev, "Failed to restore register cache: %d\n", ret);
91262306a36Sopenharmony_ci		goto err;
91362306a36Sopenharmony_ci	}
91462306a36Sopenharmony_ci
91562306a36Sopenharmony_ci	if (cs35l41->hw_cfg.bst_type == CS35L41_EXT_BOOST)
91662306a36Sopenharmony_ci		cs35l41_init_boost(cs35l41->dev, cs35l41->regmap, &cs35l41->hw_cfg);
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_cierr:
91962306a36Sopenharmony_ci	mutex_unlock(&cs35l41->fw_mutex);
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_ci	return ret;
92262306a36Sopenharmony_ci}
92362306a36Sopenharmony_ci
92462306a36Sopenharmony_cistatic int cs35l41_smart_amp(struct cs35l41_hda *cs35l41)
92562306a36Sopenharmony_ci{
92662306a36Sopenharmony_ci	__be32 halo_sts;
92762306a36Sopenharmony_ci	int ret;
92862306a36Sopenharmony_ci
92962306a36Sopenharmony_ci	ret = cs35l41_init_dsp(cs35l41);
93062306a36Sopenharmony_ci	if (ret) {
93162306a36Sopenharmony_ci		dev_warn(cs35l41->dev, "Cannot Initialize Firmware. Error: %d\n", ret);
93262306a36Sopenharmony_ci		goto clean_dsp;
93362306a36Sopenharmony_ci	}
93462306a36Sopenharmony_ci
93562306a36Sopenharmony_ci	ret = cs35l41_write_fs_errata(cs35l41->dev, cs35l41->regmap);
93662306a36Sopenharmony_ci	if (ret) {
93762306a36Sopenharmony_ci		dev_err(cs35l41->dev, "Cannot Write FS Errata: %d\n", ret);
93862306a36Sopenharmony_ci		goto clean_dsp;
93962306a36Sopenharmony_ci	}
94062306a36Sopenharmony_ci
94162306a36Sopenharmony_ci	ret = cs_dsp_run(&cs35l41->cs_dsp);
94262306a36Sopenharmony_ci	if (ret) {
94362306a36Sopenharmony_ci		dev_err(cs35l41->dev, "Fail to start dsp: %d\n", ret);
94462306a36Sopenharmony_ci		goto clean_dsp;
94562306a36Sopenharmony_ci	}
94662306a36Sopenharmony_ci
94762306a36Sopenharmony_ci	ret = read_poll_timeout(hda_cs_dsp_read_ctl, ret,
94862306a36Sopenharmony_ci				be32_to_cpu(halo_sts) == HALO_STATE_CODE_RUN,
94962306a36Sopenharmony_ci				1000, 15000, false, &cs35l41->cs_dsp, HALO_STATE_DSP_CTL_NAME,
95062306a36Sopenharmony_ci				HALO_STATE_DSP_CTL_TYPE, HALO_STATE_DSP_CTL_ALG,
95162306a36Sopenharmony_ci				&halo_sts, sizeof(halo_sts));
95262306a36Sopenharmony_ci
95362306a36Sopenharmony_ci	if (ret) {
95462306a36Sopenharmony_ci		dev_err(cs35l41->dev, "Timeout waiting for HALO Core to start. State: %u\n",
95562306a36Sopenharmony_ci			 halo_sts);
95662306a36Sopenharmony_ci		goto clean_dsp;
95762306a36Sopenharmony_ci	}
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_ci	ret = cs35l41_set_cspl_mbox_cmd(cs35l41->dev, cs35l41->regmap, CSPL_MBOX_CMD_PAUSE);
96062306a36Sopenharmony_ci	if (ret) {
96162306a36Sopenharmony_ci		dev_err(cs35l41->dev, "Error waiting for DSP to pause: %u\n", ret);
96262306a36Sopenharmony_ci		goto clean_dsp;
96362306a36Sopenharmony_ci	}
96462306a36Sopenharmony_ci
96562306a36Sopenharmony_ci	cs35l41->firmware_running = true;
96662306a36Sopenharmony_ci
96762306a36Sopenharmony_ci	return 0;
96862306a36Sopenharmony_ci
96962306a36Sopenharmony_ciclean_dsp:
97062306a36Sopenharmony_ci	cs35l41_shutdown_dsp(cs35l41);
97162306a36Sopenharmony_ci	return ret;
97262306a36Sopenharmony_ci}
97362306a36Sopenharmony_ci
97462306a36Sopenharmony_cistatic void cs35l41_load_firmware(struct cs35l41_hda *cs35l41, bool load)
97562306a36Sopenharmony_ci{
97662306a36Sopenharmony_ci	if (cs35l41->firmware_running && !load) {
97762306a36Sopenharmony_ci		dev_dbg(cs35l41->dev, "Unloading Firmware\n");
97862306a36Sopenharmony_ci		cs35l41_shutdown_dsp(cs35l41);
97962306a36Sopenharmony_ci	} else if (!cs35l41->firmware_running && load) {
98062306a36Sopenharmony_ci		dev_dbg(cs35l41->dev, "Loading Firmware\n");
98162306a36Sopenharmony_ci		cs35l41_smart_amp(cs35l41);
98262306a36Sopenharmony_ci	} else {
98362306a36Sopenharmony_ci		dev_dbg(cs35l41->dev, "Unable to Load firmware.\n");
98462306a36Sopenharmony_ci	}
98562306a36Sopenharmony_ci}
98662306a36Sopenharmony_ci
98762306a36Sopenharmony_cistatic int cs35l41_fw_load_ctl_get(struct snd_kcontrol *kcontrol,
98862306a36Sopenharmony_ci				   struct snd_ctl_elem_value *ucontrol)
98962306a36Sopenharmony_ci{
99062306a36Sopenharmony_ci	struct cs35l41_hda *cs35l41 = snd_kcontrol_chip(kcontrol);
99162306a36Sopenharmony_ci
99262306a36Sopenharmony_ci	ucontrol->value.integer.value[0] = cs35l41->request_fw_load;
99362306a36Sopenharmony_ci	return 0;
99462306a36Sopenharmony_ci}
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_cistatic void cs35l41_fw_load_work(struct work_struct *work)
99762306a36Sopenharmony_ci{
99862306a36Sopenharmony_ci	struct cs35l41_hda *cs35l41 = container_of(work, struct cs35l41_hda, fw_load_work);
99962306a36Sopenharmony_ci
100062306a36Sopenharmony_ci	pm_runtime_get_sync(cs35l41->dev);
100162306a36Sopenharmony_ci
100262306a36Sopenharmony_ci	mutex_lock(&cs35l41->fw_mutex);
100362306a36Sopenharmony_ci
100462306a36Sopenharmony_ci	/* Recheck if playback is ongoing, mutex will block playback during firmware loading */
100562306a36Sopenharmony_ci	if (cs35l41->playback_started)
100662306a36Sopenharmony_ci		dev_err(cs35l41->dev, "Cannot Load/Unload firmware during Playback. Retrying...\n");
100762306a36Sopenharmony_ci	else
100862306a36Sopenharmony_ci		cs35l41_load_firmware(cs35l41, cs35l41->request_fw_load);
100962306a36Sopenharmony_ci
101062306a36Sopenharmony_ci	cs35l41->fw_request_ongoing = false;
101162306a36Sopenharmony_ci	mutex_unlock(&cs35l41->fw_mutex);
101262306a36Sopenharmony_ci
101362306a36Sopenharmony_ci	pm_runtime_mark_last_busy(cs35l41->dev);
101462306a36Sopenharmony_ci	pm_runtime_put_autosuspend(cs35l41->dev);
101562306a36Sopenharmony_ci}
101662306a36Sopenharmony_ci
101762306a36Sopenharmony_cistatic int cs35l41_fw_load_ctl_put(struct snd_kcontrol *kcontrol,
101862306a36Sopenharmony_ci				   struct snd_ctl_elem_value *ucontrol)
101962306a36Sopenharmony_ci{
102062306a36Sopenharmony_ci	struct cs35l41_hda *cs35l41 = snd_kcontrol_chip(kcontrol);
102162306a36Sopenharmony_ci
102262306a36Sopenharmony_ci	if (cs35l41->request_fw_load == ucontrol->value.integer.value[0])
102362306a36Sopenharmony_ci		return 0;
102462306a36Sopenharmony_ci
102562306a36Sopenharmony_ci	if (cs35l41->fw_request_ongoing) {
102662306a36Sopenharmony_ci		dev_dbg(cs35l41->dev, "Existing request not complete\n");
102762306a36Sopenharmony_ci		return -EBUSY;
102862306a36Sopenharmony_ci	}
102962306a36Sopenharmony_ci
103062306a36Sopenharmony_ci	/* Check if playback is ongoing when initial request is made */
103162306a36Sopenharmony_ci	if (cs35l41->playback_started) {
103262306a36Sopenharmony_ci		dev_err(cs35l41->dev, "Cannot Load/Unload firmware during Playback\n");
103362306a36Sopenharmony_ci		return -EBUSY;
103462306a36Sopenharmony_ci	}
103562306a36Sopenharmony_ci
103662306a36Sopenharmony_ci	cs35l41->fw_request_ongoing = true;
103762306a36Sopenharmony_ci	cs35l41->request_fw_load = ucontrol->value.integer.value[0];
103862306a36Sopenharmony_ci	schedule_work(&cs35l41->fw_load_work);
103962306a36Sopenharmony_ci
104062306a36Sopenharmony_ci	return 1;
104162306a36Sopenharmony_ci}
104262306a36Sopenharmony_ci
104362306a36Sopenharmony_cistatic int cs35l41_fw_type_ctl_get(struct snd_kcontrol *kcontrol,
104462306a36Sopenharmony_ci				   struct snd_ctl_elem_value *ucontrol)
104562306a36Sopenharmony_ci{
104662306a36Sopenharmony_ci	struct cs35l41_hda *cs35l41 = snd_kcontrol_chip(kcontrol);
104762306a36Sopenharmony_ci
104862306a36Sopenharmony_ci	ucontrol->value.enumerated.item[0] = cs35l41->firmware_type;
104962306a36Sopenharmony_ci
105062306a36Sopenharmony_ci	return 0;
105162306a36Sopenharmony_ci}
105262306a36Sopenharmony_ci
105362306a36Sopenharmony_cistatic int cs35l41_fw_type_ctl_put(struct snd_kcontrol *kcontrol,
105462306a36Sopenharmony_ci				   struct snd_ctl_elem_value *ucontrol)
105562306a36Sopenharmony_ci{
105662306a36Sopenharmony_ci	struct cs35l41_hda *cs35l41 = snd_kcontrol_chip(kcontrol);
105762306a36Sopenharmony_ci
105862306a36Sopenharmony_ci	if (ucontrol->value.enumerated.item[0] < HDA_CS_DSP_NUM_FW) {
105962306a36Sopenharmony_ci		if (cs35l41->firmware_type != ucontrol->value.enumerated.item[0]) {
106062306a36Sopenharmony_ci			cs35l41->firmware_type = ucontrol->value.enumerated.item[0];
106162306a36Sopenharmony_ci			return 1;
106262306a36Sopenharmony_ci		} else {
106362306a36Sopenharmony_ci			return 0;
106462306a36Sopenharmony_ci		}
106562306a36Sopenharmony_ci	}
106662306a36Sopenharmony_ci
106762306a36Sopenharmony_ci	return -EINVAL;
106862306a36Sopenharmony_ci}
106962306a36Sopenharmony_ci
107062306a36Sopenharmony_cistatic int cs35l41_fw_type_ctl_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
107162306a36Sopenharmony_ci{
107262306a36Sopenharmony_ci	return snd_ctl_enum_info(uinfo, 1, ARRAY_SIZE(hda_cs_dsp_fw_ids), hda_cs_dsp_fw_ids);
107362306a36Sopenharmony_ci}
107462306a36Sopenharmony_ci
107562306a36Sopenharmony_cistatic int cs35l41_create_controls(struct cs35l41_hda *cs35l41)
107662306a36Sopenharmony_ci{
107762306a36Sopenharmony_ci	char fw_type_ctl_name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
107862306a36Sopenharmony_ci	char fw_load_ctl_name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
107962306a36Sopenharmony_ci	struct snd_kcontrol_new fw_type_ctl = {
108062306a36Sopenharmony_ci		.name = fw_type_ctl_name,
108162306a36Sopenharmony_ci		.iface = SNDRV_CTL_ELEM_IFACE_CARD,
108262306a36Sopenharmony_ci		.info = cs35l41_fw_type_ctl_info,
108362306a36Sopenharmony_ci		.get = cs35l41_fw_type_ctl_get,
108462306a36Sopenharmony_ci		.put = cs35l41_fw_type_ctl_put,
108562306a36Sopenharmony_ci	};
108662306a36Sopenharmony_ci	struct snd_kcontrol_new fw_load_ctl = {
108762306a36Sopenharmony_ci		.name = fw_load_ctl_name,
108862306a36Sopenharmony_ci		.iface = SNDRV_CTL_ELEM_IFACE_CARD,
108962306a36Sopenharmony_ci		.info = snd_ctl_boolean_mono_info,
109062306a36Sopenharmony_ci		.get = cs35l41_fw_load_ctl_get,
109162306a36Sopenharmony_ci		.put = cs35l41_fw_load_ctl_put,
109262306a36Sopenharmony_ci	};
109362306a36Sopenharmony_ci	int ret;
109462306a36Sopenharmony_ci
109562306a36Sopenharmony_ci	scnprintf(fw_type_ctl_name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "%s DSP1 Firmware Type",
109662306a36Sopenharmony_ci		  cs35l41->amp_name);
109762306a36Sopenharmony_ci	scnprintf(fw_load_ctl_name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "%s DSP1 Firmware Load",
109862306a36Sopenharmony_ci		  cs35l41->amp_name);
109962306a36Sopenharmony_ci
110062306a36Sopenharmony_ci	ret = snd_ctl_add(cs35l41->codec->card, snd_ctl_new1(&fw_type_ctl, cs35l41));
110162306a36Sopenharmony_ci	if (ret) {
110262306a36Sopenharmony_ci		dev_err(cs35l41->dev, "Failed to add KControl %s = %d\n", fw_type_ctl.name, ret);
110362306a36Sopenharmony_ci		return ret;
110462306a36Sopenharmony_ci	}
110562306a36Sopenharmony_ci
110662306a36Sopenharmony_ci	dev_dbg(cs35l41->dev, "Added Control %s\n", fw_type_ctl.name);
110762306a36Sopenharmony_ci
110862306a36Sopenharmony_ci	ret = snd_ctl_add(cs35l41->codec->card, snd_ctl_new1(&fw_load_ctl, cs35l41));
110962306a36Sopenharmony_ci	if (ret) {
111062306a36Sopenharmony_ci		dev_err(cs35l41->dev, "Failed to add KControl %s = %d\n", fw_load_ctl.name, ret);
111162306a36Sopenharmony_ci		return ret;
111262306a36Sopenharmony_ci	}
111362306a36Sopenharmony_ci
111462306a36Sopenharmony_ci	dev_dbg(cs35l41->dev, "Added Control %s\n", fw_load_ctl.name);
111562306a36Sopenharmony_ci
111662306a36Sopenharmony_ci	return 0;
111762306a36Sopenharmony_ci}
111862306a36Sopenharmony_ci
111962306a36Sopenharmony_cistatic int cs35l41_hda_bind(struct device *dev, struct device *master, void *master_data)
112062306a36Sopenharmony_ci{
112162306a36Sopenharmony_ci	struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev);
112262306a36Sopenharmony_ci	struct hda_component *comps = master_data;
112362306a36Sopenharmony_ci	unsigned int sleep_flags;
112462306a36Sopenharmony_ci	int ret = 0;
112562306a36Sopenharmony_ci
112662306a36Sopenharmony_ci	if (!comps || cs35l41->index < 0 || cs35l41->index >= HDA_MAX_COMPONENTS)
112762306a36Sopenharmony_ci		return -EINVAL;
112862306a36Sopenharmony_ci
112962306a36Sopenharmony_ci	comps = &comps[cs35l41->index];
113062306a36Sopenharmony_ci	if (comps->dev)
113162306a36Sopenharmony_ci		return -EBUSY;
113262306a36Sopenharmony_ci
113362306a36Sopenharmony_ci	pm_runtime_get_sync(dev);
113462306a36Sopenharmony_ci
113562306a36Sopenharmony_ci	mutex_lock(&cs35l41->fw_mutex);
113662306a36Sopenharmony_ci
113762306a36Sopenharmony_ci	comps->dev = dev;
113862306a36Sopenharmony_ci	if (!cs35l41->acpi_subsystem_id)
113962306a36Sopenharmony_ci		cs35l41->acpi_subsystem_id = kasprintf(GFP_KERNEL, "%.8x",
114062306a36Sopenharmony_ci						       comps->codec->core.subsystem_id);
114162306a36Sopenharmony_ci	cs35l41->codec = comps->codec;
114262306a36Sopenharmony_ci	strscpy(comps->name, dev_name(dev), sizeof(comps->name));
114362306a36Sopenharmony_ci
114462306a36Sopenharmony_ci	cs35l41->firmware_type = HDA_CS_DSP_FW_SPK_PROT;
114562306a36Sopenharmony_ci
114662306a36Sopenharmony_ci	if (firmware_autostart) {
114762306a36Sopenharmony_ci		dev_dbg(cs35l41->dev, "Firmware Autostart.\n");
114862306a36Sopenharmony_ci		cs35l41->request_fw_load = true;
114962306a36Sopenharmony_ci		if (cs35l41_smart_amp(cs35l41) < 0)
115062306a36Sopenharmony_ci			dev_warn(cs35l41->dev, "Cannot Run Firmware, reverting to dsp bypass...\n");
115162306a36Sopenharmony_ci	} else {
115262306a36Sopenharmony_ci		dev_dbg(cs35l41->dev, "Firmware Autostart is disabled.\n");
115362306a36Sopenharmony_ci	}
115462306a36Sopenharmony_ci
115562306a36Sopenharmony_ci	ret = cs35l41_create_controls(cs35l41);
115662306a36Sopenharmony_ci
115762306a36Sopenharmony_ci	comps->playback_hook = cs35l41_hda_playback_hook;
115862306a36Sopenharmony_ci	comps->pre_playback_hook = cs35l41_hda_pre_playback_hook;
115962306a36Sopenharmony_ci	comps->post_playback_hook = cs35l41_hda_post_playback_hook;
116062306a36Sopenharmony_ci
116162306a36Sopenharmony_ci	mutex_unlock(&cs35l41->fw_mutex);
116262306a36Sopenharmony_ci
116362306a36Sopenharmony_ci	sleep_flags = lock_system_sleep();
116462306a36Sopenharmony_ci	if (!device_link_add(&comps->codec->core.dev, cs35l41->dev, DL_FLAG_STATELESS))
116562306a36Sopenharmony_ci		dev_warn(dev, "Unable to create device link\n");
116662306a36Sopenharmony_ci	unlock_system_sleep(sleep_flags);
116762306a36Sopenharmony_ci
116862306a36Sopenharmony_ci	pm_runtime_mark_last_busy(dev);
116962306a36Sopenharmony_ci	pm_runtime_put_autosuspend(dev);
117062306a36Sopenharmony_ci
117162306a36Sopenharmony_ci	dev_info(cs35l41->dev,
117262306a36Sopenharmony_ci		 "CS35L41 Bound - SSID: %s, BST: %d, VSPK: %d, CH: %c, FW EN: %d, SPKID: %d\n",
117362306a36Sopenharmony_ci		 cs35l41->acpi_subsystem_id, cs35l41->hw_cfg.bst_type,
117462306a36Sopenharmony_ci		 cs35l41->hw_cfg.gpio1.func == CS35l41_VSPK_SWITCH,
117562306a36Sopenharmony_ci		 cs35l41->hw_cfg.spk_pos ? 'R' : 'L',
117662306a36Sopenharmony_ci		 cs35l41->firmware_running, cs35l41->speaker_id);
117762306a36Sopenharmony_ci
117862306a36Sopenharmony_ci	return ret;
117962306a36Sopenharmony_ci}
118062306a36Sopenharmony_ci
118162306a36Sopenharmony_cistatic void cs35l41_hda_unbind(struct device *dev, struct device *master, void *master_data)
118262306a36Sopenharmony_ci{
118362306a36Sopenharmony_ci	struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev);
118462306a36Sopenharmony_ci	struct hda_component *comps = master_data;
118562306a36Sopenharmony_ci	unsigned int sleep_flags;
118662306a36Sopenharmony_ci
118762306a36Sopenharmony_ci	if (comps[cs35l41->index].dev == dev) {
118862306a36Sopenharmony_ci		memset(&comps[cs35l41->index], 0, sizeof(*comps));
118962306a36Sopenharmony_ci		sleep_flags = lock_system_sleep();
119062306a36Sopenharmony_ci		device_link_remove(&comps->codec->core.dev, cs35l41->dev);
119162306a36Sopenharmony_ci		unlock_system_sleep(sleep_flags);
119262306a36Sopenharmony_ci	}
119362306a36Sopenharmony_ci}
119462306a36Sopenharmony_ci
119562306a36Sopenharmony_cistatic const struct component_ops cs35l41_hda_comp_ops = {
119662306a36Sopenharmony_ci	.bind = cs35l41_hda_bind,
119762306a36Sopenharmony_ci	.unbind = cs35l41_hda_unbind,
119862306a36Sopenharmony_ci};
119962306a36Sopenharmony_ci
120062306a36Sopenharmony_cistatic irqreturn_t cs35l41_bst_short_err(int irq, void *data)
120162306a36Sopenharmony_ci{
120262306a36Sopenharmony_ci	struct cs35l41_hda *cs35l41 = data;
120362306a36Sopenharmony_ci
120462306a36Sopenharmony_ci	dev_crit_ratelimited(cs35l41->dev, "LBST Error\n");
120562306a36Sopenharmony_ci	set_bit(CS35L41_BST_SHORT_ERR_RLS_SHIFT, &cs35l41->irq_errors);
120662306a36Sopenharmony_ci
120762306a36Sopenharmony_ci	return IRQ_HANDLED;
120862306a36Sopenharmony_ci}
120962306a36Sopenharmony_ci
121062306a36Sopenharmony_cistatic irqreturn_t cs35l41_bst_dcm_uvp_err(int irq, void *data)
121162306a36Sopenharmony_ci{
121262306a36Sopenharmony_ci	struct cs35l41_hda *cs35l41 = data;
121362306a36Sopenharmony_ci
121462306a36Sopenharmony_ci	dev_crit_ratelimited(cs35l41->dev, "DCM VBST Under Voltage Error\n");
121562306a36Sopenharmony_ci	set_bit(CS35L41_BST_UVP_ERR_RLS_SHIFT, &cs35l41->irq_errors);
121662306a36Sopenharmony_ci
121762306a36Sopenharmony_ci	return IRQ_HANDLED;
121862306a36Sopenharmony_ci}
121962306a36Sopenharmony_ci
122062306a36Sopenharmony_cistatic irqreturn_t cs35l41_bst_ovp_err(int irq, void *data)
122162306a36Sopenharmony_ci{
122262306a36Sopenharmony_ci	struct cs35l41_hda *cs35l41 = data;
122362306a36Sopenharmony_ci
122462306a36Sopenharmony_ci	dev_crit_ratelimited(cs35l41->dev, "VBST Over Voltage error\n");
122562306a36Sopenharmony_ci	set_bit(CS35L41_BST_OVP_ERR_RLS_SHIFT, &cs35l41->irq_errors);
122662306a36Sopenharmony_ci
122762306a36Sopenharmony_ci	return IRQ_HANDLED;
122862306a36Sopenharmony_ci}
122962306a36Sopenharmony_ci
123062306a36Sopenharmony_cistatic irqreturn_t cs35l41_temp_err(int irq, void *data)
123162306a36Sopenharmony_ci{
123262306a36Sopenharmony_ci	struct cs35l41_hda *cs35l41 = data;
123362306a36Sopenharmony_ci
123462306a36Sopenharmony_ci	dev_crit_ratelimited(cs35l41->dev, "Over temperature error\n");
123562306a36Sopenharmony_ci	set_bit(CS35L41_TEMP_ERR_RLS_SHIFT, &cs35l41->irq_errors);
123662306a36Sopenharmony_ci
123762306a36Sopenharmony_ci	return IRQ_HANDLED;
123862306a36Sopenharmony_ci}
123962306a36Sopenharmony_ci
124062306a36Sopenharmony_cistatic irqreturn_t cs35l41_temp_warn(int irq, void *data)
124162306a36Sopenharmony_ci{
124262306a36Sopenharmony_ci	struct cs35l41_hda *cs35l41 = data;
124362306a36Sopenharmony_ci
124462306a36Sopenharmony_ci	dev_crit_ratelimited(cs35l41->dev, "Over temperature warning\n");
124562306a36Sopenharmony_ci	set_bit(CS35L41_TEMP_WARN_ERR_RLS_SHIFT, &cs35l41->irq_errors);
124662306a36Sopenharmony_ci
124762306a36Sopenharmony_ci	return IRQ_HANDLED;
124862306a36Sopenharmony_ci}
124962306a36Sopenharmony_ci
125062306a36Sopenharmony_cistatic irqreturn_t cs35l41_amp_short(int irq, void *data)
125162306a36Sopenharmony_ci{
125262306a36Sopenharmony_ci	struct cs35l41_hda *cs35l41 = data;
125362306a36Sopenharmony_ci
125462306a36Sopenharmony_ci	dev_crit_ratelimited(cs35l41->dev, "Amp short error\n");
125562306a36Sopenharmony_ci	set_bit(CS35L41_AMP_SHORT_ERR_RLS_SHIFT, &cs35l41->irq_errors);
125662306a36Sopenharmony_ci
125762306a36Sopenharmony_ci	return IRQ_HANDLED;
125862306a36Sopenharmony_ci}
125962306a36Sopenharmony_ci
126062306a36Sopenharmony_cistatic const struct cs35l41_irq cs35l41_irqs[] = {
126162306a36Sopenharmony_ci	CS35L41_IRQ(BST_OVP_ERR, "Boost Overvoltage Error", cs35l41_bst_ovp_err),
126262306a36Sopenharmony_ci	CS35L41_IRQ(BST_DCM_UVP_ERR, "Boost Undervoltage Error", cs35l41_bst_dcm_uvp_err),
126362306a36Sopenharmony_ci	CS35L41_IRQ(BST_SHORT_ERR, "Boost Inductor Short Error", cs35l41_bst_short_err),
126462306a36Sopenharmony_ci	CS35L41_IRQ(TEMP_WARN, "Temperature Warning", cs35l41_temp_warn),
126562306a36Sopenharmony_ci	CS35L41_IRQ(TEMP_ERR, "Temperature Error", cs35l41_temp_err),
126662306a36Sopenharmony_ci	CS35L41_IRQ(AMP_SHORT_ERR, "Amp Short", cs35l41_amp_short),
126762306a36Sopenharmony_ci};
126862306a36Sopenharmony_ci
126962306a36Sopenharmony_cistatic const struct regmap_irq cs35l41_reg_irqs[] = {
127062306a36Sopenharmony_ci	CS35L41_REG_IRQ(IRQ1_STATUS1, BST_OVP_ERR),
127162306a36Sopenharmony_ci	CS35L41_REG_IRQ(IRQ1_STATUS1, BST_DCM_UVP_ERR),
127262306a36Sopenharmony_ci	CS35L41_REG_IRQ(IRQ1_STATUS1, BST_SHORT_ERR),
127362306a36Sopenharmony_ci	CS35L41_REG_IRQ(IRQ1_STATUS1, TEMP_WARN),
127462306a36Sopenharmony_ci	CS35L41_REG_IRQ(IRQ1_STATUS1, TEMP_ERR),
127562306a36Sopenharmony_ci	CS35L41_REG_IRQ(IRQ1_STATUS1, AMP_SHORT_ERR),
127662306a36Sopenharmony_ci};
127762306a36Sopenharmony_ci
127862306a36Sopenharmony_cistatic struct regmap_irq_chip cs35l41_regmap_irq_chip = {
127962306a36Sopenharmony_ci	.name = "cs35l41 IRQ1 Controller",
128062306a36Sopenharmony_ci	.status_base = CS35L41_IRQ1_STATUS1,
128162306a36Sopenharmony_ci	.mask_base = CS35L41_IRQ1_MASK1,
128262306a36Sopenharmony_ci	.ack_base = CS35L41_IRQ1_STATUS1,
128362306a36Sopenharmony_ci	.num_regs = 4,
128462306a36Sopenharmony_ci	.irqs = cs35l41_reg_irqs,
128562306a36Sopenharmony_ci	.num_irqs = ARRAY_SIZE(cs35l41_reg_irqs),
128662306a36Sopenharmony_ci	.runtime_pm = true,
128762306a36Sopenharmony_ci};
128862306a36Sopenharmony_ci
128962306a36Sopenharmony_cistatic int cs35l41_hda_apply_properties(struct cs35l41_hda *cs35l41)
129062306a36Sopenharmony_ci{
129162306a36Sopenharmony_ci	struct cs35l41_hw_cfg *hw_cfg = &cs35l41->hw_cfg;
129262306a36Sopenharmony_ci	bool using_irq = false;
129362306a36Sopenharmony_ci	int irq, irq_pol;
129462306a36Sopenharmony_ci	int ret;
129562306a36Sopenharmony_ci	int i;
129662306a36Sopenharmony_ci
129762306a36Sopenharmony_ci	if (!cs35l41->hw_cfg.valid)
129862306a36Sopenharmony_ci		return -EINVAL;
129962306a36Sopenharmony_ci
130062306a36Sopenharmony_ci	ret = cs35l41_init_boost(cs35l41->dev, cs35l41->regmap, hw_cfg);
130162306a36Sopenharmony_ci	if (ret)
130262306a36Sopenharmony_ci		return ret;
130362306a36Sopenharmony_ci
130462306a36Sopenharmony_ci	if (hw_cfg->gpio1.valid) {
130562306a36Sopenharmony_ci		switch (hw_cfg->gpio1.func) {
130662306a36Sopenharmony_ci		case CS35L41_NOT_USED:
130762306a36Sopenharmony_ci			break;
130862306a36Sopenharmony_ci		case CS35l41_VSPK_SWITCH:
130962306a36Sopenharmony_ci			hw_cfg->gpio1.func = CS35L41_GPIO1_GPIO;
131062306a36Sopenharmony_ci			hw_cfg->gpio1.out_en = true;
131162306a36Sopenharmony_ci			break;
131262306a36Sopenharmony_ci		case CS35l41_SYNC:
131362306a36Sopenharmony_ci			hw_cfg->gpio1.func = CS35L41_GPIO1_MDSYNC;
131462306a36Sopenharmony_ci			break;
131562306a36Sopenharmony_ci		default:
131662306a36Sopenharmony_ci			dev_err(cs35l41->dev, "Invalid function %d for GPIO1\n",
131762306a36Sopenharmony_ci				hw_cfg->gpio1.func);
131862306a36Sopenharmony_ci			return -EINVAL;
131962306a36Sopenharmony_ci		}
132062306a36Sopenharmony_ci	}
132162306a36Sopenharmony_ci
132262306a36Sopenharmony_ci	if (hw_cfg->gpio2.valid) {
132362306a36Sopenharmony_ci		switch (hw_cfg->gpio2.func) {
132462306a36Sopenharmony_ci		case CS35L41_NOT_USED:
132562306a36Sopenharmony_ci			break;
132662306a36Sopenharmony_ci		case CS35L41_INTERRUPT:
132762306a36Sopenharmony_ci			using_irq = true;
132862306a36Sopenharmony_ci			hw_cfg->gpio2.func = CS35L41_GPIO2_INT_OPEN_DRAIN;
132962306a36Sopenharmony_ci			break;
133062306a36Sopenharmony_ci		default:
133162306a36Sopenharmony_ci			dev_err(cs35l41->dev, "Invalid GPIO2 function %d\n", hw_cfg->gpio2.func);
133262306a36Sopenharmony_ci			return -EINVAL;
133362306a36Sopenharmony_ci		}
133462306a36Sopenharmony_ci	}
133562306a36Sopenharmony_ci
133662306a36Sopenharmony_ci	irq_pol = cs35l41_gpio_config(cs35l41->regmap, hw_cfg);
133762306a36Sopenharmony_ci
133862306a36Sopenharmony_ci	if (cs35l41->irq && using_irq) {
133962306a36Sopenharmony_ci		ret = devm_regmap_add_irq_chip(cs35l41->dev, cs35l41->regmap, cs35l41->irq,
134062306a36Sopenharmony_ci					       IRQF_ONESHOT | IRQF_SHARED | irq_pol,
134162306a36Sopenharmony_ci					       0, &cs35l41_regmap_irq_chip, &cs35l41->irq_data);
134262306a36Sopenharmony_ci		if (ret)
134362306a36Sopenharmony_ci			return ret;
134462306a36Sopenharmony_ci
134562306a36Sopenharmony_ci		for (i = 0; i < ARRAY_SIZE(cs35l41_irqs); i++) {
134662306a36Sopenharmony_ci			irq = regmap_irq_get_virq(cs35l41->irq_data, cs35l41_irqs[i].irq);
134762306a36Sopenharmony_ci			if (irq < 0)
134862306a36Sopenharmony_ci				return irq;
134962306a36Sopenharmony_ci
135062306a36Sopenharmony_ci			ret = devm_request_threaded_irq(cs35l41->dev, irq, NULL,
135162306a36Sopenharmony_ci							cs35l41_irqs[i].handler,
135262306a36Sopenharmony_ci							IRQF_ONESHOT | IRQF_SHARED | irq_pol,
135362306a36Sopenharmony_ci							cs35l41_irqs[i].name, cs35l41);
135462306a36Sopenharmony_ci			if (ret)
135562306a36Sopenharmony_ci				return ret;
135662306a36Sopenharmony_ci		}
135762306a36Sopenharmony_ci	}
135862306a36Sopenharmony_ci
135962306a36Sopenharmony_ci	return cs35l41_hda_channel_map(cs35l41->dev, 0, NULL, 1, &hw_cfg->spk_pos);
136062306a36Sopenharmony_ci}
136162306a36Sopenharmony_ci
136262306a36Sopenharmony_ciint cs35l41_get_speaker_id(struct device *dev, int amp_index, int num_amps, int fixed_gpio_id)
136362306a36Sopenharmony_ci{
136462306a36Sopenharmony_ci	struct gpio_desc *speaker_id_desc;
136562306a36Sopenharmony_ci	int speaker_id = -ENODEV;
136662306a36Sopenharmony_ci
136762306a36Sopenharmony_ci	if (fixed_gpio_id >= 0) {
136862306a36Sopenharmony_ci		dev_dbg(dev, "Found Fixed Speaker ID GPIO (index = %d)\n", fixed_gpio_id);
136962306a36Sopenharmony_ci		speaker_id_desc = gpiod_get_index(dev, NULL, fixed_gpio_id, GPIOD_IN);
137062306a36Sopenharmony_ci		if (IS_ERR(speaker_id_desc)) {
137162306a36Sopenharmony_ci			speaker_id = PTR_ERR(speaker_id_desc);
137262306a36Sopenharmony_ci			return speaker_id;
137362306a36Sopenharmony_ci		}
137462306a36Sopenharmony_ci		speaker_id = gpiod_get_value_cansleep(speaker_id_desc);
137562306a36Sopenharmony_ci		gpiod_put(speaker_id_desc);
137662306a36Sopenharmony_ci		dev_dbg(dev, "Speaker ID = %d\n", speaker_id);
137762306a36Sopenharmony_ci	} else {
137862306a36Sopenharmony_ci		int base_index;
137962306a36Sopenharmony_ci		int gpios_per_amp;
138062306a36Sopenharmony_ci		int count;
138162306a36Sopenharmony_ci		int tmp;
138262306a36Sopenharmony_ci		int i;
138362306a36Sopenharmony_ci
138462306a36Sopenharmony_ci		count = gpiod_count(dev, "spk-id");
138562306a36Sopenharmony_ci		if (count > 0) {
138662306a36Sopenharmony_ci			speaker_id = 0;
138762306a36Sopenharmony_ci			gpios_per_amp = count / num_amps;
138862306a36Sopenharmony_ci			base_index = gpios_per_amp * amp_index;
138962306a36Sopenharmony_ci
139062306a36Sopenharmony_ci			if (count % num_amps)
139162306a36Sopenharmony_ci				return -EINVAL;
139262306a36Sopenharmony_ci
139362306a36Sopenharmony_ci			dev_dbg(dev, "Found %d Speaker ID GPIOs per Amp\n", gpios_per_amp);
139462306a36Sopenharmony_ci
139562306a36Sopenharmony_ci			for (i = 0; i < gpios_per_amp; i++) {
139662306a36Sopenharmony_ci				speaker_id_desc = gpiod_get_index(dev, "spk-id", i + base_index,
139762306a36Sopenharmony_ci								  GPIOD_IN);
139862306a36Sopenharmony_ci				if (IS_ERR(speaker_id_desc)) {
139962306a36Sopenharmony_ci					speaker_id = PTR_ERR(speaker_id_desc);
140062306a36Sopenharmony_ci					break;
140162306a36Sopenharmony_ci				}
140262306a36Sopenharmony_ci				tmp = gpiod_get_value_cansleep(speaker_id_desc);
140362306a36Sopenharmony_ci				gpiod_put(speaker_id_desc);
140462306a36Sopenharmony_ci				if (tmp < 0) {
140562306a36Sopenharmony_ci					speaker_id = tmp;
140662306a36Sopenharmony_ci					break;
140762306a36Sopenharmony_ci				}
140862306a36Sopenharmony_ci				speaker_id |= tmp << i;
140962306a36Sopenharmony_ci			}
141062306a36Sopenharmony_ci			dev_dbg(dev, "Speaker ID = %d\n", speaker_id);
141162306a36Sopenharmony_ci		}
141262306a36Sopenharmony_ci	}
141362306a36Sopenharmony_ci	return speaker_id;
141462306a36Sopenharmony_ci}
141562306a36Sopenharmony_ci
141662306a36Sopenharmony_cistatic int cs35l41_hda_read_acpi(struct cs35l41_hda *cs35l41, const char *hid, int id)
141762306a36Sopenharmony_ci{
141862306a36Sopenharmony_ci	struct cs35l41_hw_cfg *hw_cfg = &cs35l41->hw_cfg;
141962306a36Sopenharmony_ci	u32 values[HDA_MAX_COMPONENTS];
142062306a36Sopenharmony_ci	struct acpi_device *adev;
142162306a36Sopenharmony_ci	struct device *physdev;
142262306a36Sopenharmony_ci	const char *sub;
142362306a36Sopenharmony_ci	char *property;
142462306a36Sopenharmony_ci	size_t nval;
142562306a36Sopenharmony_ci	int i, ret;
142662306a36Sopenharmony_ci
142762306a36Sopenharmony_ci	adev = acpi_dev_get_first_match_dev(hid, NULL, -1);
142862306a36Sopenharmony_ci	if (!adev) {
142962306a36Sopenharmony_ci		dev_err(cs35l41->dev, "Failed to find an ACPI device for %s\n", hid);
143062306a36Sopenharmony_ci		return -ENODEV;
143162306a36Sopenharmony_ci	}
143262306a36Sopenharmony_ci
143362306a36Sopenharmony_ci	physdev = get_device(acpi_get_first_physical_node(adev));
143462306a36Sopenharmony_ci	acpi_dev_put(adev);
143562306a36Sopenharmony_ci
143662306a36Sopenharmony_ci	sub = acpi_get_subsystem_id(ACPI_HANDLE(physdev));
143762306a36Sopenharmony_ci	if (IS_ERR(sub))
143862306a36Sopenharmony_ci		sub = NULL;
143962306a36Sopenharmony_ci	cs35l41->acpi_subsystem_id = sub;
144062306a36Sopenharmony_ci
144162306a36Sopenharmony_ci	ret = cs35l41_add_dsd_properties(cs35l41, physdev, id, hid);
144262306a36Sopenharmony_ci	if (!ret) {
144362306a36Sopenharmony_ci		dev_info(cs35l41->dev, "Using extra _DSD properties, bypassing _DSD in ACPI\n");
144462306a36Sopenharmony_ci		goto put_physdev;
144562306a36Sopenharmony_ci	}
144662306a36Sopenharmony_ci
144762306a36Sopenharmony_ci	property = "cirrus,dev-index";
144862306a36Sopenharmony_ci	ret = device_property_count_u32(physdev, property);
144962306a36Sopenharmony_ci	if (ret <= 0)
145062306a36Sopenharmony_ci		goto err;
145162306a36Sopenharmony_ci
145262306a36Sopenharmony_ci	if (ret > ARRAY_SIZE(values)) {
145362306a36Sopenharmony_ci		ret = -EINVAL;
145462306a36Sopenharmony_ci		goto err;
145562306a36Sopenharmony_ci	}
145662306a36Sopenharmony_ci	nval = ret;
145762306a36Sopenharmony_ci
145862306a36Sopenharmony_ci	ret = device_property_read_u32_array(physdev, property, values, nval);
145962306a36Sopenharmony_ci	if (ret)
146062306a36Sopenharmony_ci		goto err;
146162306a36Sopenharmony_ci
146262306a36Sopenharmony_ci	cs35l41->index = -1;
146362306a36Sopenharmony_ci	for (i = 0; i < nval; i++) {
146462306a36Sopenharmony_ci		if (values[i] == id) {
146562306a36Sopenharmony_ci			cs35l41->index = i;
146662306a36Sopenharmony_ci			break;
146762306a36Sopenharmony_ci		}
146862306a36Sopenharmony_ci	}
146962306a36Sopenharmony_ci	if (cs35l41->index == -1) {
147062306a36Sopenharmony_ci		dev_err(cs35l41->dev, "No index found in %s\n", property);
147162306a36Sopenharmony_ci		ret = -ENODEV;
147262306a36Sopenharmony_ci		goto err;
147362306a36Sopenharmony_ci	}
147462306a36Sopenharmony_ci
147562306a36Sopenharmony_ci	/* To use the same release code for all laptop variants we can't use devm_ version of
147662306a36Sopenharmony_ci	 * gpiod_get here, as CLSA010* don't have a fully functional bios with an _DSD node
147762306a36Sopenharmony_ci	 */
147862306a36Sopenharmony_ci	cs35l41->reset_gpio = fwnode_gpiod_get_index(acpi_fwnode_handle(adev), "reset", cs35l41->index,
147962306a36Sopenharmony_ci						     GPIOD_OUT_LOW, "cs35l41-reset");
148062306a36Sopenharmony_ci
148162306a36Sopenharmony_ci	property = "cirrus,speaker-position";
148262306a36Sopenharmony_ci	ret = device_property_read_u32_array(physdev, property, values, nval);
148362306a36Sopenharmony_ci	if (ret)
148462306a36Sopenharmony_ci		goto err;
148562306a36Sopenharmony_ci	hw_cfg->spk_pos = values[cs35l41->index];
148662306a36Sopenharmony_ci
148762306a36Sopenharmony_ci	cs35l41->channel_index = 0;
148862306a36Sopenharmony_ci	for (i = 0; i < cs35l41->index; i++)
148962306a36Sopenharmony_ci		if (values[i] == hw_cfg->spk_pos)
149062306a36Sopenharmony_ci			cs35l41->channel_index++;
149162306a36Sopenharmony_ci
149262306a36Sopenharmony_ci	property = "cirrus,gpio1-func";
149362306a36Sopenharmony_ci	ret = device_property_read_u32_array(physdev, property, values, nval);
149462306a36Sopenharmony_ci	if (ret)
149562306a36Sopenharmony_ci		goto err;
149662306a36Sopenharmony_ci	hw_cfg->gpio1.func = values[cs35l41->index];
149762306a36Sopenharmony_ci	hw_cfg->gpio1.valid = true;
149862306a36Sopenharmony_ci
149962306a36Sopenharmony_ci	property = "cirrus,gpio2-func";
150062306a36Sopenharmony_ci	ret = device_property_read_u32_array(physdev, property, values, nval);
150162306a36Sopenharmony_ci	if (ret)
150262306a36Sopenharmony_ci		goto err;
150362306a36Sopenharmony_ci	hw_cfg->gpio2.func = values[cs35l41->index];
150462306a36Sopenharmony_ci	hw_cfg->gpio2.valid = true;
150562306a36Sopenharmony_ci
150662306a36Sopenharmony_ci	property = "cirrus,boost-peak-milliamp";
150762306a36Sopenharmony_ci	ret = device_property_read_u32_array(physdev, property, values, nval);
150862306a36Sopenharmony_ci	if (ret == 0)
150962306a36Sopenharmony_ci		hw_cfg->bst_ipk = values[cs35l41->index];
151062306a36Sopenharmony_ci	else
151162306a36Sopenharmony_ci		hw_cfg->bst_ipk = -1;
151262306a36Sopenharmony_ci
151362306a36Sopenharmony_ci	property = "cirrus,boost-ind-nanohenry";
151462306a36Sopenharmony_ci	ret = device_property_read_u32_array(physdev, property, values, nval);
151562306a36Sopenharmony_ci	if (ret == 0)
151662306a36Sopenharmony_ci		hw_cfg->bst_ind = values[cs35l41->index];
151762306a36Sopenharmony_ci	else
151862306a36Sopenharmony_ci		hw_cfg->bst_ind = -1;
151962306a36Sopenharmony_ci
152062306a36Sopenharmony_ci	property = "cirrus,boost-cap-microfarad";
152162306a36Sopenharmony_ci	ret = device_property_read_u32_array(physdev, property, values, nval);
152262306a36Sopenharmony_ci	if (ret == 0)
152362306a36Sopenharmony_ci		hw_cfg->bst_cap = values[cs35l41->index];
152462306a36Sopenharmony_ci	else
152562306a36Sopenharmony_ci		hw_cfg->bst_cap = -1;
152662306a36Sopenharmony_ci
152762306a36Sopenharmony_ci	cs35l41->speaker_id = cs35l41_get_speaker_id(physdev, cs35l41->index, nval, -1);
152862306a36Sopenharmony_ci
152962306a36Sopenharmony_ci	if (hw_cfg->bst_ind > 0 || hw_cfg->bst_cap > 0 || hw_cfg->bst_ipk > 0)
153062306a36Sopenharmony_ci		hw_cfg->bst_type = CS35L41_INT_BOOST;
153162306a36Sopenharmony_ci	else
153262306a36Sopenharmony_ci		hw_cfg->bst_type = CS35L41_EXT_BOOST;
153362306a36Sopenharmony_ci
153462306a36Sopenharmony_ci	hw_cfg->valid = true;
153562306a36Sopenharmony_ci	put_device(physdev);
153662306a36Sopenharmony_ci
153762306a36Sopenharmony_ci	return 0;
153862306a36Sopenharmony_ci
153962306a36Sopenharmony_cierr:
154062306a36Sopenharmony_ci	dev_err(cs35l41->dev, "Failed property %s: %d\n", property, ret);
154162306a36Sopenharmony_ci	hw_cfg->valid = false;
154262306a36Sopenharmony_ci	hw_cfg->gpio1.valid = false;
154362306a36Sopenharmony_ci	hw_cfg->gpio2.valid = false;
154462306a36Sopenharmony_ciput_physdev:
154562306a36Sopenharmony_ci	put_device(physdev);
154662306a36Sopenharmony_ci
154762306a36Sopenharmony_ci	return ret;
154862306a36Sopenharmony_ci}
154962306a36Sopenharmony_ci
155062306a36Sopenharmony_ciint cs35l41_hda_probe(struct device *dev, const char *device_name, int id, int irq,
155162306a36Sopenharmony_ci		      struct regmap *regmap)
155262306a36Sopenharmony_ci{
155362306a36Sopenharmony_ci	unsigned int int_sts, regid, reg_revid, mtl_revid, chipid, int_status;
155462306a36Sopenharmony_ci	struct cs35l41_hda *cs35l41;
155562306a36Sopenharmony_ci	int ret;
155662306a36Sopenharmony_ci
155762306a36Sopenharmony_ci	BUILD_BUG_ON(ARRAY_SIZE(cs35l41_irqs) != ARRAY_SIZE(cs35l41_reg_irqs));
155862306a36Sopenharmony_ci	BUILD_BUG_ON(ARRAY_SIZE(cs35l41_irqs) != CS35L41_NUM_IRQ);
155962306a36Sopenharmony_ci
156062306a36Sopenharmony_ci	if (IS_ERR(regmap))
156162306a36Sopenharmony_ci		return PTR_ERR(regmap);
156262306a36Sopenharmony_ci
156362306a36Sopenharmony_ci	cs35l41 = devm_kzalloc(dev, sizeof(*cs35l41), GFP_KERNEL);
156462306a36Sopenharmony_ci	if (!cs35l41)
156562306a36Sopenharmony_ci		return -ENOMEM;
156662306a36Sopenharmony_ci
156762306a36Sopenharmony_ci	cs35l41->dev = dev;
156862306a36Sopenharmony_ci	cs35l41->irq = irq;
156962306a36Sopenharmony_ci	cs35l41->regmap = regmap;
157062306a36Sopenharmony_ci	dev_set_drvdata(dev, cs35l41);
157162306a36Sopenharmony_ci
157262306a36Sopenharmony_ci	ret = cs35l41_hda_read_acpi(cs35l41, device_name, id);
157362306a36Sopenharmony_ci	if (ret)
157462306a36Sopenharmony_ci		return dev_err_probe(cs35l41->dev, ret, "Platform not supported\n");
157562306a36Sopenharmony_ci
157662306a36Sopenharmony_ci	if (IS_ERR(cs35l41->reset_gpio)) {
157762306a36Sopenharmony_ci		ret = PTR_ERR(cs35l41->reset_gpio);
157862306a36Sopenharmony_ci		cs35l41->reset_gpio = NULL;
157962306a36Sopenharmony_ci		if (ret == -EBUSY) {
158062306a36Sopenharmony_ci			dev_info(cs35l41->dev, "Reset line busy, assuming shared reset\n");
158162306a36Sopenharmony_ci		} else {
158262306a36Sopenharmony_ci			dev_err_probe(cs35l41->dev, ret, "Failed to get reset GPIO\n");
158362306a36Sopenharmony_ci			goto err;
158462306a36Sopenharmony_ci		}
158562306a36Sopenharmony_ci	}
158662306a36Sopenharmony_ci	if (cs35l41->reset_gpio) {
158762306a36Sopenharmony_ci		usleep_range(2000, 2100);
158862306a36Sopenharmony_ci		gpiod_set_value_cansleep(cs35l41->reset_gpio, 1);
158962306a36Sopenharmony_ci	}
159062306a36Sopenharmony_ci
159162306a36Sopenharmony_ci	usleep_range(2000, 2100);
159262306a36Sopenharmony_ci
159362306a36Sopenharmony_ci	ret = regmap_read_poll_timeout(cs35l41->regmap, CS35L41_IRQ1_STATUS4, int_status,
159462306a36Sopenharmony_ci				       int_status & CS35L41_OTP_BOOT_DONE, 1000, 100000);
159562306a36Sopenharmony_ci	if (ret) {
159662306a36Sopenharmony_ci		dev_err(cs35l41->dev, "Failed waiting for OTP_BOOT_DONE: %d\n", ret);
159762306a36Sopenharmony_ci		goto err;
159862306a36Sopenharmony_ci	}
159962306a36Sopenharmony_ci
160062306a36Sopenharmony_ci	ret = regmap_read(cs35l41->regmap, CS35L41_IRQ1_STATUS3, &int_sts);
160162306a36Sopenharmony_ci	if (ret || (int_sts & CS35L41_OTP_BOOT_ERR)) {
160262306a36Sopenharmony_ci		dev_err(cs35l41->dev, "OTP Boot status %x error: %d\n",
160362306a36Sopenharmony_ci			int_sts & CS35L41_OTP_BOOT_ERR, ret);
160462306a36Sopenharmony_ci		ret = -EIO;
160562306a36Sopenharmony_ci		goto err;
160662306a36Sopenharmony_ci	}
160762306a36Sopenharmony_ci
160862306a36Sopenharmony_ci	ret = regmap_read(cs35l41->regmap, CS35L41_DEVID, &regid);
160962306a36Sopenharmony_ci	if (ret) {
161062306a36Sopenharmony_ci		dev_err(cs35l41->dev, "Get Device ID failed: %d\n", ret);
161162306a36Sopenharmony_ci		goto err;
161262306a36Sopenharmony_ci	}
161362306a36Sopenharmony_ci
161462306a36Sopenharmony_ci	ret = regmap_read(cs35l41->regmap, CS35L41_REVID, &reg_revid);
161562306a36Sopenharmony_ci	if (ret) {
161662306a36Sopenharmony_ci		dev_err(cs35l41->dev, "Get Revision ID failed: %d\n", ret);
161762306a36Sopenharmony_ci		goto err;
161862306a36Sopenharmony_ci	}
161962306a36Sopenharmony_ci
162062306a36Sopenharmony_ci	mtl_revid = reg_revid & CS35L41_MTLREVID_MASK;
162162306a36Sopenharmony_ci
162262306a36Sopenharmony_ci	chipid = (mtl_revid % 2) ? CS35L41R_CHIP_ID : CS35L41_CHIP_ID;
162362306a36Sopenharmony_ci	if (regid != chipid) {
162462306a36Sopenharmony_ci		dev_err(cs35l41->dev, "CS35L41 Device ID (%X). Expected ID %X\n", regid, chipid);
162562306a36Sopenharmony_ci		ret = -ENODEV;
162662306a36Sopenharmony_ci		goto err;
162762306a36Sopenharmony_ci	}
162862306a36Sopenharmony_ci
162962306a36Sopenharmony_ci	ret = cs35l41_test_key_unlock(cs35l41->dev, cs35l41->regmap);
163062306a36Sopenharmony_ci	if (ret)
163162306a36Sopenharmony_ci		goto err;
163262306a36Sopenharmony_ci
163362306a36Sopenharmony_ci	ret = cs35l41_register_errata_patch(cs35l41->dev, cs35l41->regmap, reg_revid);
163462306a36Sopenharmony_ci	if (ret)
163562306a36Sopenharmony_ci		goto err;
163662306a36Sopenharmony_ci
163762306a36Sopenharmony_ci	ret = cs35l41_otp_unpack(cs35l41->dev, cs35l41->regmap);
163862306a36Sopenharmony_ci	if (ret) {
163962306a36Sopenharmony_ci		dev_err(cs35l41->dev, "OTP Unpack failed: %d\n", ret);
164062306a36Sopenharmony_ci		goto err;
164162306a36Sopenharmony_ci	}
164262306a36Sopenharmony_ci
164362306a36Sopenharmony_ci	ret = cs35l41_test_key_lock(cs35l41->dev, cs35l41->regmap);
164462306a36Sopenharmony_ci	if (ret)
164562306a36Sopenharmony_ci		goto err;
164662306a36Sopenharmony_ci
164762306a36Sopenharmony_ci	ret = regmap_multi_reg_write(cs35l41->regmap, cs35l41_hda_mute,
164862306a36Sopenharmony_ci				     ARRAY_SIZE(cs35l41_hda_mute));
164962306a36Sopenharmony_ci	if (ret)
165062306a36Sopenharmony_ci		goto err;
165162306a36Sopenharmony_ci
165262306a36Sopenharmony_ci	INIT_WORK(&cs35l41->fw_load_work, cs35l41_fw_load_work);
165362306a36Sopenharmony_ci	mutex_init(&cs35l41->fw_mutex);
165462306a36Sopenharmony_ci
165562306a36Sopenharmony_ci	pm_runtime_set_autosuspend_delay(cs35l41->dev, 3000);
165662306a36Sopenharmony_ci	pm_runtime_use_autosuspend(cs35l41->dev);
165762306a36Sopenharmony_ci	pm_runtime_mark_last_busy(cs35l41->dev);
165862306a36Sopenharmony_ci	pm_runtime_set_active(cs35l41->dev);
165962306a36Sopenharmony_ci	pm_runtime_get_noresume(cs35l41->dev);
166062306a36Sopenharmony_ci	pm_runtime_enable(cs35l41->dev);
166162306a36Sopenharmony_ci
166262306a36Sopenharmony_ci	ret = cs35l41_hda_apply_properties(cs35l41);
166362306a36Sopenharmony_ci	if (ret)
166462306a36Sopenharmony_ci		goto err_pm;
166562306a36Sopenharmony_ci
166662306a36Sopenharmony_ci	pm_runtime_put_autosuspend(cs35l41->dev);
166762306a36Sopenharmony_ci
166862306a36Sopenharmony_ci	ret = component_add(cs35l41->dev, &cs35l41_hda_comp_ops);
166962306a36Sopenharmony_ci	if (ret) {
167062306a36Sopenharmony_ci		dev_err(cs35l41->dev, "Register component failed: %d\n", ret);
167162306a36Sopenharmony_ci		goto err_pm;
167262306a36Sopenharmony_ci	}
167362306a36Sopenharmony_ci
167462306a36Sopenharmony_ci	dev_info(cs35l41->dev, "Cirrus Logic CS35L41 (%x), Revision: %02X\n", regid, reg_revid);
167562306a36Sopenharmony_ci
167662306a36Sopenharmony_ci	return 0;
167762306a36Sopenharmony_ci
167862306a36Sopenharmony_cierr_pm:
167962306a36Sopenharmony_ci	pm_runtime_dont_use_autosuspend(cs35l41->dev);
168062306a36Sopenharmony_ci	pm_runtime_disable(cs35l41->dev);
168162306a36Sopenharmony_ci	pm_runtime_put_noidle(cs35l41->dev);
168262306a36Sopenharmony_ci
168362306a36Sopenharmony_cierr:
168462306a36Sopenharmony_ci	if (cs35l41_safe_reset(cs35l41->regmap, cs35l41->hw_cfg.bst_type))
168562306a36Sopenharmony_ci		gpiod_set_value_cansleep(cs35l41->reset_gpio, 0);
168662306a36Sopenharmony_ci	gpiod_put(cs35l41->reset_gpio);
168762306a36Sopenharmony_ci	kfree(cs35l41->acpi_subsystem_id);
168862306a36Sopenharmony_ci
168962306a36Sopenharmony_ci	return ret;
169062306a36Sopenharmony_ci}
169162306a36Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(cs35l41_hda_probe, SND_HDA_SCODEC_CS35L41);
169262306a36Sopenharmony_ci
169362306a36Sopenharmony_civoid cs35l41_hda_remove(struct device *dev)
169462306a36Sopenharmony_ci{
169562306a36Sopenharmony_ci	struct cs35l41_hda *cs35l41 = dev_get_drvdata(dev);
169662306a36Sopenharmony_ci
169762306a36Sopenharmony_ci	pm_runtime_get_sync(cs35l41->dev);
169862306a36Sopenharmony_ci	pm_runtime_dont_use_autosuspend(cs35l41->dev);
169962306a36Sopenharmony_ci	pm_runtime_disable(cs35l41->dev);
170062306a36Sopenharmony_ci
170162306a36Sopenharmony_ci	if (cs35l41->halo_initialized)
170262306a36Sopenharmony_ci		cs35l41_remove_dsp(cs35l41);
170362306a36Sopenharmony_ci
170462306a36Sopenharmony_ci	component_del(cs35l41->dev, &cs35l41_hda_comp_ops);
170562306a36Sopenharmony_ci
170662306a36Sopenharmony_ci	pm_runtime_put_noidle(cs35l41->dev);
170762306a36Sopenharmony_ci
170862306a36Sopenharmony_ci	if (cs35l41_safe_reset(cs35l41->regmap, cs35l41->hw_cfg.bst_type))
170962306a36Sopenharmony_ci		gpiod_set_value_cansleep(cs35l41->reset_gpio, 0);
171062306a36Sopenharmony_ci	gpiod_put(cs35l41->reset_gpio);
171162306a36Sopenharmony_ci	kfree(cs35l41->acpi_subsystem_id);
171262306a36Sopenharmony_ci}
171362306a36Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(cs35l41_hda_remove, SND_HDA_SCODEC_CS35L41);
171462306a36Sopenharmony_ci
171562306a36Sopenharmony_ciconst struct dev_pm_ops cs35l41_hda_pm_ops = {
171662306a36Sopenharmony_ci	RUNTIME_PM_OPS(cs35l41_runtime_suspend, cs35l41_runtime_resume,
171762306a36Sopenharmony_ci		       cs35l41_runtime_idle)
171862306a36Sopenharmony_ci	.prepare = cs35l41_system_suspend_prep,
171962306a36Sopenharmony_ci	SYSTEM_SLEEP_PM_OPS(cs35l41_system_suspend, cs35l41_system_resume)
172062306a36Sopenharmony_ci};
172162306a36Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(cs35l41_hda_pm_ops, SND_HDA_SCODEC_CS35L41);
172262306a36Sopenharmony_ci
172362306a36Sopenharmony_ciMODULE_DESCRIPTION("CS35L41 HDA Driver");
172462306a36Sopenharmony_ciMODULE_IMPORT_NS(SND_HDA_CS_DSP_CONTROLS);
172562306a36Sopenharmony_ciMODULE_AUTHOR("Lucas Tanure, Cirrus Logic Inc, <tanureal@opensource.cirrus.com>");
172662306a36Sopenharmony_ciMODULE_LICENSE("GPL");
172762306a36Sopenharmony_ciMODULE_IMPORT_NS(FW_CS_DSP);
1728