xref: /openbmc/linux/sound/soc/codecs/cs42l51.c (revision b5a0e5e4)
11802d0beSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
272ed5a8cSapatard@mandriva.com /*
372ed5a8cSapatard@mandriva.com  * cs42l51.c
472ed5a8cSapatard@mandriva.com  *
572ed5a8cSapatard@mandriva.com  * ASoC Driver for Cirrus Logic CS42L51 codecs
672ed5a8cSapatard@mandriva.com  *
772ed5a8cSapatard@mandriva.com  * Copyright (c) 2010 Arnaud Patard <apatard@mandriva.com>
872ed5a8cSapatard@mandriva.com  *
972ed5a8cSapatard@mandriva.com  * Based on cs4270.c - Copyright (c) Freescale Semiconductor
1072ed5a8cSapatard@mandriva.com  *
1172ed5a8cSapatard@mandriva.com  * For now:
1272ed5a8cSapatard@mandriva.com  *  - Only I2C is support. Not SPI
1372ed5a8cSapatard@mandriva.com  *  - master mode *NOT* supported
1472ed5a8cSapatard@mandriva.com  */
1572ed5a8cSapatard@mandriva.com 
16318e741eSOlivier Moysan #include <linux/clk.h>
1772ed5a8cSapatard@mandriva.com #include <linux/module.h>
1872ed5a8cSapatard@mandriva.com #include <linux/slab.h>
1972ed5a8cSapatard@mandriva.com #include <sound/core.h>
2072ed5a8cSapatard@mandriva.com #include <sound/soc.h>
2172ed5a8cSapatard@mandriva.com #include <sound/tlv.h>
2272ed5a8cSapatard@mandriva.com #include <sound/initval.h>
2372ed5a8cSapatard@mandriva.com #include <sound/pcm_params.h>
2472ed5a8cSapatard@mandriva.com #include <sound/pcm.h>
2511b9cd74SOlivier Moysan #include <linux/gpio/consumer.h>
26da071489SMark Brown #include <linux/regmap.h>
27f77b6ea7SOlivier Moysan #include <linux/regulator/consumer.h>
2872ed5a8cSapatard@mandriva.com 
2972ed5a8cSapatard@mandriva.com #include "cs42l51.h"
3072ed5a8cSapatard@mandriva.com 
3172ed5a8cSapatard@mandriva.com enum master_slave_mode {
3272ed5a8cSapatard@mandriva.com 	MODE_SLAVE,
3372ed5a8cSapatard@mandriva.com 	MODE_SLAVE_AUTO,
3472ed5a8cSapatard@mandriva.com 	MODE_MASTER,
3572ed5a8cSapatard@mandriva.com };
3672ed5a8cSapatard@mandriva.com 
37f77b6ea7SOlivier Moysan static const char * const cs42l51_supply_names[] = {
38f77b6ea7SOlivier Moysan 	"VL",
39f77b6ea7SOlivier Moysan 	"VD",
40f77b6ea7SOlivier Moysan 	"VA",
41f77b6ea7SOlivier Moysan 	"VAHP",
42f77b6ea7SOlivier Moysan };
43f77b6ea7SOlivier Moysan 
4472ed5a8cSapatard@mandriva.com struct cs42l51_private {
4572ed5a8cSapatard@mandriva.com 	unsigned int mclk;
46318e741eSOlivier Moysan 	struct clk *mclk_handle;
4772ed5a8cSapatard@mandriva.com 	unsigned int audio_mode;	/* The mode (I2S or left-justified) */
4872ed5a8cSapatard@mandriva.com 	enum master_slave_mode func;
49f77b6ea7SOlivier Moysan 	struct regulator_bulk_data supplies[ARRAY_SIZE(cs42l51_supply_names)];
5011b9cd74SOlivier Moysan 	struct gpio_desc *reset_gpio;
5175a71482SOlivier Moysan 	struct regmap *regmap;
5272ed5a8cSapatard@mandriva.com };
5372ed5a8cSapatard@mandriva.com 
542bf1e87bSCharles Keepax #define CS42L51_FORMATS (SNDRV_PCM_FMTBIT_S16_LE  | SNDRV_PCM_FMTBIT_S18_3LE | \
552bf1e87bSCharles Keepax 			 SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE)
5672ed5a8cSapatard@mandriva.com 
cs42l51_get_chan_mix(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)5772ed5a8cSapatard@mandriva.com static int cs42l51_get_chan_mix(struct snd_kcontrol *kcontrol,
5872ed5a8cSapatard@mandriva.com 			struct snd_ctl_elem_value *ucontrol)
5972ed5a8cSapatard@mandriva.com {
601fa49856SKuninori Morimoto 	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
61a11f8a1cSKuninori Morimoto 	unsigned long value = snd_soc_component_read(component, CS42L51_PCM_MIXER)&3;
6272ed5a8cSapatard@mandriva.com 
6372ed5a8cSapatard@mandriva.com 	switch (value) {
6472ed5a8cSapatard@mandriva.com 	default:
6572ed5a8cSapatard@mandriva.com 	case 0:
6689300b4eSTakashi Iwai 		ucontrol->value.enumerated.item[0] = 0;
6772ed5a8cSapatard@mandriva.com 		break;
6872ed5a8cSapatard@mandriva.com 	/* same value : (L+R)/2 and (R+L)/2 */
6972ed5a8cSapatard@mandriva.com 	case 1:
7072ed5a8cSapatard@mandriva.com 	case 2:
7189300b4eSTakashi Iwai 		ucontrol->value.enumerated.item[0] = 1;
7272ed5a8cSapatard@mandriva.com 		break;
7372ed5a8cSapatard@mandriva.com 	case 3:
7489300b4eSTakashi Iwai 		ucontrol->value.enumerated.item[0] = 2;
7572ed5a8cSapatard@mandriva.com 		break;
7672ed5a8cSapatard@mandriva.com 	}
7772ed5a8cSapatard@mandriva.com 
7872ed5a8cSapatard@mandriva.com 	return 0;
7972ed5a8cSapatard@mandriva.com }
8072ed5a8cSapatard@mandriva.com 
8172ed5a8cSapatard@mandriva.com #define CHAN_MIX_NORMAL	0x00
8272ed5a8cSapatard@mandriva.com #define CHAN_MIX_BOTH	0x55
8372ed5a8cSapatard@mandriva.com #define CHAN_MIX_SWAP	0xFF
8472ed5a8cSapatard@mandriva.com 
cs42l51_set_chan_mix(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)8572ed5a8cSapatard@mandriva.com static int cs42l51_set_chan_mix(struct snd_kcontrol *kcontrol,
8672ed5a8cSapatard@mandriva.com 			struct snd_ctl_elem_value *ucontrol)
8772ed5a8cSapatard@mandriva.com {
881fa49856SKuninori Morimoto 	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
8972ed5a8cSapatard@mandriva.com 	unsigned char val;
9072ed5a8cSapatard@mandriva.com 
9189300b4eSTakashi Iwai 	switch (ucontrol->value.enumerated.item[0]) {
9272ed5a8cSapatard@mandriva.com 	default:
9372ed5a8cSapatard@mandriva.com 	case 0:
9472ed5a8cSapatard@mandriva.com 		val = CHAN_MIX_NORMAL;
9572ed5a8cSapatard@mandriva.com 		break;
9672ed5a8cSapatard@mandriva.com 	case 1:
9772ed5a8cSapatard@mandriva.com 		val = CHAN_MIX_BOTH;
9872ed5a8cSapatard@mandriva.com 		break;
9972ed5a8cSapatard@mandriva.com 	case 2:
10072ed5a8cSapatard@mandriva.com 		val = CHAN_MIX_SWAP;
10172ed5a8cSapatard@mandriva.com 		break;
10272ed5a8cSapatard@mandriva.com 	}
10372ed5a8cSapatard@mandriva.com 
1041fa49856SKuninori Morimoto 	snd_soc_component_write(component, CS42L51_PCM_MIXER, val);
10572ed5a8cSapatard@mandriva.com 
10672ed5a8cSapatard@mandriva.com 	return 1;
10772ed5a8cSapatard@mandriva.com }
10872ed5a8cSapatard@mandriva.com 
10972ed5a8cSapatard@mandriva.com static const DECLARE_TLV_DB_SCALE(adc_pcm_tlv, -5150, 50, 0);
11072ed5a8cSapatard@mandriva.com static const DECLARE_TLV_DB_SCALE(tone_tlv, -1050, 150, 0);
1117272e051SBrian Austin 
1127272e051SBrian Austin static const DECLARE_TLV_DB_SCALE(aout_tlv, -10200, 50, 0);
11372ed5a8cSapatard@mandriva.com 
11472ed5a8cSapatard@mandriva.com static const DECLARE_TLV_DB_SCALE(boost_tlv, 1600, 1600, 0);
115e04232c3SOlivier Moysan static const DECLARE_TLV_DB_SCALE(adc_boost_tlv, 2000, 2000, 0);
11672ed5a8cSapatard@mandriva.com static const char *chan_mix[] = {
11772ed5a8cSapatard@mandriva.com 	"L R",
11872ed5a8cSapatard@mandriva.com 	"L+R",
11972ed5a8cSapatard@mandriva.com 	"R L",
12072ed5a8cSapatard@mandriva.com };
12172ed5a8cSapatard@mandriva.com 
12264108872SGuillermo Rodríguez static const DECLARE_TLV_DB_SCALE(pga_tlv, -300, 50, 0);
12364108872SGuillermo Rodríguez static const DECLARE_TLV_DB_SCALE(adc_att_tlv, -9600, 100, 0);
12464108872SGuillermo Rodríguez 
1256109ab2bSTakashi Iwai static SOC_ENUM_SINGLE_EXT_DECL(cs42l51_chan_mix, chan_mix);
12672ed5a8cSapatard@mandriva.com 
12772ed5a8cSapatard@mandriva.com static const struct snd_kcontrol_new cs42l51_snd_controls[] = {
12872ed5a8cSapatard@mandriva.com 	SOC_DOUBLE_R_SX_TLV("PCM Playback Volume",
12972ed5a8cSapatard@mandriva.com 			CS42L51_PCMA_VOL, CS42L51_PCMB_VOL,
1307272e051SBrian Austin 			0, 0x19, 0x7F, adc_pcm_tlv),
13172ed5a8cSapatard@mandriva.com 	SOC_DOUBLE_R("PCM Playback Switch",
13272ed5a8cSapatard@mandriva.com 			CS42L51_PCMA_VOL, CS42L51_PCMB_VOL, 7, 1, 1),
13372ed5a8cSapatard@mandriva.com 	SOC_DOUBLE_R_SX_TLV("Analog Playback Volume",
13472ed5a8cSapatard@mandriva.com 			CS42L51_AOUTA_VOL, CS42L51_AOUTB_VOL,
1351d99f243SBrian Austin 			0, 0x34, 0xE4, aout_tlv),
13672ed5a8cSapatard@mandriva.com 	SOC_DOUBLE_R_SX_TLV("ADC Mixer Volume",
13772ed5a8cSapatard@mandriva.com 			CS42L51_ADCA_VOL, CS42L51_ADCB_VOL,
1387272e051SBrian Austin 			0, 0x19, 0x7F, adc_pcm_tlv),
13972ed5a8cSapatard@mandriva.com 	SOC_DOUBLE_R("ADC Mixer Switch",
14072ed5a8cSapatard@mandriva.com 			CS42L51_ADCA_VOL, CS42L51_ADCB_VOL, 7, 1, 1),
14164108872SGuillermo Rodríguez 	SOC_DOUBLE_R_SX_TLV("ADC Attenuator Volume",
14264108872SGuillermo Rodríguez 			CS42L51_ADCA_ATT, CS42L51_ADCB_ATT,
14364108872SGuillermo Rodríguez 			0, 0xA0, 96, adc_att_tlv),
14464108872SGuillermo Rodríguez 	SOC_DOUBLE_R_SX_TLV("PGA Volume",
14564108872SGuillermo Rodríguez 			CS42L51_ALC_PGA_CTL, CS42L51_ALC_PGB_CTL,
1463d1bb6ccSCharles Keepax 			0, 0x1A, 30, pga_tlv),
14772ed5a8cSapatard@mandriva.com 	SOC_SINGLE("Playback Deemphasis Switch", CS42L51_DAC_CTL, 3, 1, 0),
14872ed5a8cSapatard@mandriva.com 	SOC_SINGLE("Auto-Mute Switch", CS42L51_DAC_CTL, 2, 1, 0),
14972ed5a8cSapatard@mandriva.com 	SOC_SINGLE("Soft Ramp Switch", CS42L51_DAC_CTL, 1, 1, 0),
15072ed5a8cSapatard@mandriva.com 	SOC_SINGLE("Zero Cross Switch", CS42L51_DAC_CTL, 0, 0, 0),
15172ed5a8cSapatard@mandriva.com 	SOC_DOUBLE_TLV("Mic Boost Volume",
15272ed5a8cSapatard@mandriva.com 			CS42L51_MIC_CTL, 0, 1, 1, 0, boost_tlv),
153e04232c3SOlivier Moysan 	SOC_DOUBLE_TLV("ADC Boost Volume",
154e04232c3SOlivier Moysan 		       CS42L51_MIC_CTL, 5, 6, 1, 0, adc_boost_tlv),
15572ed5a8cSapatard@mandriva.com 	SOC_SINGLE_TLV("Bass Volume", CS42L51_TONE_CTL, 0, 0xf, 1, tone_tlv),
15672ed5a8cSapatard@mandriva.com 	SOC_SINGLE_TLV("Treble Volume", CS42L51_TONE_CTL, 4, 0xf, 1, tone_tlv),
15772ed5a8cSapatard@mandriva.com 	SOC_ENUM_EXT("PCM channel mixer",
15872ed5a8cSapatard@mandriva.com 			cs42l51_chan_mix,
15972ed5a8cSapatard@mandriva.com 			cs42l51_get_chan_mix, cs42l51_set_chan_mix),
16072ed5a8cSapatard@mandriva.com };
16172ed5a8cSapatard@mandriva.com 
16272ed5a8cSapatard@mandriva.com /*
16372ed5a8cSapatard@mandriva.com  * to power down, one must:
16472ed5a8cSapatard@mandriva.com  * 1.) Enable the PDN bit
16572ed5a8cSapatard@mandriva.com  * 2.) enable power-down for the select channels
16672ed5a8cSapatard@mandriva.com  * 3.) disable the PDN bit.
16772ed5a8cSapatard@mandriva.com  */
cs42l51_pdn_event(struct snd_soc_dapm_widget * w,struct snd_kcontrol * kcontrol,int event)16872ed5a8cSapatard@mandriva.com static int cs42l51_pdn_event(struct snd_soc_dapm_widget *w,
16972ed5a8cSapatard@mandriva.com 		struct snd_kcontrol *kcontrol, int event)
17072ed5a8cSapatard@mandriva.com {
1711fa49856SKuninori Morimoto 	struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
172ceb3c068SLars-Peter Clausen 
17372ed5a8cSapatard@mandriva.com 	switch (event) {
17472ed5a8cSapatard@mandriva.com 	case SND_SOC_DAPM_PRE_PMD:
1751fa49856SKuninori Morimoto 		snd_soc_component_update_bits(component, CS42L51_POWER_CTL1,
176e94de1e8SAxel Lin 				    CS42L51_POWER_CTL1_PDN,
177e94de1e8SAxel Lin 				    CS42L51_POWER_CTL1_PDN);
17872ed5a8cSapatard@mandriva.com 		break;
17972ed5a8cSapatard@mandriva.com 	default:
18072ed5a8cSapatard@mandriva.com 	case SND_SOC_DAPM_POST_PMD:
1811fa49856SKuninori Morimoto 		snd_soc_component_update_bits(component, CS42L51_POWER_CTL1,
182e94de1e8SAxel Lin 				    CS42L51_POWER_CTL1_PDN, 0);
18372ed5a8cSapatard@mandriva.com 		break;
18472ed5a8cSapatard@mandriva.com 	}
18572ed5a8cSapatard@mandriva.com 
18672ed5a8cSapatard@mandriva.com 	return 0;
18772ed5a8cSapatard@mandriva.com }
18872ed5a8cSapatard@mandriva.com 
18972ed5a8cSapatard@mandriva.com static const char *cs42l51_dac_names[] = {"Direct PCM",
19072ed5a8cSapatard@mandriva.com 	"DSP PCM", "ADC"};
1916109ab2bSTakashi Iwai static SOC_ENUM_SINGLE_DECL(cs42l51_dac_mux_enum,
1926109ab2bSTakashi Iwai 			    CS42L51_DAC_CTL, 6, cs42l51_dac_names);
19372ed5a8cSapatard@mandriva.com static const struct snd_kcontrol_new cs42l51_dac_mux_controls =
19472ed5a8cSapatard@mandriva.com 	SOC_DAPM_ENUM("Route", cs42l51_dac_mux_enum);
19572ed5a8cSapatard@mandriva.com 
19672ed5a8cSapatard@mandriva.com static const char *cs42l51_adcl_names[] = {"AIN1 Left", "AIN2 Left",
19772ed5a8cSapatard@mandriva.com 	"MIC Left", "MIC+preamp Left"};
1986109ab2bSTakashi Iwai static SOC_ENUM_SINGLE_DECL(cs42l51_adcl_mux_enum,
1996109ab2bSTakashi Iwai 			    CS42L51_ADC_INPUT, 4, cs42l51_adcl_names);
20072ed5a8cSapatard@mandriva.com static const struct snd_kcontrol_new cs42l51_adcl_mux_controls =
20172ed5a8cSapatard@mandriva.com 	SOC_DAPM_ENUM("Route", cs42l51_adcl_mux_enum);
20272ed5a8cSapatard@mandriva.com 
20372ed5a8cSapatard@mandriva.com static const char *cs42l51_adcr_names[] = {"AIN1 Right", "AIN2 Right",
20472ed5a8cSapatard@mandriva.com 	"MIC Right", "MIC+preamp Right"};
2056109ab2bSTakashi Iwai static SOC_ENUM_SINGLE_DECL(cs42l51_adcr_mux_enum,
2066109ab2bSTakashi Iwai 			    CS42L51_ADC_INPUT, 6, cs42l51_adcr_names);
20772ed5a8cSapatard@mandriva.com static const struct snd_kcontrol_new cs42l51_adcr_mux_controls =
20872ed5a8cSapatard@mandriva.com 	SOC_DAPM_ENUM("Route", cs42l51_adcr_mux_enum);
20972ed5a8cSapatard@mandriva.com 
21072ed5a8cSapatard@mandriva.com static const struct snd_soc_dapm_widget cs42l51_dapm_widgets[] = {
2114110e9a1SOlivier Moysan 	SND_SOC_DAPM_SUPPLY("Mic Bias", CS42L51_MIC_POWER_CTL, 1, 1, NULL,
2124110e9a1SOlivier Moysan 			    SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
21372ed5a8cSapatard@mandriva.com 	SND_SOC_DAPM_PGA_E("Left PGA", CS42L51_POWER_CTL1, 3, 1, NULL, 0,
21472ed5a8cSapatard@mandriva.com 		cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD),
21572ed5a8cSapatard@mandriva.com 	SND_SOC_DAPM_PGA_E("Right PGA", CS42L51_POWER_CTL1, 4, 1, NULL, 0,
21672ed5a8cSapatard@mandriva.com 		cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD),
21772ed5a8cSapatard@mandriva.com 	SND_SOC_DAPM_ADC_E("Left ADC", "Left HiFi Capture",
21872ed5a8cSapatard@mandriva.com 		CS42L51_POWER_CTL1, 1, 1,
21972ed5a8cSapatard@mandriva.com 		cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD),
22072ed5a8cSapatard@mandriva.com 	SND_SOC_DAPM_ADC_E("Right ADC", "Right HiFi Capture",
22172ed5a8cSapatard@mandriva.com 		CS42L51_POWER_CTL1, 2, 1,
22272ed5a8cSapatard@mandriva.com 		cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD),
223abe3b672SOlivier Moysan 	SND_SOC_DAPM_DAC_E("Left DAC", NULL, CS42L51_POWER_CTL1, 5, 1,
22472ed5a8cSapatard@mandriva.com 			   cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD),
225abe3b672SOlivier Moysan 	SND_SOC_DAPM_DAC_E("Right DAC", NULL, CS42L51_POWER_CTL1, 6, 1,
22672ed5a8cSapatard@mandriva.com 			   cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD),
22772ed5a8cSapatard@mandriva.com 
22872ed5a8cSapatard@mandriva.com 	/* analog/mic */
22972ed5a8cSapatard@mandriva.com 	SND_SOC_DAPM_INPUT("AIN1L"),
23072ed5a8cSapatard@mandriva.com 	SND_SOC_DAPM_INPUT("AIN1R"),
23172ed5a8cSapatard@mandriva.com 	SND_SOC_DAPM_INPUT("AIN2L"),
23272ed5a8cSapatard@mandriva.com 	SND_SOC_DAPM_INPUT("AIN2R"),
23372ed5a8cSapatard@mandriva.com 	SND_SOC_DAPM_INPUT("MICL"),
23472ed5a8cSapatard@mandriva.com 	SND_SOC_DAPM_INPUT("MICR"),
23572ed5a8cSapatard@mandriva.com 
23672ed5a8cSapatard@mandriva.com 	SND_SOC_DAPM_MIXER("Mic Preamp Left",
23772ed5a8cSapatard@mandriva.com 		CS42L51_MIC_POWER_CTL, 2, 1, NULL, 0),
23872ed5a8cSapatard@mandriva.com 	SND_SOC_DAPM_MIXER("Mic Preamp Right",
23972ed5a8cSapatard@mandriva.com 		CS42L51_MIC_POWER_CTL, 3, 1, NULL, 0),
24072ed5a8cSapatard@mandriva.com 
24172ed5a8cSapatard@mandriva.com 	/* HP */
24272ed5a8cSapatard@mandriva.com 	SND_SOC_DAPM_OUTPUT("HPL"),
24372ed5a8cSapatard@mandriva.com 	SND_SOC_DAPM_OUTPUT("HPR"),
24472ed5a8cSapatard@mandriva.com 
24572ed5a8cSapatard@mandriva.com 	/* mux */
24672ed5a8cSapatard@mandriva.com 	SND_SOC_DAPM_MUX("DAC Mux", SND_SOC_NOPM, 0, 0,
24772ed5a8cSapatard@mandriva.com 		&cs42l51_dac_mux_controls),
24872ed5a8cSapatard@mandriva.com 	SND_SOC_DAPM_MUX("PGA-ADC Mux Left", SND_SOC_NOPM, 0, 0,
24972ed5a8cSapatard@mandriva.com 		&cs42l51_adcl_mux_controls),
25072ed5a8cSapatard@mandriva.com 	SND_SOC_DAPM_MUX("PGA-ADC Mux Right", SND_SOC_NOPM, 0, 0,
25172ed5a8cSapatard@mandriva.com 		&cs42l51_adcr_mux_controls),
25272ed5a8cSapatard@mandriva.com };
25372ed5a8cSapatard@mandriva.com 
mclk_event(struct snd_soc_dapm_widget * w,struct snd_kcontrol * kcontrol,int event)25420afe581SOlivier Moysan static int mclk_event(struct snd_soc_dapm_widget *w,
25520afe581SOlivier Moysan 		      struct snd_kcontrol *kcontrol, int event)
25620afe581SOlivier Moysan {
25720afe581SOlivier Moysan 	struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm);
25820afe581SOlivier Moysan 	struct cs42l51_private *cs42l51 = snd_soc_component_get_drvdata(comp);
25920afe581SOlivier Moysan 
26020afe581SOlivier Moysan 	switch (event) {
26120afe581SOlivier Moysan 	case SND_SOC_DAPM_PRE_PMU:
26220afe581SOlivier Moysan 		return clk_prepare_enable(cs42l51->mclk_handle);
26320afe581SOlivier Moysan 	case SND_SOC_DAPM_POST_PMD:
26420afe581SOlivier Moysan 		/* Delay mclk shutdown to fulfill power-down sequence requirements */
26520afe581SOlivier Moysan 		msleep(20);
26620afe581SOlivier Moysan 		clk_disable_unprepare(cs42l51->mclk_handle);
26720afe581SOlivier Moysan 		break;
26820afe581SOlivier Moysan 	}
26920afe581SOlivier Moysan 
27020afe581SOlivier Moysan 	return 0;
27120afe581SOlivier Moysan }
27220afe581SOlivier Moysan 
2735e8d63a7SOlivier Moysan static const struct snd_soc_dapm_widget cs42l51_dapm_mclk_widgets[] = {
27420afe581SOlivier Moysan 	SND_SOC_DAPM_SUPPLY("MCLK", SND_SOC_NOPM, 0, 0, mclk_event,
27520afe581SOlivier Moysan 			    SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
2765e8d63a7SOlivier Moysan };
2775e8d63a7SOlivier Moysan 
27872ed5a8cSapatard@mandriva.com static const struct snd_soc_dapm_route cs42l51_routes[] = {
27972ed5a8cSapatard@mandriva.com 	{"HPL", NULL, "Left DAC"},
28072ed5a8cSapatard@mandriva.com 	{"HPR", NULL, "Right DAC"},
28172ed5a8cSapatard@mandriva.com 
282abe3b672SOlivier Moysan 	{"Right DAC", NULL, "DAC Mux"},
283abe3b672SOlivier Moysan 	{"Left DAC", NULL, "DAC Mux"},
284abe3b672SOlivier Moysan 
285abe3b672SOlivier Moysan 	{"DAC Mux", "Direct PCM", "Playback"},
286abe3b672SOlivier Moysan 	{"DAC Mux", "DSP PCM", "Playback"},
287abe3b672SOlivier Moysan 
28872ed5a8cSapatard@mandriva.com 	{"Left ADC", NULL, "Left PGA"},
28972ed5a8cSapatard@mandriva.com 	{"Right ADC", NULL, "Right PGA"},
29072ed5a8cSapatard@mandriva.com 
29172ed5a8cSapatard@mandriva.com 	{"Mic Preamp Left",  NULL,  "MICL"},
29272ed5a8cSapatard@mandriva.com 	{"Mic Preamp Right", NULL,  "MICR"},
29372ed5a8cSapatard@mandriva.com 
29472ed5a8cSapatard@mandriva.com 	{"PGA-ADC Mux Left",  "AIN1 Left",        "AIN1L" },
29572ed5a8cSapatard@mandriva.com 	{"PGA-ADC Mux Left",  "AIN2 Left",        "AIN2L" },
29672ed5a8cSapatard@mandriva.com 	{"PGA-ADC Mux Left",  "MIC Left",         "MICL"  },
29772ed5a8cSapatard@mandriva.com 	{"PGA-ADC Mux Left",  "MIC+preamp Left",  "Mic Preamp Left" },
29872ed5a8cSapatard@mandriva.com 	{"PGA-ADC Mux Right", "AIN1 Right",       "AIN1R" },
29972ed5a8cSapatard@mandriva.com 	{"PGA-ADC Mux Right", "AIN2 Right",       "AIN2R" },
30072ed5a8cSapatard@mandriva.com 	{"PGA-ADC Mux Right", "MIC Right",        "MICR" },
30172ed5a8cSapatard@mandriva.com 	{"PGA-ADC Mux Right", "MIC+preamp Right", "Mic Preamp Right" },
30272ed5a8cSapatard@mandriva.com 
30372ed5a8cSapatard@mandriva.com 	{"Left PGA", NULL, "PGA-ADC Mux Left"},
30472ed5a8cSapatard@mandriva.com 	{"Right PGA", NULL, "PGA-ADC Mux Right"},
30572ed5a8cSapatard@mandriva.com };
30672ed5a8cSapatard@mandriva.com 
cs42l51_set_dai_fmt(struct snd_soc_dai * codec_dai,unsigned int format)30772ed5a8cSapatard@mandriva.com static int cs42l51_set_dai_fmt(struct snd_soc_dai *codec_dai,
30872ed5a8cSapatard@mandriva.com 		unsigned int format)
30972ed5a8cSapatard@mandriva.com {
3101fa49856SKuninori Morimoto 	struct snd_soc_component *component = codec_dai->component;
3111fa49856SKuninori Morimoto 	struct cs42l51_private *cs42l51 = snd_soc_component_get_drvdata(component);
31272ed5a8cSapatard@mandriva.com 
31372ed5a8cSapatard@mandriva.com 	switch (format & SND_SOC_DAIFMT_FORMAT_MASK) {
31472ed5a8cSapatard@mandriva.com 	case SND_SOC_DAIFMT_I2S:
31572ed5a8cSapatard@mandriva.com 	case SND_SOC_DAIFMT_LEFT_J:
31672ed5a8cSapatard@mandriva.com 	case SND_SOC_DAIFMT_RIGHT_J:
31772ed5a8cSapatard@mandriva.com 		cs42l51->audio_mode = format & SND_SOC_DAIFMT_FORMAT_MASK;
31872ed5a8cSapatard@mandriva.com 		break;
31972ed5a8cSapatard@mandriva.com 	default:
3201fa49856SKuninori Morimoto 		dev_err(component->dev, "invalid DAI format\n");
321ac60155fSAxel Lin 		return -EINVAL;
32272ed5a8cSapatard@mandriva.com 	}
32372ed5a8cSapatard@mandriva.com 
32472ed5a8cSapatard@mandriva.com 	switch (format & SND_SOC_DAIFMT_MASTER_MASK) {
32572ed5a8cSapatard@mandriva.com 	case SND_SOC_DAIFMT_CBM_CFM:
32672ed5a8cSapatard@mandriva.com 		cs42l51->func = MODE_MASTER;
32772ed5a8cSapatard@mandriva.com 		break;
32872ed5a8cSapatard@mandriva.com 	case SND_SOC_DAIFMT_CBS_CFS:
32972ed5a8cSapatard@mandriva.com 		cs42l51->func = MODE_SLAVE_AUTO;
33072ed5a8cSapatard@mandriva.com 		break;
33172ed5a8cSapatard@mandriva.com 	default:
3321fa49856SKuninori Morimoto 		dev_err(component->dev, "Unknown master/slave configuration\n");
333ac60155fSAxel Lin 		return -EINVAL;
33472ed5a8cSapatard@mandriva.com 	}
33572ed5a8cSapatard@mandriva.com 
336ac60155fSAxel Lin 	return 0;
33772ed5a8cSapatard@mandriva.com }
33872ed5a8cSapatard@mandriva.com 
33972ed5a8cSapatard@mandriva.com struct cs42l51_ratios {
34072ed5a8cSapatard@mandriva.com 	unsigned int ratio;
34172ed5a8cSapatard@mandriva.com 	unsigned char speed_mode;
34272ed5a8cSapatard@mandriva.com 	unsigned char mclk;
34372ed5a8cSapatard@mandriva.com };
34472ed5a8cSapatard@mandriva.com 
34572ed5a8cSapatard@mandriva.com static struct cs42l51_ratios slave_ratios[] = {
34672ed5a8cSapatard@mandriva.com 	{  512, CS42L51_QSM_MODE, 0 }, {  768, CS42L51_QSM_MODE, 0 },
34772ed5a8cSapatard@mandriva.com 	{ 1024, CS42L51_QSM_MODE, 0 }, { 1536, CS42L51_QSM_MODE, 0 },
34872ed5a8cSapatard@mandriva.com 	{ 2048, CS42L51_QSM_MODE, 0 }, { 3072, CS42L51_QSM_MODE, 0 },
34972ed5a8cSapatard@mandriva.com 	{  256, CS42L51_HSM_MODE, 0 }, {  384, CS42L51_HSM_MODE, 0 },
35072ed5a8cSapatard@mandriva.com 	{  512, CS42L51_HSM_MODE, 0 }, {  768, CS42L51_HSM_MODE, 0 },
35172ed5a8cSapatard@mandriva.com 	{ 1024, CS42L51_HSM_MODE, 0 }, { 1536, CS42L51_HSM_MODE, 0 },
35272ed5a8cSapatard@mandriva.com 	{  128, CS42L51_SSM_MODE, 0 }, {  192, CS42L51_SSM_MODE, 0 },
35372ed5a8cSapatard@mandriva.com 	{  256, CS42L51_SSM_MODE, 0 }, {  384, CS42L51_SSM_MODE, 0 },
35472ed5a8cSapatard@mandriva.com 	{  512, CS42L51_SSM_MODE, 0 }, {  768, CS42L51_SSM_MODE, 0 },
35572ed5a8cSapatard@mandriva.com 	{  128, CS42L51_DSM_MODE, 0 }, {  192, CS42L51_DSM_MODE, 0 },
35672ed5a8cSapatard@mandriva.com 	{  256, CS42L51_DSM_MODE, 0 }, {  384, CS42L51_DSM_MODE, 0 },
35772ed5a8cSapatard@mandriva.com };
35872ed5a8cSapatard@mandriva.com 
35972ed5a8cSapatard@mandriva.com static struct cs42l51_ratios slave_auto_ratios[] = {
36072ed5a8cSapatard@mandriva.com 	{ 1024, CS42L51_QSM_MODE, 0 }, { 1536, CS42L51_QSM_MODE, 0 },
36172ed5a8cSapatard@mandriva.com 	{ 2048, CS42L51_QSM_MODE, 1 }, { 3072, CS42L51_QSM_MODE, 1 },
36272ed5a8cSapatard@mandriva.com 	{  512, CS42L51_HSM_MODE, 0 }, {  768, CS42L51_HSM_MODE, 0 },
36372ed5a8cSapatard@mandriva.com 	{ 1024, CS42L51_HSM_MODE, 1 }, { 1536, CS42L51_HSM_MODE, 1 },
36472ed5a8cSapatard@mandriva.com 	{  256, CS42L51_SSM_MODE, 0 }, {  384, CS42L51_SSM_MODE, 0 },
36572ed5a8cSapatard@mandriva.com 	{  512, CS42L51_SSM_MODE, 1 }, {  768, CS42L51_SSM_MODE, 1 },
36672ed5a8cSapatard@mandriva.com 	{  128, CS42L51_DSM_MODE, 0 }, {  192, CS42L51_DSM_MODE, 0 },
36772ed5a8cSapatard@mandriva.com 	{  256, CS42L51_DSM_MODE, 1 }, {  384, CS42L51_DSM_MODE, 1 },
36872ed5a8cSapatard@mandriva.com };
36972ed5a8cSapatard@mandriva.com 
3702f7c4ce0SOlivier Moysan /*
3712f7c4ce0SOlivier Moysan  * Master mode mclk/fs ratios.
3722f7c4ce0SOlivier Moysan  * Recommended configurations are SSM for 4-50khz and DSM for 50-100kHz ranges
3732f7c4ce0SOlivier Moysan  * The table below provides support of following ratios:
3742f7c4ce0SOlivier Moysan  * 128: SSM (%128) with div2 disabled
3752f7c4ce0SOlivier Moysan  * 256: SSM (%128) with div2 enabled
3762f7c4ce0SOlivier Moysan  * In both cases, if sampling rate is above 50kHz, SSM is overridden
3772f7c4ce0SOlivier Moysan  * with DSM (%128) configuration
3782f7c4ce0SOlivier Moysan  */
3792f7c4ce0SOlivier Moysan static struct cs42l51_ratios master_ratios[] = {
3802f7c4ce0SOlivier Moysan 	{ 128, CS42L51_SSM_MODE, 0 }, { 256, CS42L51_SSM_MODE, 1 },
3812f7c4ce0SOlivier Moysan };
3822f7c4ce0SOlivier Moysan 
cs42l51_set_dai_sysclk(struct snd_soc_dai * codec_dai,int clk_id,unsigned int freq,int dir)38372ed5a8cSapatard@mandriva.com static int cs42l51_set_dai_sysclk(struct snd_soc_dai *codec_dai,
38472ed5a8cSapatard@mandriva.com 		int clk_id, unsigned int freq, int dir)
38572ed5a8cSapatard@mandriva.com {
3861fa49856SKuninori Morimoto 	struct snd_soc_component *component = codec_dai->component;
3871fa49856SKuninori Morimoto 	struct cs42l51_private *cs42l51 = snd_soc_component_get_drvdata(component);
38872ed5a8cSapatard@mandriva.com 
38972ed5a8cSapatard@mandriva.com 	cs42l51->mclk = freq;
39072ed5a8cSapatard@mandriva.com 	return 0;
39172ed5a8cSapatard@mandriva.com }
39272ed5a8cSapatard@mandriva.com 
cs42l51_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * params,struct snd_soc_dai * dai)39372ed5a8cSapatard@mandriva.com static int cs42l51_hw_params(struct snd_pcm_substream *substream,
39472ed5a8cSapatard@mandriva.com 		struct snd_pcm_hw_params *params,
39572ed5a8cSapatard@mandriva.com 		struct snd_soc_dai *dai)
39672ed5a8cSapatard@mandriva.com {
3971fa49856SKuninori Morimoto 	struct snd_soc_component *component = dai->component;
3981fa49856SKuninori Morimoto 	struct cs42l51_private *cs42l51 = snd_soc_component_get_drvdata(component);
39972ed5a8cSapatard@mandriva.com 	int ret;
40072ed5a8cSapatard@mandriva.com 	unsigned int i;
40172ed5a8cSapatard@mandriva.com 	unsigned int rate;
40272ed5a8cSapatard@mandriva.com 	unsigned int ratio;
40372ed5a8cSapatard@mandriva.com 	struct cs42l51_ratios *ratios = NULL;
40472ed5a8cSapatard@mandriva.com 	int nr_ratios = 0;
4052f7c4ce0SOlivier Moysan 	int intf_ctl, power_ctl, fmt, mode;
40672ed5a8cSapatard@mandriva.com 
40772ed5a8cSapatard@mandriva.com 	switch (cs42l51->func) {
40872ed5a8cSapatard@mandriva.com 	case MODE_MASTER:
4092f7c4ce0SOlivier Moysan 		ratios = master_ratios;
4102f7c4ce0SOlivier Moysan 		nr_ratios = ARRAY_SIZE(master_ratios);
4112f7c4ce0SOlivier Moysan 		break;
41272ed5a8cSapatard@mandriva.com 	case MODE_SLAVE:
41372ed5a8cSapatard@mandriva.com 		ratios = slave_ratios;
41472ed5a8cSapatard@mandriva.com 		nr_ratios = ARRAY_SIZE(slave_ratios);
41572ed5a8cSapatard@mandriva.com 		break;
41672ed5a8cSapatard@mandriva.com 	case MODE_SLAVE_AUTO:
41772ed5a8cSapatard@mandriva.com 		ratios = slave_auto_ratios;
41872ed5a8cSapatard@mandriva.com 		nr_ratios = ARRAY_SIZE(slave_auto_ratios);
41972ed5a8cSapatard@mandriva.com 		break;
42072ed5a8cSapatard@mandriva.com 	}
42172ed5a8cSapatard@mandriva.com 
42272ed5a8cSapatard@mandriva.com 	/* Figure out which MCLK/LRCK ratio to use */
42372ed5a8cSapatard@mandriva.com 	rate = params_rate(params);     /* Sampling rate, in Hz */
42472ed5a8cSapatard@mandriva.com 	ratio = cs42l51->mclk / rate;    /* MCLK/LRCK ratio */
42572ed5a8cSapatard@mandriva.com 	for (i = 0; i < nr_ratios; i++) {
42672ed5a8cSapatard@mandriva.com 		if (ratios[i].ratio == ratio)
42772ed5a8cSapatard@mandriva.com 			break;
42872ed5a8cSapatard@mandriva.com 	}
42972ed5a8cSapatard@mandriva.com 
43072ed5a8cSapatard@mandriva.com 	if (i == nr_ratios) {
43172ed5a8cSapatard@mandriva.com 		/* We did not find a matching ratio */
4321fa49856SKuninori Morimoto 		dev_err(component->dev, "could not find matching ratio\n");
43372ed5a8cSapatard@mandriva.com 		return -EINVAL;
43472ed5a8cSapatard@mandriva.com 	}
43572ed5a8cSapatard@mandriva.com 
436a11f8a1cSKuninori Morimoto 	intf_ctl = snd_soc_component_read(component, CS42L51_INTF_CTL);
437a11f8a1cSKuninori Morimoto 	power_ctl = snd_soc_component_read(component, CS42L51_MIC_POWER_CTL);
43872ed5a8cSapatard@mandriva.com 
43972ed5a8cSapatard@mandriva.com 	intf_ctl &= ~(CS42L51_INTF_CTL_MASTER | CS42L51_INTF_CTL_ADC_I2S
44072ed5a8cSapatard@mandriva.com 			| CS42L51_INTF_CTL_DAC_FORMAT(7));
44172ed5a8cSapatard@mandriva.com 	power_ctl &= ~(CS42L51_MIC_POWER_CTL_SPEED(3)
44272ed5a8cSapatard@mandriva.com 			| CS42L51_MIC_POWER_CTL_MCLK_DIV2);
44372ed5a8cSapatard@mandriva.com 
44472ed5a8cSapatard@mandriva.com 	switch (cs42l51->func) {
44572ed5a8cSapatard@mandriva.com 	case MODE_MASTER:
44672ed5a8cSapatard@mandriva.com 		intf_ctl |= CS42L51_INTF_CTL_MASTER;
4472f7c4ce0SOlivier Moysan 		mode = ratios[i].speed_mode;
4482f7c4ce0SOlivier Moysan 		/* Force DSM mode if sampling rate is above 50kHz */
4492f7c4ce0SOlivier Moysan 		if (rate > 50000)
4502f7c4ce0SOlivier Moysan 			mode = CS42L51_DSM_MODE;
4512f7c4ce0SOlivier Moysan 		power_ctl |= CS42L51_MIC_POWER_CTL_SPEED(mode);
4522f7c4ce0SOlivier Moysan 		/*
4532f7c4ce0SOlivier Moysan 		 * Auto detect mode is not applicable for master mode and has to
4542f7c4ce0SOlivier Moysan 		 * be disabled. Otherwise SPEED[1:0] bits will be ignored.
4552f7c4ce0SOlivier Moysan 		 */
4562f7c4ce0SOlivier Moysan 		power_ctl &= ~CS42L51_MIC_POWER_CTL_AUTO;
45772ed5a8cSapatard@mandriva.com 		break;
45872ed5a8cSapatard@mandriva.com 	case MODE_SLAVE:
45972ed5a8cSapatard@mandriva.com 		power_ctl |= CS42L51_MIC_POWER_CTL_SPEED(ratios[i].speed_mode);
46072ed5a8cSapatard@mandriva.com 		break;
46172ed5a8cSapatard@mandriva.com 	case MODE_SLAVE_AUTO:
46272ed5a8cSapatard@mandriva.com 		power_ctl |= CS42L51_MIC_POWER_CTL_AUTO;
46372ed5a8cSapatard@mandriva.com 		break;
46472ed5a8cSapatard@mandriva.com 	}
46572ed5a8cSapatard@mandriva.com 
46672ed5a8cSapatard@mandriva.com 	switch (cs42l51->audio_mode) {
46772ed5a8cSapatard@mandriva.com 	case SND_SOC_DAIFMT_I2S:
46872ed5a8cSapatard@mandriva.com 		intf_ctl |= CS42L51_INTF_CTL_ADC_I2S;
46972ed5a8cSapatard@mandriva.com 		intf_ctl |= CS42L51_INTF_CTL_DAC_FORMAT(CS42L51_DAC_DIF_I2S);
47072ed5a8cSapatard@mandriva.com 		break;
47172ed5a8cSapatard@mandriva.com 	case SND_SOC_DAIFMT_LEFT_J:
47272ed5a8cSapatard@mandriva.com 		intf_ctl |= CS42L51_INTF_CTL_DAC_FORMAT(CS42L51_DAC_DIF_LJ24);
47372ed5a8cSapatard@mandriva.com 		break;
47472ed5a8cSapatard@mandriva.com 	case SND_SOC_DAIFMT_RIGHT_J:
4751b6b0dfaSMark Brown 		switch (params_width(params)) {
4761b6b0dfaSMark Brown 		case 16:
47772ed5a8cSapatard@mandriva.com 			fmt = CS42L51_DAC_DIF_RJ16;
47872ed5a8cSapatard@mandriva.com 			break;
4791b6b0dfaSMark Brown 		case 18:
48072ed5a8cSapatard@mandriva.com 			fmt = CS42L51_DAC_DIF_RJ18;
48172ed5a8cSapatard@mandriva.com 			break;
4821b6b0dfaSMark Brown 		case 20:
48372ed5a8cSapatard@mandriva.com 			fmt = CS42L51_DAC_DIF_RJ20;
48472ed5a8cSapatard@mandriva.com 			break;
4851b6b0dfaSMark Brown 		case 24:
48672ed5a8cSapatard@mandriva.com 			fmt = CS42L51_DAC_DIF_RJ24;
48772ed5a8cSapatard@mandriva.com 			break;
48872ed5a8cSapatard@mandriva.com 		default:
4891fa49856SKuninori Morimoto 			dev_err(component->dev, "unknown format\n");
49072ed5a8cSapatard@mandriva.com 			return -EINVAL;
49172ed5a8cSapatard@mandriva.com 		}
49272ed5a8cSapatard@mandriva.com 		intf_ctl |= CS42L51_INTF_CTL_DAC_FORMAT(fmt);
49372ed5a8cSapatard@mandriva.com 		break;
49472ed5a8cSapatard@mandriva.com 	default:
4951fa49856SKuninori Morimoto 		dev_err(component->dev, "unknown format\n");
49672ed5a8cSapatard@mandriva.com 		return -EINVAL;
49772ed5a8cSapatard@mandriva.com 	}
49872ed5a8cSapatard@mandriva.com 
49972ed5a8cSapatard@mandriva.com 	if (ratios[i].mclk)
50072ed5a8cSapatard@mandriva.com 		power_ctl |= CS42L51_MIC_POWER_CTL_MCLK_DIV2;
50172ed5a8cSapatard@mandriva.com 
5021fa49856SKuninori Morimoto 	ret = snd_soc_component_write(component, CS42L51_INTF_CTL, intf_ctl);
50372ed5a8cSapatard@mandriva.com 	if (ret < 0)
50472ed5a8cSapatard@mandriva.com 		return ret;
50572ed5a8cSapatard@mandriva.com 
5061fa49856SKuninori Morimoto 	ret = snd_soc_component_write(component, CS42L51_MIC_POWER_CTL, power_ctl);
50772ed5a8cSapatard@mandriva.com 	if (ret < 0)
50872ed5a8cSapatard@mandriva.com 		return ret;
50972ed5a8cSapatard@mandriva.com 
51072ed5a8cSapatard@mandriva.com 	return 0;
51172ed5a8cSapatard@mandriva.com }
51272ed5a8cSapatard@mandriva.com 
cs42l51_dai_mute(struct snd_soc_dai * dai,int mute,int direction)51303c0f1b5SKuninori Morimoto static int cs42l51_dai_mute(struct snd_soc_dai *dai, int mute, int direction)
51472ed5a8cSapatard@mandriva.com {
5151fa49856SKuninori Morimoto 	struct snd_soc_component *component = dai->component;
51672ed5a8cSapatard@mandriva.com 	int reg;
51772ed5a8cSapatard@mandriva.com 	int mask = CS42L51_DAC_OUT_CTL_DACA_MUTE|CS42L51_DAC_OUT_CTL_DACB_MUTE;
51872ed5a8cSapatard@mandriva.com 
519a11f8a1cSKuninori Morimoto 	reg = snd_soc_component_read(component, CS42L51_DAC_OUT_CTL);
52072ed5a8cSapatard@mandriva.com 
52172ed5a8cSapatard@mandriva.com 	if (mute)
52272ed5a8cSapatard@mandriva.com 		reg |= mask;
52372ed5a8cSapatard@mandriva.com 	else
52472ed5a8cSapatard@mandriva.com 		reg &= ~mask;
52572ed5a8cSapatard@mandriva.com 
5261fa49856SKuninori Morimoto 	return snd_soc_component_write(component, CS42L51_DAC_OUT_CTL, reg);
52772ed5a8cSapatard@mandriva.com }
52872ed5a8cSapatard@mandriva.com 
cs42l51_of_xlate_dai_id(struct snd_soc_component * component,struct device_node * endpoint)529ad6bb306SOlivier Moysan static int cs42l51_of_xlate_dai_id(struct snd_soc_component *component,
530ad6bb306SOlivier Moysan 				   struct device_node *endpoint)
531ad6bb306SOlivier Moysan {
532ad6bb306SOlivier Moysan 	/* return dai id 0, whatever the endpoint index */
533ad6bb306SOlivier Moysan 	return 0;
534ad6bb306SOlivier Moysan }
535ad6bb306SOlivier Moysan 
53685e7652dSLars-Peter Clausen static const struct snd_soc_dai_ops cs42l51_dai_ops = {
53772ed5a8cSapatard@mandriva.com 	.hw_params      = cs42l51_hw_params,
53872ed5a8cSapatard@mandriva.com 	.set_sysclk     = cs42l51_set_dai_sysclk,
53972ed5a8cSapatard@mandriva.com 	.set_fmt        = cs42l51_set_dai_fmt,
54003c0f1b5SKuninori Morimoto 	.mute_stream    = cs42l51_dai_mute,
54103c0f1b5SKuninori Morimoto 	.no_capture_mute = 1,
54272ed5a8cSapatard@mandriva.com };
54372ed5a8cSapatard@mandriva.com 
544f0fba2adSLiam Girdwood static struct snd_soc_dai_driver cs42l51_dai = {
545f0fba2adSLiam Girdwood 	.name = "cs42l51-hifi",
54672ed5a8cSapatard@mandriva.com 	.playback = {
54772ed5a8cSapatard@mandriva.com 		.stream_name = "Playback",
54872ed5a8cSapatard@mandriva.com 		.channels_min = 1,
54972ed5a8cSapatard@mandriva.com 		.channels_max = 2,
55072ed5a8cSapatard@mandriva.com 		.rates = SNDRV_PCM_RATE_8000_96000,
55172ed5a8cSapatard@mandriva.com 		.formats = CS42L51_FORMATS,
55272ed5a8cSapatard@mandriva.com 	},
55372ed5a8cSapatard@mandriva.com 	.capture = {
55472ed5a8cSapatard@mandriva.com 		.stream_name = "Capture",
55572ed5a8cSapatard@mandriva.com 		.channels_min = 1,
55672ed5a8cSapatard@mandriva.com 		.channels_max = 2,
55772ed5a8cSapatard@mandriva.com 		.rates = SNDRV_PCM_RATE_8000_96000,
55872ed5a8cSapatard@mandriva.com 		.formats = CS42L51_FORMATS,
55972ed5a8cSapatard@mandriva.com 	},
56072ed5a8cSapatard@mandriva.com 	.ops = &cs42l51_dai_ops,
56172ed5a8cSapatard@mandriva.com };
56272ed5a8cSapatard@mandriva.com 
cs42l51_component_probe(struct snd_soc_component * component)5631fa49856SKuninori Morimoto static int cs42l51_component_probe(struct snd_soc_component *component)
56472ed5a8cSapatard@mandriva.com {
565f0fba2adSLiam Girdwood 	int ret, reg;
5665e8d63a7SOlivier Moysan 	struct snd_soc_dapm_context *dapm;
567318e741eSOlivier Moysan 	struct cs42l51_private *cs42l51;
5685e8d63a7SOlivier Moysan 
569318e741eSOlivier Moysan 	cs42l51 = snd_soc_component_get_drvdata(component);
5705e8d63a7SOlivier Moysan 	dapm = snd_soc_component_get_dapm(component);
571318e741eSOlivier Moysan 
572318e741eSOlivier Moysan 	if (cs42l51->mclk_handle)
5735e8d63a7SOlivier Moysan 		snd_soc_dapm_new_controls(dapm, cs42l51_dapm_mclk_widgets, 1);
57472ed5a8cSapatard@mandriva.com 
575f0fba2adSLiam Girdwood 	/*
576f0fba2adSLiam Girdwood 	 * DAC configuration
577f0fba2adSLiam Girdwood 	 * - Use signal processor
578f0fba2adSLiam Girdwood 	 * - auto mute
579f0fba2adSLiam Girdwood 	 * - vol changes immediate
580f0fba2adSLiam Girdwood 	 * - no de-emphasize
581f0fba2adSLiam Girdwood 	 */
582f0fba2adSLiam Girdwood 	reg = CS42L51_DAC_CTL_DATA_SEL(1)
583f0fba2adSLiam Girdwood 		| CS42L51_DAC_CTL_AMUTE | CS42L51_DAC_CTL_DACSZ(0);
5841fa49856SKuninori Morimoto 	ret = snd_soc_component_write(component, CS42L51_DAC_CTL, reg);
585f0fba2adSLiam Girdwood 	if (ret < 0)
586f0fba2adSLiam Girdwood 		return ret;
587f0fba2adSLiam Girdwood 
58872ed5a8cSapatard@mandriva.com 	return 0;
58972ed5a8cSapatard@mandriva.com }
59072ed5a8cSapatard@mandriva.com 
5911fa49856SKuninori Morimoto static const struct snd_soc_component_driver soc_component_device_cs42l51 = {
5921fa49856SKuninori Morimoto 	.probe			= cs42l51_component_probe,
5933f7cec04SAxel Lin 	.controls		= cs42l51_snd_controls,
5943f7cec04SAxel Lin 	.num_controls		= ARRAY_SIZE(cs42l51_snd_controls),
5953f7cec04SAxel Lin 	.dapm_widgets		= cs42l51_dapm_widgets,
5963f7cec04SAxel Lin 	.num_dapm_widgets	= ARRAY_SIZE(cs42l51_dapm_widgets),
5973f7cec04SAxel Lin 	.dapm_routes		= cs42l51_routes,
5983f7cec04SAxel Lin 	.num_dapm_routes	= ARRAY_SIZE(cs42l51_routes),
599ad6bb306SOlivier Moysan 	.of_xlate_dai_id	= cs42l51_of_xlate_dai_id,
6001fa49856SKuninori Morimoto 	.idle_bias_on		= 1,
6011fa49856SKuninori Morimoto 	.use_pmdown_time	= 1,
6021fa49856SKuninori Morimoto 	.endianness		= 1,
603f0fba2adSLiam Girdwood };
60472ed5a8cSapatard@mandriva.com 
cs42l51_writeable_reg(struct device * dev,unsigned int reg)60575a71482SOlivier Moysan static bool cs42l51_writeable_reg(struct device *dev, unsigned int reg)
60675a71482SOlivier Moysan {
60775a71482SOlivier Moysan 	switch (reg) {
60875a71482SOlivier Moysan 	case CS42L51_POWER_CTL1:
60975a71482SOlivier Moysan 	case CS42L51_MIC_POWER_CTL:
61075a71482SOlivier Moysan 	case CS42L51_INTF_CTL:
61175a71482SOlivier Moysan 	case CS42L51_MIC_CTL:
61275a71482SOlivier Moysan 	case CS42L51_ADC_CTL:
61375a71482SOlivier Moysan 	case CS42L51_ADC_INPUT:
61475a71482SOlivier Moysan 	case CS42L51_DAC_OUT_CTL:
61575a71482SOlivier Moysan 	case CS42L51_DAC_CTL:
61675a71482SOlivier Moysan 	case CS42L51_ALC_PGA_CTL:
61775a71482SOlivier Moysan 	case CS42L51_ALC_PGB_CTL:
61875a71482SOlivier Moysan 	case CS42L51_ADCA_ATT:
61975a71482SOlivier Moysan 	case CS42L51_ADCB_ATT:
62075a71482SOlivier Moysan 	case CS42L51_ADCA_VOL:
62175a71482SOlivier Moysan 	case CS42L51_ADCB_VOL:
62275a71482SOlivier Moysan 	case CS42L51_PCMA_VOL:
62375a71482SOlivier Moysan 	case CS42L51_PCMB_VOL:
62475a71482SOlivier Moysan 	case CS42L51_BEEP_FREQ:
62575a71482SOlivier Moysan 	case CS42L51_BEEP_VOL:
62675a71482SOlivier Moysan 	case CS42L51_BEEP_CONF:
62775a71482SOlivier Moysan 	case CS42L51_TONE_CTL:
62875a71482SOlivier Moysan 	case CS42L51_AOUTA_VOL:
62975a71482SOlivier Moysan 	case CS42L51_AOUTB_VOL:
63075a71482SOlivier Moysan 	case CS42L51_PCM_MIXER:
63175a71482SOlivier Moysan 	case CS42L51_LIMIT_THRES_DIS:
63275a71482SOlivier Moysan 	case CS42L51_LIMIT_REL:
63375a71482SOlivier Moysan 	case CS42L51_LIMIT_ATT:
63475a71482SOlivier Moysan 	case CS42L51_ALC_EN:
63575a71482SOlivier Moysan 	case CS42L51_ALC_REL:
63675a71482SOlivier Moysan 	case CS42L51_ALC_THRES:
63775a71482SOlivier Moysan 	case CS42L51_NOISE_CONF:
63875a71482SOlivier Moysan 	case CS42L51_CHARGE_FREQ:
63975a71482SOlivier Moysan 		return true;
64075a71482SOlivier Moysan 	default:
64175a71482SOlivier Moysan 		return false;
64275a71482SOlivier Moysan 	}
64375a71482SOlivier Moysan }
64475a71482SOlivier Moysan 
cs42l51_volatile_reg(struct device * dev,unsigned int reg)64575a71482SOlivier Moysan static bool cs42l51_volatile_reg(struct device *dev, unsigned int reg)
64675a71482SOlivier Moysan {
64775a71482SOlivier Moysan 	switch (reg) {
64875a71482SOlivier Moysan 	case CS42L51_STATUS:
64975a71482SOlivier Moysan 		return true;
65075a71482SOlivier Moysan 	default:
65175a71482SOlivier Moysan 		return false;
65275a71482SOlivier Moysan 	}
65375a71482SOlivier Moysan }
65475a71482SOlivier Moysan 
cs42l51_readable_reg(struct device * dev,unsigned int reg)65575a71482SOlivier Moysan static bool cs42l51_readable_reg(struct device *dev, unsigned int reg)
65675a71482SOlivier Moysan {
65775a71482SOlivier Moysan 	switch (reg) {
65875a71482SOlivier Moysan 	case CS42L51_CHIP_REV_ID:
65975a71482SOlivier Moysan 	case CS42L51_POWER_CTL1:
66075a71482SOlivier Moysan 	case CS42L51_MIC_POWER_CTL:
66175a71482SOlivier Moysan 	case CS42L51_INTF_CTL:
66275a71482SOlivier Moysan 	case CS42L51_MIC_CTL:
66375a71482SOlivier Moysan 	case CS42L51_ADC_CTL:
66475a71482SOlivier Moysan 	case CS42L51_ADC_INPUT:
66575a71482SOlivier Moysan 	case CS42L51_DAC_OUT_CTL:
66675a71482SOlivier Moysan 	case CS42L51_DAC_CTL:
66775a71482SOlivier Moysan 	case CS42L51_ALC_PGA_CTL:
66875a71482SOlivier Moysan 	case CS42L51_ALC_PGB_CTL:
66975a71482SOlivier Moysan 	case CS42L51_ADCA_ATT:
67075a71482SOlivier Moysan 	case CS42L51_ADCB_ATT:
67175a71482SOlivier Moysan 	case CS42L51_ADCA_VOL:
67275a71482SOlivier Moysan 	case CS42L51_ADCB_VOL:
67375a71482SOlivier Moysan 	case CS42L51_PCMA_VOL:
67475a71482SOlivier Moysan 	case CS42L51_PCMB_VOL:
67575a71482SOlivier Moysan 	case CS42L51_BEEP_FREQ:
67675a71482SOlivier Moysan 	case CS42L51_BEEP_VOL:
67775a71482SOlivier Moysan 	case CS42L51_BEEP_CONF:
67875a71482SOlivier Moysan 	case CS42L51_TONE_CTL:
67975a71482SOlivier Moysan 	case CS42L51_AOUTA_VOL:
68075a71482SOlivier Moysan 	case CS42L51_AOUTB_VOL:
68175a71482SOlivier Moysan 	case CS42L51_PCM_MIXER:
68275a71482SOlivier Moysan 	case CS42L51_LIMIT_THRES_DIS:
68375a71482SOlivier Moysan 	case CS42L51_LIMIT_REL:
68475a71482SOlivier Moysan 	case CS42L51_LIMIT_ATT:
68575a71482SOlivier Moysan 	case CS42L51_ALC_EN:
68675a71482SOlivier Moysan 	case CS42L51_ALC_REL:
68775a71482SOlivier Moysan 	case CS42L51_ALC_THRES:
68875a71482SOlivier Moysan 	case CS42L51_NOISE_CONF:
68975a71482SOlivier Moysan 	case CS42L51_STATUS:
69075a71482SOlivier Moysan 	case CS42L51_CHARGE_FREQ:
69175a71482SOlivier Moysan 		return true;
69275a71482SOlivier Moysan 	default:
69375a71482SOlivier Moysan 		return false;
69475a71482SOlivier Moysan 	}
69575a71482SOlivier Moysan }
69675a71482SOlivier Moysan 
697a1253ef6SBrian Austin const struct regmap_config cs42l51_regmap = {
69875a71482SOlivier Moysan 	.reg_bits = 8,
69975a71482SOlivier Moysan 	.reg_stride = 1,
70075a71482SOlivier Moysan 	.val_bits = 8,
70175a71482SOlivier Moysan 	.use_single_write = true,
70275a71482SOlivier Moysan 	.readable_reg = cs42l51_readable_reg,
70375a71482SOlivier Moysan 	.volatile_reg = cs42l51_volatile_reg,
70475a71482SOlivier Moysan 	.writeable_reg = cs42l51_writeable_reg,
705da071489SMark Brown 	.max_register = CS42L51_CHARGE_FREQ,
706*b5a0e5e4SMark Brown 	.cache_type = REGCACHE_MAPLE,
707da071489SMark Brown };
708a1253ef6SBrian Austin EXPORT_SYMBOL_GPL(cs42l51_regmap);
709da071489SMark Brown 
cs42l51_probe(struct device * dev,struct regmap * regmap)710a1253ef6SBrian Austin int cs42l51_probe(struct device *dev, struct regmap *regmap)
71172ed5a8cSapatard@mandriva.com {
712f0fba2adSLiam Girdwood 	struct cs42l51_private *cs42l51;
713da071489SMark Brown 	unsigned int val;
714f77b6ea7SOlivier Moysan 	int ret, i;
71572ed5a8cSapatard@mandriva.com 
716a1253ef6SBrian Austin 	if (IS_ERR(regmap))
717a1253ef6SBrian Austin 		return PTR_ERR(regmap);
718a1253ef6SBrian Austin 
719a1253ef6SBrian Austin 	cs42l51 = devm_kzalloc(dev, sizeof(struct cs42l51_private),
720a1253ef6SBrian Austin 			       GFP_KERNEL);
721a1253ef6SBrian Austin 	if (!cs42l51)
722a1253ef6SBrian Austin 		return -ENOMEM;
723a1253ef6SBrian Austin 
724a1253ef6SBrian Austin 	dev_set_drvdata(dev, cs42l51);
72575a71482SOlivier Moysan 	cs42l51->regmap = regmap;
726da071489SMark Brown 
727c0998e01SChristophe JAILLET 	cs42l51->mclk_handle = devm_clk_get_optional(dev, "MCLK");
728c0998e01SChristophe JAILLET 	if (IS_ERR(cs42l51->mclk_handle))
729318e741eSOlivier Moysan 		return PTR_ERR(cs42l51->mclk_handle);
730318e741eSOlivier Moysan 
731f77b6ea7SOlivier Moysan 	for (i = 0; i < ARRAY_SIZE(cs42l51->supplies); i++)
732f77b6ea7SOlivier Moysan 		cs42l51->supplies[i].supply = cs42l51_supply_names[i];
733f77b6ea7SOlivier Moysan 
734f77b6ea7SOlivier Moysan 	ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(cs42l51->supplies),
735f77b6ea7SOlivier Moysan 				      cs42l51->supplies);
736f77b6ea7SOlivier Moysan 	if (ret != 0) {
737f77b6ea7SOlivier Moysan 		dev_err(dev, "Failed to request supplies: %d\n", ret);
738f77b6ea7SOlivier Moysan 		return ret;
739f77b6ea7SOlivier Moysan 	}
740f77b6ea7SOlivier Moysan 
741f77b6ea7SOlivier Moysan 	ret = regulator_bulk_enable(ARRAY_SIZE(cs42l51->supplies),
742f77b6ea7SOlivier Moysan 				    cs42l51->supplies);
743f77b6ea7SOlivier Moysan 	if (ret != 0) {
744f77b6ea7SOlivier Moysan 		dev_err(dev, "Failed to enable supplies: %d\n", ret);
745f77b6ea7SOlivier Moysan 		return ret;
746f77b6ea7SOlivier Moysan 	}
747f77b6ea7SOlivier Moysan 
74811b9cd74SOlivier Moysan 	cs42l51->reset_gpio = devm_gpiod_get_optional(dev, "reset",
74911b9cd74SOlivier Moysan 						      GPIOD_OUT_LOW);
75011b9cd74SOlivier Moysan 	if (IS_ERR(cs42l51->reset_gpio))
75111b9cd74SOlivier Moysan 		return PTR_ERR(cs42l51->reset_gpio);
75211b9cd74SOlivier Moysan 
75311b9cd74SOlivier Moysan 	if (cs42l51->reset_gpio) {
75411b9cd74SOlivier Moysan 		dev_dbg(dev, "Release reset gpio\n");
75511b9cd74SOlivier Moysan 		gpiod_set_value_cansleep(cs42l51->reset_gpio, 0);
75611b9cd74SOlivier Moysan 		mdelay(2);
75711b9cd74SOlivier Moysan 	}
75811b9cd74SOlivier Moysan 
759f0fba2adSLiam Girdwood 	/* Verify that we have a CS42L51 */
760da071489SMark Brown 	ret = regmap_read(regmap, CS42L51_CHIP_REV_ID, &val);
761f0fba2adSLiam Girdwood 	if (ret < 0) {
762a1253ef6SBrian Austin 		dev_err(dev, "failed to read I2C\n");
763f0fba2adSLiam Girdwood 		goto error;
764f0fba2adSLiam Girdwood 	}
76572ed5a8cSapatard@mandriva.com 
766da071489SMark Brown 	if ((val != CS42L51_MK_CHIP_REV(CS42L51_CHIP_ID, CS42L51_CHIP_REV_A)) &&
767da071489SMark Brown 	    (val != CS42L51_MK_CHIP_REV(CS42L51_CHIP_ID, CS42L51_CHIP_REV_B))) {
768a1253ef6SBrian Austin 		dev_err(dev, "Invalid chip id: %x\n", val);
769f0fba2adSLiam Girdwood 		ret = -ENODEV;
770f0fba2adSLiam Girdwood 		goto error;
771f0fba2adSLiam Girdwood 	}
7721025c05fSAxel Lin 	dev_info(dev, "Cirrus Logic CS42L51, Revision: %02X\n",
7731025c05fSAxel Lin 		 val & CS42L51_CHIP_REV_MASK);
774f0fba2adSLiam Girdwood 
7751fa49856SKuninori Morimoto 	ret = devm_snd_soc_register_component(dev,
7761fa49856SKuninori Morimoto 			&soc_component_device_cs42l51, &cs42l51_dai, 1);
777f77b6ea7SOlivier Moysan 	if (ret < 0)
778f77b6ea7SOlivier Moysan 		goto error;
779f77b6ea7SOlivier Moysan 
780f77b6ea7SOlivier Moysan 	return 0;
781f77b6ea7SOlivier Moysan 
782f0fba2adSLiam Girdwood error:
783f77b6ea7SOlivier Moysan 	regulator_bulk_disable(ARRAY_SIZE(cs42l51->supplies),
784f77b6ea7SOlivier Moysan 			       cs42l51->supplies);
785f0fba2adSLiam Girdwood 	return ret;
786f0fba2adSLiam Girdwood }
787a1253ef6SBrian Austin EXPORT_SYMBOL_GPL(cs42l51_probe);
788f0fba2adSLiam Girdwood 
cs42l51_remove(struct device * dev)78973d4c313SUwe Kleine-König void cs42l51_remove(struct device *dev)
790f77b6ea7SOlivier Moysan {
791f77b6ea7SOlivier Moysan 	struct cs42l51_private *cs42l51 = dev_get_drvdata(dev);
79273d4c313SUwe Kleine-König 	int ret;
793f77b6ea7SOlivier Moysan 
79411b9cd74SOlivier Moysan 	gpiod_set_value_cansleep(cs42l51->reset_gpio, 1);
79511b9cd74SOlivier Moysan 
79673d4c313SUwe Kleine-König 	ret = regulator_bulk_disable(ARRAY_SIZE(cs42l51->supplies),
797f77b6ea7SOlivier Moysan 				     cs42l51->supplies);
79873d4c313SUwe Kleine-König 	if (ret)
79973d4c313SUwe Kleine-König 		dev_warn(dev, "Failed to disable all regulators (%pe)\n",
80073d4c313SUwe Kleine-König 			 ERR_PTR(ret));
80173d4c313SUwe Kleine-König 
802f77b6ea7SOlivier Moysan }
803f77b6ea7SOlivier Moysan EXPORT_SYMBOL_GPL(cs42l51_remove);
804f77b6ea7SOlivier Moysan 
cs42l51_suspend(struct device * dev)80575a71482SOlivier Moysan int __maybe_unused cs42l51_suspend(struct device *dev)
80675a71482SOlivier Moysan {
80775a71482SOlivier Moysan 	struct cs42l51_private *cs42l51 = dev_get_drvdata(dev);
80875a71482SOlivier Moysan 
80975a71482SOlivier Moysan 	regcache_cache_only(cs42l51->regmap, true);
81075a71482SOlivier Moysan 	regcache_mark_dirty(cs42l51->regmap);
81175a71482SOlivier Moysan 
81275a71482SOlivier Moysan 	return 0;
81375a71482SOlivier Moysan }
81475a71482SOlivier Moysan EXPORT_SYMBOL_GPL(cs42l51_suspend);
81575a71482SOlivier Moysan 
cs42l51_resume(struct device * dev)81675a71482SOlivier Moysan int __maybe_unused cs42l51_resume(struct device *dev)
81775a71482SOlivier Moysan {
81875a71482SOlivier Moysan 	struct cs42l51_private *cs42l51 = dev_get_drvdata(dev);
81975a71482SOlivier Moysan 
82075a71482SOlivier Moysan 	regcache_cache_only(cs42l51->regmap, false);
82175a71482SOlivier Moysan 
82275a71482SOlivier Moysan 	return regcache_sync(cs42l51->regmap);
82375a71482SOlivier Moysan }
82475a71482SOlivier Moysan EXPORT_SYMBOL_GPL(cs42l51_resume);
82575a71482SOlivier Moysan 
8262cb1e025SThomas Petazzoni MODULE_AUTHOR("Arnaud Patard <arnaud.patard@rtp-net.org>");
827dfd72a68SThomas Petazzoni MODULE_DESCRIPTION("Cirrus Logic CS42L51 ALSA SoC Codec Driver");
828dfd72a68SThomas Petazzoni MODULE_LICENSE("GPL");
829dfd72a68SThomas Petazzoni