18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * HD audio interface patch for Cirrus Logic CS420x chip
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (c) 2009 Takashi Iwai <tiwai@suse.de>
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/init.h>
98c2ecf20Sopenharmony_ci#include <linux/slab.h>
108c2ecf20Sopenharmony_ci#include <linux/module.h>
118c2ecf20Sopenharmony_ci#include <sound/core.h>
128c2ecf20Sopenharmony_ci#include <sound/tlv.h>
138c2ecf20Sopenharmony_ci#include <sound/hda_codec.h>
148c2ecf20Sopenharmony_ci#include "hda_local.h"
158c2ecf20Sopenharmony_ci#include "hda_auto_parser.h"
168c2ecf20Sopenharmony_ci#include "hda_jack.h"
178c2ecf20Sopenharmony_ci#include "hda_generic.h"
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci/*
208c2ecf20Sopenharmony_ci */
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_cistruct cs_spec {
238c2ecf20Sopenharmony_ci	struct hda_gen_spec gen;
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci	unsigned int gpio_mask;
268c2ecf20Sopenharmony_ci	unsigned int gpio_dir;
278c2ecf20Sopenharmony_ci	unsigned int gpio_data;
288c2ecf20Sopenharmony_ci	unsigned int gpio_eapd_hp; /* EAPD GPIO bit for headphones */
298c2ecf20Sopenharmony_ci	unsigned int gpio_eapd_speaker; /* EAPD GPIO bit for speakers */
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci	/* CS421x */
328c2ecf20Sopenharmony_ci	unsigned int spdif_detect:1;
338c2ecf20Sopenharmony_ci	unsigned int spdif_present:1;
348c2ecf20Sopenharmony_ci	unsigned int sense_b:1;
358c2ecf20Sopenharmony_ci	hda_nid_t vendor_nid;
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci	/* for MBP SPDIF control */
388c2ecf20Sopenharmony_ci	int (*spdif_sw_put)(struct snd_kcontrol *kcontrol,
398c2ecf20Sopenharmony_ci			    struct snd_ctl_elem_value *ucontrol);
408c2ecf20Sopenharmony_ci};
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci/* available models with CS420x */
438c2ecf20Sopenharmony_cienum {
448c2ecf20Sopenharmony_ci	CS420X_MBP53,
458c2ecf20Sopenharmony_ci	CS420X_MBP55,
468c2ecf20Sopenharmony_ci	CS420X_IMAC27,
478c2ecf20Sopenharmony_ci	CS420X_GPIO_13,
488c2ecf20Sopenharmony_ci	CS420X_GPIO_23,
498c2ecf20Sopenharmony_ci	CS420X_MBP101,
508c2ecf20Sopenharmony_ci	CS420X_MBP81,
518c2ecf20Sopenharmony_ci	CS420X_MBA42,
528c2ecf20Sopenharmony_ci	CS420X_AUTO,
538c2ecf20Sopenharmony_ci	/* aliases */
548c2ecf20Sopenharmony_ci	CS420X_IMAC27_122 = CS420X_GPIO_23,
558c2ecf20Sopenharmony_ci	CS420X_APPLE = CS420X_GPIO_13,
568c2ecf20Sopenharmony_ci};
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci/* CS421x boards */
598c2ecf20Sopenharmony_cienum {
608c2ecf20Sopenharmony_ci	CS421X_CDB4210,
618c2ecf20Sopenharmony_ci	CS421X_SENSE_B,
628c2ecf20Sopenharmony_ci	CS421X_STUMPY,
638c2ecf20Sopenharmony_ci};
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci/* Vendor-specific processing widget */
668c2ecf20Sopenharmony_ci#define CS420X_VENDOR_NID	0x11
678c2ecf20Sopenharmony_ci#define CS_DIG_OUT1_PIN_NID	0x10
688c2ecf20Sopenharmony_ci#define CS_DIG_OUT2_PIN_NID	0x15
698c2ecf20Sopenharmony_ci#define CS_DMIC1_PIN_NID	0x0e
708c2ecf20Sopenharmony_ci#define CS_DMIC2_PIN_NID	0x12
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci/* coef indices */
738c2ecf20Sopenharmony_ci#define IDX_SPDIF_STAT		0x0000
748c2ecf20Sopenharmony_ci#define IDX_SPDIF_CTL		0x0001
758c2ecf20Sopenharmony_ci#define IDX_ADC_CFG		0x0002
768c2ecf20Sopenharmony_ci/* SZC bitmask, 4 modes below:
778c2ecf20Sopenharmony_ci * 0 = immediate,
788c2ecf20Sopenharmony_ci * 1 = digital immediate, analog zero-cross
798c2ecf20Sopenharmony_ci * 2 = digtail & analog soft-ramp
808c2ecf20Sopenharmony_ci * 3 = digital soft-ramp, analog zero-cross
818c2ecf20Sopenharmony_ci */
828c2ecf20Sopenharmony_ci#define   CS_COEF_ADC_SZC_MASK		(3 << 0)
838c2ecf20Sopenharmony_ci#define   CS_COEF_ADC_MIC_SZC_MODE	(3 << 0) /* SZC setup for mic */
848c2ecf20Sopenharmony_ci#define   CS_COEF_ADC_LI_SZC_MODE	(3 << 0) /* SZC setup for line-in */
858c2ecf20Sopenharmony_ci/* PGA mode: 0 = differential, 1 = signle-ended */
868c2ecf20Sopenharmony_ci#define   CS_COEF_ADC_MIC_PGA_MODE	(1 << 5) /* PGA setup for mic */
878c2ecf20Sopenharmony_ci#define   CS_COEF_ADC_LI_PGA_MODE	(1 << 6) /* PGA setup for line-in */
888c2ecf20Sopenharmony_ci#define IDX_DAC_CFG		0x0003
898c2ecf20Sopenharmony_ci/* SZC bitmask, 4 modes below:
908c2ecf20Sopenharmony_ci * 0 = Immediate
918c2ecf20Sopenharmony_ci * 1 = zero-cross
928c2ecf20Sopenharmony_ci * 2 = soft-ramp
938c2ecf20Sopenharmony_ci * 3 = soft-ramp on zero-cross
948c2ecf20Sopenharmony_ci */
958c2ecf20Sopenharmony_ci#define   CS_COEF_DAC_HP_SZC_MODE	(3 << 0) /* nid 0x02 */
968c2ecf20Sopenharmony_ci#define   CS_COEF_DAC_LO_SZC_MODE	(3 << 2) /* nid 0x03 */
978c2ecf20Sopenharmony_ci#define   CS_COEF_DAC_SPK_SZC_MODE	(3 << 4) /* nid 0x04 */
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci#define IDX_BEEP_CFG		0x0004
1008c2ecf20Sopenharmony_ci/* 0x0008 - test reg key */
1018c2ecf20Sopenharmony_ci/* 0x0009 - 0x0014 -> 12 test regs */
1028c2ecf20Sopenharmony_ci/* 0x0015 - visibility reg */
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci/* Cirrus Logic CS4208 */
1058c2ecf20Sopenharmony_ci#define CS4208_VENDOR_NID	0x24
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci/*
1088c2ecf20Sopenharmony_ci * Cirrus Logic CS4210
1098c2ecf20Sopenharmony_ci *
1108c2ecf20Sopenharmony_ci * 1 DAC => HP(sense) / Speakers,
1118c2ecf20Sopenharmony_ci * 1 ADC <= LineIn(sense) / MicIn / DMicIn,
1128c2ecf20Sopenharmony_ci * 1 SPDIF OUT => SPDIF Trasmitter(sense)
1138c2ecf20Sopenharmony_ci*/
1148c2ecf20Sopenharmony_ci#define CS4210_DAC_NID		0x02
1158c2ecf20Sopenharmony_ci#define CS4210_ADC_NID		0x03
1168c2ecf20Sopenharmony_ci#define CS4210_VENDOR_NID	0x0B
1178c2ecf20Sopenharmony_ci#define CS421X_DMIC_PIN_NID	0x09 /* Port E */
1188c2ecf20Sopenharmony_ci#define CS421X_SPDIF_PIN_NID	0x0A /* Port H */
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci#define CS421X_IDX_DEV_CFG	0x01
1218c2ecf20Sopenharmony_ci#define CS421X_IDX_ADC_CFG	0x02
1228c2ecf20Sopenharmony_ci#define CS421X_IDX_DAC_CFG	0x03
1238c2ecf20Sopenharmony_ci#define CS421X_IDX_SPK_CTL	0x04
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci/* Cirrus Logic CS4213 is like CS4210 but does not have SPDIF input/output */
1268c2ecf20Sopenharmony_ci#define CS4213_VENDOR_NID	0x09
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_cistatic inline int cs_vendor_coef_get(struct hda_codec *codec, unsigned int idx)
1308c2ecf20Sopenharmony_ci{
1318c2ecf20Sopenharmony_ci	struct cs_spec *spec = codec->spec;
1328c2ecf20Sopenharmony_ci	snd_hda_codec_write(codec, spec->vendor_nid, 0,
1338c2ecf20Sopenharmony_ci			    AC_VERB_SET_COEF_INDEX, idx);
1348c2ecf20Sopenharmony_ci	return snd_hda_codec_read(codec, spec->vendor_nid, 0,
1358c2ecf20Sopenharmony_ci				  AC_VERB_GET_PROC_COEF, 0);
1368c2ecf20Sopenharmony_ci}
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_cistatic inline void cs_vendor_coef_set(struct hda_codec *codec, unsigned int idx,
1398c2ecf20Sopenharmony_ci				      unsigned int coef)
1408c2ecf20Sopenharmony_ci{
1418c2ecf20Sopenharmony_ci	struct cs_spec *spec = codec->spec;
1428c2ecf20Sopenharmony_ci	snd_hda_codec_write(codec, spec->vendor_nid, 0,
1438c2ecf20Sopenharmony_ci			    AC_VERB_SET_COEF_INDEX, idx);
1448c2ecf20Sopenharmony_ci	snd_hda_codec_write(codec, spec->vendor_nid, 0,
1458c2ecf20Sopenharmony_ci			    AC_VERB_SET_PROC_COEF, coef);
1468c2ecf20Sopenharmony_ci}
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci/*
1498c2ecf20Sopenharmony_ci * auto-mute and auto-mic switching
1508c2ecf20Sopenharmony_ci * CS421x auto-output redirecting
1518c2ecf20Sopenharmony_ci * HP/SPK/SPDIF
1528c2ecf20Sopenharmony_ci */
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_cistatic void cs_automute(struct hda_codec *codec)
1558c2ecf20Sopenharmony_ci{
1568c2ecf20Sopenharmony_ci	struct cs_spec *spec = codec->spec;
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	/* mute HPs if spdif jack (SENSE_B) is present */
1598c2ecf20Sopenharmony_ci	spec->gen.master_mute = !!(spec->spdif_present && spec->sense_b);
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	snd_hda_gen_update_outputs(codec);
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	if (spec->gpio_eapd_hp || spec->gpio_eapd_speaker) {
1648c2ecf20Sopenharmony_ci		if (spec->gen.automute_speaker)
1658c2ecf20Sopenharmony_ci			spec->gpio_data = spec->gen.hp_jack_present ?
1668c2ecf20Sopenharmony_ci				spec->gpio_eapd_hp : spec->gpio_eapd_speaker;
1678c2ecf20Sopenharmony_ci		else
1688c2ecf20Sopenharmony_ci			spec->gpio_data =
1698c2ecf20Sopenharmony_ci				spec->gpio_eapd_hp | spec->gpio_eapd_speaker;
1708c2ecf20Sopenharmony_ci		snd_hda_codec_write(codec, 0x01, 0,
1718c2ecf20Sopenharmony_ci				    AC_VERB_SET_GPIO_DATA, spec->gpio_data);
1728c2ecf20Sopenharmony_ci	}
1738c2ecf20Sopenharmony_ci}
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_cistatic bool is_active_pin(struct hda_codec *codec, hda_nid_t nid)
1768c2ecf20Sopenharmony_ci{
1778c2ecf20Sopenharmony_ci	unsigned int val;
1788c2ecf20Sopenharmony_ci	val = snd_hda_codec_get_pincfg(codec, nid);
1798c2ecf20Sopenharmony_ci	return (get_defcfg_connect(val) != AC_JACK_PORT_NONE);
1808c2ecf20Sopenharmony_ci}
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_cistatic void init_input_coef(struct hda_codec *codec)
1838c2ecf20Sopenharmony_ci{
1848c2ecf20Sopenharmony_ci	struct cs_spec *spec = codec->spec;
1858c2ecf20Sopenharmony_ci	unsigned int coef;
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	/* CS420x has multiple ADC, CS421x has single ADC */
1888c2ecf20Sopenharmony_ci	if (spec->vendor_nid == CS420X_VENDOR_NID) {
1898c2ecf20Sopenharmony_ci		coef = cs_vendor_coef_get(codec, IDX_BEEP_CFG);
1908c2ecf20Sopenharmony_ci		if (is_active_pin(codec, CS_DMIC2_PIN_NID))
1918c2ecf20Sopenharmony_ci			coef |= 1 << 4; /* DMIC2 2 chan on, GPIO1 off */
1928c2ecf20Sopenharmony_ci		if (is_active_pin(codec, CS_DMIC1_PIN_NID))
1938c2ecf20Sopenharmony_ci			coef |= 1 << 3; /* DMIC1 2 chan on, GPIO0 off
1948c2ecf20Sopenharmony_ci					 * No effect if SPDIF_OUT2 is
1958c2ecf20Sopenharmony_ci					 * selected in IDX_SPDIF_CTL.
1968c2ecf20Sopenharmony_ci					*/
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci		cs_vendor_coef_set(codec, IDX_BEEP_CFG, coef);
1998c2ecf20Sopenharmony_ci	}
2008c2ecf20Sopenharmony_ci}
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_cistatic const struct hda_verb cs_coef_init_verbs[] = {
2038c2ecf20Sopenharmony_ci	{0x11, AC_VERB_SET_PROC_STATE, 1},
2048c2ecf20Sopenharmony_ci	{0x11, AC_VERB_SET_COEF_INDEX, IDX_DAC_CFG},
2058c2ecf20Sopenharmony_ci	{0x11, AC_VERB_SET_PROC_COEF,
2068c2ecf20Sopenharmony_ci	 (0x002a /* DAC1/2/3 SZCMode Soft Ramp */
2078c2ecf20Sopenharmony_ci	  | 0x0040 /* Mute DACs on FIFO error */
2088c2ecf20Sopenharmony_ci	  | 0x1000 /* Enable DACs High Pass Filter */
2098c2ecf20Sopenharmony_ci	  | 0x0400 /* Disable Coefficient Auto increment */
2108c2ecf20Sopenharmony_ci	  )},
2118c2ecf20Sopenharmony_ci	/* ADC1/2 - Digital and Analog Soft Ramp */
2128c2ecf20Sopenharmony_ci	{0x11, AC_VERB_SET_COEF_INDEX, IDX_ADC_CFG},
2138c2ecf20Sopenharmony_ci	{0x11, AC_VERB_SET_PROC_COEF, 0x000a},
2148c2ecf20Sopenharmony_ci	/* Beep */
2158c2ecf20Sopenharmony_ci	{0x11, AC_VERB_SET_COEF_INDEX, IDX_BEEP_CFG},
2168c2ecf20Sopenharmony_ci	{0x11, AC_VERB_SET_PROC_COEF, 0x0007}, /* Enable Beep thru DAC1/2/3 */
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	{} /* terminator */
2198c2ecf20Sopenharmony_ci};
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_cistatic const struct hda_verb cs4208_coef_init_verbs[] = {
2228c2ecf20Sopenharmony_ci	{0x01, AC_VERB_SET_POWER_STATE, 0x00}, /* AFG: D0 */
2238c2ecf20Sopenharmony_ci	{0x24, AC_VERB_SET_PROC_STATE, 0x01},  /* VPW: processing on */
2248c2ecf20Sopenharmony_ci	{0x24, AC_VERB_SET_COEF_INDEX, 0x0033},
2258c2ecf20Sopenharmony_ci	{0x24, AC_VERB_SET_PROC_COEF, 0x0001}, /* A1 ICS */
2268c2ecf20Sopenharmony_ci	{0x24, AC_VERB_SET_COEF_INDEX, 0x0034},
2278c2ecf20Sopenharmony_ci	{0x24, AC_VERB_SET_PROC_COEF, 0x1C01}, /* A1 Enable, A Thresh = 300mV */
2288c2ecf20Sopenharmony_ci	{} /* terminator */
2298c2ecf20Sopenharmony_ci};
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci/* Errata: CS4207 rev C0/C1/C2 Silicon
2328c2ecf20Sopenharmony_ci *
2338c2ecf20Sopenharmony_ci * http://www.cirrus.com/en/pubs/errata/ER880C3.pdf
2348c2ecf20Sopenharmony_ci *
2358c2ecf20Sopenharmony_ci * 6. At high temperature (TA > +85°C), the digital supply current (IVD)
2368c2ecf20Sopenharmony_ci * may be excessive (up to an additional 200 μA), which is most easily
2378c2ecf20Sopenharmony_ci * observed while the part is being held in reset (RESET# active low).
2388c2ecf20Sopenharmony_ci *
2398c2ecf20Sopenharmony_ci * Root Cause: At initial powerup of the device, the logic that drives
2408c2ecf20Sopenharmony_ci * the clock and write enable to the S/PDIF SRC RAMs is not properly
2418c2ecf20Sopenharmony_ci * initialized.
2428c2ecf20Sopenharmony_ci * Certain random patterns will cause a steady leakage current in those
2438c2ecf20Sopenharmony_ci * RAM cells. The issue will resolve once the SRCs are used (turned on).
2448c2ecf20Sopenharmony_ci *
2458c2ecf20Sopenharmony_ci * Workaround: The following verb sequence briefly turns on the S/PDIF SRC
2468c2ecf20Sopenharmony_ci * blocks, which will alleviate the issue.
2478c2ecf20Sopenharmony_ci */
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_cistatic const struct hda_verb cs_errata_init_verbs[] = {
2508c2ecf20Sopenharmony_ci	{0x01, AC_VERB_SET_POWER_STATE, 0x00}, /* AFG: D0 */
2518c2ecf20Sopenharmony_ci	{0x11, AC_VERB_SET_PROC_STATE, 0x01},  /* VPW: processing on */
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	{0x11, AC_VERB_SET_COEF_INDEX, 0x0008},
2548c2ecf20Sopenharmony_ci	{0x11, AC_VERB_SET_PROC_COEF, 0x9999},
2558c2ecf20Sopenharmony_ci	{0x11, AC_VERB_SET_COEF_INDEX, 0x0017},
2568c2ecf20Sopenharmony_ci	{0x11, AC_VERB_SET_PROC_COEF, 0xa412},
2578c2ecf20Sopenharmony_ci	{0x11, AC_VERB_SET_COEF_INDEX, 0x0001},
2588c2ecf20Sopenharmony_ci	{0x11, AC_VERB_SET_PROC_COEF, 0x0009},
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	{0x07, AC_VERB_SET_POWER_STATE, 0x00}, /* S/PDIF Rx: D0 */
2618c2ecf20Sopenharmony_ci	{0x08, AC_VERB_SET_POWER_STATE, 0x00}, /* S/PDIF Tx: D0 */
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	{0x11, AC_VERB_SET_COEF_INDEX, 0x0017},
2648c2ecf20Sopenharmony_ci	{0x11, AC_VERB_SET_PROC_COEF, 0x2412},
2658c2ecf20Sopenharmony_ci	{0x11, AC_VERB_SET_COEF_INDEX, 0x0008},
2668c2ecf20Sopenharmony_ci	{0x11, AC_VERB_SET_PROC_COEF, 0x0000},
2678c2ecf20Sopenharmony_ci	{0x11, AC_VERB_SET_COEF_INDEX, 0x0001},
2688c2ecf20Sopenharmony_ci	{0x11, AC_VERB_SET_PROC_COEF, 0x0008},
2698c2ecf20Sopenharmony_ci	{0x11, AC_VERB_SET_PROC_STATE, 0x00},
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci#if 0 /* Don't to set to D3 as we are in power-up sequence */
2728c2ecf20Sopenharmony_ci	{0x07, AC_VERB_SET_POWER_STATE, 0x03}, /* S/PDIF Rx: D3 */
2738c2ecf20Sopenharmony_ci	{0x08, AC_VERB_SET_POWER_STATE, 0x03}, /* S/PDIF Tx: D3 */
2748c2ecf20Sopenharmony_ci	/*{0x01, AC_VERB_SET_POWER_STATE, 0x03},*/ /* AFG: D3 This is already handled */
2758c2ecf20Sopenharmony_ci#endif
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	{} /* terminator */
2788c2ecf20Sopenharmony_ci};
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci/* SPDIF setup */
2818c2ecf20Sopenharmony_cistatic void init_digital_coef(struct hda_codec *codec)
2828c2ecf20Sopenharmony_ci{
2838c2ecf20Sopenharmony_ci	unsigned int coef;
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	coef = 0x0002; /* SRC_MUTE soft-mute on SPDIF (if no lock) */
2868c2ecf20Sopenharmony_ci	coef |= 0x0008; /* Replace with mute on error */
2878c2ecf20Sopenharmony_ci	if (is_active_pin(codec, CS_DIG_OUT2_PIN_NID))
2888c2ecf20Sopenharmony_ci		coef |= 0x4000; /* RX to TX1 or TX2 Loopthru / SPDIF2
2898c2ecf20Sopenharmony_ci				 * SPDIF_OUT2 is shared with GPIO1 and
2908c2ecf20Sopenharmony_ci				 * DMIC_SDA2.
2918c2ecf20Sopenharmony_ci				 */
2928c2ecf20Sopenharmony_ci	cs_vendor_coef_set(codec, IDX_SPDIF_CTL, coef);
2938c2ecf20Sopenharmony_ci}
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_cistatic int cs_init(struct hda_codec *codec)
2968c2ecf20Sopenharmony_ci{
2978c2ecf20Sopenharmony_ci	struct cs_spec *spec = codec->spec;
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	if (spec->vendor_nid == CS420X_VENDOR_NID) {
3008c2ecf20Sopenharmony_ci		/* init_verb sequence for C0/C1/C2 errata*/
3018c2ecf20Sopenharmony_ci		snd_hda_sequence_write(codec, cs_errata_init_verbs);
3028c2ecf20Sopenharmony_ci		snd_hda_sequence_write(codec, cs_coef_init_verbs);
3038c2ecf20Sopenharmony_ci	} else if (spec->vendor_nid == CS4208_VENDOR_NID) {
3048c2ecf20Sopenharmony_ci		snd_hda_sequence_write(codec, cs4208_coef_init_verbs);
3058c2ecf20Sopenharmony_ci	}
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	snd_hda_gen_init(codec);
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	if (spec->gpio_mask) {
3108c2ecf20Sopenharmony_ci		snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_MASK,
3118c2ecf20Sopenharmony_ci				    spec->gpio_mask);
3128c2ecf20Sopenharmony_ci		snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DIRECTION,
3138c2ecf20Sopenharmony_ci				    spec->gpio_dir);
3148c2ecf20Sopenharmony_ci		snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA,
3158c2ecf20Sopenharmony_ci				    spec->gpio_data);
3168c2ecf20Sopenharmony_ci	}
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	if (spec->vendor_nid == CS420X_VENDOR_NID) {
3198c2ecf20Sopenharmony_ci		init_input_coef(codec);
3208c2ecf20Sopenharmony_ci		init_digital_coef(codec);
3218c2ecf20Sopenharmony_ci	}
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci	return 0;
3248c2ecf20Sopenharmony_ci}
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_cistatic int cs_build_controls(struct hda_codec *codec)
3278c2ecf20Sopenharmony_ci{
3288c2ecf20Sopenharmony_ci	int err;
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	err = snd_hda_gen_build_controls(codec);
3318c2ecf20Sopenharmony_ci	if (err < 0)
3328c2ecf20Sopenharmony_ci		return err;
3338c2ecf20Sopenharmony_ci	snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_BUILD);
3348c2ecf20Sopenharmony_ci	return 0;
3358c2ecf20Sopenharmony_ci}
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci#define cs_free		snd_hda_gen_free
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_cistatic const struct hda_codec_ops cs_patch_ops = {
3408c2ecf20Sopenharmony_ci	.build_controls = cs_build_controls,
3418c2ecf20Sopenharmony_ci	.build_pcms = snd_hda_gen_build_pcms,
3428c2ecf20Sopenharmony_ci	.init = cs_init,
3438c2ecf20Sopenharmony_ci	.free = cs_free,
3448c2ecf20Sopenharmony_ci	.unsol_event = snd_hda_jack_unsol_event,
3458c2ecf20Sopenharmony_ci};
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_cistatic int cs_parse_auto_config(struct hda_codec *codec)
3488c2ecf20Sopenharmony_ci{
3498c2ecf20Sopenharmony_ci	struct cs_spec *spec = codec->spec;
3508c2ecf20Sopenharmony_ci	int err;
3518c2ecf20Sopenharmony_ci	int i;
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci	err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, 0);
3548c2ecf20Sopenharmony_ci	if (err < 0)
3558c2ecf20Sopenharmony_ci		return err;
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg);
3588c2ecf20Sopenharmony_ci	if (err < 0)
3598c2ecf20Sopenharmony_ci		return err;
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci	/* keep the ADCs powered up when it's dynamically switchable */
3628c2ecf20Sopenharmony_ci	if (spec->gen.dyn_adc_switch) {
3638c2ecf20Sopenharmony_ci		unsigned int done = 0;
3648c2ecf20Sopenharmony_ci		for (i = 0; i < spec->gen.input_mux.num_items; i++) {
3658c2ecf20Sopenharmony_ci			int idx = spec->gen.dyn_adc_idx[i];
3668c2ecf20Sopenharmony_ci			if (done & (1 << idx))
3678c2ecf20Sopenharmony_ci				continue;
3688c2ecf20Sopenharmony_ci			snd_hda_gen_fix_pin_power(codec,
3698c2ecf20Sopenharmony_ci						  spec->gen.adc_nids[idx]);
3708c2ecf20Sopenharmony_ci			done |= 1 << idx;
3718c2ecf20Sopenharmony_ci		}
3728c2ecf20Sopenharmony_ci	}
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci	return 0;
3758c2ecf20Sopenharmony_ci}
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_cistatic const struct hda_model_fixup cs420x_models[] = {
3788c2ecf20Sopenharmony_ci	{ .id = CS420X_MBP53, .name = "mbp53" },
3798c2ecf20Sopenharmony_ci	{ .id = CS420X_MBP55, .name = "mbp55" },
3808c2ecf20Sopenharmony_ci	{ .id = CS420X_IMAC27, .name = "imac27" },
3818c2ecf20Sopenharmony_ci	{ .id = CS420X_IMAC27_122, .name = "imac27_122" },
3828c2ecf20Sopenharmony_ci	{ .id = CS420X_APPLE, .name = "apple" },
3838c2ecf20Sopenharmony_ci	{ .id = CS420X_MBP101, .name = "mbp101" },
3848c2ecf20Sopenharmony_ci	{ .id = CS420X_MBP81, .name = "mbp81" },
3858c2ecf20Sopenharmony_ci	{ .id = CS420X_MBA42, .name = "mba42" },
3868c2ecf20Sopenharmony_ci	{}
3878c2ecf20Sopenharmony_ci};
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_cistatic const struct snd_pci_quirk cs420x_fixup_tbl[] = {
3908c2ecf20Sopenharmony_ci	SND_PCI_QUIRK(0x10de, 0x0ac0, "MacBookPro 5,3", CS420X_MBP53),
3918c2ecf20Sopenharmony_ci	SND_PCI_QUIRK(0x10de, 0x0d94, "MacBookAir 3,1(2)", CS420X_MBP55),
3928c2ecf20Sopenharmony_ci	SND_PCI_QUIRK(0x10de, 0xcb79, "MacBookPro 5,5", CS420X_MBP55),
3938c2ecf20Sopenharmony_ci	SND_PCI_QUIRK(0x10de, 0xcb89, "MacBookPro 7,1", CS420X_MBP55),
3948c2ecf20Sopenharmony_ci	/* this conflicts with too many other models */
3958c2ecf20Sopenharmony_ci	/*SND_PCI_QUIRK(0x8086, 0x7270, "IMac 27 Inch", CS420X_IMAC27),*/
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci	/* codec SSID */
3988c2ecf20Sopenharmony_ci	SND_PCI_QUIRK(0x106b, 0x0600, "iMac 14,1", CS420X_IMAC27_122),
3998c2ecf20Sopenharmony_ci	SND_PCI_QUIRK(0x106b, 0x0900, "iMac 12,1", CS420X_IMAC27_122),
4008c2ecf20Sopenharmony_ci	SND_PCI_QUIRK(0x106b, 0x1c00, "MacBookPro 8,1", CS420X_MBP81),
4018c2ecf20Sopenharmony_ci	SND_PCI_QUIRK(0x106b, 0x2000, "iMac 12,2", CS420X_IMAC27_122),
4028c2ecf20Sopenharmony_ci	SND_PCI_QUIRK(0x106b, 0x2800, "MacBookPro 10,1", CS420X_MBP101),
4038c2ecf20Sopenharmony_ci	SND_PCI_QUIRK(0x106b, 0x5600, "MacBookAir 5,2", CS420X_MBP81),
4048c2ecf20Sopenharmony_ci	SND_PCI_QUIRK(0x106b, 0x5b00, "MacBookAir 4,2", CS420X_MBA42),
4058c2ecf20Sopenharmony_ci	SND_PCI_QUIRK_VENDOR(0x106b, "Apple", CS420X_APPLE),
4068c2ecf20Sopenharmony_ci	{} /* terminator */
4078c2ecf20Sopenharmony_ci};
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_cistatic const struct hda_pintbl mbp53_pincfgs[] = {
4108c2ecf20Sopenharmony_ci	{ 0x09, 0x012b4050 },
4118c2ecf20Sopenharmony_ci	{ 0x0a, 0x90100141 },
4128c2ecf20Sopenharmony_ci	{ 0x0b, 0x90100140 },
4138c2ecf20Sopenharmony_ci	{ 0x0c, 0x018b3020 },
4148c2ecf20Sopenharmony_ci	{ 0x0d, 0x90a00110 },
4158c2ecf20Sopenharmony_ci	{ 0x0e, 0x400000f0 },
4168c2ecf20Sopenharmony_ci	{ 0x0f, 0x01cbe030 },
4178c2ecf20Sopenharmony_ci	{ 0x10, 0x014be060 },
4188c2ecf20Sopenharmony_ci	{ 0x12, 0x400000f0 },
4198c2ecf20Sopenharmony_ci	{ 0x15, 0x400000f0 },
4208c2ecf20Sopenharmony_ci	{} /* terminator */
4218c2ecf20Sopenharmony_ci};
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_cistatic const struct hda_pintbl mbp55_pincfgs[] = {
4248c2ecf20Sopenharmony_ci	{ 0x09, 0x012b4030 },
4258c2ecf20Sopenharmony_ci	{ 0x0a, 0x90100121 },
4268c2ecf20Sopenharmony_ci	{ 0x0b, 0x90100120 },
4278c2ecf20Sopenharmony_ci	{ 0x0c, 0x400000f0 },
4288c2ecf20Sopenharmony_ci	{ 0x0d, 0x90a00110 },
4298c2ecf20Sopenharmony_ci	{ 0x0e, 0x400000f0 },
4308c2ecf20Sopenharmony_ci	{ 0x0f, 0x400000f0 },
4318c2ecf20Sopenharmony_ci	{ 0x10, 0x014be040 },
4328c2ecf20Sopenharmony_ci	{ 0x12, 0x400000f0 },
4338c2ecf20Sopenharmony_ci	{ 0x15, 0x400000f0 },
4348c2ecf20Sopenharmony_ci	{} /* terminator */
4358c2ecf20Sopenharmony_ci};
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_cistatic const struct hda_pintbl imac27_pincfgs[] = {
4388c2ecf20Sopenharmony_ci	{ 0x09, 0x012b4050 },
4398c2ecf20Sopenharmony_ci	{ 0x0a, 0x90100140 },
4408c2ecf20Sopenharmony_ci	{ 0x0b, 0x90100142 },
4418c2ecf20Sopenharmony_ci	{ 0x0c, 0x018b3020 },
4428c2ecf20Sopenharmony_ci	{ 0x0d, 0x90a00110 },
4438c2ecf20Sopenharmony_ci	{ 0x0e, 0x400000f0 },
4448c2ecf20Sopenharmony_ci	{ 0x0f, 0x01cbe030 },
4458c2ecf20Sopenharmony_ci	{ 0x10, 0x014be060 },
4468c2ecf20Sopenharmony_ci	{ 0x12, 0x01ab9070 },
4478c2ecf20Sopenharmony_ci	{ 0x15, 0x400000f0 },
4488c2ecf20Sopenharmony_ci	{} /* terminator */
4498c2ecf20Sopenharmony_ci};
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_cistatic const struct hda_pintbl mbp101_pincfgs[] = {
4528c2ecf20Sopenharmony_ci	{ 0x0d, 0x40ab90f0 },
4538c2ecf20Sopenharmony_ci	{ 0x0e, 0x90a600f0 },
4548c2ecf20Sopenharmony_ci	{ 0x12, 0x50a600f0 },
4558c2ecf20Sopenharmony_ci	{} /* terminator */
4568c2ecf20Sopenharmony_ci};
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_cistatic const struct hda_pintbl mba42_pincfgs[] = {
4598c2ecf20Sopenharmony_ci	{ 0x09, 0x012b4030 }, /* HP */
4608c2ecf20Sopenharmony_ci	{ 0x0a, 0x400000f0 },
4618c2ecf20Sopenharmony_ci	{ 0x0b, 0x90100120 }, /* speaker */
4628c2ecf20Sopenharmony_ci	{ 0x0c, 0x400000f0 },
4638c2ecf20Sopenharmony_ci	{ 0x0d, 0x90a00110 }, /* mic */
4648c2ecf20Sopenharmony_ci	{ 0x0e, 0x400000f0 },
4658c2ecf20Sopenharmony_ci	{ 0x0f, 0x400000f0 },
4668c2ecf20Sopenharmony_ci	{ 0x10, 0x400000f0 },
4678c2ecf20Sopenharmony_ci	{ 0x12, 0x400000f0 },
4688c2ecf20Sopenharmony_ci	{ 0x15, 0x400000f0 },
4698c2ecf20Sopenharmony_ci	{} /* terminator */
4708c2ecf20Sopenharmony_ci};
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_cistatic const struct hda_pintbl mba6_pincfgs[] = {
4738c2ecf20Sopenharmony_ci	{ 0x10, 0x032120f0 }, /* HP */
4748c2ecf20Sopenharmony_ci	{ 0x11, 0x500000f0 },
4758c2ecf20Sopenharmony_ci	{ 0x12, 0x90100010 }, /* Speaker */
4768c2ecf20Sopenharmony_ci	{ 0x13, 0x500000f0 },
4778c2ecf20Sopenharmony_ci	{ 0x14, 0x500000f0 },
4788c2ecf20Sopenharmony_ci	{ 0x15, 0x770000f0 },
4798c2ecf20Sopenharmony_ci	{ 0x16, 0x770000f0 },
4808c2ecf20Sopenharmony_ci	{ 0x17, 0x430000f0 },
4818c2ecf20Sopenharmony_ci	{ 0x18, 0x43ab9030 }, /* Mic */
4828c2ecf20Sopenharmony_ci	{ 0x19, 0x770000f0 },
4838c2ecf20Sopenharmony_ci	{ 0x1a, 0x770000f0 },
4848c2ecf20Sopenharmony_ci	{ 0x1b, 0x770000f0 },
4858c2ecf20Sopenharmony_ci	{ 0x1c, 0x90a00090 },
4868c2ecf20Sopenharmony_ci	{ 0x1d, 0x500000f0 },
4878c2ecf20Sopenharmony_ci	{ 0x1e, 0x500000f0 },
4888c2ecf20Sopenharmony_ci	{ 0x1f, 0x500000f0 },
4898c2ecf20Sopenharmony_ci	{ 0x20, 0x500000f0 },
4908c2ecf20Sopenharmony_ci	{ 0x21, 0x430000f0 },
4918c2ecf20Sopenharmony_ci	{ 0x22, 0x430000f0 },
4928c2ecf20Sopenharmony_ci	{} /* terminator */
4938c2ecf20Sopenharmony_ci};
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_cistatic void cs420x_fixup_gpio_13(struct hda_codec *codec,
4968c2ecf20Sopenharmony_ci				 const struct hda_fixup *fix, int action)
4978c2ecf20Sopenharmony_ci{
4988c2ecf20Sopenharmony_ci	if (action == HDA_FIXUP_ACT_PRE_PROBE) {
4998c2ecf20Sopenharmony_ci		struct cs_spec *spec = codec->spec;
5008c2ecf20Sopenharmony_ci		spec->gpio_eapd_hp = 2; /* GPIO1 = headphones */
5018c2ecf20Sopenharmony_ci		spec->gpio_eapd_speaker = 8; /* GPIO3 = speakers */
5028c2ecf20Sopenharmony_ci		spec->gpio_mask = spec->gpio_dir =
5038c2ecf20Sopenharmony_ci			spec->gpio_eapd_hp | spec->gpio_eapd_speaker;
5048c2ecf20Sopenharmony_ci	}
5058c2ecf20Sopenharmony_ci}
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_cistatic void cs420x_fixup_gpio_23(struct hda_codec *codec,
5088c2ecf20Sopenharmony_ci				 const struct hda_fixup *fix, int action)
5098c2ecf20Sopenharmony_ci{
5108c2ecf20Sopenharmony_ci	if (action == HDA_FIXUP_ACT_PRE_PROBE) {
5118c2ecf20Sopenharmony_ci		struct cs_spec *spec = codec->spec;
5128c2ecf20Sopenharmony_ci		spec->gpio_eapd_hp = 4; /* GPIO2 = headphones */
5138c2ecf20Sopenharmony_ci		spec->gpio_eapd_speaker = 8; /* GPIO3 = speakers */
5148c2ecf20Sopenharmony_ci		spec->gpio_mask = spec->gpio_dir =
5158c2ecf20Sopenharmony_ci			spec->gpio_eapd_hp | spec->gpio_eapd_speaker;
5168c2ecf20Sopenharmony_ci	}
5178c2ecf20Sopenharmony_ci}
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_cistatic const struct hda_fixup cs420x_fixups[] = {
5208c2ecf20Sopenharmony_ci	[CS420X_MBP53] = {
5218c2ecf20Sopenharmony_ci		.type = HDA_FIXUP_PINS,
5228c2ecf20Sopenharmony_ci		.v.pins = mbp53_pincfgs,
5238c2ecf20Sopenharmony_ci		.chained = true,
5248c2ecf20Sopenharmony_ci		.chain_id = CS420X_APPLE,
5258c2ecf20Sopenharmony_ci	},
5268c2ecf20Sopenharmony_ci	[CS420X_MBP55] = {
5278c2ecf20Sopenharmony_ci		.type = HDA_FIXUP_PINS,
5288c2ecf20Sopenharmony_ci		.v.pins = mbp55_pincfgs,
5298c2ecf20Sopenharmony_ci		.chained = true,
5308c2ecf20Sopenharmony_ci		.chain_id = CS420X_GPIO_13,
5318c2ecf20Sopenharmony_ci	},
5328c2ecf20Sopenharmony_ci	[CS420X_IMAC27] = {
5338c2ecf20Sopenharmony_ci		.type = HDA_FIXUP_PINS,
5348c2ecf20Sopenharmony_ci		.v.pins = imac27_pincfgs,
5358c2ecf20Sopenharmony_ci		.chained = true,
5368c2ecf20Sopenharmony_ci		.chain_id = CS420X_GPIO_13,
5378c2ecf20Sopenharmony_ci	},
5388c2ecf20Sopenharmony_ci	[CS420X_GPIO_13] = {
5398c2ecf20Sopenharmony_ci		.type = HDA_FIXUP_FUNC,
5408c2ecf20Sopenharmony_ci		.v.func = cs420x_fixup_gpio_13,
5418c2ecf20Sopenharmony_ci	},
5428c2ecf20Sopenharmony_ci	[CS420X_GPIO_23] = {
5438c2ecf20Sopenharmony_ci		.type = HDA_FIXUP_FUNC,
5448c2ecf20Sopenharmony_ci		.v.func = cs420x_fixup_gpio_23,
5458c2ecf20Sopenharmony_ci	},
5468c2ecf20Sopenharmony_ci	[CS420X_MBP101] = {
5478c2ecf20Sopenharmony_ci		.type = HDA_FIXUP_PINS,
5488c2ecf20Sopenharmony_ci		.v.pins = mbp101_pincfgs,
5498c2ecf20Sopenharmony_ci		.chained = true,
5508c2ecf20Sopenharmony_ci		.chain_id = CS420X_GPIO_13,
5518c2ecf20Sopenharmony_ci	},
5528c2ecf20Sopenharmony_ci	[CS420X_MBP81] = {
5538c2ecf20Sopenharmony_ci		.type = HDA_FIXUP_VERBS,
5548c2ecf20Sopenharmony_ci		.v.verbs = (const struct hda_verb[]) {
5558c2ecf20Sopenharmony_ci			/* internal mic ADC2: right only, single ended */
5568c2ecf20Sopenharmony_ci			{0x11, AC_VERB_SET_COEF_INDEX, IDX_ADC_CFG},
5578c2ecf20Sopenharmony_ci			{0x11, AC_VERB_SET_PROC_COEF, 0x102a},
5588c2ecf20Sopenharmony_ci			{}
5598c2ecf20Sopenharmony_ci		},
5608c2ecf20Sopenharmony_ci		.chained = true,
5618c2ecf20Sopenharmony_ci		.chain_id = CS420X_GPIO_13,
5628c2ecf20Sopenharmony_ci	},
5638c2ecf20Sopenharmony_ci	[CS420X_MBA42] = {
5648c2ecf20Sopenharmony_ci		.type = HDA_FIXUP_PINS,
5658c2ecf20Sopenharmony_ci		.v.pins = mba42_pincfgs,
5668c2ecf20Sopenharmony_ci		.chained = true,
5678c2ecf20Sopenharmony_ci		.chain_id = CS420X_GPIO_13,
5688c2ecf20Sopenharmony_ci	},
5698c2ecf20Sopenharmony_ci};
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_cistatic struct cs_spec *cs_alloc_spec(struct hda_codec *codec, int vendor_nid)
5728c2ecf20Sopenharmony_ci{
5738c2ecf20Sopenharmony_ci	struct cs_spec *spec;
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ci	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
5768c2ecf20Sopenharmony_ci	if (!spec)
5778c2ecf20Sopenharmony_ci		return NULL;
5788c2ecf20Sopenharmony_ci	codec->spec = spec;
5798c2ecf20Sopenharmony_ci	spec->vendor_nid = vendor_nid;
5808c2ecf20Sopenharmony_ci	codec->power_save_node = 1;
5818c2ecf20Sopenharmony_ci	snd_hda_gen_spec_init(&spec->gen);
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_ci	return spec;
5848c2ecf20Sopenharmony_ci}
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_cistatic int patch_cs420x(struct hda_codec *codec)
5878c2ecf20Sopenharmony_ci{
5888c2ecf20Sopenharmony_ci	struct cs_spec *spec;
5898c2ecf20Sopenharmony_ci	int err;
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_ci	spec = cs_alloc_spec(codec, CS420X_VENDOR_NID);
5928c2ecf20Sopenharmony_ci	if (!spec)
5938c2ecf20Sopenharmony_ci		return -ENOMEM;
5948c2ecf20Sopenharmony_ci
5958c2ecf20Sopenharmony_ci	codec->patch_ops = cs_patch_ops;
5968c2ecf20Sopenharmony_ci	spec->gen.automute_hook = cs_automute;
5978c2ecf20Sopenharmony_ci	codec->single_adc_amp = 1;
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_ci	snd_hda_pick_fixup(codec, cs420x_models, cs420x_fixup_tbl,
6008c2ecf20Sopenharmony_ci			   cs420x_fixups);
6018c2ecf20Sopenharmony_ci	snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
6028c2ecf20Sopenharmony_ci
6038c2ecf20Sopenharmony_ci	err = cs_parse_auto_config(codec);
6048c2ecf20Sopenharmony_ci	if (err < 0)
6058c2ecf20Sopenharmony_ci		goto error;
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_ci	snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
6088c2ecf20Sopenharmony_ci
6098c2ecf20Sopenharmony_ci	return 0;
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_ci error:
6128c2ecf20Sopenharmony_ci	cs_free(codec);
6138c2ecf20Sopenharmony_ci	return err;
6148c2ecf20Sopenharmony_ci}
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_ci/*
6178c2ecf20Sopenharmony_ci * CS4208 support:
6188c2ecf20Sopenharmony_ci * Its layout is no longer compatible with CS4206/CS4207
6198c2ecf20Sopenharmony_ci */
6208c2ecf20Sopenharmony_cienum {
6218c2ecf20Sopenharmony_ci	CS4208_MAC_AUTO,
6228c2ecf20Sopenharmony_ci	CS4208_MBA6,
6238c2ecf20Sopenharmony_ci	CS4208_MBP11,
6248c2ecf20Sopenharmony_ci	CS4208_MACMINI,
6258c2ecf20Sopenharmony_ci	CS4208_GPIO0,
6268c2ecf20Sopenharmony_ci};
6278c2ecf20Sopenharmony_ci
6288c2ecf20Sopenharmony_cistatic const struct hda_model_fixup cs4208_models[] = {
6298c2ecf20Sopenharmony_ci	{ .id = CS4208_GPIO0, .name = "gpio0" },
6308c2ecf20Sopenharmony_ci	{ .id = CS4208_MBA6, .name = "mba6" },
6318c2ecf20Sopenharmony_ci	{ .id = CS4208_MBP11, .name = "mbp11" },
6328c2ecf20Sopenharmony_ci	{ .id = CS4208_MACMINI, .name = "macmini" },
6338c2ecf20Sopenharmony_ci	{}
6348c2ecf20Sopenharmony_ci};
6358c2ecf20Sopenharmony_ci
6368c2ecf20Sopenharmony_cistatic const struct snd_pci_quirk cs4208_fixup_tbl[] = {
6378c2ecf20Sopenharmony_ci	SND_PCI_QUIRK_VENDOR(0x106b, "Apple", CS4208_MAC_AUTO),
6388c2ecf20Sopenharmony_ci	{} /* terminator */
6398c2ecf20Sopenharmony_ci};
6408c2ecf20Sopenharmony_ci
6418c2ecf20Sopenharmony_ci/* codec SSID matching */
6428c2ecf20Sopenharmony_cistatic const struct snd_pci_quirk cs4208_mac_fixup_tbl[] = {
6438c2ecf20Sopenharmony_ci	SND_PCI_QUIRK(0x106b, 0x5e00, "MacBookPro 11,2", CS4208_MBP11),
6448c2ecf20Sopenharmony_ci	SND_PCI_QUIRK(0x106b, 0x6c00, "MacMini 7,1", CS4208_MACMINI),
6458c2ecf20Sopenharmony_ci	SND_PCI_QUIRK(0x106b, 0x7100, "MacBookAir 6,1", CS4208_MBA6),
6468c2ecf20Sopenharmony_ci	SND_PCI_QUIRK(0x106b, 0x7200, "MacBookAir 6,2", CS4208_MBA6),
6478c2ecf20Sopenharmony_ci	SND_PCI_QUIRK(0x106b, 0x7b00, "MacBookPro 12,1", CS4208_MBP11),
6488c2ecf20Sopenharmony_ci	{} /* terminator */
6498c2ecf20Sopenharmony_ci};
6508c2ecf20Sopenharmony_ci
6518c2ecf20Sopenharmony_cistatic void cs4208_fixup_gpio0(struct hda_codec *codec,
6528c2ecf20Sopenharmony_ci			       const struct hda_fixup *fix, int action)
6538c2ecf20Sopenharmony_ci{
6548c2ecf20Sopenharmony_ci	if (action == HDA_FIXUP_ACT_PRE_PROBE) {
6558c2ecf20Sopenharmony_ci		struct cs_spec *spec = codec->spec;
6568c2ecf20Sopenharmony_ci		spec->gpio_eapd_hp = 0;
6578c2ecf20Sopenharmony_ci		spec->gpio_eapd_speaker = 1;
6588c2ecf20Sopenharmony_ci		spec->gpio_mask = spec->gpio_dir =
6598c2ecf20Sopenharmony_ci			spec->gpio_eapd_hp | spec->gpio_eapd_speaker;
6608c2ecf20Sopenharmony_ci	}
6618c2ecf20Sopenharmony_ci}
6628c2ecf20Sopenharmony_ci
6638c2ecf20Sopenharmony_cistatic const struct hda_fixup cs4208_fixups[];
6648c2ecf20Sopenharmony_ci
6658c2ecf20Sopenharmony_ci/* remap the fixup from codec SSID and apply it */
6668c2ecf20Sopenharmony_cistatic void cs4208_fixup_mac(struct hda_codec *codec,
6678c2ecf20Sopenharmony_ci			     const struct hda_fixup *fix, int action)
6688c2ecf20Sopenharmony_ci{
6698c2ecf20Sopenharmony_ci	if (action != HDA_FIXUP_ACT_PRE_PROBE)
6708c2ecf20Sopenharmony_ci		return;
6718c2ecf20Sopenharmony_ci
6728c2ecf20Sopenharmony_ci	codec->fixup_id = HDA_FIXUP_ID_NOT_SET;
6738c2ecf20Sopenharmony_ci	snd_hda_pick_fixup(codec, NULL, cs4208_mac_fixup_tbl, cs4208_fixups);
6748c2ecf20Sopenharmony_ci	if (codec->fixup_id == HDA_FIXUP_ID_NOT_SET)
6758c2ecf20Sopenharmony_ci		codec->fixup_id = CS4208_GPIO0; /* default fixup */
6768c2ecf20Sopenharmony_ci	snd_hda_apply_fixup(codec, action);
6778c2ecf20Sopenharmony_ci}
6788c2ecf20Sopenharmony_ci
6798c2ecf20Sopenharmony_ci/* MacMini 7,1 has the inverted jack detection */
6808c2ecf20Sopenharmony_cistatic void cs4208_fixup_macmini(struct hda_codec *codec,
6818c2ecf20Sopenharmony_ci				 const struct hda_fixup *fix, int action)
6828c2ecf20Sopenharmony_ci{
6838c2ecf20Sopenharmony_ci	static const struct hda_pintbl pincfgs[] = {
6848c2ecf20Sopenharmony_ci		{ 0x18, 0x00ab9150 }, /* mic (audio-in) jack: disable detect */
6858c2ecf20Sopenharmony_ci		{ 0x21, 0x004be140 }, /* SPDIF: disable detect */
6868c2ecf20Sopenharmony_ci		{ }
6878c2ecf20Sopenharmony_ci	};
6888c2ecf20Sopenharmony_ci
6898c2ecf20Sopenharmony_ci	if (action == HDA_FIXUP_ACT_PRE_PROBE) {
6908c2ecf20Sopenharmony_ci		/* HP pin (0x10) has an inverted detection */
6918c2ecf20Sopenharmony_ci		codec->inv_jack_detect = 1;
6928c2ecf20Sopenharmony_ci		/* disable the bogus Mic and SPDIF jack detections */
6938c2ecf20Sopenharmony_ci		snd_hda_apply_pincfgs(codec, pincfgs);
6948c2ecf20Sopenharmony_ci	}
6958c2ecf20Sopenharmony_ci}
6968c2ecf20Sopenharmony_ci
6978c2ecf20Sopenharmony_cistatic int cs4208_spdif_sw_put(struct snd_kcontrol *kcontrol,
6988c2ecf20Sopenharmony_ci			       struct snd_ctl_elem_value *ucontrol)
6998c2ecf20Sopenharmony_ci{
7008c2ecf20Sopenharmony_ci	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
7018c2ecf20Sopenharmony_ci	struct cs_spec *spec = codec->spec;
7028c2ecf20Sopenharmony_ci	hda_nid_t pin = spec->gen.autocfg.dig_out_pins[0];
7038c2ecf20Sopenharmony_ci	int pinctl = ucontrol->value.integer.value[0] ? PIN_OUT : 0;
7048c2ecf20Sopenharmony_ci
7058c2ecf20Sopenharmony_ci	snd_hda_set_pin_ctl_cache(codec, pin, pinctl);
7068c2ecf20Sopenharmony_ci	return spec->spdif_sw_put(kcontrol, ucontrol);
7078c2ecf20Sopenharmony_ci}
7088c2ecf20Sopenharmony_ci
7098c2ecf20Sopenharmony_ci/* hook the SPDIF switch */
7108c2ecf20Sopenharmony_cistatic void cs4208_fixup_spdif_switch(struct hda_codec *codec,
7118c2ecf20Sopenharmony_ci				      const struct hda_fixup *fix, int action)
7128c2ecf20Sopenharmony_ci{
7138c2ecf20Sopenharmony_ci	if (action == HDA_FIXUP_ACT_BUILD) {
7148c2ecf20Sopenharmony_ci		struct cs_spec *spec = codec->spec;
7158c2ecf20Sopenharmony_ci		struct snd_kcontrol *kctl;
7168c2ecf20Sopenharmony_ci
7178c2ecf20Sopenharmony_ci		if (!spec->gen.autocfg.dig_out_pins[0])
7188c2ecf20Sopenharmony_ci			return;
7198c2ecf20Sopenharmony_ci		kctl = snd_hda_find_mixer_ctl(codec, "IEC958 Playback Switch");
7208c2ecf20Sopenharmony_ci		if (!kctl)
7218c2ecf20Sopenharmony_ci			return;
7228c2ecf20Sopenharmony_ci		spec->spdif_sw_put = kctl->put;
7238c2ecf20Sopenharmony_ci		kctl->put = cs4208_spdif_sw_put;
7248c2ecf20Sopenharmony_ci	}
7258c2ecf20Sopenharmony_ci}
7268c2ecf20Sopenharmony_ci
7278c2ecf20Sopenharmony_cistatic const struct hda_fixup cs4208_fixups[] = {
7288c2ecf20Sopenharmony_ci	[CS4208_MBA6] = {
7298c2ecf20Sopenharmony_ci		.type = HDA_FIXUP_PINS,
7308c2ecf20Sopenharmony_ci		.v.pins = mba6_pincfgs,
7318c2ecf20Sopenharmony_ci		.chained = true,
7328c2ecf20Sopenharmony_ci		.chain_id = CS4208_GPIO0,
7338c2ecf20Sopenharmony_ci	},
7348c2ecf20Sopenharmony_ci	[CS4208_MBP11] = {
7358c2ecf20Sopenharmony_ci		.type = HDA_FIXUP_FUNC,
7368c2ecf20Sopenharmony_ci		.v.func = cs4208_fixup_spdif_switch,
7378c2ecf20Sopenharmony_ci		.chained = true,
7388c2ecf20Sopenharmony_ci		.chain_id = CS4208_GPIO0,
7398c2ecf20Sopenharmony_ci	},
7408c2ecf20Sopenharmony_ci	[CS4208_MACMINI] = {
7418c2ecf20Sopenharmony_ci		.type = HDA_FIXUP_FUNC,
7428c2ecf20Sopenharmony_ci		.v.func = cs4208_fixup_macmini,
7438c2ecf20Sopenharmony_ci		.chained = true,
7448c2ecf20Sopenharmony_ci		.chain_id = CS4208_GPIO0,
7458c2ecf20Sopenharmony_ci	},
7468c2ecf20Sopenharmony_ci	[CS4208_GPIO0] = {
7478c2ecf20Sopenharmony_ci		.type = HDA_FIXUP_FUNC,
7488c2ecf20Sopenharmony_ci		.v.func = cs4208_fixup_gpio0,
7498c2ecf20Sopenharmony_ci	},
7508c2ecf20Sopenharmony_ci	[CS4208_MAC_AUTO] = {
7518c2ecf20Sopenharmony_ci		.type = HDA_FIXUP_FUNC,
7528c2ecf20Sopenharmony_ci		.v.func = cs4208_fixup_mac,
7538c2ecf20Sopenharmony_ci	},
7548c2ecf20Sopenharmony_ci};
7558c2ecf20Sopenharmony_ci
7568c2ecf20Sopenharmony_ci/* correct the 0dB offset of input pins */
7578c2ecf20Sopenharmony_cistatic void cs4208_fix_amp_caps(struct hda_codec *codec, hda_nid_t adc)
7588c2ecf20Sopenharmony_ci{
7598c2ecf20Sopenharmony_ci	unsigned int caps;
7608c2ecf20Sopenharmony_ci
7618c2ecf20Sopenharmony_ci	caps = query_amp_caps(codec, adc, HDA_INPUT);
7628c2ecf20Sopenharmony_ci	caps &= ~(AC_AMPCAP_OFFSET);
7638c2ecf20Sopenharmony_ci	caps |= 0x02;
7648c2ecf20Sopenharmony_ci	snd_hda_override_amp_caps(codec, adc, HDA_INPUT, caps);
7658c2ecf20Sopenharmony_ci}
7668c2ecf20Sopenharmony_ci
7678c2ecf20Sopenharmony_cistatic int patch_cs4208(struct hda_codec *codec)
7688c2ecf20Sopenharmony_ci{
7698c2ecf20Sopenharmony_ci	struct cs_spec *spec;
7708c2ecf20Sopenharmony_ci	int err;
7718c2ecf20Sopenharmony_ci
7728c2ecf20Sopenharmony_ci	spec = cs_alloc_spec(codec, CS4208_VENDOR_NID);
7738c2ecf20Sopenharmony_ci	if (!spec)
7748c2ecf20Sopenharmony_ci		return -ENOMEM;
7758c2ecf20Sopenharmony_ci
7768c2ecf20Sopenharmony_ci	codec->patch_ops = cs_patch_ops;
7778c2ecf20Sopenharmony_ci	spec->gen.automute_hook = cs_automute;
7788c2ecf20Sopenharmony_ci	/* exclude NID 0x10 (HP) from output volumes due to different steps */
7798c2ecf20Sopenharmony_ci	spec->gen.out_vol_mask = 1ULL << 0x10;
7808c2ecf20Sopenharmony_ci
7818c2ecf20Sopenharmony_ci	snd_hda_pick_fixup(codec, cs4208_models, cs4208_fixup_tbl,
7828c2ecf20Sopenharmony_ci			   cs4208_fixups);
7838c2ecf20Sopenharmony_ci	snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
7848c2ecf20Sopenharmony_ci
7858c2ecf20Sopenharmony_ci	snd_hda_override_wcaps(codec, 0x18,
7868c2ecf20Sopenharmony_ci			       get_wcaps(codec, 0x18) | AC_WCAP_STEREO);
7878c2ecf20Sopenharmony_ci	cs4208_fix_amp_caps(codec, 0x18);
7888c2ecf20Sopenharmony_ci	cs4208_fix_amp_caps(codec, 0x1b);
7898c2ecf20Sopenharmony_ci	cs4208_fix_amp_caps(codec, 0x1c);
7908c2ecf20Sopenharmony_ci
7918c2ecf20Sopenharmony_ci	err = cs_parse_auto_config(codec);
7928c2ecf20Sopenharmony_ci	if (err < 0)
7938c2ecf20Sopenharmony_ci		goto error;
7948c2ecf20Sopenharmony_ci
7958c2ecf20Sopenharmony_ci	snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
7968c2ecf20Sopenharmony_ci
7978c2ecf20Sopenharmony_ci	return 0;
7988c2ecf20Sopenharmony_ci
7998c2ecf20Sopenharmony_ci error:
8008c2ecf20Sopenharmony_ci	cs_free(codec);
8018c2ecf20Sopenharmony_ci	return err;
8028c2ecf20Sopenharmony_ci}
8038c2ecf20Sopenharmony_ci
8048c2ecf20Sopenharmony_ci/*
8058c2ecf20Sopenharmony_ci * Cirrus Logic CS4210
8068c2ecf20Sopenharmony_ci *
8078c2ecf20Sopenharmony_ci * 1 DAC => HP(sense) / Speakers,
8088c2ecf20Sopenharmony_ci * 1 ADC <= LineIn(sense) / MicIn / DMicIn,
8098c2ecf20Sopenharmony_ci * 1 SPDIF OUT => SPDIF Trasmitter(sense)
8108c2ecf20Sopenharmony_ci*/
8118c2ecf20Sopenharmony_ci
8128c2ecf20Sopenharmony_ci/* CS4210 board names */
8138c2ecf20Sopenharmony_cistatic const struct hda_model_fixup cs421x_models[] = {
8148c2ecf20Sopenharmony_ci	{ .id = CS421X_CDB4210, .name = "cdb4210" },
8158c2ecf20Sopenharmony_ci	{ .id = CS421X_STUMPY, .name = "stumpy" },
8168c2ecf20Sopenharmony_ci	{}
8178c2ecf20Sopenharmony_ci};
8188c2ecf20Sopenharmony_ci
8198c2ecf20Sopenharmony_cistatic const struct snd_pci_quirk cs421x_fixup_tbl[] = {
8208c2ecf20Sopenharmony_ci	/* Test Intel board + CDB2410  */
8218c2ecf20Sopenharmony_ci	SND_PCI_QUIRK(0x8086, 0x5001, "DP45SG/CDB4210", CS421X_CDB4210),
8228c2ecf20Sopenharmony_ci	{} /* terminator */
8238c2ecf20Sopenharmony_ci};
8248c2ecf20Sopenharmony_ci
8258c2ecf20Sopenharmony_ci/* CS4210 board pinconfigs */
8268c2ecf20Sopenharmony_ci/* Default CS4210 (CDB4210)*/
8278c2ecf20Sopenharmony_cistatic const struct hda_pintbl cdb4210_pincfgs[] = {
8288c2ecf20Sopenharmony_ci	{ 0x05, 0x0321401f },
8298c2ecf20Sopenharmony_ci	{ 0x06, 0x90170010 },
8308c2ecf20Sopenharmony_ci	{ 0x07, 0x03813031 },
8318c2ecf20Sopenharmony_ci	{ 0x08, 0xb7a70037 },
8328c2ecf20Sopenharmony_ci	{ 0x09, 0xb7a6003e },
8338c2ecf20Sopenharmony_ci	{ 0x0a, 0x034510f0 },
8348c2ecf20Sopenharmony_ci	{} /* terminator */
8358c2ecf20Sopenharmony_ci};
8368c2ecf20Sopenharmony_ci
8378c2ecf20Sopenharmony_ci/* Stumpy ChromeBox */
8388c2ecf20Sopenharmony_cistatic const struct hda_pintbl stumpy_pincfgs[] = {
8398c2ecf20Sopenharmony_ci	{ 0x05, 0x022120f0 },
8408c2ecf20Sopenharmony_ci	{ 0x06, 0x901700f0 },
8418c2ecf20Sopenharmony_ci	{ 0x07, 0x02a120f0 },
8428c2ecf20Sopenharmony_ci	{ 0x08, 0x77a70037 },
8438c2ecf20Sopenharmony_ci	{ 0x09, 0x77a6003e },
8448c2ecf20Sopenharmony_ci	{ 0x0a, 0x434510f0 },
8458c2ecf20Sopenharmony_ci	{} /* terminator */
8468c2ecf20Sopenharmony_ci};
8478c2ecf20Sopenharmony_ci
8488c2ecf20Sopenharmony_ci/* Setup GPIO/SENSE for each board (if used) */
8498c2ecf20Sopenharmony_cistatic void cs421x_fixup_sense_b(struct hda_codec *codec,
8508c2ecf20Sopenharmony_ci				 const struct hda_fixup *fix, int action)
8518c2ecf20Sopenharmony_ci{
8528c2ecf20Sopenharmony_ci	struct cs_spec *spec = codec->spec;
8538c2ecf20Sopenharmony_ci	if (action == HDA_FIXUP_ACT_PRE_PROBE)
8548c2ecf20Sopenharmony_ci		spec->sense_b = 1;
8558c2ecf20Sopenharmony_ci}
8568c2ecf20Sopenharmony_ci
8578c2ecf20Sopenharmony_cistatic const struct hda_fixup cs421x_fixups[] = {
8588c2ecf20Sopenharmony_ci	[CS421X_CDB4210] = {
8598c2ecf20Sopenharmony_ci		.type = HDA_FIXUP_PINS,
8608c2ecf20Sopenharmony_ci		.v.pins = cdb4210_pincfgs,
8618c2ecf20Sopenharmony_ci		.chained = true,
8628c2ecf20Sopenharmony_ci		.chain_id = CS421X_SENSE_B,
8638c2ecf20Sopenharmony_ci	},
8648c2ecf20Sopenharmony_ci	[CS421X_SENSE_B] = {
8658c2ecf20Sopenharmony_ci		.type = HDA_FIXUP_FUNC,
8668c2ecf20Sopenharmony_ci		.v.func = cs421x_fixup_sense_b,
8678c2ecf20Sopenharmony_ci	},
8688c2ecf20Sopenharmony_ci	[CS421X_STUMPY] = {
8698c2ecf20Sopenharmony_ci		.type = HDA_FIXUP_PINS,
8708c2ecf20Sopenharmony_ci		.v.pins = stumpy_pincfgs,
8718c2ecf20Sopenharmony_ci	},
8728c2ecf20Sopenharmony_ci};
8738c2ecf20Sopenharmony_ci
8748c2ecf20Sopenharmony_cistatic const struct hda_verb cs421x_coef_init_verbs[] = {
8758c2ecf20Sopenharmony_ci	{0x0B, AC_VERB_SET_PROC_STATE, 1},
8768c2ecf20Sopenharmony_ci	{0x0B, AC_VERB_SET_COEF_INDEX, CS421X_IDX_DEV_CFG},
8778c2ecf20Sopenharmony_ci	/*
8788c2ecf20Sopenharmony_ci	    Disable Coefficient Index Auto-Increment(DAI)=1,
8798c2ecf20Sopenharmony_ci	    PDREF=0
8808c2ecf20Sopenharmony_ci	*/
8818c2ecf20Sopenharmony_ci	{0x0B, AC_VERB_SET_PROC_COEF, 0x0001 },
8828c2ecf20Sopenharmony_ci
8838c2ecf20Sopenharmony_ci	{0x0B, AC_VERB_SET_COEF_INDEX, CS421X_IDX_ADC_CFG},
8848c2ecf20Sopenharmony_ci	/* ADC SZCMode = Digital Soft Ramp */
8858c2ecf20Sopenharmony_ci	{0x0B, AC_VERB_SET_PROC_COEF, 0x0002 },
8868c2ecf20Sopenharmony_ci
8878c2ecf20Sopenharmony_ci	{0x0B, AC_VERB_SET_COEF_INDEX, CS421X_IDX_DAC_CFG},
8888c2ecf20Sopenharmony_ci	{0x0B, AC_VERB_SET_PROC_COEF,
8898c2ecf20Sopenharmony_ci	 (0x0002 /* DAC SZCMode = Digital Soft Ramp */
8908c2ecf20Sopenharmony_ci	  | 0x0004 /* Mute DAC on FIFO error */
8918c2ecf20Sopenharmony_ci	  | 0x0008 /* Enable DAC High Pass Filter */
8928c2ecf20Sopenharmony_ci	  )},
8938c2ecf20Sopenharmony_ci	{} /* terminator */
8948c2ecf20Sopenharmony_ci};
8958c2ecf20Sopenharmony_ci
8968c2ecf20Sopenharmony_ci/* Errata: CS4210 rev A1 Silicon
8978c2ecf20Sopenharmony_ci *
8988c2ecf20Sopenharmony_ci * http://www.cirrus.com/en/pubs/errata/
8998c2ecf20Sopenharmony_ci *
9008c2ecf20Sopenharmony_ci * Description:
9018c2ecf20Sopenharmony_ci * 1. Performance degredation is present in the ADC.
9028c2ecf20Sopenharmony_ci * 2. Speaker output is not completely muted upon HP detect.
9038c2ecf20Sopenharmony_ci * 3. Noise is present when clipping occurs on the amplified
9048c2ecf20Sopenharmony_ci *    speaker outputs.
9058c2ecf20Sopenharmony_ci *
9068c2ecf20Sopenharmony_ci * Workaround:
9078c2ecf20Sopenharmony_ci * The following verb sequence written to the registers during
9088c2ecf20Sopenharmony_ci * initialization will correct the issues listed above.
9098c2ecf20Sopenharmony_ci */
9108c2ecf20Sopenharmony_ci
9118c2ecf20Sopenharmony_cistatic const struct hda_verb cs421x_coef_init_verbs_A1_silicon_fixes[] = {
9128c2ecf20Sopenharmony_ci	{0x0B, AC_VERB_SET_PROC_STATE, 0x01},  /* VPW: processing on */
9138c2ecf20Sopenharmony_ci
9148c2ecf20Sopenharmony_ci	{0x0B, AC_VERB_SET_COEF_INDEX, 0x0006},
9158c2ecf20Sopenharmony_ci	{0x0B, AC_VERB_SET_PROC_COEF, 0x9999}, /* Test mode: on */
9168c2ecf20Sopenharmony_ci
9178c2ecf20Sopenharmony_ci	{0x0B, AC_VERB_SET_COEF_INDEX, 0x000A},
9188c2ecf20Sopenharmony_ci	{0x0B, AC_VERB_SET_PROC_COEF, 0x14CB}, /* Chop double */
9198c2ecf20Sopenharmony_ci
9208c2ecf20Sopenharmony_ci	{0x0B, AC_VERB_SET_COEF_INDEX, 0x0011},
9218c2ecf20Sopenharmony_ci	{0x0B, AC_VERB_SET_PROC_COEF, 0xA2D0}, /* Increase ADC current */
9228c2ecf20Sopenharmony_ci
9238c2ecf20Sopenharmony_ci	{0x0B, AC_VERB_SET_COEF_INDEX, 0x001A},
9248c2ecf20Sopenharmony_ci	{0x0B, AC_VERB_SET_PROC_COEF, 0x02A9}, /* Mute speaker */
9258c2ecf20Sopenharmony_ci
9268c2ecf20Sopenharmony_ci	{0x0B, AC_VERB_SET_COEF_INDEX, 0x001B},
9278c2ecf20Sopenharmony_ci	{0x0B, AC_VERB_SET_PROC_COEF, 0X1006}, /* Remove noise */
9288c2ecf20Sopenharmony_ci
9298c2ecf20Sopenharmony_ci	{} /* terminator */
9308c2ecf20Sopenharmony_ci};
9318c2ecf20Sopenharmony_ci
9328c2ecf20Sopenharmony_ci/* Speaker Amp Gain is controlled by the vendor widget's coef 4 */
9338c2ecf20Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(cs421x_speaker_boost_db_scale, 900, 300, 0);
9348c2ecf20Sopenharmony_ci
9358c2ecf20Sopenharmony_cistatic int cs421x_boost_vol_info(struct snd_kcontrol *kcontrol,
9368c2ecf20Sopenharmony_ci				struct snd_ctl_elem_info *uinfo)
9378c2ecf20Sopenharmony_ci{
9388c2ecf20Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
9398c2ecf20Sopenharmony_ci	uinfo->count = 1;
9408c2ecf20Sopenharmony_ci	uinfo->value.integer.min = 0;
9418c2ecf20Sopenharmony_ci	uinfo->value.integer.max = 3;
9428c2ecf20Sopenharmony_ci	return 0;
9438c2ecf20Sopenharmony_ci}
9448c2ecf20Sopenharmony_ci
9458c2ecf20Sopenharmony_cistatic int cs421x_boost_vol_get(struct snd_kcontrol *kcontrol,
9468c2ecf20Sopenharmony_ci				struct snd_ctl_elem_value *ucontrol)
9478c2ecf20Sopenharmony_ci{
9488c2ecf20Sopenharmony_ci	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
9498c2ecf20Sopenharmony_ci
9508c2ecf20Sopenharmony_ci	ucontrol->value.integer.value[0] =
9518c2ecf20Sopenharmony_ci		cs_vendor_coef_get(codec, CS421X_IDX_SPK_CTL) & 0x0003;
9528c2ecf20Sopenharmony_ci	return 0;
9538c2ecf20Sopenharmony_ci}
9548c2ecf20Sopenharmony_ci
9558c2ecf20Sopenharmony_cistatic int cs421x_boost_vol_put(struct snd_kcontrol *kcontrol,
9568c2ecf20Sopenharmony_ci				struct snd_ctl_elem_value *ucontrol)
9578c2ecf20Sopenharmony_ci{
9588c2ecf20Sopenharmony_ci	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
9598c2ecf20Sopenharmony_ci
9608c2ecf20Sopenharmony_ci	unsigned int vol = ucontrol->value.integer.value[0];
9618c2ecf20Sopenharmony_ci	unsigned int coef =
9628c2ecf20Sopenharmony_ci		cs_vendor_coef_get(codec, CS421X_IDX_SPK_CTL);
9638c2ecf20Sopenharmony_ci	unsigned int original_coef = coef;
9648c2ecf20Sopenharmony_ci
9658c2ecf20Sopenharmony_ci	coef &= ~0x0003;
9668c2ecf20Sopenharmony_ci	coef |= (vol & 0x0003);
9678c2ecf20Sopenharmony_ci	if (original_coef == coef)
9688c2ecf20Sopenharmony_ci		return 0;
9698c2ecf20Sopenharmony_ci	else {
9708c2ecf20Sopenharmony_ci		cs_vendor_coef_set(codec, CS421X_IDX_SPK_CTL, coef);
9718c2ecf20Sopenharmony_ci		return 1;
9728c2ecf20Sopenharmony_ci	}
9738c2ecf20Sopenharmony_ci}
9748c2ecf20Sopenharmony_ci
9758c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new cs421x_speaker_boost_ctl = {
9768c2ecf20Sopenharmony_ci
9778c2ecf20Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
9788c2ecf20Sopenharmony_ci	.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
9798c2ecf20Sopenharmony_ci			SNDRV_CTL_ELEM_ACCESS_TLV_READ),
9808c2ecf20Sopenharmony_ci	.name = "Speaker Boost Playback Volume",
9818c2ecf20Sopenharmony_ci	.info = cs421x_boost_vol_info,
9828c2ecf20Sopenharmony_ci	.get = cs421x_boost_vol_get,
9838c2ecf20Sopenharmony_ci	.put = cs421x_boost_vol_put,
9848c2ecf20Sopenharmony_ci	.tlv = { .p = cs421x_speaker_boost_db_scale },
9858c2ecf20Sopenharmony_ci};
9868c2ecf20Sopenharmony_ci
9878c2ecf20Sopenharmony_cistatic void cs4210_pinmux_init(struct hda_codec *codec)
9888c2ecf20Sopenharmony_ci{
9898c2ecf20Sopenharmony_ci	struct cs_spec *spec = codec->spec;
9908c2ecf20Sopenharmony_ci	unsigned int def_conf, coef;
9918c2ecf20Sopenharmony_ci
9928c2ecf20Sopenharmony_ci	/* GPIO, DMIC_SCL, DMIC_SDA and SENSE_B are multiplexed */
9938c2ecf20Sopenharmony_ci	coef = cs_vendor_coef_get(codec, CS421X_IDX_DEV_CFG);
9948c2ecf20Sopenharmony_ci
9958c2ecf20Sopenharmony_ci	if (spec->gpio_mask)
9968c2ecf20Sopenharmony_ci		coef |= 0x0008; /* B1,B2 are GPIOs */
9978c2ecf20Sopenharmony_ci	else
9988c2ecf20Sopenharmony_ci		coef &= ~0x0008;
9998c2ecf20Sopenharmony_ci
10008c2ecf20Sopenharmony_ci	if (spec->sense_b)
10018c2ecf20Sopenharmony_ci		coef |= 0x0010; /* B2 is SENSE_B, not inverted  */
10028c2ecf20Sopenharmony_ci	else
10038c2ecf20Sopenharmony_ci		coef &= ~0x0010;
10048c2ecf20Sopenharmony_ci
10058c2ecf20Sopenharmony_ci	cs_vendor_coef_set(codec, CS421X_IDX_DEV_CFG, coef);
10068c2ecf20Sopenharmony_ci
10078c2ecf20Sopenharmony_ci	if ((spec->gpio_mask || spec->sense_b) &&
10088c2ecf20Sopenharmony_ci	    is_active_pin(codec, CS421X_DMIC_PIN_NID)) {
10098c2ecf20Sopenharmony_ci
10108c2ecf20Sopenharmony_ci		/*
10118c2ecf20Sopenharmony_ci		    GPIO or SENSE_B forced - disconnect the DMIC pin.
10128c2ecf20Sopenharmony_ci		*/
10138c2ecf20Sopenharmony_ci		def_conf = snd_hda_codec_get_pincfg(codec, CS421X_DMIC_PIN_NID);
10148c2ecf20Sopenharmony_ci		def_conf &= ~AC_DEFCFG_PORT_CONN;
10158c2ecf20Sopenharmony_ci		def_conf |= (AC_JACK_PORT_NONE << AC_DEFCFG_PORT_CONN_SHIFT);
10168c2ecf20Sopenharmony_ci		snd_hda_codec_set_pincfg(codec, CS421X_DMIC_PIN_NID, def_conf);
10178c2ecf20Sopenharmony_ci	}
10188c2ecf20Sopenharmony_ci}
10198c2ecf20Sopenharmony_ci
10208c2ecf20Sopenharmony_cistatic void cs4210_spdif_automute(struct hda_codec *codec,
10218c2ecf20Sopenharmony_ci				  struct hda_jack_callback *tbl)
10228c2ecf20Sopenharmony_ci{
10238c2ecf20Sopenharmony_ci	struct cs_spec *spec = codec->spec;
10248c2ecf20Sopenharmony_ci	bool spdif_present = false;
10258c2ecf20Sopenharmony_ci	hda_nid_t spdif_pin = spec->gen.autocfg.dig_out_pins[0];
10268c2ecf20Sopenharmony_ci
10278c2ecf20Sopenharmony_ci	/* detect on spdif is specific to CS4210 */
10288c2ecf20Sopenharmony_ci	if (!spec->spdif_detect ||
10298c2ecf20Sopenharmony_ci	    spec->vendor_nid != CS4210_VENDOR_NID)
10308c2ecf20Sopenharmony_ci		return;
10318c2ecf20Sopenharmony_ci
10328c2ecf20Sopenharmony_ci	spdif_present = snd_hda_jack_detect(codec, spdif_pin);
10338c2ecf20Sopenharmony_ci	if (spdif_present == spec->spdif_present)
10348c2ecf20Sopenharmony_ci		return;
10358c2ecf20Sopenharmony_ci
10368c2ecf20Sopenharmony_ci	spec->spdif_present = spdif_present;
10378c2ecf20Sopenharmony_ci	/* SPDIF TX on/off */
10388c2ecf20Sopenharmony_ci	snd_hda_set_pin_ctl(codec, spdif_pin, spdif_present ? PIN_OUT : 0);
10398c2ecf20Sopenharmony_ci
10408c2ecf20Sopenharmony_ci	cs_automute(codec);
10418c2ecf20Sopenharmony_ci}
10428c2ecf20Sopenharmony_ci
10438c2ecf20Sopenharmony_cistatic void parse_cs421x_digital(struct hda_codec *codec)
10448c2ecf20Sopenharmony_ci{
10458c2ecf20Sopenharmony_ci	struct cs_spec *spec = codec->spec;
10468c2ecf20Sopenharmony_ci	struct auto_pin_cfg *cfg = &spec->gen.autocfg;
10478c2ecf20Sopenharmony_ci	int i;
10488c2ecf20Sopenharmony_ci
10498c2ecf20Sopenharmony_ci	for (i = 0; i < cfg->dig_outs; i++) {
10508c2ecf20Sopenharmony_ci		hda_nid_t nid = cfg->dig_out_pins[i];
10518c2ecf20Sopenharmony_ci		if (get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP) {
10528c2ecf20Sopenharmony_ci			spec->spdif_detect = 1;
10538c2ecf20Sopenharmony_ci			snd_hda_jack_detect_enable_callback(codec, nid,
10548c2ecf20Sopenharmony_ci							    cs4210_spdif_automute);
10558c2ecf20Sopenharmony_ci		}
10568c2ecf20Sopenharmony_ci	}
10578c2ecf20Sopenharmony_ci}
10588c2ecf20Sopenharmony_ci
10598c2ecf20Sopenharmony_cistatic int cs421x_init(struct hda_codec *codec)
10608c2ecf20Sopenharmony_ci{
10618c2ecf20Sopenharmony_ci	struct cs_spec *spec = codec->spec;
10628c2ecf20Sopenharmony_ci
10638c2ecf20Sopenharmony_ci	if (spec->vendor_nid == CS4210_VENDOR_NID) {
10648c2ecf20Sopenharmony_ci		snd_hda_sequence_write(codec, cs421x_coef_init_verbs);
10658c2ecf20Sopenharmony_ci		snd_hda_sequence_write(codec, cs421x_coef_init_verbs_A1_silicon_fixes);
10668c2ecf20Sopenharmony_ci		cs4210_pinmux_init(codec);
10678c2ecf20Sopenharmony_ci	}
10688c2ecf20Sopenharmony_ci
10698c2ecf20Sopenharmony_ci	snd_hda_gen_init(codec);
10708c2ecf20Sopenharmony_ci
10718c2ecf20Sopenharmony_ci	if (spec->gpio_mask) {
10728c2ecf20Sopenharmony_ci		snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_MASK,
10738c2ecf20Sopenharmony_ci				    spec->gpio_mask);
10748c2ecf20Sopenharmony_ci		snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DIRECTION,
10758c2ecf20Sopenharmony_ci				    spec->gpio_dir);
10768c2ecf20Sopenharmony_ci		snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA,
10778c2ecf20Sopenharmony_ci				    spec->gpio_data);
10788c2ecf20Sopenharmony_ci	}
10798c2ecf20Sopenharmony_ci
10808c2ecf20Sopenharmony_ci	init_input_coef(codec);
10818c2ecf20Sopenharmony_ci
10828c2ecf20Sopenharmony_ci	cs4210_spdif_automute(codec, NULL);
10838c2ecf20Sopenharmony_ci
10848c2ecf20Sopenharmony_ci	return 0;
10858c2ecf20Sopenharmony_ci}
10868c2ecf20Sopenharmony_ci
10878c2ecf20Sopenharmony_cistatic void fix_volume_caps(struct hda_codec *codec, hda_nid_t dac)
10888c2ecf20Sopenharmony_ci{
10898c2ecf20Sopenharmony_ci	unsigned int caps;
10908c2ecf20Sopenharmony_ci
10918c2ecf20Sopenharmony_ci	/* set the upper-limit for mixer amp to 0dB */
10928c2ecf20Sopenharmony_ci	caps = query_amp_caps(codec, dac, HDA_OUTPUT);
10938c2ecf20Sopenharmony_ci	caps &= ~(0x7f << AC_AMPCAP_NUM_STEPS_SHIFT);
10948c2ecf20Sopenharmony_ci	caps |= ((caps >> AC_AMPCAP_OFFSET_SHIFT) & 0x7f)
10958c2ecf20Sopenharmony_ci		<< AC_AMPCAP_NUM_STEPS_SHIFT;
10968c2ecf20Sopenharmony_ci	snd_hda_override_amp_caps(codec, dac, HDA_OUTPUT, caps);
10978c2ecf20Sopenharmony_ci}
10988c2ecf20Sopenharmony_ci
10998c2ecf20Sopenharmony_cistatic int cs421x_parse_auto_config(struct hda_codec *codec)
11008c2ecf20Sopenharmony_ci{
11018c2ecf20Sopenharmony_ci	struct cs_spec *spec = codec->spec;
11028c2ecf20Sopenharmony_ci	hda_nid_t dac = CS4210_DAC_NID;
11038c2ecf20Sopenharmony_ci	int err;
11048c2ecf20Sopenharmony_ci
11058c2ecf20Sopenharmony_ci	fix_volume_caps(codec, dac);
11068c2ecf20Sopenharmony_ci
11078c2ecf20Sopenharmony_ci	err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, 0);
11088c2ecf20Sopenharmony_ci	if (err < 0)
11098c2ecf20Sopenharmony_ci		return err;
11108c2ecf20Sopenharmony_ci
11118c2ecf20Sopenharmony_ci	err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg);
11128c2ecf20Sopenharmony_ci	if (err < 0)
11138c2ecf20Sopenharmony_ci		return err;
11148c2ecf20Sopenharmony_ci
11158c2ecf20Sopenharmony_ci	parse_cs421x_digital(codec);
11168c2ecf20Sopenharmony_ci
11178c2ecf20Sopenharmony_ci	if (spec->gen.autocfg.speaker_outs &&
11188c2ecf20Sopenharmony_ci	    spec->vendor_nid == CS4210_VENDOR_NID) {
11198c2ecf20Sopenharmony_ci		if (!snd_hda_gen_add_kctl(&spec->gen, NULL,
11208c2ecf20Sopenharmony_ci					  &cs421x_speaker_boost_ctl))
11218c2ecf20Sopenharmony_ci			return -ENOMEM;
11228c2ecf20Sopenharmony_ci	}
11238c2ecf20Sopenharmony_ci
11248c2ecf20Sopenharmony_ci	return 0;
11258c2ecf20Sopenharmony_ci}
11268c2ecf20Sopenharmony_ci
11278c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
11288c2ecf20Sopenharmony_ci/*
11298c2ecf20Sopenharmony_ci	Manage PDREF, when transitioning to D3hot
11308c2ecf20Sopenharmony_ci	(DAC,ADC) -> D3, PDREF=1, AFG->D3
11318c2ecf20Sopenharmony_ci*/
11328c2ecf20Sopenharmony_cistatic int cs421x_suspend(struct hda_codec *codec)
11338c2ecf20Sopenharmony_ci{
11348c2ecf20Sopenharmony_ci	struct cs_spec *spec = codec->spec;
11358c2ecf20Sopenharmony_ci	unsigned int coef;
11368c2ecf20Sopenharmony_ci
11378c2ecf20Sopenharmony_ci	snd_hda_shutup_pins(codec);
11388c2ecf20Sopenharmony_ci
11398c2ecf20Sopenharmony_ci	snd_hda_codec_write(codec, CS4210_DAC_NID, 0,
11408c2ecf20Sopenharmony_ci			    AC_VERB_SET_POWER_STATE,  AC_PWRST_D3);
11418c2ecf20Sopenharmony_ci	snd_hda_codec_write(codec, CS4210_ADC_NID, 0,
11428c2ecf20Sopenharmony_ci			    AC_VERB_SET_POWER_STATE,  AC_PWRST_D3);
11438c2ecf20Sopenharmony_ci
11448c2ecf20Sopenharmony_ci	if (spec->vendor_nid == CS4210_VENDOR_NID) {
11458c2ecf20Sopenharmony_ci		coef = cs_vendor_coef_get(codec, CS421X_IDX_DEV_CFG);
11468c2ecf20Sopenharmony_ci		coef |= 0x0004; /* PDREF */
11478c2ecf20Sopenharmony_ci		cs_vendor_coef_set(codec, CS421X_IDX_DEV_CFG, coef);
11488c2ecf20Sopenharmony_ci	}
11498c2ecf20Sopenharmony_ci
11508c2ecf20Sopenharmony_ci	return 0;
11518c2ecf20Sopenharmony_ci}
11528c2ecf20Sopenharmony_ci#endif
11538c2ecf20Sopenharmony_ci
11548c2ecf20Sopenharmony_cistatic const struct hda_codec_ops cs421x_patch_ops = {
11558c2ecf20Sopenharmony_ci	.build_controls = snd_hda_gen_build_controls,
11568c2ecf20Sopenharmony_ci	.build_pcms = snd_hda_gen_build_pcms,
11578c2ecf20Sopenharmony_ci	.init = cs421x_init,
11588c2ecf20Sopenharmony_ci	.free = cs_free,
11598c2ecf20Sopenharmony_ci	.unsol_event = snd_hda_jack_unsol_event,
11608c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
11618c2ecf20Sopenharmony_ci	.suspend = cs421x_suspend,
11628c2ecf20Sopenharmony_ci#endif
11638c2ecf20Sopenharmony_ci};
11648c2ecf20Sopenharmony_ci
11658c2ecf20Sopenharmony_cistatic int patch_cs4210(struct hda_codec *codec)
11668c2ecf20Sopenharmony_ci{
11678c2ecf20Sopenharmony_ci	struct cs_spec *spec;
11688c2ecf20Sopenharmony_ci	int err;
11698c2ecf20Sopenharmony_ci
11708c2ecf20Sopenharmony_ci	spec = cs_alloc_spec(codec, CS4210_VENDOR_NID);
11718c2ecf20Sopenharmony_ci	if (!spec)
11728c2ecf20Sopenharmony_ci		return -ENOMEM;
11738c2ecf20Sopenharmony_ci
11748c2ecf20Sopenharmony_ci	codec->patch_ops = cs421x_patch_ops;
11758c2ecf20Sopenharmony_ci	spec->gen.automute_hook = cs_automute;
11768c2ecf20Sopenharmony_ci
11778c2ecf20Sopenharmony_ci	snd_hda_pick_fixup(codec, cs421x_models, cs421x_fixup_tbl,
11788c2ecf20Sopenharmony_ci			   cs421x_fixups);
11798c2ecf20Sopenharmony_ci	snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
11808c2ecf20Sopenharmony_ci
11818c2ecf20Sopenharmony_ci	/*
11828c2ecf20Sopenharmony_ci	    Update the GPIO/DMIC/SENSE_B pinmux before the configuration
11838c2ecf20Sopenharmony_ci	    is auto-parsed. If GPIO or SENSE_B is forced, DMIC input
11848c2ecf20Sopenharmony_ci	    is disabled.
11858c2ecf20Sopenharmony_ci	*/
11868c2ecf20Sopenharmony_ci	cs4210_pinmux_init(codec);
11878c2ecf20Sopenharmony_ci
11888c2ecf20Sopenharmony_ci	err = cs421x_parse_auto_config(codec);
11898c2ecf20Sopenharmony_ci	if (err < 0)
11908c2ecf20Sopenharmony_ci		goto error;
11918c2ecf20Sopenharmony_ci
11928c2ecf20Sopenharmony_ci	snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
11938c2ecf20Sopenharmony_ci
11948c2ecf20Sopenharmony_ci	return 0;
11958c2ecf20Sopenharmony_ci
11968c2ecf20Sopenharmony_ci error:
11978c2ecf20Sopenharmony_ci	cs_free(codec);
11988c2ecf20Sopenharmony_ci	return err;
11998c2ecf20Sopenharmony_ci}
12008c2ecf20Sopenharmony_ci
12018c2ecf20Sopenharmony_cistatic int patch_cs4213(struct hda_codec *codec)
12028c2ecf20Sopenharmony_ci{
12038c2ecf20Sopenharmony_ci	struct cs_spec *spec;
12048c2ecf20Sopenharmony_ci	int err;
12058c2ecf20Sopenharmony_ci
12068c2ecf20Sopenharmony_ci	spec = cs_alloc_spec(codec, CS4213_VENDOR_NID);
12078c2ecf20Sopenharmony_ci	if (!spec)
12088c2ecf20Sopenharmony_ci		return -ENOMEM;
12098c2ecf20Sopenharmony_ci
12108c2ecf20Sopenharmony_ci	codec->patch_ops = cs421x_patch_ops;
12118c2ecf20Sopenharmony_ci
12128c2ecf20Sopenharmony_ci	err = cs421x_parse_auto_config(codec);
12138c2ecf20Sopenharmony_ci	if (err < 0)
12148c2ecf20Sopenharmony_ci		goto error;
12158c2ecf20Sopenharmony_ci
12168c2ecf20Sopenharmony_ci	return 0;
12178c2ecf20Sopenharmony_ci
12188c2ecf20Sopenharmony_ci error:
12198c2ecf20Sopenharmony_ci	cs_free(codec);
12208c2ecf20Sopenharmony_ci	return err;
12218c2ecf20Sopenharmony_ci}
12228c2ecf20Sopenharmony_ci
12238c2ecf20Sopenharmony_ci
12248c2ecf20Sopenharmony_ci/*
12258c2ecf20Sopenharmony_ci * patch entries
12268c2ecf20Sopenharmony_ci */
12278c2ecf20Sopenharmony_cistatic const struct hda_device_id snd_hda_id_cirrus[] = {
12288c2ecf20Sopenharmony_ci	HDA_CODEC_ENTRY(0x10134206, "CS4206", patch_cs420x),
12298c2ecf20Sopenharmony_ci	HDA_CODEC_ENTRY(0x10134207, "CS4207", patch_cs420x),
12308c2ecf20Sopenharmony_ci	HDA_CODEC_ENTRY(0x10134208, "CS4208", patch_cs4208),
12318c2ecf20Sopenharmony_ci	HDA_CODEC_ENTRY(0x10134210, "CS4210", patch_cs4210),
12328c2ecf20Sopenharmony_ci	HDA_CODEC_ENTRY(0x10134213, "CS4213", patch_cs4213),
12338c2ecf20Sopenharmony_ci	{} /* terminator */
12348c2ecf20Sopenharmony_ci};
12358c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(hdaudio, snd_hda_id_cirrus);
12368c2ecf20Sopenharmony_ci
12378c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
12388c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Cirrus Logic HD-audio codec");
12398c2ecf20Sopenharmony_ci
12408c2ecf20Sopenharmony_cistatic struct hda_codec_driver cirrus_driver = {
12418c2ecf20Sopenharmony_ci	.id = snd_hda_id_cirrus,
12428c2ecf20Sopenharmony_ci};
12438c2ecf20Sopenharmony_ci
12448c2ecf20Sopenharmony_cimodule_hda_codec_driver(cirrus_driver);
1245