162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci//
362306a36Sopenharmony_ci// HDA audio driver for Cirrus Logic CS35L56 smart amp
462306a36Sopenharmony_ci//
562306a36Sopenharmony_ci// Copyright (C) 2023 Cirrus Logic, Inc. and
662306a36Sopenharmony_ci//                    Cirrus Logic International Semiconductor Ltd.
762306a36Sopenharmony_ci//
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/acpi.h>
1062306a36Sopenharmony_ci#include <linux/debugfs.h>
1162306a36Sopenharmony_ci#include <linux/gpio/consumer.h>
1262306a36Sopenharmony_ci#include <linux/module.h>
1362306a36Sopenharmony_ci#include <linux/pm_runtime.h>
1462306a36Sopenharmony_ci#include <linux/regmap.h>
1562306a36Sopenharmony_ci#include <linux/slab.h>
1662306a36Sopenharmony_ci#include <sound/core.h>
1762306a36Sopenharmony_ci#include <sound/hda_codec.h>
1862306a36Sopenharmony_ci#include <sound/tlv.h>
1962306a36Sopenharmony_ci#include "cs35l56_hda.h"
2062306a36Sopenharmony_ci#include "hda_component.h"
2162306a36Sopenharmony_ci#include "hda_cs_dsp_ctl.h"
2262306a36Sopenharmony_ci#include "hda_generic.h"
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci /*
2562306a36Sopenharmony_ci  * The cs35l56_hda_dai_config[] reg sequence configures the device as
2662306a36Sopenharmony_ci  *  ASP1_BCLK_FREQ = 3.072 MHz
2762306a36Sopenharmony_ci  *  ASP1_RX_WIDTH = 32 cycles per slot, ASP1_TX_WIDTH = 32 cycles per slot, ASP1_FMT = I2S
2862306a36Sopenharmony_ci  *  ASP1_DOUT_HIZ_CONTROL = Hi-Z during unused timeslots
2962306a36Sopenharmony_ci  *  ASP1_RX_WL = 24 bits per sample
3062306a36Sopenharmony_ci  *  ASP1_TX_WL = 24 bits per sample
3162306a36Sopenharmony_ci  *  ASP1_RXn_EN 1..3 and ASP1_TXn_EN 1..4 disabled
3262306a36Sopenharmony_ci  */
3362306a36Sopenharmony_cistatic const struct reg_sequence cs35l56_hda_dai_config[] = {
3462306a36Sopenharmony_ci	{ CS35L56_ASP1_CONTROL1,	0x00000021 },
3562306a36Sopenharmony_ci	{ CS35L56_ASP1_CONTROL2,	0x20200200 },
3662306a36Sopenharmony_ci	{ CS35L56_ASP1_CONTROL3,	0x00000003 },
3762306a36Sopenharmony_ci	{ CS35L56_ASP1_DATA_CONTROL5,	0x00000018 },
3862306a36Sopenharmony_ci	{ CS35L56_ASP1_DATA_CONTROL1,	0x00000018 },
3962306a36Sopenharmony_ci	{ CS35L56_ASP1_ENABLES1,	0x00000000 },
4062306a36Sopenharmony_ci};
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_cistatic void cs35l56_hda_play(struct cs35l56_hda *cs35l56)
4362306a36Sopenharmony_ci{
4462306a36Sopenharmony_ci	unsigned int val;
4562306a36Sopenharmony_ci	int ret;
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	pm_runtime_get_sync(cs35l56->base.dev);
4862306a36Sopenharmony_ci	ret = cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_AUDIO_PLAY);
4962306a36Sopenharmony_ci	if (ret == 0) {
5062306a36Sopenharmony_ci		/* Wait for firmware to enter PS0 power state */
5162306a36Sopenharmony_ci		ret = regmap_read_poll_timeout(cs35l56->base.regmap,
5262306a36Sopenharmony_ci					       CS35L56_TRANSDUCER_ACTUAL_PS,
5362306a36Sopenharmony_ci					       val, (val == CS35L56_PS0),
5462306a36Sopenharmony_ci					       CS35L56_PS0_POLL_US,
5562306a36Sopenharmony_ci					       CS35L56_PS0_TIMEOUT_US);
5662306a36Sopenharmony_ci		if (ret)
5762306a36Sopenharmony_ci			dev_warn(cs35l56->base.dev, "PS0 wait failed: %d\n", ret);
5862306a36Sopenharmony_ci	}
5962306a36Sopenharmony_ci	regmap_set_bits(cs35l56->base.regmap, CS35L56_ASP1_ENABLES1,
6062306a36Sopenharmony_ci			BIT(CS35L56_ASP_RX1_EN_SHIFT) | BIT(CS35L56_ASP_RX2_EN_SHIFT) |
6162306a36Sopenharmony_ci			cs35l56->asp_tx_mask);
6262306a36Sopenharmony_ci	cs35l56->playing = true;
6362306a36Sopenharmony_ci}
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_cistatic void cs35l56_hda_pause(struct cs35l56_hda *cs35l56)
6662306a36Sopenharmony_ci{
6762306a36Sopenharmony_ci	cs35l56->playing = false;
6862306a36Sopenharmony_ci	cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_AUDIO_PAUSE);
6962306a36Sopenharmony_ci	regmap_clear_bits(cs35l56->base.regmap, CS35L56_ASP1_ENABLES1,
7062306a36Sopenharmony_ci			  BIT(CS35L56_ASP_RX1_EN_SHIFT) | BIT(CS35L56_ASP_RX2_EN_SHIFT) |
7162306a36Sopenharmony_ci			  BIT(CS35L56_ASP_TX1_EN_SHIFT) | BIT(CS35L56_ASP_TX2_EN_SHIFT) |
7262306a36Sopenharmony_ci			  BIT(CS35L56_ASP_TX3_EN_SHIFT) | BIT(CS35L56_ASP_TX4_EN_SHIFT));
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	pm_runtime_mark_last_busy(cs35l56->base.dev);
7562306a36Sopenharmony_ci	pm_runtime_put_autosuspend(cs35l56->base.dev);
7662306a36Sopenharmony_ci}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_cistatic void cs35l56_hda_playback_hook(struct device *dev, int action)
7962306a36Sopenharmony_ci{
8062306a36Sopenharmony_ci	struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev);
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	dev_dbg(cs35l56->base.dev, "%s()%d: action: %d\n", __func__, __LINE__, action);
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	switch (action) {
8562306a36Sopenharmony_ci	case HDA_GEN_PCM_ACT_PREPARE:
8662306a36Sopenharmony_ci		if (cs35l56->playing)
8762306a36Sopenharmony_ci			break;
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci		/* If we're suspended: flag that resume should start playback */
9062306a36Sopenharmony_ci		if (cs35l56->suspended) {
9162306a36Sopenharmony_ci			cs35l56->playing = true;
9262306a36Sopenharmony_ci			break;
9362306a36Sopenharmony_ci		}
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci		cs35l56_hda_play(cs35l56);
9662306a36Sopenharmony_ci		break;
9762306a36Sopenharmony_ci	case HDA_GEN_PCM_ACT_CLEANUP:
9862306a36Sopenharmony_ci		if (!cs35l56->playing)
9962306a36Sopenharmony_ci			break;
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci		cs35l56_hda_pause(cs35l56);
10262306a36Sopenharmony_ci		break;
10362306a36Sopenharmony_ci	default:
10462306a36Sopenharmony_ci		break;
10562306a36Sopenharmony_ci	}
10662306a36Sopenharmony_ci}
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_cistatic int cs35l56_hda_runtime_suspend(struct device *dev)
10962306a36Sopenharmony_ci{
11062306a36Sopenharmony_ci	struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev);
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	if (cs35l56->cs_dsp.booted)
11362306a36Sopenharmony_ci		cs_dsp_stop(&cs35l56->cs_dsp);
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	return cs35l56_runtime_suspend_common(&cs35l56->base);
11662306a36Sopenharmony_ci}
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_cistatic int cs35l56_hda_runtime_resume(struct device *dev)
11962306a36Sopenharmony_ci{
12062306a36Sopenharmony_ci	struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev);
12162306a36Sopenharmony_ci	int ret;
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	ret = cs35l56_runtime_resume_common(&cs35l56->base, false);
12462306a36Sopenharmony_ci	if (ret < 0)
12562306a36Sopenharmony_ci		return ret;
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	if (cs35l56->cs_dsp.booted) {
12862306a36Sopenharmony_ci		ret = cs_dsp_run(&cs35l56->cs_dsp);
12962306a36Sopenharmony_ci		if (ret) {
13062306a36Sopenharmony_ci			dev_dbg(cs35l56->base.dev, "%s: cs_dsp_run ret %d\n", __func__, ret);
13162306a36Sopenharmony_ci			goto err;
13262306a36Sopenharmony_ci		}
13362306a36Sopenharmony_ci	}
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	return 0;
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_cierr:
13862306a36Sopenharmony_ci	cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_ALLOW_AUTO_HIBERNATE);
13962306a36Sopenharmony_ci	regmap_write(cs35l56->base.regmap, CS35L56_DSP_VIRTUAL1_MBOX_1,
14062306a36Sopenharmony_ci		     CS35L56_MBOX_CMD_HIBERNATE_NOW);
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	regcache_cache_only(cs35l56->base.regmap, true);
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	return ret;
14562306a36Sopenharmony_ci}
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_cistatic int cs35l56_hda_mixer_info(struct snd_kcontrol *kcontrol,
14862306a36Sopenharmony_ci				  struct snd_ctl_elem_info *uinfo)
14962306a36Sopenharmony_ci{
15062306a36Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
15162306a36Sopenharmony_ci	uinfo->count = 1;
15262306a36Sopenharmony_ci	uinfo->value.enumerated.items = CS35L56_NUM_INPUT_SRC;
15362306a36Sopenharmony_ci	if (uinfo->value.enumerated.item >= CS35L56_NUM_INPUT_SRC)
15462306a36Sopenharmony_ci		uinfo->value.enumerated.item = CS35L56_NUM_INPUT_SRC - 1;
15562306a36Sopenharmony_ci	strscpy(uinfo->value.enumerated.name, cs35l56_tx_input_texts[uinfo->value.enumerated.item],
15662306a36Sopenharmony_ci		sizeof(uinfo->value.enumerated.name));
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	return 0;
15962306a36Sopenharmony_ci}
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_cistatic int cs35l56_hda_mixer_get(struct snd_kcontrol *kcontrol,
16262306a36Sopenharmony_ci				 struct snd_ctl_elem_value *ucontrol)
16362306a36Sopenharmony_ci{
16462306a36Sopenharmony_ci	struct cs35l56_hda *cs35l56 = (struct cs35l56_hda *)kcontrol->private_data;
16562306a36Sopenharmony_ci	unsigned int reg_val;
16662306a36Sopenharmony_ci	int i;
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	regmap_read(cs35l56->base.regmap, kcontrol->private_value, &reg_val);
16962306a36Sopenharmony_ci	reg_val &= CS35L56_ASP_TXn_SRC_MASK;
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	for (i = 0; i < CS35L56_NUM_INPUT_SRC; ++i) {
17262306a36Sopenharmony_ci		if (cs35l56_tx_input_values[i] == reg_val) {
17362306a36Sopenharmony_ci			ucontrol->value.enumerated.item[0] = i;
17462306a36Sopenharmony_ci			break;
17562306a36Sopenharmony_ci		}
17662306a36Sopenharmony_ci	}
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	return 0;
17962306a36Sopenharmony_ci}
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_cistatic int cs35l56_hda_mixer_put(struct snd_kcontrol *kcontrol,
18262306a36Sopenharmony_ci				 struct snd_ctl_elem_value *ucontrol)
18362306a36Sopenharmony_ci{
18462306a36Sopenharmony_ci	struct cs35l56_hda *cs35l56 = (struct cs35l56_hda *)kcontrol->private_data;
18562306a36Sopenharmony_ci	unsigned int item = ucontrol->value.enumerated.item[0];
18662306a36Sopenharmony_ci	bool changed;
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	if (item >= CS35L56_NUM_INPUT_SRC)
18962306a36Sopenharmony_ci		return -EINVAL;
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	regmap_update_bits_check(cs35l56->base.regmap, kcontrol->private_value,
19262306a36Sopenharmony_ci				 CS35L56_INPUT_MASK, cs35l56_tx_input_values[item],
19362306a36Sopenharmony_ci				 &changed);
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	return changed;
19662306a36Sopenharmony_ci}
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_cistatic int cs35l56_hda_posture_info(struct snd_kcontrol *kcontrol,
19962306a36Sopenharmony_ci				    struct snd_ctl_elem_info *uinfo)
20062306a36Sopenharmony_ci{
20162306a36Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
20262306a36Sopenharmony_ci	uinfo->count = 1;
20362306a36Sopenharmony_ci	uinfo->value.integer.min = CS35L56_MAIN_POSTURE_MIN;
20462306a36Sopenharmony_ci	uinfo->value.integer.max = CS35L56_MAIN_POSTURE_MAX;
20562306a36Sopenharmony_ci	return 0;
20662306a36Sopenharmony_ci}
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_cistatic int cs35l56_hda_posture_get(struct snd_kcontrol *kcontrol,
20962306a36Sopenharmony_ci				   struct snd_ctl_elem_value *ucontrol)
21062306a36Sopenharmony_ci{
21162306a36Sopenharmony_ci	struct cs35l56_hda *cs35l56 = (struct cs35l56_hda *)kcontrol->private_data;
21262306a36Sopenharmony_ci	unsigned int pos;
21362306a36Sopenharmony_ci	int ret;
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	ret = regmap_read(cs35l56->base.regmap, CS35L56_MAIN_POSTURE_NUMBER, &pos);
21662306a36Sopenharmony_ci	if (ret)
21762306a36Sopenharmony_ci		return ret;
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	ucontrol->value.integer.value[0] = pos;
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	return 0;
22262306a36Sopenharmony_ci}
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_cistatic int cs35l56_hda_posture_put(struct snd_kcontrol *kcontrol,
22562306a36Sopenharmony_ci				   struct snd_ctl_elem_value *ucontrol)
22662306a36Sopenharmony_ci{
22762306a36Sopenharmony_ci	struct cs35l56_hda *cs35l56 = (struct cs35l56_hda *)kcontrol->private_data;
22862306a36Sopenharmony_ci	unsigned long pos = ucontrol->value.integer.value[0];
22962306a36Sopenharmony_ci	bool changed;
23062306a36Sopenharmony_ci	int ret;
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	if ((pos < CS35L56_MAIN_POSTURE_MIN) ||
23362306a36Sopenharmony_ci	    (pos > CS35L56_MAIN_POSTURE_MAX))
23462306a36Sopenharmony_ci		return -EINVAL;
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	ret = regmap_update_bits_check(cs35l56->base.regmap,
23762306a36Sopenharmony_ci				       CS35L56_MAIN_POSTURE_NUMBER,
23862306a36Sopenharmony_ci				       CS35L56_MAIN_POSTURE_MASK,
23962306a36Sopenharmony_ci				       pos, &changed);
24062306a36Sopenharmony_ci	if (ret)
24162306a36Sopenharmony_ci		return ret;
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	return changed;
24462306a36Sopenharmony_ci}
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_cistatic const struct {
24762306a36Sopenharmony_ci	const char *name;
24862306a36Sopenharmony_ci	unsigned int reg;
24962306a36Sopenharmony_ci} cs35l56_hda_mixer_controls[] = {
25062306a36Sopenharmony_ci	{ "ASP1 TX1 Source", CS35L56_ASP1TX1_INPUT },
25162306a36Sopenharmony_ci	{ "ASP1 TX2 Source", CS35L56_ASP1TX2_INPUT },
25262306a36Sopenharmony_ci	{ "ASP1 TX3 Source", CS35L56_ASP1TX3_INPUT },
25362306a36Sopenharmony_ci	{ "ASP1 TX4 Source", CS35L56_ASP1TX4_INPUT },
25462306a36Sopenharmony_ci};
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(cs35l56_hda_vol_tlv, -10000, 25, 0);
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_cistatic int cs35l56_hda_vol_info(struct snd_kcontrol *kcontrol,
25962306a36Sopenharmony_ci				struct snd_ctl_elem_info *uinfo)
26062306a36Sopenharmony_ci{
26162306a36Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
26262306a36Sopenharmony_ci	uinfo->count = 1;
26362306a36Sopenharmony_ci	uinfo->value.integer.step = 1;
26462306a36Sopenharmony_ci	uinfo->value.integer.min = 0;
26562306a36Sopenharmony_ci	uinfo->value.integer.max = CS35L56_MAIN_RENDER_USER_VOLUME_MAX -
26662306a36Sopenharmony_ci				   CS35L56_MAIN_RENDER_USER_VOLUME_MIN;
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	return 0;
26962306a36Sopenharmony_ci}
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_cistatic int cs35l56_hda_vol_get(struct snd_kcontrol *kcontrol,
27262306a36Sopenharmony_ci			       struct snd_ctl_elem_value *ucontrol)
27362306a36Sopenharmony_ci{
27462306a36Sopenharmony_ci	struct cs35l56_hda *cs35l56 = (struct cs35l56_hda *)kcontrol->private_data;
27562306a36Sopenharmony_ci	unsigned int raw_vol;
27662306a36Sopenharmony_ci	int vol;
27762306a36Sopenharmony_ci	int ret;
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	ret = regmap_read(cs35l56->base.regmap, CS35L56_MAIN_RENDER_USER_VOLUME, &raw_vol);
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	if (ret)
28262306a36Sopenharmony_ci		return ret;
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	vol = (s16)(raw_vol & 0xFFFF);
28562306a36Sopenharmony_ci	vol >>= CS35L56_MAIN_RENDER_USER_VOLUME_SHIFT;
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	if (vol & BIT(CS35L56_MAIN_RENDER_USER_VOLUME_SIGNBIT))
28862306a36Sopenharmony_ci		vol |= ~((int)(BIT(CS35L56_MAIN_RENDER_USER_VOLUME_SIGNBIT) - 1));
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	ucontrol->value.integer.value[0] = vol - CS35L56_MAIN_RENDER_USER_VOLUME_MIN;
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	return 0;
29362306a36Sopenharmony_ci}
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_cistatic int cs35l56_hda_vol_put(struct snd_kcontrol *kcontrol,
29662306a36Sopenharmony_ci			       struct snd_ctl_elem_value *ucontrol)
29762306a36Sopenharmony_ci{
29862306a36Sopenharmony_ci	struct cs35l56_hda *cs35l56 = (struct cs35l56_hda *)kcontrol->private_data;
29962306a36Sopenharmony_ci	long vol = ucontrol->value.integer.value[0];
30062306a36Sopenharmony_ci	unsigned int raw_vol;
30162306a36Sopenharmony_ci	bool changed;
30262306a36Sopenharmony_ci	int ret;
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	if ((vol < 0) || (vol > (CS35L56_MAIN_RENDER_USER_VOLUME_MAX -
30562306a36Sopenharmony_ci				 CS35L56_MAIN_RENDER_USER_VOLUME_MIN)))
30662306a36Sopenharmony_ci		return -EINVAL;
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	raw_vol = (vol + CS35L56_MAIN_RENDER_USER_VOLUME_MIN) <<
30962306a36Sopenharmony_ci		  CS35L56_MAIN_RENDER_USER_VOLUME_SHIFT;
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	ret = regmap_update_bits_check(cs35l56->base.regmap,
31262306a36Sopenharmony_ci				       CS35L56_MAIN_RENDER_USER_VOLUME,
31362306a36Sopenharmony_ci				       CS35L56_MAIN_RENDER_USER_VOLUME_MASK,
31462306a36Sopenharmony_ci				       raw_vol, &changed);
31562306a36Sopenharmony_ci	if (ret)
31662306a36Sopenharmony_ci		return ret;
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	return changed;
31962306a36Sopenharmony_ci}
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_cistatic void cs35l56_hda_create_controls(struct cs35l56_hda *cs35l56)
32262306a36Sopenharmony_ci{
32362306a36Sopenharmony_ci	struct snd_kcontrol_new ctl_template = {
32462306a36Sopenharmony_ci		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
32562306a36Sopenharmony_ci		.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
32662306a36Sopenharmony_ci		.info = cs35l56_hda_posture_info,
32762306a36Sopenharmony_ci		.get = cs35l56_hda_posture_get,
32862306a36Sopenharmony_ci		.put = cs35l56_hda_posture_put,
32962306a36Sopenharmony_ci	};
33062306a36Sopenharmony_ci	char name[64];
33162306a36Sopenharmony_ci	int i;
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	snprintf(name, sizeof(name), "%s Posture Number", cs35l56->amp_name);
33462306a36Sopenharmony_ci	ctl_template.name = name;
33562306a36Sopenharmony_ci	cs35l56->posture_ctl = snd_ctl_new1(&ctl_template, cs35l56);
33662306a36Sopenharmony_ci	if (snd_ctl_add(cs35l56->codec->card, cs35l56->posture_ctl))
33762306a36Sopenharmony_ci		dev_err(cs35l56->base.dev, "Failed to add KControl: %s\n", ctl_template.name);
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	/* Mixer controls */
34062306a36Sopenharmony_ci	ctl_template.info = cs35l56_hda_mixer_info;
34162306a36Sopenharmony_ci	ctl_template.get = cs35l56_hda_mixer_get;
34262306a36Sopenharmony_ci	ctl_template.put = cs35l56_hda_mixer_put;
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	BUILD_BUG_ON(ARRAY_SIZE(cs35l56->mixer_ctl) != ARRAY_SIZE(cs35l56_hda_mixer_controls));
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(cs35l56_hda_mixer_controls); ++i) {
34762306a36Sopenharmony_ci		snprintf(name, sizeof(name), "%s %s", cs35l56->amp_name,
34862306a36Sopenharmony_ci			 cs35l56_hda_mixer_controls[i].name);
34962306a36Sopenharmony_ci		ctl_template.private_value = cs35l56_hda_mixer_controls[i].reg;
35062306a36Sopenharmony_ci		cs35l56->mixer_ctl[i] = snd_ctl_new1(&ctl_template, cs35l56);
35162306a36Sopenharmony_ci		if (snd_ctl_add(cs35l56->codec->card, cs35l56->mixer_ctl[i])) {
35262306a36Sopenharmony_ci			dev_err(cs35l56->base.dev, "Failed to add KControl: %s\n",
35362306a36Sopenharmony_ci				ctl_template.name);
35462306a36Sopenharmony_ci		}
35562306a36Sopenharmony_ci	}
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	ctl_template.info = cs35l56_hda_vol_info;
35862306a36Sopenharmony_ci	ctl_template.get = cs35l56_hda_vol_get;
35962306a36Sopenharmony_ci	ctl_template.put = cs35l56_hda_vol_put;
36062306a36Sopenharmony_ci	ctl_template.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ);
36162306a36Sopenharmony_ci	ctl_template.tlv.p = cs35l56_hda_vol_tlv;
36262306a36Sopenharmony_ci	snprintf(name, sizeof(name), "%s Speaker Playback Volume", cs35l56->amp_name);
36362306a36Sopenharmony_ci	ctl_template.name = name;
36462306a36Sopenharmony_ci	cs35l56->volume_ctl = snd_ctl_new1(&ctl_template, cs35l56);
36562306a36Sopenharmony_ci	if (snd_ctl_add(cs35l56->codec->card, cs35l56->volume_ctl))
36662306a36Sopenharmony_ci		dev_err(cs35l56->base.dev, "Failed to add KControl: %s\n", ctl_template.name);
36762306a36Sopenharmony_ci}
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_cistatic void cs35l56_hda_remove_controls(struct cs35l56_hda *cs35l56)
37062306a36Sopenharmony_ci{
37162306a36Sopenharmony_ci	int i;
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	for (i = ARRAY_SIZE(cs35l56->mixer_ctl) - 1; i >= 0; i--)
37462306a36Sopenharmony_ci		snd_ctl_remove(cs35l56->codec->card, cs35l56->mixer_ctl[i]);
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	snd_ctl_remove(cs35l56->codec->card, cs35l56->posture_ctl);
37762306a36Sopenharmony_ci	snd_ctl_remove(cs35l56->codec->card, cs35l56->volume_ctl);
37862306a36Sopenharmony_ci}
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_cistatic const struct cs_dsp_client_ops cs35l56_hda_client_ops = {
38162306a36Sopenharmony_ci	.control_remove = hda_cs_dsp_control_remove,
38262306a36Sopenharmony_ci};
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_cistatic int cs35l56_hda_request_firmware_file(struct cs35l56_hda *cs35l56,
38562306a36Sopenharmony_ci					     const struct firmware **firmware, char **filename,
38662306a36Sopenharmony_ci					     const char *dir, const char *system_name,
38762306a36Sopenharmony_ci					     const char *amp_name,
38862306a36Sopenharmony_ci					     const char *filetype)
38962306a36Sopenharmony_ci{
39062306a36Sopenharmony_ci	char *s, c;
39162306a36Sopenharmony_ci	int ret = 0;
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	if (system_name && amp_name)
39462306a36Sopenharmony_ci		*filename = kasprintf(GFP_KERNEL, "%scs35l56%s-%02x-dsp1-misc-%s-%s.%s", dir,
39562306a36Sopenharmony_ci				      cs35l56->base.secured ? "s" : "", cs35l56->base.rev,
39662306a36Sopenharmony_ci				      system_name, amp_name, filetype);
39762306a36Sopenharmony_ci	else if (system_name)
39862306a36Sopenharmony_ci		*filename = kasprintf(GFP_KERNEL, "%scs35l56%s-%02x-dsp1-misc-%s.%s", dir,
39962306a36Sopenharmony_ci				      cs35l56->base.secured ? "s" : "", cs35l56->base.rev,
40062306a36Sopenharmony_ci				      system_name, filetype);
40162306a36Sopenharmony_ci	else
40262306a36Sopenharmony_ci		*filename = kasprintf(GFP_KERNEL, "%scs35l56%s-%02x-dsp1-misc.%s", dir,
40362306a36Sopenharmony_ci				      cs35l56->base.secured ? "s" : "", cs35l56->base.rev,
40462306a36Sopenharmony_ci				      filetype);
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	if (!*filename)
40762306a36Sopenharmony_ci		return -ENOMEM;
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	/*
41062306a36Sopenharmony_ci	 * Make sure that filename is lower-case and any non alpha-numeric
41162306a36Sopenharmony_ci	 * characters except full stop and forward slash are replaced with
41262306a36Sopenharmony_ci	 * hyphens.
41362306a36Sopenharmony_ci	 */
41462306a36Sopenharmony_ci	s = *filename;
41562306a36Sopenharmony_ci	while (*s) {
41662306a36Sopenharmony_ci		c = *s;
41762306a36Sopenharmony_ci		if (isalnum(c))
41862306a36Sopenharmony_ci			*s = tolower(c);
41962306a36Sopenharmony_ci		else if (c != '.' && c != '/')
42062306a36Sopenharmony_ci			*s = '-';
42162306a36Sopenharmony_ci		s++;
42262306a36Sopenharmony_ci	}
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci	ret = firmware_request_nowarn(firmware, *filename, cs35l56->base.dev);
42562306a36Sopenharmony_ci	if (ret) {
42662306a36Sopenharmony_ci		dev_dbg(cs35l56->base.dev, "Failed to request '%s'\n", *filename);
42762306a36Sopenharmony_ci		kfree(*filename);
42862306a36Sopenharmony_ci		*filename = NULL;
42962306a36Sopenharmony_ci		return ret;
43062306a36Sopenharmony_ci	}
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	dev_dbg(cs35l56->base.dev, "Found '%s'\n", *filename);
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	return 0;
43562306a36Sopenharmony_ci}
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_cistatic const char cirrus_dir[] = "cirrus/";
43862306a36Sopenharmony_cistatic void cs35l56_hda_request_firmware_files(struct cs35l56_hda *cs35l56,
43962306a36Sopenharmony_ci					       const struct firmware **wmfw_firmware,
44062306a36Sopenharmony_ci					       char **wmfw_filename,
44162306a36Sopenharmony_ci					       const struct firmware **coeff_firmware,
44262306a36Sopenharmony_ci					       char **coeff_filename)
44362306a36Sopenharmony_ci{
44462306a36Sopenharmony_ci	const char *system_name = cs35l56->system_name;
44562306a36Sopenharmony_ci	const char *amp_name = cs35l56->amp_name;
44662306a36Sopenharmony_ci	int ret;
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	if (system_name && amp_name) {
44962306a36Sopenharmony_ci		if (!cs35l56_hda_request_firmware_file(cs35l56, wmfw_firmware, wmfw_filename,
45062306a36Sopenharmony_ci						       cirrus_dir, system_name, amp_name, "wmfw")) {
45162306a36Sopenharmony_ci			cs35l56_hda_request_firmware_file(cs35l56, coeff_firmware, coeff_filename,
45262306a36Sopenharmony_ci							  cirrus_dir, system_name, amp_name, "bin");
45362306a36Sopenharmony_ci			return;
45462306a36Sopenharmony_ci		}
45562306a36Sopenharmony_ci	}
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	if (system_name) {
45862306a36Sopenharmony_ci		if (!cs35l56_hda_request_firmware_file(cs35l56, wmfw_firmware, wmfw_filename,
45962306a36Sopenharmony_ci						       cirrus_dir, system_name, NULL, "wmfw")) {
46062306a36Sopenharmony_ci			if (amp_name)
46162306a36Sopenharmony_ci				cs35l56_hda_request_firmware_file(cs35l56,
46262306a36Sopenharmony_ci								  coeff_firmware, coeff_filename,
46362306a36Sopenharmony_ci								  cirrus_dir, system_name,
46462306a36Sopenharmony_ci								  amp_name, "bin");
46562306a36Sopenharmony_ci			if (!*coeff_firmware)
46662306a36Sopenharmony_ci				cs35l56_hda_request_firmware_file(cs35l56,
46762306a36Sopenharmony_ci								  coeff_firmware, coeff_filename,
46862306a36Sopenharmony_ci								  cirrus_dir, system_name,
46962306a36Sopenharmony_ci								  NULL, "bin");
47062306a36Sopenharmony_ci			return;
47162306a36Sopenharmony_ci		}
47262306a36Sopenharmony_ci	}
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci	ret = cs35l56_hda_request_firmware_file(cs35l56, wmfw_firmware, wmfw_filename,
47562306a36Sopenharmony_ci						cirrus_dir, NULL, NULL, "wmfw");
47662306a36Sopenharmony_ci	if (!ret) {
47762306a36Sopenharmony_ci		cs35l56_hda_request_firmware_file(cs35l56, coeff_firmware, coeff_filename,
47862306a36Sopenharmony_ci						  cirrus_dir, NULL, NULL, "bin");
47962306a36Sopenharmony_ci		return;
48062306a36Sopenharmony_ci	}
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci	/* When a firmware file is not found must still search for the coeff files */
48362306a36Sopenharmony_ci	if (system_name) {
48462306a36Sopenharmony_ci		if (amp_name)
48562306a36Sopenharmony_ci			cs35l56_hda_request_firmware_file(cs35l56, coeff_firmware, coeff_filename,
48662306a36Sopenharmony_ci							  cirrus_dir, system_name, amp_name, "bin");
48762306a36Sopenharmony_ci		if (!*coeff_firmware)
48862306a36Sopenharmony_ci			cs35l56_hda_request_firmware_file(cs35l56, coeff_firmware, coeff_filename,
48962306a36Sopenharmony_ci							  cirrus_dir, system_name, NULL, "bin");
49062306a36Sopenharmony_ci	}
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	if (!*coeff_firmware)
49362306a36Sopenharmony_ci		cs35l56_hda_request_firmware_file(cs35l56, coeff_firmware, coeff_filename,
49462306a36Sopenharmony_ci						  cirrus_dir, NULL, NULL, "bin");
49562306a36Sopenharmony_ci}
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_cistatic void cs35l56_hda_release_firmware_files(const struct firmware *wmfw_firmware,
49862306a36Sopenharmony_ci					       char *wmfw_filename,
49962306a36Sopenharmony_ci					       const struct firmware *coeff_firmware,
50062306a36Sopenharmony_ci					       char *coeff_filename)
50162306a36Sopenharmony_ci{
50262306a36Sopenharmony_ci	if (wmfw_firmware)
50362306a36Sopenharmony_ci		release_firmware(wmfw_firmware);
50462306a36Sopenharmony_ci	kfree(wmfw_filename);
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci	if (coeff_firmware)
50762306a36Sopenharmony_ci		release_firmware(coeff_firmware);
50862306a36Sopenharmony_ci	kfree(coeff_filename);
50962306a36Sopenharmony_ci}
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_cistatic void cs35l56_hda_add_dsp_controls(struct cs35l56_hda *cs35l56)
51262306a36Sopenharmony_ci{
51362306a36Sopenharmony_ci	struct hda_cs_dsp_ctl_info info;
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	info.device_name = cs35l56->amp_name;
51662306a36Sopenharmony_ci	info.fw_type = HDA_CS_DSP_FW_MISC;
51762306a36Sopenharmony_ci	info.card = cs35l56->codec->card;
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci	hda_cs_dsp_add_controls(&cs35l56->cs_dsp, &info);
52062306a36Sopenharmony_ci}
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_cistatic int cs35l56_hda_fw_load(struct cs35l56_hda *cs35l56)
52362306a36Sopenharmony_ci{
52462306a36Sopenharmony_ci	const struct firmware *coeff_firmware = NULL;
52562306a36Sopenharmony_ci	const struct firmware *wmfw_firmware = NULL;
52662306a36Sopenharmony_ci	char *coeff_filename = NULL;
52762306a36Sopenharmony_ci	char *wmfw_filename = NULL;
52862306a36Sopenharmony_ci	unsigned int firmware_missing;
52962306a36Sopenharmony_ci	int ret = 0;
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	/* Prepare for a new DSP power-up */
53262306a36Sopenharmony_ci	if (cs35l56->base.fw_patched)
53362306a36Sopenharmony_ci		cs_dsp_power_down(&cs35l56->cs_dsp);
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	cs35l56->base.fw_patched = false;
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci	pm_runtime_get_sync(cs35l56->base.dev);
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci	ret = regmap_read(cs35l56->base.regmap, CS35L56_PROTECTION_STATUS, &firmware_missing);
54062306a36Sopenharmony_ci	if (ret) {
54162306a36Sopenharmony_ci		dev_err(cs35l56->base.dev, "Failed to read PROTECTION_STATUS: %d\n", ret);
54262306a36Sopenharmony_ci		goto err_pm_put;
54362306a36Sopenharmony_ci	}
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci	firmware_missing &= CS35L56_FIRMWARE_MISSING;
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci	/*
54862306a36Sopenharmony_ci	 * Firmware can only be downloaded if the CS35L56 is secured or is
54962306a36Sopenharmony_ci	 * running from the built-in ROM. If it is secured the BIOS will have
55062306a36Sopenharmony_ci	 * downloaded firmware, and the wmfw/bin files will only contain
55162306a36Sopenharmony_ci	 * tunings that are safe to download with the firmware running.
55262306a36Sopenharmony_ci	 */
55362306a36Sopenharmony_ci	if (cs35l56->base.secured || firmware_missing) {
55462306a36Sopenharmony_ci		cs35l56_hda_request_firmware_files(cs35l56, &wmfw_firmware, &wmfw_filename,
55562306a36Sopenharmony_ci						   &coeff_firmware, &coeff_filename);
55662306a36Sopenharmony_ci	}
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci	/*
55962306a36Sopenharmony_ci	 * If the BIOS didn't patch the firmware a bin file is mandatory to
56062306a36Sopenharmony_ci	 * enable the ASP·
56162306a36Sopenharmony_ci	 */
56262306a36Sopenharmony_ci	if (!coeff_firmware && firmware_missing) {
56362306a36Sopenharmony_ci		dev_err(cs35l56->base.dev, ".bin file required but not found\n");
56462306a36Sopenharmony_ci		ret = -ENOENT;
56562306a36Sopenharmony_ci		goto err_fw_release;
56662306a36Sopenharmony_ci	}
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	mutex_lock(&cs35l56->base.irq_lock);
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	/*
57162306a36Sopenharmony_ci	 * When the device is running in secure mode the firmware files can
57262306a36Sopenharmony_ci	 * only contain insecure tunings and therefore we do not need to
57362306a36Sopenharmony_ci	 * shutdown the firmware to apply them and can use the lower cost
57462306a36Sopenharmony_ci	 * reinit sequence instead.
57562306a36Sopenharmony_ci	 */
57662306a36Sopenharmony_ci	if (!cs35l56->base.secured && (wmfw_firmware || coeff_firmware)) {
57762306a36Sopenharmony_ci		ret = cs35l56_firmware_shutdown(&cs35l56->base);
57862306a36Sopenharmony_ci		if (ret)
57962306a36Sopenharmony_ci			goto err;
58062306a36Sopenharmony_ci	}
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci	ret = cs_dsp_power_up(&cs35l56->cs_dsp, wmfw_firmware, wmfw_filename,
58362306a36Sopenharmony_ci			      coeff_firmware, coeff_filename, "misc");
58462306a36Sopenharmony_ci	if (ret) {
58562306a36Sopenharmony_ci		dev_dbg(cs35l56->base.dev, "%s: cs_dsp_power_up ret %d\n", __func__, ret);
58662306a36Sopenharmony_ci		goto err;
58762306a36Sopenharmony_ci	}
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci	if (wmfw_filename)
59062306a36Sopenharmony_ci		dev_dbg(cs35l56->base.dev, "Loaded WMFW Firmware: %s\n", wmfw_filename);
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_ci	if (coeff_filename)
59362306a36Sopenharmony_ci		dev_dbg(cs35l56->base.dev, "Loaded Coefficients: %s\n", coeff_filename);
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci	if (cs35l56->base.secured) {
59662306a36Sopenharmony_ci		ret = cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_AUDIO_REINIT);
59762306a36Sopenharmony_ci		if (ret)
59862306a36Sopenharmony_ci			goto err_powered_up;
59962306a36Sopenharmony_ci	} else if (wmfw_firmware || coeff_firmware) {
60062306a36Sopenharmony_ci		/* If we downloaded firmware, reset the device and wait for it to boot */
60162306a36Sopenharmony_ci		cs35l56_system_reset(&cs35l56->base, false);
60262306a36Sopenharmony_ci		regcache_mark_dirty(cs35l56->base.regmap);
60362306a36Sopenharmony_ci		ret = cs35l56_wait_for_firmware_boot(&cs35l56->base);
60462306a36Sopenharmony_ci		if (ret)
60562306a36Sopenharmony_ci			goto err_powered_up;
60662306a36Sopenharmony_ci	}
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci	/* Disable auto-hibernate so that runtime_pm has control */
60962306a36Sopenharmony_ci	ret = cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_PREVENT_AUTO_HIBERNATE);
61062306a36Sopenharmony_ci	if (ret)
61162306a36Sopenharmony_ci		goto err_powered_up;
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci	regcache_sync(cs35l56->base.regmap);
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	regmap_clear_bits(cs35l56->base.regmap, CS35L56_PROTECTION_STATUS,
61662306a36Sopenharmony_ci			  CS35L56_FIRMWARE_MISSING);
61762306a36Sopenharmony_ci	cs35l56->base.fw_patched = true;
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci	ret = cs_dsp_run(&cs35l56->cs_dsp);
62062306a36Sopenharmony_ci	if (ret)
62162306a36Sopenharmony_ci		dev_dbg(cs35l56->base.dev, "%s: cs_dsp_run ret %d\n", __func__, ret);
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_cierr_powered_up:
62462306a36Sopenharmony_ci	if (!cs35l56->base.fw_patched)
62562306a36Sopenharmony_ci		cs_dsp_power_down(&cs35l56->cs_dsp);
62662306a36Sopenharmony_cierr:
62762306a36Sopenharmony_ci	mutex_unlock(&cs35l56->base.irq_lock);
62862306a36Sopenharmony_cierr_fw_release:
62962306a36Sopenharmony_ci	cs35l56_hda_release_firmware_files(wmfw_firmware, wmfw_filename,
63062306a36Sopenharmony_ci					   coeff_firmware, coeff_filename);
63162306a36Sopenharmony_cierr_pm_put:
63262306a36Sopenharmony_ci	pm_runtime_put(cs35l56->base.dev);
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci	return ret;
63562306a36Sopenharmony_ci}
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_cistatic int cs35l56_hda_bind(struct device *dev, struct device *master, void *master_data)
63862306a36Sopenharmony_ci{
63962306a36Sopenharmony_ci	struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev);
64062306a36Sopenharmony_ci	struct hda_component *comps = master_data;
64162306a36Sopenharmony_ci	int ret;
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_ci	if (!comps || cs35l56->index < 0 || cs35l56->index >= HDA_MAX_COMPONENTS)
64462306a36Sopenharmony_ci		return -EINVAL;
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_ci	comps = &comps[cs35l56->index];
64762306a36Sopenharmony_ci	if (comps->dev)
64862306a36Sopenharmony_ci		return -EBUSY;
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci	comps->dev = dev;
65162306a36Sopenharmony_ci	cs35l56->codec = comps->codec;
65262306a36Sopenharmony_ci	strscpy(comps->name, dev_name(dev), sizeof(comps->name));
65362306a36Sopenharmony_ci	comps->playback_hook = cs35l56_hda_playback_hook;
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci	ret = cs35l56_hda_fw_load(cs35l56);
65662306a36Sopenharmony_ci	if (ret)
65762306a36Sopenharmony_ci		return ret;
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_ci	cs35l56_hda_create_controls(cs35l56);
66062306a36Sopenharmony_ci	cs35l56_hda_add_dsp_controls(cs35l56);
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_SND_DEBUG)
66362306a36Sopenharmony_ci	cs35l56->debugfs_root = debugfs_create_dir(dev_name(cs35l56->base.dev), sound_debugfs_root);
66462306a36Sopenharmony_ci	cs_dsp_init_debugfs(&cs35l56->cs_dsp, cs35l56->debugfs_root);
66562306a36Sopenharmony_ci#endif
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci	dev_dbg(cs35l56->base.dev, "Bound\n");
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci	return 0;
67062306a36Sopenharmony_ci}
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_cistatic void cs35l56_hda_unbind(struct device *dev, struct device *master, void *master_data)
67362306a36Sopenharmony_ci{
67462306a36Sopenharmony_ci	struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev);
67562306a36Sopenharmony_ci	struct hda_component *comps = master_data;
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci	cs35l56_hda_remove_controls(cs35l56);
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_SND_DEBUG)
68062306a36Sopenharmony_ci	cs_dsp_cleanup_debugfs(&cs35l56->cs_dsp);
68162306a36Sopenharmony_ci	debugfs_remove_recursive(cs35l56->debugfs_root);
68262306a36Sopenharmony_ci#endif
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_ci	if (cs35l56->base.fw_patched)
68562306a36Sopenharmony_ci		cs_dsp_power_down(&cs35l56->cs_dsp);
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_ci	cs_dsp_remove(&cs35l56->cs_dsp);
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci	if (comps[cs35l56->index].dev == dev)
69062306a36Sopenharmony_ci		memset(&comps[cs35l56->index], 0, sizeof(*comps));
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci	dev_dbg(cs35l56->base.dev, "Unbound\n");
69362306a36Sopenharmony_ci}
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_cistatic const struct component_ops cs35l56_hda_comp_ops = {
69662306a36Sopenharmony_ci	.bind = cs35l56_hda_bind,
69762306a36Sopenharmony_ci	.unbind = cs35l56_hda_unbind,
69862306a36Sopenharmony_ci};
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_cistatic int cs35l56_hda_system_suspend(struct device *dev)
70162306a36Sopenharmony_ci{
70262306a36Sopenharmony_ci	struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev);
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci	if (cs35l56->playing)
70562306a36Sopenharmony_ci		cs35l56_hda_pause(cs35l56);
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_ci	cs35l56->suspended = true;
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci	/*
71062306a36Sopenharmony_ci	 * The interrupt line is normally shared, but after we start suspending
71162306a36Sopenharmony_ci	 * we can't check if our device is the source of an interrupt, and can't
71262306a36Sopenharmony_ci	 * clear it. Prevent this race by temporarily disabling the parent irq
71362306a36Sopenharmony_ci	 * until we reach _no_irq.
71462306a36Sopenharmony_ci	 */
71562306a36Sopenharmony_ci	if (cs35l56->base.irq)
71662306a36Sopenharmony_ci		disable_irq(cs35l56->base.irq);
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci	return pm_runtime_force_suspend(dev);
71962306a36Sopenharmony_ci}
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_cistatic int cs35l56_hda_system_suspend_late(struct device *dev)
72262306a36Sopenharmony_ci{
72362306a36Sopenharmony_ci	struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev);
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_ci	/*
72662306a36Sopenharmony_ci	 * RESET is usually shared by all amps so it must not be asserted until
72762306a36Sopenharmony_ci	 * all driver instances have done their suspend() stage.
72862306a36Sopenharmony_ci	 */
72962306a36Sopenharmony_ci	if (cs35l56->base.reset_gpio) {
73062306a36Sopenharmony_ci		gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 0);
73162306a36Sopenharmony_ci		cs35l56_wait_min_reset_pulse();
73262306a36Sopenharmony_ci	}
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_ci	return 0;
73562306a36Sopenharmony_ci}
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_cistatic int cs35l56_hda_system_suspend_no_irq(struct device *dev)
73862306a36Sopenharmony_ci{
73962306a36Sopenharmony_ci	struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev);
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_ci	/* Handlers are now disabled so the parent IRQ can safely be re-enabled. */
74262306a36Sopenharmony_ci	if (cs35l56->base.irq)
74362306a36Sopenharmony_ci		enable_irq(cs35l56->base.irq);
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_ci	return 0;
74662306a36Sopenharmony_ci}
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_cistatic int cs35l56_hda_system_resume_no_irq(struct device *dev)
74962306a36Sopenharmony_ci{
75062306a36Sopenharmony_ci	struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev);
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_ci	/*
75362306a36Sopenharmony_ci	 * WAKE interrupts unmask if the CS35L56 hibernates, which can cause
75462306a36Sopenharmony_ci	 * spurious interrupts, and the interrupt line is normally shared.
75562306a36Sopenharmony_ci	 * We can't check if our device is the source of an interrupt, and can't
75662306a36Sopenharmony_ci	 * clear it, until it has fully resumed. Prevent this race by temporarily
75762306a36Sopenharmony_ci	 * disabling the parent irq until we complete resume().
75862306a36Sopenharmony_ci	 */
75962306a36Sopenharmony_ci	if (cs35l56->base.irq)
76062306a36Sopenharmony_ci		disable_irq(cs35l56->base.irq);
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_ci	return 0;
76362306a36Sopenharmony_ci}
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_cistatic int cs35l56_hda_system_resume_early(struct device *dev)
76662306a36Sopenharmony_ci{
76762306a36Sopenharmony_ci	struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev);
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_ci	/* Ensure a spec-compliant RESET pulse. */
77062306a36Sopenharmony_ci	if (cs35l56->base.reset_gpio) {
77162306a36Sopenharmony_ci		gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 0);
77262306a36Sopenharmony_ci		cs35l56_wait_min_reset_pulse();
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_ci		/* Release shared RESET before drivers start resume(). */
77562306a36Sopenharmony_ci		gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 1);
77662306a36Sopenharmony_ci		cs35l56_wait_control_port_ready();
77762306a36Sopenharmony_ci	}
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_ci	return 0;
78062306a36Sopenharmony_ci}
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_cistatic int cs35l56_hda_system_resume(struct device *dev)
78362306a36Sopenharmony_ci{
78462306a36Sopenharmony_ci	struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev);
78562306a36Sopenharmony_ci	int ret;
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ci	/* Undo pm_runtime_force_suspend() before re-enabling the irq */
78862306a36Sopenharmony_ci	ret = pm_runtime_force_resume(dev);
78962306a36Sopenharmony_ci	if (cs35l56->base.irq)
79062306a36Sopenharmony_ci		enable_irq(cs35l56->base.irq);
79162306a36Sopenharmony_ci
79262306a36Sopenharmony_ci	if (ret)
79362306a36Sopenharmony_ci		return ret;
79462306a36Sopenharmony_ci
79562306a36Sopenharmony_ci	cs35l56->suspended = false;
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_ci	ret = cs35l56_is_fw_reload_needed(&cs35l56->base);
79862306a36Sopenharmony_ci	dev_dbg(cs35l56->base.dev, "fw_reload_needed: %d\n", ret);
79962306a36Sopenharmony_ci	if (ret > 0) {
80062306a36Sopenharmony_ci		ret = cs35l56_hda_fw_load(cs35l56);
80162306a36Sopenharmony_ci		if (ret)
80262306a36Sopenharmony_ci			return ret;
80362306a36Sopenharmony_ci	}
80462306a36Sopenharmony_ci
80562306a36Sopenharmony_ci	if (cs35l56->playing)
80662306a36Sopenharmony_ci		cs35l56_hda_play(cs35l56);
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_ci	return 0;
80962306a36Sopenharmony_ci}
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_cistatic int cs35l56_hda_read_acpi(struct cs35l56_hda *cs35l56, int id)
81262306a36Sopenharmony_ci{
81362306a36Sopenharmony_ci	u32 values[HDA_MAX_COMPONENTS];
81462306a36Sopenharmony_ci	struct acpi_device *adev;
81562306a36Sopenharmony_ci	const char *property, *sub;
81662306a36Sopenharmony_ci	size_t nval;
81762306a36Sopenharmony_ci	int i, ret;
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ci	/*
82062306a36Sopenharmony_ci	 * ACPI_COMPANION isn't available when this driver was instantiated by
82162306a36Sopenharmony_ci	 * the serial-multi-instantiate driver, so lookup the node by HID
82262306a36Sopenharmony_ci	 */
82362306a36Sopenharmony_ci	if (!ACPI_COMPANION(cs35l56->base.dev)) {
82462306a36Sopenharmony_ci		adev = acpi_dev_get_first_match_dev("CSC3556", NULL, -1);
82562306a36Sopenharmony_ci		if (!adev) {
82662306a36Sopenharmony_ci			dev_err(cs35l56->base.dev, "Failed to find an ACPI device for %s\n",
82762306a36Sopenharmony_ci				dev_name(cs35l56->base.dev));
82862306a36Sopenharmony_ci			return -ENODEV;
82962306a36Sopenharmony_ci		}
83062306a36Sopenharmony_ci		ACPI_COMPANION_SET(cs35l56->base.dev, adev);
83162306a36Sopenharmony_ci	}
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci	property = "cirrus,dev-index";
83462306a36Sopenharmony_ci	ret = device_property_count_u32(cs35l56->base.dev, property);
83562306a36Sopenharmony_ci	if (ret <= 0)
83662306a36Sopenharmony_ci		goto err;
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_ci	if (ret > ARRAY_SIZE(values)) {
83962306a36Sopenharmony_ci		ret = -EINVAL;
84062306a36Sopenharmony_ci		goto err;
84162306a36Sopenharmony_ci	}
84262306a36Sopenharmony_ci	nval = ret;
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_ci	ret = device_property_read_u32_array(cs35l56->base.dev, property, values, nval);
84562306a36Sopenharmony_ci	if (ret)
84662306a36Sopenharmony_ci		goto err;
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_ci	cs35l56->index = -1;
84962306a36Sopenharmony_ci	for (i = 0; i < nval; i++) {
85062306a36Sopenharmony_ci		if (values[i] == id) {
85162306a36Sopenharmony_ci			cs35l56->index = i;
85262306a36Sopenharmony_ci			break;
85362306a36Sopenharmony_ci		}
85462306a36Sopenharmony_ci	}
85562306a36Sopenharmony_ci	/*
85662306a36Sopenharmony_ci	 * It's not an error for the ID to be missing: for I2C there can be
85762306a36Sopenharmony_ci	 * an alias address that is not a real device. So reject silently.
85862306a36Sopenharmony_ci	 */
85962306a36Sopenharmony_ci	if (cs35l56->index == -1) {
86062306a36Sopenharmony_ci		dev_dbg(cs35l56->base.dev, "No index found in %s\n", property);
86162306a36Sopenharmony_ci		ret = -ENODEV;
86262306a36Sopenharmony_ci		goto err;
86362306a36Sopenharmony_ci	}
86462306a36Sopenharmony_ci
86562306a36Sopenharmony_ci	sub = acpi_get_subsystem_id(ACPI_HANDLE(cs35l56->base.dev));
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_ci	if (IS_ERR(sub)) {
86862306a36Sopenharmony_ci		dev_info(cs35l56->base.dev,
86962306a36Sopenharmony_ci			 "Read ACPI _SUB failed(%ld): fallback to generic firmware\n",
87062306a36Sopenharmony_ci			 PTR_ERR(sub));
87162306a36Sopenharmony_ci	} else {
87262306a36Sopenharmony_ci		cs35l56->system_name = sub;
87362306a36Sopenharmony_ci	}
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_ci	cs35l56->base.reset_gpio = devm_gpiod_get_index_optional(cs35l56->base.dev,
87662306a36Sopenharmony_ci								 "reset",
87762306a36Sopenharmony_ci								 cs35l56->index,
87862306a36Sopenharmony_ci								 GPIOD_OUT_LOW);
87962306a36Sopenharmony_ci	if (IS_ERR(cs35l56->base.reset_gpio)) {
88062306a36Sopenharmony_ci		ret = PTR_ERR(cs35l56->base.reset_gpio);
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_ci		/*
88362306a36Sopenharmony_ci		 * If RESET is shared the first amp to probe will grab the reset
88462306a36Sopenharmony_ci		 * line and reset all the amps
88562306a36Sopenharmony_ci		 */
88662306a36Sopenharmony_ci		if (ret != -EBUSY)
88762306a36Sopenharmony_ci			return dev_err_probe(cs35l56->base.dev, ret, "Failed to get reset GPIO\n");
88862306a36Sopenharmony_ci
88962306a36Sopenharmony_ci		dev_info(cs35l56->base.dev, "Reset GPIO busy, assume shared reset\n");
89062306a36Sopenharmony_ci		cs35l56->base.reset_gpio = NULL;
89162306a36Sopenharmony_ci	}
89262306a36Sopenharmony_ci
89362306a36Sopenharmony_ci	return 0;
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_cierr:
89662306a36Sopenharmony_ci	if (ret != -ENODEV)
89762306a36Sopenharmony_ci		dev_err(cs35l56->base.dev, "Failed property %s: %d\n", property, ret);
89862306a36Sopenharmony_ci
89962306a36Sopenharmony_ci	return ret;
90062306a36Sopenharmony_ci}
90162306a36Sopenharmony_ci
90262306a36Sopenharmony_ciint cs35l56_hda_common_probe(struct cs35l56_hda *cs35l56, int id)
90362306a36Sopenharmony_ci{
90462306a36Sopenharmony_ci	int ret;
90562306a36Sopenharmony_ci
90662306a36Sopenharmony_ci	mutex_init(&cs35l56->base.irq_lock);
90762306a36Sopenharmony_ci	dev_set_drvdata(cs35l56->base.dev, cs35l56);
90862306a36Sopenharmony_ci
90962306a36Sopenharmony_ci	ret = cs35l56_hda_read_acpi(cs35l56, id);
91062306a36Sopenharmony_ci	if (ret)
91162306a36Sopenharmony_ci		goto err;
91262306a36Sopenharmony_ci
91362306a36Sopenharmony_ci	cs35l56->amp_name = devm_kasprintf(cs35l56->base.dev, GFP_KERNEL, "AMP%d",
91462306a36Sopenharmony_ci					   cs35l56->index + 1);
91562306a36Sopenharmony_ci	if (!cs35l56->amp_name) {
91662306a36Sopenharmony_ci		ret = -ENOMEM;
91762306a36Sopenharmony_ci		goto err;
91862306a36Sopenharmony_ci	}
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_ci	cs35l56_init_cs_dsp(&cs35l56->base, &cs35l56->cs_dsp);
92162306a36Sopenharmony_ci	cs35l56->cs_dsp.client_ops = &cs35l56_hda_client_ops;
92262306a36Sopenharmony_ci
92362306a36Sopenharmony_ci	if (cs35l56->base.reset_gpio) {
92462306a36Sopenharmony_ci		dev_dbg(cs35l56->base.dev, "Hard reset\n");
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_ci		/*
92762306a36Sopenharmony_ci		 * The GPIOD_OUT_LOW to *_gpiod_get_*() will be ignored if the
92862306a36Sopenharmony_ci		 * ACPI defines a different default state. So explicitly set low.
92962306a36Sopenharmony_ci		 */
93062306a36Sopenharmony_ci		gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 0);
93162306a36Sopenharmony_ci		cs35l56_wait_min_reset_pulse();
93262306a36Sopenharmony_ci		gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 1);
93362306a36Sopenharmony_ci	}
93462306a36Sopenharmony_ci
93562306a36Sopenharmony_ci	ret = cs35l56_hw_init(&cs35l56->base);
93662306a36Sopenharmony_ci	if (ret < 0)
93762306a36Sopenharmony_ci		goto err;
93862306a36Sopenharmony_ci
93962306a36Sopenharmony_ci	/* Reset the device and wait for it to boot */
94062306a36Sopenharmony_ci	cs35l56_system_reset(&cs35l56->base, false);
94162306a36Sopenharmony_ci	ret = cs35l56_wait_for_firmware_boot(&cs35l56->base);
94262306a36Sopenharmony_ci	if (ret)
94362306a36Sopenharmony_ci		goto err;
94462306a36Sopenharmony_ci
94562306a36Sopenharmony_ci	ret = cs35l56_set_patch(&cs35l56->base);
94662306a36Sopenharmony_ci	if (ret)
94762306a36Sopenharmony_ci		goto err;
94862306a36Sopenharmony_ci
94962306a36Sopenharmony_ci	regcache_mark_dirty(cs35l56->base.regmap);
95062306a36Sopenharmony_ci	regcache_sync(cs35l56->base.regmap);
95162306a36Sopenharmony_ci
95262306a36Sopenharmony_ci	/* Disable auto-hibernate so that runtime_pm has control */
95362306a36Sopenharmony_ci	ret = cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_PREVENT_AUTO_HIBERNATE);
95462306a36Sopenharmony_ci	if (ret)
95562306a36Sopenharmony_ci		goto err;
95662306a36Sopenharmony_ci
95762306a36Sopenharmony_ci	ret = cs_dsp_halo_init(&cs35l56->cs_dsp);
95862306a36Sopenharmony_ci	if (ret) {
95962306a36Sopenharmony_ci		dev_err_probe(cs35l56->base.dev, ret, "cs_dsp_halo_init failed\n");
96062306a36Sopenharmony_ci		goto err;
96162306a36Sopenharmony_ci	}
96262306a36Sopenharmony_ci
96362306a36Sopenharmony_ci	dev_dbg(cs35l56->base.dev, "DSP system name: '%s', amp name: '%s'\n",
96462306a36Sopenharmony_ci		cs35l56->system_name, cs35l56->amp_name);
96562306a36Sopenharmony_ci
96662306a36Sopenharmony_ci	regmap_multi_reg_write(cs35l56->base.regmap, cs35l56_hda_dai_config,
96762306a36Sopenharmony_ci			       ARRAY_SIZE(cs35l56_hda_dai_config));
96862306a36Sopenharmony_ci
96962306a36Sopenharmony_ci	/*
97062306a36Sopenharmony_ci	 * By default only enable one ASP1TXn, where n=amplifier index,
97162306a36Sopenharmony_ci	 * This prevents multiple amps trying to drive the same slot.
97262306a36Sopenharmony_ci	 */
97362306a36Sopenharmony_ci	cs35l56->asp_tx_mask = BIT(cs35l56->index);
97462306a36Sopenharmony_ci
97562306a36Sopenharmony_ci	pm_runtime_set_autosuspend_delay(cs35l56->base.dev, 3000);
97662306a36Sopenharmony_ci	pm_runtime_use_autosuspend(cs35l56->base.dev);
97762306a36Sopenharmony_ci	pm_runtime_set_active(cs35l56->base.dev);
97862306a36Sopenharmony_ci	pm_runtime_mark_last_busy(cs35l56->base.dev);
97962306a36Sopenharmony_ci	pm_runtime_enable(cs35l56->base.dev);
98062306a36Sopenharmony_ci
98162306a36Sopenharmony_ci	ret = component_add(cs35l56->base.dev, &cs35l56_hda_comp_ops);
98262306a36Sopenharmony_ci	if (ret) {
98362306a36Sopenharmony_ci		dev_err(cs35l56->base.dev, "Register component failed: %d\n", ret);
98462306a36Sopenharmony_ci		goto pm_err;
98562306a36Sopenharmony_ci	}
98662306a36Sopenharmony_ci
98762306a36Sopenharmony_ci	cs35l56->base.init_done = true;
98862306a36Sopenharmony_ci
98962306a36Sopenharmony_ci	return 0;
99062306a36Sopenharmony_ci
99162306a36Sopenharmony_cipm_err:
99262306a36Sopenharmony_ci	pm_runtime_disable(cs35l56->base.dev);
99362306a36Sopenharmony_cierr:
99462306a36Sopenharmony_ci	gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 0);
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_ci	return ret;
99762306a36Sopenharmony_ci}
99862306a36Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(cs35l56_hda_common_probe, SND_HDA_SCODEC_CS35L56);
99962306a36Sopenharmony_ci
100062306a36Sopenharmony_civoid cs35l56_hda_remove(struct device *dev)
100162306a36Sopenharmony_ci{
100262306a36Sopenharmony_ci	struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev);
100362306a36Sopenharmony_ci
100462306a36Sopenharmony_ci	pm_runtime_dont_use_autosuspend(cs35l56->base.dev);
100562306a36Sopenharmony_ci	pm_runtime_get_sync(cs35l56->base.dev);
100662306a36Sopenharmony_ci	pm_runtime_disable(cs35l56->base.dev);
100762306a36Sopenharmony_ci
100862306a36Sopenharmony_ci	component_del(cs35l56->base.dev, &cs35l56_hda_comp_ops);
100962306a36Sopenharmony_ci
101062306a36Sopenharmony_ci	kfree(cs35l56->system_name);
101162306a36Sopenharmony_ci	pm_runtime_put_noidle(cs35l56->base.dev);
101262306a36Sopenharmony_ci
101362306a36Sopenharmony_ci	gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 0);
101462306a36Sopenharmony_ci}
101562306a36Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(cs35l56_hda_remove, SND_HDA_SCODEC_CS35L56);
101662306a36Sopenharmony_ci
101762306a36Sopenharmony_ciconst struct dev_pm_ops cs35l56_hda_pm_ops = {
101862306a36Sopenharmony_ci	RUNTIME_PM_OPS(cs35l56_hda_runtime_suspend, cs35l56_hda_runtime_resume, NULL)
101962306a36Sopenharmony_ci	SYSTEM_SLEEP_PM_OPS(cs35l56_hda_system_suspend, cs35l56_hda_system_resume)
102062306a36Sopenharmony_ci	LATE_SYSTEM_SLEEP_PM_OPS(cs35l56_hda_system_suspend_late,
102162306a36Sopenharmony_ci				 cs35l56_hda_system_resume_early)
102262306a36Sopenharmony_ci	NOIRQ_SYSTEM_SLEEP_PM_OPS(cs35l56_hda_system_suspend_no_irq,
102362306a36Sopenharmony_ci				  cs35l56_hda_system_resume_no_irq)
102462306a36Sopenharmony_ci};
102562306a36Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(cs35l56_hda_pm_ops, SND_HDA_SCODEC_CS35L56);
102662306a36Sopenharmony_ci
102762306a36Sopenharmony_ciMODULE_DESCRIPTION("CS35L56 HDA Driver");
102862306a36Sopenharmony_ciMODULE_IMPORT_NS(SND_HDA_CS_DSP_CONTROLS);
102962306a36Sopenharmony_ciMODULE_IMPORT_NS(SND_SOC_CS35L56_SHARED);
103062306a36Sopenharmony_ciMODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
103162306a36Sopenharmony_ciMODULE_AUTHOR("Simon Trimmer <simont@opensource.cirrus.com>");
103262306a36Sopenharmony_ciMODULE_LICENSE("GPL");
103362306a36Sopenharmony_ciMODULE_IMPORT_NS(FW_CS_DSP);
1034