xref: /openbmc/linux/sound/soc/codecs/cs42l51.c (revision f0fba2ad)
172ed5a8cSapatard@mandriva.com /*
272ed5a8cSapatard@mandriva.com  * cs42l51.c
372ed5a8cSapatard@mandriva.com  *
472ed5a8cSapatard@mandriva.com  * ASoC Driver for Cirrus Logic CS42L51 codecs
572ed5a8cSapatard@mandriva.com  *
672ed5a8cSapatard@mandriva.com  * Copyright (c) 2010 Arnaud Patard <apatard@mandriva.com>
772ed5a8cSapatard@mandriva.com  *
872ed5a8cSapatard@mandriva.com  * Based on cs4270.c - Copyright (c) Freescale Semiconductor
972ed5a8cSapatard@mandriva.com  *
1072ed5a8cSapatard@mandriva.com  * This program is free software; you can redistribute it and/or modify
1172ed5a8cSapatard@mandriva.com  * it under the terms of the GNU General Public License version 2 as
1272ed5a8cSapatard@mandriva.com  * published by the Free Software Foundation.
1372ed5a8cSapatard@mandriva.com  *
1472ed5a8cSapatard@mandriva.com  * This program is distributed in the hope that it will be useful,
1572ed5a8cSapatard@mandriva.com  * but WITHOUT ANY WARRANTY; without even the implied warranty of
1672ed5a8cSapatard@mandriva.com  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1772ed5a8cSapatard@mandriva.com  * GNU General Public License for more details.
1872ed5a8cSapatard@mandriva.com  *
1972ed5a8cSapatard@mandriva.com  * For now:
2072ed5a8cSapatard@mandriva.com  *  - Only I2C is support. Not SPI
2172ed5a8cSapatard@mandriva.com  *  - master mode *NOT* supported
2272ed5a8cSapatard@mandriva.com  */
2372ed5a8cSapatard@mandriva.com 
2472ed5a8cSapatard@mandriva.com #include <linux/module.h>
2572ed5a8cSapatard@mandriva.com #include <linux/platform_device.h>
2672ed5a8cSapatard@mandriva.com #include <linux/slab.h>
2772ed5a8cSapatard@mandriva.com #include <sound/core.h>
2872ed5a8cSapatard@mandriva.com #include <sound/soc.h>
2972ed5a8cSapatard@mandriva.com #include <sound/soc-dapm.h>
3072ed5a8cSapatard@mandriva.com #include <sound/tlv.h>
3172ed5a8cSapatard@mandriva.com #include <sound/initval.h>
3272ed5a8cSapatard@mandriva.com #include <sound/pcm_params.h>
3372ed5a8cSapatard@mandriva.com #include <sound/pcm.h>
3472ed5a8cSapatard@mandriva.com #include <linux/i2c.h>
3572ed5a8cSapatard@mandriva.com 
3672ed5a8cSapatard@mandriva.com #include "cs42l51.h"
3772ed5a8cSapatard@mandriva.com 
3872ed5a8cSapatard@mandriva.com enum master_slave_mode {
3972ed5a8cSapatard@mandriva.com 	MODE_SLAVE,
4072ed5a8cSapatard@mandriva.com 	MODE_SLAVE_AUTO,
4172ed5a8cSapatard@mandriva.com 	MODE_MASTER,
4272ed5a8cSapatard@mandriva.com };
4372ed5a8cSapatard@mandriva.com 
4472ed5a8cSapatard@mandriva.com struct cs42l51_private {
45f0fba2adSLiam Girdwood 	enum snd_soc_control_type control_type;
46f0fba2adSLiam Girdwood 	void *control_data;
4772ed5a8cSapatard@mandriva.com 	unsigned int mclk;
4872ed5a8cSapatard@mandriva.com 	unsigned int audio_mode;	/* The mode (I2S or left-justified) */
4972ed5a8cSapatard@mandriva.com 	enum master_slave_mode func;
5072ed5a8cSapatard@mandriva.com 	u8 reg_cache[CS42L51_NUMREGS];
5172ed5a8cSapatard@mandriva.com };
5272ed5a8cSapatard@mandriva.com 
5372ed5a8cSapatard@mandriva.com #define CS42L51_FORMATS ( \
5472ed5a8cSapatard@mandriva.com 		SNDRV_PCM_FMTBIT_S16_LE  | SNDRV_PCM_FMTBIT_S16_BE  | \
5572ed5a8cSapatard@mandriva.com 		SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S18_3BE | \
5672ed5a8cSapatard@mandriva.com 		SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE | \
5772ed5a8cSapatard@mandriva.com 		SNDRV_PCM_FMTBIT_S24_LE  | SNDRV_PCM_FMTBIT_S24_BE)
5872ed5a8cSapatard@mandriva.com 
5972ed5a8cSapatard@mandriva.com static int cs42l51_fill_cache(struct snd_soc_codec *codec)
6072ed5a8cSapatard@mandriva.com {
6172ed5a8cSapatard@mandriva.com 	u8 *cache = codec->reg_cache + 1;
6272ed5a8cSapatard@mandriva.com 	struct i2c_client *i2c_client = codec->control_data;
6372ed5a8cSapatard@mandriva.com 	s32 length;
6472ed5a8cSapatard@mandriva.com 
6572ed5a8cSapatard@mandriva.com 	length = i2c_smbus_read_i2c_block_data(i2c_client,
6672ed5a8cSapatard@mandriva.com 			CS42L51_FIRSTREG | 0x80, CS42L51_NUMREGS, cache);
6772ed5a8cSapatard@mandriva.com 	if (length != CS42L51_NUMREGS) {
6872ed5a8cSapatard@mandriva.com 		dev_err(&i2c_client->dev,
6972ed5a8cSapatard@mandriva.com 				"I2C read failure, addr=0x%x (ret=%d vs %d)\n",
7072ed5a8cSapatard@mandriva.com 				i2c_client->addr, length, CS42L51_NUMREGS);
7172ed5a8cSapatard@mandriva.com 		return -EIO;
7272ed5a8cSapatard@mandriva.com 	}
7372ed5a8cSapatard@mandriva.com 
7472ed5a8cSapatard@mandriva.com 	return 0;
7572ed5a8cSapatard@mandriva.com }
7672ed5a8cSapatard@mandriva.com 
7772ed5a8cSapatard@mandriva.com static int cs42l51_get_chan_mix(struct snd_kcontrol *kcontrol,
7872ed5a8cSapatard@mandriva.com 			struct snd_ctl_elem_value *ucontrol)
7972ed5a8cSapatard@mandriva.com {
8072ed5a8cSapatard@mandriva.com 	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
8172ed5a8cSapatard@mandriva.com 	unsigned long value = snd_soc_read(codec, CS42L51_PCM_MIXER)&3;
8272ed5a8cSapatard@mandriva.com 
8372ed5a8cSapatard@mandriva.com 	switch (value) {
8472ed5a8cSapatard@mandriva.com 	default:
8572ed5a8cSapatard@mandriva.com 	case 0:
8672ed5a8cSapatard@mandriva.com 		ucontrol->value.integer.value[0] = 0;
8772ed5a8cSapatard@mandriva.com 		break;
8872ed5a8cSapatard@mandriva.com 	/* same value : (L+R)/2 and (R+L)/2 */
8972ed5a8cSapatard@mandriva.com 	case 1:
9072ed5a8cSapatard@mandriva.com 	case 2:
9172ed5a8cSapatard@mandriva.com 		ucontrol->value.integer.value[0] = 1;
9272ed5a8cSapatard@mandriva.com 		break;
9372ed5a8cSapatard@mandriva.com 	case 3:
9472ed5a8cSapatard@mandriva.com 		ucontrol->value.integer.value[0] = 2;
9572ed5a8cSapatard@mandriva.com 		break;
9672ed5a8cSapatard@mandriva.com 	}
9772ed5a8cSapatard@mandriva.com 
9872ed5a8cSapatard@mandriva.com 	return 0;
9972ed5a8cSapatard@mandriva.com }
10072ed5a8cSapatard@mandriva.com 
10172ed5a8cSapatard@mandriva.com #define CHAN_MIX_NORMAL	0x00
10272ed5a8cSapatard@mandriva.com #define CHAN_MIX_BOTH	0x55
10372ed5a8cSapatard@mandriva.com #define CHAN_MIX_SWAP	0xFF
10472ed5a8cSapatard@mandriva.com 
10572ed5a8cSapatard@mandriva.com static int cs42l51_set_chan_mix(struct snd_kcontrol *kcontrol,
10672ed5a8cSapatard@mandriva.com 			struct snd_ctl_elem_value *ucontrol)
10772ed5a8cSapatard@mandriva.com {
10872ed5a8cSapatard@mandriva.com 	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
10972ed5a8cSapatard@mandriva.com 	unsigned char val;
11072ed5a8cSapatard@mandriva.com 
11172ed5a8cSapatard@mandriva.com 	switch (ucontrol->value.integer.value[0]) {
11272ed5a8cSapatard@mandriva.com 	default:
11372ed5a8cSapatard@mandriva.com 	case 0:
11472ed5a8cSapatard@mandriva.com 		val = CHAN_MIX_NORMAL;
11572ed5a8cSapatard@mandriva.com 		break;
11672ed5a8cSapatard@mandriva.com 	case 1:
11772ed5a8cSapatard@mandriva.com 		val = CHAN_MIX_BOTH;
11872ed5a8cSapatard@mandriva.com 		break;
11972ed5a8cSapatard@mandriva.com 	case 2:
12072ed5a8cSapatard@mandriva.com 		val = CHAN_MIX_SWAP;
12172ed5a8cSapatard@mandriva.com 		break;
12272ed5a8cSapatard@mandriva.com 	}
12372ed5a8cSapatard@mandriva.com 
12472ed5a8cSapatard@mandriva.com 	snd_soc_write(codec, CS42L51_PCM_MIXER, val);
12572ed5a8cSapatard@mandriva.com 
12672ed5a8cSapatard@mandriva.com 	return 1;
12772ed5a8cSapatard@mandriva.com }
12872ed5a8cSapatard@mandriva.com 
12972ed5a8cSapatard@mandriva.com static const DECLARE_TLV_DB_SCALE(adc_pcm_tlv, -5150, 50, 0);
13072ed5a8cSapatard@mandriva.com static const DECLARE_TLV_DB_SCALE(tone_tlv, -1050, 150, 0);
13172ed5a8cSapatard@mandriva.com /* This is a lie. after -102 db, it stays at -102 */
13272ed5a8cSapatard@mandriva.com /* maybe a range would be better */
13372ed5a8cSapatard@mandriva.com static const DECLARE_TLV_DB_SCALE(aout_tlv, -11550, 50, 0);
13472ed5a8cSapatard@mandriva.com 
13572ed5a8cSapatard@mandriva.com static const DECLARE_TLV_DB_SCALE(boost_tlv, 1600, 1600, 0);
13672ed5a8cSapatard@mandriva.com static const char *chan_mix[] = {
13772ed5a8cSapatard@mandriva.com 	"L R",
13872ed5a8cSapatard@mandriva.com 	"L+R",
13972ed5a8cSapatard@mandriva.com 	"R L",
14072ed5a8cSapatard@mandriva.com };
14172ed5a8cSapatard@mandriva.com 
14272ed5a8cSapatard@mandriva.com static const struct soc_enum cs42l51_chan_mix =
14372ed5a8cSapatard@mandriva.com 	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(chan_mix), chan_mix);
14472ed5a8cSapatard@mandriva.com 
14572ed5a8cSapatard@mandriva.com static const struct snd_kcontrol_new cs42l51_snd_controls[] = {
14672ed5a8cSapatard@mandriva.com 	SOC_DOUBLE_R_SX_TLV("PCM Playback Volume",
14772ed5a8cSapatard@mandriva.com 			CS42L51_PCMA_VOL, CS42L51_PCMB_VOL,
14872ed5a8cSapatard@mandriva.com 			7, 0xffffff99, 0x18, adc_pcm_tlv),
14972ed5a8cSapatard@mandriva.com 	SOC_DOUBLE_R("PCM Playback Switch",
15072ed5a8cSapatard@mandriva.com 			CS42L51_PCMA_VOL, CS42L51_PCMB_VOL, 7, 1, 1),
15172ed5a8cSapatard@mandriva.com 	SOC_DOUBLE_R_SX_TLV("Analog Playback Volume",
15272ed5a8cSapatard@mandriva.com 			CS42L51_AOUTA_VOL, CS42L51_AOUTB_VOL,
15372ed5a8cSapatard@mandriva.com 			8, 0xffffff19, 0x18, aout_tlv),
15472ed5a8cSapatard@mandriva.com 	SOC_DOUBLE_R_SX_TLV("ADC Mixer Volume",
15572ed5a8cSapatard@mandriva.com 			CS42L51_ADCA_VOL, CS42L51_ADCB_VOL,
15672ed5a8cSapatard@mandriva.com 			7, 0xffffff99, 0x18, adc_pcm_tlv),
15772ed5a8cSapatard@mandriva.com 	SOC_DOUBLE_R("ADC Mixer Switch",
15872ed5a8cSapatard@mandriva.com 			CS42L51_ADCA_VOL, CS42L51_ADCB_VOL, 7, 1, 1),
15972ed5a8cSapatard@mandriva.com 	SOC_SINGLE("Playback Deemphasis Switch", CS42L51_DAC_CTL, 3, 1, 0),
16072ed5a8cSapatard@mandriva.com 	SOC_SINGLE("Auto-Mute Switch", CS42L51_DAC_CTL, 2, 1, 0),
16172ed5a8cSapatard@mandriva.com 	SOC_SINGLE("Soft Ramp Switch", CS42L51_DAC_CTL, 1, 1, 0),
16272ed5a8cSapatard@mandriva.com 	SOC_SINGLE("Zero Cross Switch", CS42L51_DAC_CTL, 0, 0, 0),
16372ed5a8cSapatard@mandriva.com 	SOC_DOUBLE_TLV("Mic Boost Volume",
16472ed5a8cSapatard@mandriva.com 			CS42L51_MIC_CTL, 0, 1, 1, 0, boost_tlv),
16572ed5a8cSapatard@mandriva.com 	SOC_SINGLE_TLV("Bass Volume", CS42L51_TONE_CTL, 0, 0xf, 1, tone_tlv),
16672ed5a8cSapatard@mandriva.com 	SOC_SINGLE_TLV("Treble Volume", CS42L51_TONE_CTL, 4, 0xf, 1, tone_tlv),
16772ed5a8cSapatard@mandriva.com 	SOC_ENUM_EXT("PCM channel mixer",
16872ed5a8cSapatard@mandriva.com 			cs42l51_chan_mix,
16972ed5a8cSapatard@mandriva.com 			cs42l51_get_chan_mix, cs42l51_set_chan_mix),
17072ed5a8cSapatard@mandriva.com };
17172ed5a8cSapatard@mandriva.com 
17272ed5a8cSapatard@mandriva.com /*
17372ed5a8cSapatard@mandriva.com  * to power down, one must:
17472ed5a8cSapatard@mandriva.com  * 1.) Enable the PDN bit
17572ed5a8cSapatard@mandriva.com  * 2.) enable power-down for the select channels
17672ed5a8cSapatard@mandriva.com  * 3.) disable the PDN bit.
17772ed5a8cSapatard@mandriva.com  */
17872ed5a8cSapatard@mandriva.com static int cs42l51_pdn_event(struct snd_soc_dapm_widget *w,
17972ed5a8cSapatard@mandriva.com 		struct snd_kcontrol *kcontrol, int event)
18072ed5a8cSapatard@mandriva.com {
18172ed5a8cSapatard@mandriva.com 	unsigned long value;
18272ed5a8cSapatard@mandriva.com 
18372ed5a8cSapatard@mandriva.com 	value = snd_soc_read(w->codec, CS42L51_POWER_CTL1);
18472ed5a8cSapatard@mandriva.com 	value &= ~CS42L51_POWER_CTL1_PDN;
18572ed5a8cSapatard@mandriva.com 
18672ed5a8cSapatard@mandriva.com 	switch (event) {
18772ed5a8cSapatard@mandriva.com 	case SND_SOC_DAPM_PRE_PMD:
18872ed5a8cSapatard@mandriva.com 		value |= CS42L51_POWER_CTL1_PDN;
18972ed5a8cSapatard@mandriva.com 		break;
19072ed5a8cSapatard@mandriva.com 	default:
19172ed5a8cSapatard@mandriva.com 	case SND_SOC_DAPM_POST_PMD:
19272ed5a8cSapatard@mandriva.com 		break;
19372ed5a8cSapatard@mandriva.com 	}
19472ed5a8cSapatard@mandriva.com 	snd_soc_update_bits(w->codec, CS42L51_POWER_CTL1,
19572ed5a8cSapatard@mandriva.com 		CS42L51_POWER_CTL1_PDN, value);
19672ed5a8cSapatard@mandriva.com 
19772ed5a8cSapatard@mandriva.com 	return 0;
19872ed5a8cSapatard@mandriva.com }
19972ed5a8cSapatard@mandriva.com 
20072ed5a8cSapatard@mandriva.com static const char *cs42l51_dac_names[] = {"Direct PCM",
20172ed5a8cSapatard@mandriva.com 	"DSP PCM", "ADC"};
20272ed5a8cSapatard@mandriva.com static const struct soc_enum cs42l51_dac_mux_enum =
20372ed5a8cSapatard@mandriva.com 	SOC_ENUM_SINGLE(CS42L51_DAC_CTL, 6, 3, cs42l51_dac_names);
20472ed5a8cSapatard@mandriva.com static const struct snd_kcontrol_new cs42l51_dac_mux_controls =
20572ed5a8cSapatard@mandriva.com 	SOC_DAPM_ENUM("Route", cs42l51_dac_mux_enum);
20672ed5a8cSapatard@mandriva.com 
20772ed5a8cSapatard@mandriva.com static const char *cs42l51_adcl_names[] = {"AIN1 Left", "AIN2 Left",
20872ed5a8cSapatard@mandriva.com 	"MIC Left", "MIC+preamp Left"};
20972ed5a8cSapatard@mandriva.com static const struct soc_enum cs42l51_adcl_mux_enum =
21072ed5a8cSapatard@mandriva.com 	SOC_ENUM_SINGLE(CS42L51_ADC_INPUT, 4, 4, cs42l51_adcl_names);
21172ed5a8cSapatard@mandriva.com static const struct snd_kcontrol_new cs42l51_adcl_mux_controls =
21272ed5a8cSapatard@mandriva.com 	SOC_DAPM_ENUM("Route", cs42l51_adcl_mux_enum);
21372ed5a8cSapatard@mandriva.com 
21472ed5a8cSapatard@mandriva.com static const char *cs42l51_adcr_names[] = {"AIN1 Right", "AIN2 Right",
21572ed5a8cSapatard@mandriva.com 	"MIC Right", "MIC+preamp Right"};
21672ed5a8cSapatard@mandriva.com static const struct soc_enum cs42l51_adcr_mux_enum =
21772ed5a8cSapatard@mandriva.com 	SOC_ENUM_SINGLE(CS42L51_ADC_INPUT, 6, 4, cs42l51_adcr_names);
21872ed5a8cSapatard@mandriva.com static const struct snd_kcontrol_new cs42l51_adcr_mux_controls =
21972ed5a8cSapatard@mandriva.com 	SOC_DAPM_ENUM("Route", cs42l51_adcr_mux_enum);
22072ed5a8cSapatard@mandriva.com 
22172ed5a8cSapatard@mandriva.com static const struct snd_soc_dapm_widget cs42l51_dapm_widgets[] = {
22272ed5a8cSapatard@mandriva.com 	SND_SOC_DAPM_MICBIAS("Mic Bias", CS42L51_MIC_POWER_CTL, 1, 1),
22372ed5a8cSapatard@mandriva.com 	SND_SOC_DAPM_PGA_E("Left PGA", CS42L51_POWER_CTL1, 3, 1, NULL, 0,
22472ed5a8cSapatard@mandriva.com 		cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD),
22572ed5a8cSapatard@mandriva.com 	SND_SOC_DAPM_PGA_E("Right PGA", CS42L51_POWER_CTL1, 4, 1, NULL, 0,
22672ed5a8cSapatard@mandriva.com 		cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD),
22772ed5a8cSapatard@mandriva.com 	SND_SOC_DAPM_ADC_E("Left ADC", "Left HiFi Capture",
22872ed5a8cSapatard@mandriva.com 		CS42L51_POWER_CTL1, 1, 1,
22972ed5a8cSapatard@mandriva.com 		cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD),
23072ed5a8cSapatard@mandriva.com 	SND_SOC_DAPM_ADC_E("Right ADC", "Right HiFi Capture",
23172ed5a8cSapatard@mandriva.com 		CS42L51_POWER_CTL1, 2, 1,
23272ed5a8cSapatard@mandriva.com 		cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD),
23372ed5a8cSapatard@mandriva.com 	SND_SOC_DAPM_DAC_E("Left DAC", "Left HiFi Playback",
23472ed5a8cSapatard@mandriva.com 		CS42L51_POWER_CTL1, 5, 1,
23572ed5a8cSapatard@mandriva.com 		cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD),
23672ed5a8cSapatard@mandriva.com 	SND_SOC_DAPM_DAC_E("Right DAC", "Right HiFi Playback",
23772ed5a8cSapatard@mandriva.com 		CS42L51_POWER_CTL1, 6, 1,
23872ed5a8cSapatard@mandriva.com 		cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD),
23972ed5a8cSapatard@mandriva.com 
24072ed5a8cSapatard@mandriva.com 	/* analog/mic */
24172ed5a8cSapatard@mandriva.com 	SND_SOC_DAPM_INPUT("AIN1L"),
24272ed5a8cSapatard@mandriva.com 	SND_SOC_DAPM_INPUT("AIN1R"),
24372ed5a8cSapatard@mandriva.com 	SND_SOC_DAPM_INPUT("AIN2L"),
24472ed5a8cSapatard@mandriva.com 	SND_SOC_DAPM_INPUT("AIN2R"),
24572ed5a8cSapatard@mandriva.com 	SND_SOC_DAPM_INPUT("MICL"),
24672ed5a8cSapatard@mandriva.com 	SND_SOC_DAPM_INPUT("MICR"),
24772ed5a8cSapatard@mandriva.com 
24872ed5a8cSapatard@mandriva.com 	SND_SOC_DAPM_MIXER("Mic Preamp Left",
24972ed5a8cSapatard@mandriva.com 		CS42L51_MIC_POWER_CTL, 2, 1, NULL, 0),
25072ed5a8cSapatard@mandriva.com 	SND_SOC_DAPM_MIXER("Mic Preamp Right",
25172ed5a8cSapatard@mandriva.com 		CS42L51_MIC_POWER_CTL, 3, 1, NULL, 0),
25272ed5a8cSapatard@mandriva.com 
25372ed5a8cSapatard@mandriva.com 	/* HP */
25472ed5a8cSapatard@mandriva.com 	SND_SOC_DAPM_OUTPUT("HPL"),
25572ed5a8cSapatard@mandriva.com 	SND_SOC_DAPM_OUTPUT("HPR"),
25672ed5a8cSapatard@mandriva.com 
25772ed5a8cSapatard@mandriva.com 	/* mux */
25872ed5a8cSapatard@mandriva.com 	SND_SOC_DAPM_MUX("DAC Mux", SND_SOC_NOPM, 0, 0,
25972ed5a8cSapatard@mandriva.com 		&cs42l51_dac_mux_controls),
26072ed5a8cSapatard@mandriva.com 	SND_SOC_DAPM_MUX("PGA-ADC Mux Left", SND_SOC_NOPM, 0, 0,
26172ed5a8cSapatard@mandriva.com 		&cs42l51_adcl_mux_controls),
26272ed5a8cSapatard@mandriva.com 	SND_SOC_DAPM_MUX("PGA-ADC Mux Right", SND_SOC_NOPM, 0, 0,
26372ed5a8cSapatard@mandriva.com 		&cs42l51_adcr_mux_controls),
26472ed5a8cSapatard@mandriva.com };
26572ed5a8cSapatard@mandriva.com 
26672ed5a8cSapatard@mandriva.com static const struct snd_soc_dapm_route cs42l51_routes[] = {
26772ed5a8cSapatard@mandriva.com 	{"HPL", NULL, "Left DAC"},
26872ed5a8cSapatard@mandriva.com 	{"HPR", NULL, "Right DAC"},
26972ed5a8cSapatard@mandriva.com 
27072ed5a8cSapatard@mandriva.com 	{"Left ADC", NULL, "Left PGA"},
27172ed5a8cSapatard@mandriva.com 	{"Right ADC", NULL, "Right PGA"},
27272ed5a8cSapatard@mandriva.com 
27372ed5a8cSapatard@mandriva.com 	{"Mic Preamp Left",  NULL,  "MICL"},
27472ed5a8cSapatard@mandriva.com 	{"Mic Preamp Right", NULL,  "MICR"},
27572ed5a8cSapatard@mandriva.com 
27672ed5a8cSapatard@mandriva.com 	{"PGA-ADC Mux Left",  "AIN1 Left",        "AIN1L" },
27772ed5a8cSapatard@mandriva.com 	{"PGA-ADC Mux Left",  "AIN2 Left",        "AIN2L" },
27872ed5a8cSapatard@mandriva.com 	{"PGA-ADC Mux Left",  "MIC Left",         "MICL"  },
27972ed5a8cSapatard@mandriva.com 	{"PGA-ADC Mux Left",  "MIC+preamp Left",  "Mic Preamp Left" },
28072ed5a8cSapatard@mandriva.com 	{"PGA-ADC Mux Right", "AIN1 Right",       "AIN1R" },
28172ed5a8cSapatard@mandriva.com 	{"PGA-ADC Mux Right", "AIN2 Right",       "AIN2R" },
28272ed5a8cSapatard@mandriva.com 	{"PGA-ADC Mux Right", "MIC Right",        "MICR" },
28372ed5a8cSapatard@mandriva.com 	{"PGA-ADC Mux Right", "MIC+preamp Right", "Mic Preamp Right" },
28472ed5a8cSapatard@mandriva.com 
28572ed5a8cSapatard@mandriva.com 	{"Left PGA", NULL, "PGA-ADC Mux Left"},
28672ed5a8cSapatard@mandriva.com 	{"Right PGA", NULL, "PGA-ADC Mux Right"},
28772ed5a8cSapatard@mandriva.com };
28872ed5a8cSapatard@mandriva.com 
28972ed5a8cSapatard@mandriva.com static int cs42l51_set_dai_fmt(struct snd_soc_dai *codec_dai,
29072ed5a8cSapatard@mandriva.com 		unsigned int format)
29172ed5a8cSapatard@mandriva.com {
29272ed5a8cSapatard@mandriva.com 	struct snd_soc_codec *codec = codec_dai->codec;
29372ed5a8cSapatard@mandriva.com 	struct cs42l51_private *cs42l51 = snd_soc_codec_get_drvdata(codec);
29472ed5a8cSapatard@mandriva.com 	int ret = 0;
29572ed5a8cSapatard@mandriva.com 
29672ed5a8cSapatard@mandriva.com 	switch (format & SND_SOC_DAIFMT_FORMAT_MASK) {
29772ed5a8cSapatard@mandriva.com 	case SND_SOC_DAIFMT_I2S:
29872ed5a8cSapatard@mandriva.com 	case SND_SOC_DAIFMT_LEFT_J:
29972ed5a8cSapatard@mandriva.com 	case SND_SOC_DAIFMT_RIGHT_J:
30072ed5a8cSapatard@mandriva.com 		cs42l51->audio_mode = format & SND_SOC_DAIFMT_FORMAT_MASK;
30172ed5a8cSapatard@mandriva.com 		break;
30272ed5a8cSapatard@mandriva.com 	default:
30372ed5a8cSapatard@mandriva.com 		dev_err(codec->dev, "invalid DAI format\n");
30472ed5a8cSapatard@mandriva.com 		ret = -EINVAL;
30572ed5a8cSapatard@mandriva.com 	}
30672ed5a8cSapatard@mandriva.com 
30772ed5a8cSapatard@mandriva.com 	switch (format & SND_SOC_DAIFMT_MASTER_MASK) {
30872ed5a8cSapatard@mandriva.com 	case SND_SOC_DAIFMT_CBM_CFM:
30972ed5a8cSapatard@mandriva.com 		cs42l51->func = MODE_MASTER;
31072ed5a8cSapatard@mandriva.com 		break;
31172ed5a8cSapatard@mandriva.com 	case SND_SOC_DAIFMT_CBS_CFS:
31272ed5a8cSapatard@mandriva.com 		cs42l51->func = MODE_SLAVE_AUTO;
31372ed5a8cSapatard@mandriva.com 		break;
31472ed5a8cSapatard@mandriva.com 	default:
31572ed5a8cSapatard@mandriva.com 		ret = -EINVAL;
31672ed5a8cSapatard@mandriva.com 		break;
31772ed5a8cSapatard@mandriva.com 	}
31872ed5a8cSapatard@mandriva.com 
31972ed5a8cSapatard@mandriva.com 	return ret;
32072ed5a8cSapatard@mandriva.com }
32172ed5a8cSapatard@mandriva.com 
32272ed5a8cSapatard@mandriva.com struct cs42l51_ratios {
32372ed5a8cSapatard@mandriva.com 	unsigned int ratio;
32472ed5a8cSapatard@mandriva.com 	unsigned char speed_mode;
32572ed5a8cSapatard@mandriva.com 	unsigned char mclk;
32672ed5a8cSapatard@mandriva.com };
32772ed5a8cSapatard@mandriva.com 
32872ed5a8cSapatard@mandriva.com static struct cs42l51_ratios slave_ratios[] = {
32972ed5a8cSapatard@mandriva.com 	{  512, CS42L51_QSM_MODE, 0 }, {  768, CS42L51_QSM_MODE, 0 },
33072ed5a8cSapatard@mandriva.com 	{ 1024, CS42L51_QSM_MODE, 0 }, { 1536, CS42L51_QSM_MODE, 0 },
33172ed5a8cSapatard@mandriva.com 	{ 2048, CS42L51_QSM_MODE, 0 }, { 3072, CS42L51_QSM_MODE, 0 },
33272ed5a8cSapatard@mandriva.com 	{  256, CS42L51_HSM_MODE, 0 }, {  384, CS42L51_HSM_MODE, 0 },
33372ed5a8cSapatard@mandriva.com 	{  512, CS42L51_HSM_MODE, 0 }, {  768, CS42L51_HSM_MODE, 0 },
33472ed5a8cSapatard@mandriva.com 	{ 1024, CS42L51_HSM_MODE, 0 }, { 1536, CS42L51_HSM_MODE, 0 },
33572ed5a8cSapatard@mandriva.com 	{  128, CS42L51_SSM_MODE, 0 }, {  192, CS42L51_SSM_MODE, 0 },
33672ed5a8cSapatard@mandriva.com 	{  256, CS42L51_SSM_MODE, 0 }, {  384, CS42L51_SSM_MODE, 0 },
33772ed5a8cSapatard@mandriva.com 	{  512, CS42L51_SSM_MODE, 0 }, {  768, CS42L51_SSM_MODE, 0 },
33872ed5a8cSapatard@mandriva.com 	{  128, CS42L51_DSM_MODE, 0 }, {  192, CS42L51_DSM_MODE, 0 },
33972ed5a8cSapatard@mandriva.com 	{  256, CS42L51_DSM_MODE, 0 }, {  384, CS42L51_DSM_MODE, 0 },
34072ed5a8cSapatard@mandriva.com };
34172ed5a8cSapatard@mandriva.com 
34272ed5a8cSapatard@mandriva.com static struct cs42l51_ratios slave_auto_ratios[] = {
34372ed5a8cSapatard@mandriva.com 	{ 1024, CS42L51_QSM_MODE, 0 }, { 1536, CS42L51_QSM_MODE, 0 },
34472ed5a8cSapatard@mandriva.com 	{ 2048, CS42L51_QSM_MODE, 1 }, { 3072, CS42L51_QSM_MODE, 1 },
34572ed5a8cSapatard@mandriva.com 	{  512, CS42L51_HSM_MODE, 0 }, {  768, CS42L51_HSM_MODE, 0 },
34672ed5a8cSapatard@mandriva.com 	{ 1024, CS42L51_HSM_MODE, 1 }, { 1536, CS42L51_HSM_MODE, 1 },
34772ed5a8cSapatard@mandriva.com 	{  256, CS42L51_SSM_MODE, 0 }, {  384, CS42L51_SSM_MODE, 0 },
34872ed5a8cSapatard@mandriva.com 	{  512, CS42L51_SSM_MODE, 1 }, {  768, CS42L51_SSM_MODE, 1 },
34972ed5a8cSapatard@mandriva.com 	{  128, CS42L51_DSM_MODE, 0 }, {  192, CS42L51_DSM_MODE, 0 },
35072ed5a8cSapatard@mandriva.com 	{  256, CS42L51_DSM_MODE, 1 }, {  384, CS42L51_DSM_MODE, 1 },
35172ed5a8cSapatard@mandriva.com };
35272ed5a8cSapatard@mandriva.com 
35372ed5a8cSapatard@mandriva.com static int cs42l51_set_dai_sysclk(struct snd_soc_dai *codec_dai,
35472ed5a8cSapatard@mandriva.com 		int clk_id, unsigned int freq, int dir)
35572ed5a8cSapatard@mandriva.com {
35672ed5a8cSapatard@mandriva.com 	struct snd_soc_codec *codec = codec_dai->codec;
35772ed5a8cSapatard@mandriva.com 	struct cs42l51_private *cs42l51 = snd_soc_codec_get_drvdata(codec);
35872ed5a8cSapatard@mandriva.com 
35972ed5a8cSapatard@mandriva.com 	cs42l51->mclk = freq;
36072ed5a8cSapatard@mandriva.com 	return 0;
36172ed5a8cSapatard@mandriva.com }
36272ed5a8cSapatard@mandriva.com 
36372ed5a8cSapatard@mandriva.com static int cs42l51_hw_params(struct snd_pcm_substream *substream,
36472ed5a8cSapatard@mandriva.com 		struct snd_pcm_hw_params *params,
36572ed5a8cSapatard@mandriva.com 		struct snd_soc_dai *dai)
36672ed5a8cSapatard@mandriva.com {
36772ed5a8cSapatard@mandriva.com 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
368f0fba2adSLiam Girdwood 	struct snd_soc_codec *codec = rtd->codec;
36972ed5a8cSapatard@mandriva.com 	struct cs42l51_private *cs42l51 = snd_soc_codec_get_drvdata(codec);
37072ed5a8cSapatard@mandriva.com 	int ret;
37172ed5a8cSapatard@mandriva.com 	unsigned int i;
37272ed5a8cSapatard@mandriva.com 	unsigned int rate;
37372ed5a8cSapatard@mandriva.com 	unsigned int ratio;
37472ed5a8cSapatard@mandriva.com 	struct cs42l51_ratios *ratios = NULL;
37572ed5a8cSapatard@mandriva.com 	int nr_ratios = 0;
37672ed5a8cSapatard@mandriva.com 	int intf_ctl, power_ctl, fmt;
37772ed5a8cSapatard@mandriva.com 
37872ed5a8cSapatard@mandriva.com 	switch (cs42l51->func) {
37972ed5a8cSapatard@mandriva.com 	case MODE_MASTER:
38072ed5a8cSapatard@mandriva.com 		return -EINVAL;
38172ed5a8cSapatard@mandriva.com 	case MODE_SLAVE:
38272ed5a8cSapatard@mandriva.com 		ratios = slave_ratios;
38372ed5a8cSapatard@mandriva.com 		nr_ratios = ARRAY_SIZE(slave_ratios);
38472ed5a8cSapatard@mandriva.com 		break;
38572ed5a8cSapatard@mandriva.com 	case MODE_SLAVE_AUTO:
38672ed5a8cSapatard@mandriva.com 		ratios = slave_auto_ratios;
38772ed5a8cSapatard@mandriva.com 		nr_ratios = ARRAY_SIZE(slave_auto_ratios);
38872ed5a8cSapatard@mandriva.com 		break;
38972ed5a8cSapatard@mandriva.com 	}
39072ed5a8cSapatard@mandriva.com 
39172ed5a8cSapatard@mandriva.com 	/* Figure out which MCLK/LRCK ratio to use */
39272ed5a8cSapatard@mandriva.com 	rate = params_rate(params);     /* Sampling rate, in Hz */
39372ed5a8cSapatard@mandriva.com 	ratio = cs42l51->mclk / rate;    /* MCLK/LRCK ratio */
39472ed5a8cSapatard@mandriva.com 	for (i = 0; i < nr_ratios; i++) {
39572ed5a8cSapatard@mandriva.com 		if (ratios[i].ratio == ratio)
39672ed5a8cSapatard@mandriva.com 			break;
39772ed5a8cSapatard@mandriva.com 	}
39872ed5a8cSapatard@mandriva.com 
39972ed5a8cSapatard@mandriva.com 	if (i == nr_ratios) {
40072ed5a8cSapatard@mandriva.com 		/* We did not find a matching ratio */
40172ed5a8cSapatard@mandriva.com 		dev_err(codec->dev, "could not find matching ratio\n");
40272ed5a8cSapatard@mandriva.com 		return -EINVAL;
40372ed5a8cSapatard@mandriva.com 	}
40472ed5a8cSapatard@mandriva.com 
40572ed5a8cSapatard@mandriva.com 	intf_ctl = snd_soc_read(codec, CS42L51_INTF_CTL);
40672ed5a8cSapatard@mandriva.com 	power_ctl = snd_soc_read(codec, CS42L51_MIC_POWER_CTL);
40772ed5a8cSapatard@mandriva.com 
40872ed5a8cSapatard@mandriva.com 	intf_ctl &= ~(CS42L51_INTF_CTL_MASTER | CS42L51_INTF_CTL_ADC_I2S
40972ed5a8cSapatard@mandriva.com 			| CS42L51_INTF_CTL_DAC_FORMAT(7));
41072ed5a8cSapatard@mandriva.com 	power_ctl &= ~(CS42L51_MIC_POWER_CTL_SPEED(3)
41172ed5a8cSapatard@mandriva.com 			| CS42L51_MIC_POWER_CTL_MCLK_DIV2);
41272ed5a8cSapatard@mandriva.com 
41372ed5a8cSapatard@mandriva.com 	switch (cs42l51->func) {
41472ed5a8cSapatard@mandriva.com 	case MODE_MASTER:
41572ed5a8cSapatard@mandriva.com 		intf_ctl |= CS42L51_INTF_CTL_MASTER;
41672ed5a8cSapatard@mandriva.com 		power_ctl |= CS42L51_MIC_POWER_CTL_SPEED(ratios[i].speed_mode);
41772ed5a8cSapatard@mandriva.com 		break;
41872ed5a8cSapatard@mandriva.com 	case MODE_SLAVE:
41972ed5a8cSapatard@mandriva.com 		power_ctl |= CS42L51_MIC_POWER_CTL_SPEED(ratios[i].speed_mode);
42072ed5a8cSapatard@mandriva.com 		break;
42172ed5a8cSapatard@mandriva.com 	case MODE_SLAVE_AUTO:
42272ed5a8cSapatard@mandriva.com 		power_ctl |= CS42L51_MIC_POWER_CTL_AUTO;
42372ed5a8cSapatard@mandriva.com 		break;
42472ed5a8cSapatard@mandriva.com 	}
42572ed5a8cSapatard@mandriva.com 
42672ed5a8cSapatard@mandriva.com 	switch (cs42l51->audio_mode) {
42772ed5a8cSapatard@mandriva.com 	case SND_SOC_DAIFMT_I2S:
42872ed5a8cSapatard@mandriva.com 		intf_ctl |= CS42L51_INTF_CTL_ADC_I2S;
42972ed5a8cSapatard@mandriva.com 		intf_ctl |= CS42L51_INTF_CTL_DAC_FORMAT(CS42L51_DAC_DIF_I2S);
43072ed5a8cSapatard@mandriva.com 		break;
43172ed5a8cSapatard@mandriva.com 	case SND_SOC_DAIFMT_LEFT_J:
43272ed5a8cSapatard@mandriva.com 		intf_ctl |= CS42L51_INTF_CTL_DAC_FORMAT(CS42L51_DAC_DIF_LJ24);
43372ed5a8cSapatard@mandriva.com 		break;
43472ed5a8cSapatard@mandriva.com 	case SND_SOC_DAIFMT_RIGHT_J:
43572ed5a8cSapatard@mandriva.com 		switch (params_format(params)) {
43672ed5a8cSapatard@mandriva.com 		case SNDRV_PCM_FORMAT_S16_LE:
43772ed5a8cSapatard@mandriva.com 		case SNDRV_PCM_FORMAT_S16_BE:
43872ed5a8cSapatard@mandriva.com 			fmt = CS42L51_DAC_DIF_RJ16;
43972ed5a8cSapatard@mandriva.com 			break;
44072ed5a8cSapatard@mandriva.com 		case SNDRV_PCM_FORMAT_S18_3LE:
44172ed5a8cSapatard@mandriva.com 		case SNDRV_PCM_FORMAT_S18_3BE:
44272ed5a8cSapatard@mandriva.com 			fmt = CS42L51_DAC_DIF_RJ18;
44372ed5a8cSapatard@mandriva.com 			break;
44472ed5a8cSapatard@mandriva.com 		case SNDRV_PCM_FORMAT_S20_3LE:
44572ed5a8cSapatard@mandriva.com 		case SNDRV_PCM_FORMAT_S20_3BE:
44672ed5a8cSapatard@mandriva.com 			fmt = CS42L51_DAC_DIF_RJ20;
44772ed5a8cSapatard@mandriva.com 			break;
44872ed5a8cSapatard@mandriva.com 		case SNDRV_PCM_FORMAT_S24_LE:
44972ed5a8cSapatard@mandriva.com 		case SNDRV_PCM_FORMAT_S24_BE:
45072ed5a8cSapatard@mandriva.com 			fmt = CS42L51_DAC_DIF_RJ24;
45172ed5a8cSapatard@mandriva.com 			break;
45272ed5a8cSapatard@mandriva.com 		default:
45372ed5a8cSapatard@mandriva.com 			dev_err(codec->dev, "unknown format\n");
45472ed5a8cSapatard@mandriva.com 			return -EINVAL;
45572ed5a8cSapatard@mandriva.com 		}
45672ed5a8cSapatard@mandriva.com 		intf_ctl |= CS42L51_INTF_CTL_DAC_FORMAT(fmt);
45772ed5a8cSapatard@mandriva.com 		break;
45872ed5a8cSapatard@mandriva.com 	default:
45972ed5a8cSapatard@mandriva.com 		dev_err(codec->dev, "unknown format\n");
46072ed5a8cSapatard@mandriva.com 		return -EINVAL;
46172ed5a8cSapatard@mandriva.com 	}
46272ed5a8cSapatard@mandriva.com 
46372ed5a8cSapatard@mandriva.com 	if (ratios[i].mclk)
46472ed5a8cSapatard@mandriva.com 		power_ctl |= CS42L51_MIC_POWER_CTL_MCLK_DIV2;
46572ed5a8cSapatard@mandriva.com 
46672ed5a8cSapatard@mandriva.com 	ret = snd_soc_write(codec, CS42L51_INTF_CTL, intf_ctl);
46772ed5a8cSapatard@mandriva.com 	if (ret < 0)
46872ed5a8cSapatard@mandriva.com 		return ret;
46972ed5a8cSapatard@mandriva.com 
47072ed5a8cSapatard@mandriva.com 	ret = snd_soc_write(codec, CS42L51_MIC_POWER_CTL, power_ctl);
47172ed5a8cSapatard@mandriva.com 	if (ret < 0)
47272ed5a8cSapatard@mandriva.com 		return ret;
47372ed5a8cSapatard@mandriva.com 
47472ed5a8cSapatard@mandriva.com 	return 0;
47572ed5a8cSapatard@mandriva.com }
47672ed5a8cSapatard@mandriva.com 
47772ed5a8cSapatard@mandriva.com static int cs42l51_dai_mute(struct snd_soc_dai *dai, int mute)
47872ed5a8cSapatard@mandriva.com {
47972ed5a8cSapatard@mandriva.com 	struct snd_soc_codec *codec = dai->codec;
48072ed5a8cSapatard@mandriva.com 	int reg;
48172ed5a8cSapatard@mandriva.com 	int mask = CS42L51_DAC_OUT_CTL_DACA_MUTE|CS42L51_DAC_OUT_CTL_DACB_MUTE;
48272ed5a8cSapatard@mandriva.com 
48372ed5a8cSapatard@mandriva.com 	reg = snd_soc_read(codec, CS42L51_DAC_OUT_CTL);
48472ed5a8cSapatard@mandriva.com 
48572ed5a8cSapatard@mandriva.com 	if (mute)
48672ed5a8cSapatard@mandriva.com 		reg |= mask;
48772ed5a8cSapatard@mandriva.com 	else
48872ed5a8cSapatard@mandriva.com 		reg &= ~mask;
48972ed5a8cSapatard@mandriva.com 
49072ed5a8cSapatard@mandriva.com 	return snd_soc_write(codec, CS42L51_DAC_OUT_CTL, reg);
49172ed5a8cSapatard@mandriva.com }
49272ed5a8cSapatard@mandriva.com 
49372ed5a8cSapatard@mandriva.com static struct snd_soc_dai_ops cs42l51_dai_ops = {
49472ed5a8cSapatard@mandriva.com 	.hw_params      = cs42l51_hw_params,
49572ed5a8cSapatard@mandriva.com 	.set_sysclk     = cs42l51_set_dai_sysclk,
49672ed5a8cSapatard@mandriva.com 	.set_fmt        = cs42l51_set_dai_fmt,
49772ed5a8cSapatard@mandriva.com 	.digital_mute   = cs42l51_dai_mute,
49872ed5a8cSapatard@mandriva.com };
49972ed5a8cSapatard@mandriva.com 
500f0fba2adSLiam Girdwood static struct snd_soc_dai_driver cs42l51_dai = {
501f0fba2adSLiam Girdwood 	.name = "cs42l51-hifi",
50272ed5a8cSapatard@mandriva.com 	.playback = {
50372ed5a8cSapatard@mandriva.com 		.stream_name = "Playback",
50472ed5a8cSapatard@mandriva.com 		.channels_min = 1,
50572ed5a8cSapatard@mandriva.com 		.channels_max = 2,
50672ed5a8cSapatard@mandriva.com 		.rates = SNDRV_PCM_RATE_8000_96000,
50772ed5a8cSapatard@mandriva.com 		.formats = CS42L51_FORMATS,
50872ed5a8cSapatard@mandriva.com 	},
50972ed5a8cSapatard@mandriva.com 	.capture = {
51072ed5a8cSapatard@mandriva.com 		.stream_name = "Capture",
51172ed5a8cSapatard@mandriva.com 		.channels_min = 1,
51272ed5a8cSapatard@mandriva.com 		.channels_max = 2,
51372ed5a8cSapatard@mandriva.com 		.rates = SNDRV_PCM_RATE_8000_96000,
51472ed5a8cSapatard@mandriva.com 		.formats = CS42L51_FORMATS,
51572ed5a8cSapatard@mandriva.com 	},
51672ed5a8cSapatard@mandriva.com 	.ops = &cs42l51_dai_ops,
51772ed5a8cSapatard@mandriva.com };
51872ed5a8cSapatard@mandriva.com 
519f0fba2adSLiam Girdwood static int cs42l51_probe(struct snd_soc_codec *codec)
52072ed5a8cSapatard@mandriva.com {
521f0fba2adSLiam Girdwood 	struct cs42l51_private *cs42l51 = snd_soc_codec_get_drvdata(codec);
522f0fba2adSLiam Girdwood 	int ret, reg;
52372ed5a8cSapatard@mandriva.com 
524f0fba2adSLiam Girdwood 	codec->control_data = cs42l51->control_data;
52572ed5a8cSapatard@mandriva.com 
526f0fba2adSLiam Girdwood 	ret = cs42l51_fill_cache(codec);
52772ed5a8cSapatard@mandriva.com 	if (ret < 0) {
528f0fba2adSLiam Girdwood 		dev_err(codec->dev, "failed to fill register cache\n");
52972ed5a8cSapatard@mandriva.com 		return ret;
53072ed5a8cSapatard@mandriva.com 	}
53172ed5a8cSapatard@mandriva.com 
532f0fba2adSLiam Girdwood 	ret = snd_soc_codec_set_cache_io(codec, 8, 8, cs42l51->control_type);
533f0fba2adSLiam Girdwood 	if (ret < 0) {
534f0fba2adSLiam Girdwood 		dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
535f0fba2adSLiam Girdwood 		return ret;
536f0fba2adSLiam Girdwood 	}
537f0fba2adSLiam Girdwood 
538f0fba2adSLiam Girdwood 	/*
539f0fba2adSLiam Girdwood 	 * DAC configuration
540f0fba2adSLiam Girdwood 	 * - Use signal processor
541f0fba2adSLiam Girdwood 	 * - auto mute
542f0fba2adSLiam Girdwood 	 * - vol changes immediate
543f0fba2adSLiam Girdwood 	 * - no de-emphasize
544f0fba2adSLiam Girdwood 	 */
545f0fba2adSLiam Girdwood 	reg = CS42L51_DAC_CTL_DATA_SEL(1)
546f0fba2adSLiam Girdwood 		| CS42L51_DAC_CTL_AMUTE | CS42L51_DAC_CTL_DACSZ(0);
547f0fba2adSLiam Girdwood 	ret = snd_soc_write(codec, CS42L51_DAC_CTL, reg);
548f0fba2adSLiam Girdwood 	if (ret < 0)
549f0fba2adSLiam Girdwood 		return ret;
550f0fba2adSLiam Girdwood 
55172ed5a8cSapatard@mandriva.com 	snd_soc_add_controls(codec, cs42l51_snd_controls,
55272ed5a8cSapatard@mandriva.com 		ARRAY_SIZE(cs42l51_snd_controls));
55372ed5a8cSapatard@mandriva.com 	snd_soc_dapm_new_controls(codec, cs42l51_dapm_widgets,
55472ed5a8cSapatard@mandriva.com 		ARRAY_SIZE(cs42l51_dapm_widgets));
55572ed5a8cSapatard@mandriva.com 	snd_soc_dapm_add_routes(codec, cs42l51_routes,
55672ed5a8cSapatard@mandriva.com 		ARRAY_SIZE(cs42l51_routes));
55772ed5a8cSapatard@mandriva.com 
55872ed5a8cSapatard@mandriva.com 	return 0;
55972ed5a8cSapatard@mandriva.com }
56072ed5a8cSapatard@mandriva.com 
561f0fba2adSLiam Girdwood static struct snd_soc_codec_driver soc_codec_device_cs42l51 = {
562f0fba2adSLiam Girdwood 	.probe =	cs42l51_probe,
563f0fba2adSLiam Girdwood 	.reg_cache_size = CS42L51_NUMREGS,
564f0fba2adSLiam Girdwood 	.reg_word_size = sizeof(u8),
565f0fba2adSLiam Girdwood };
56672ed5a8cSapatard@mandriva.com 
567f0fba2adSLiam Girdwood static int cs42l51_i2c_probe(struct i2c_client *i2c_client,
568f0fba2adSLiam Girdwood 	const struct i2c_device_id *id)
56972ed5a8cSapatard@mandriva.com {
570f0fba2adSLiam Girdwood 	struct cs42l51_private *cs42l51;
571f0fba2adSLiam Girdwood 	int ret;
57272ed5a8cSapatard@mandriva.com 
573f0fba2adSLiam Girdwood 	/* Verify that we have a CS42L51 */
574f0fba2adSLiam Girdwood 	ret = i2c_smbus_read_byte_data(i2c_client, CS42L51_CHIP_REV_ID);
575f0fba2adSLiam Girdwood 	if (ret < 0) {
576f0fba2adSLiam Girdwood 		dev_err(&i2c_client->dev, "failed to read I2C\n");
577f0fba2adSLiam Girdwood 		goto error;
578f0fba2adSLiam Girdwood 	}
57972ed5a8cSapatard@mandriva.com 
580f0fba2adSLiam Girdwood 	if ((ret != CS42L51_MK_CHIP_REV(CS42L51_CHIP_ID, CS42L51_CHIP_REV_A)) &&
581f0fba2adSLiam Girdwood 	    (ret != CS42L51_MK_CHIP_REV(CS42L51_CHIP_ID, CS42L51_CHIP_REV_B))) {
582f0fba2adSLiam Girdwood 		dev_err(&i2c_client->dev, "Invalid chip id\n");
583f0fba2adSLiam Girdwood 		ret = -ENODEV;
584f0fba2adSLiam Girdwood 		goto error;
585f0fba2adSLiam Girdwood 	}
586f0fba2adSLiam Girdwood 
587f0fba2adSLiam Girdwood 	dev_info(&i2c_client->dev, "found device cs42l51 rev %d\n",
588f0fba2adSLiam Girdwood 				ret & 7);
589f0fba2adSLiam Girdwood 
590f0fba2adSLiam Girdwood 	cs42l51 = kzalloc(sizeof(struct cs42l51_private), GFP_KERNEL);
591f0fba2adSLiam Girdwood 	if (!cs42l51) {
592f0fba2adSLiam Girdwood 		dev_err(&i2c_client->dev, "could not allocate codec\n");
593f0fba2adSLiam Girdwood 		return -ENOMEM;
594f0fba2adSLiam Girdwood 	}
595f0fba2adSLiam Girdwood 
596f0fba2adSLiam Girdwood 	i2c_set_clientdata(i2c_client, cs42l51);
597f0fba2adSLiam Girdwood 	cs42l51->control_data = i2c_client;
598f0fba2adSLiam Girdwood 	cs42l51->control_type = SND_SOC_I2C;
599f0fba2adSLiam Girdwood 
600f0fba2adSLiam Girdwood 	ret =  snd_soc_register_codec(&i2c_client->dev,
601f0fba2adSLiam Girdwood 			&soc_codec_device_cs42l51, &cs42l51_dai, 1);
602f0fba2adSLiam Girdwood 	if (ret < 0)
603f0fba2adSLiam Girdwood 		kfree(cs42l51);
604f0fba2adSLiam Girdwood error:
605f0fba2adSLiam Girdwood 	return ret;
606f0fba2adSLiam Girdwood }
607f0fba2adSLiam Girdwood 
608f0fba2adSLiam Girdwood static int cs42l51_i2c_remove(struct i2c_client *client)
609f0fba2adSLiam Girdwood {
610f0fba2adSLiam Girdwood 	struct cs42l51_private *cs42l51 = i2c_get_clientdata(client);
611f0fba2adSLiam Girdwood 
612f0fba2adSLiam Girdwood 	snd_soc_unregister_codec(&client->dev);
613f0fba2adSLiam Girdwood 	kfree(cs42l51);
61472ed5a8cSapatard@mandriva.com 	return 0;
61572ed5a8cSapatard@mandriva.com }
61672ed5a8cSapatard@mandriva.com 
617f0fba2adSLiam Girdwood static const struct i2c_device_id cs42l51_id[] = {
618f0fba2adSLiam Girdwood 	{"cs42l51", 0},
619f0fba2adSLiam Girdwood 	{}
62072ed5a8cSapatard@mandriva.com };
621f0fba2adSLiam Girdwood MODULE_DEVICE_TABLE(i2c, cs42l51_id);
622f0fba2adSLiam Girdwood 
623f0fba2adSLiam Girdwood static struct i2c_driver cs42l51_i2c_driver = {
624f0fba2adSLiam Girdwood 	.driver = {
625f0fba2adSLiam Girdwood 		.name = "cs42L51-codec",
626f0fba2adSLiam Girdwood 		.owner = THIS_MODULE,
627f0fba2adSLiam Girdwood 	},
628f0fba2adSLiam Girdwood 	.id_table = cs42l51_id,
629f0fba2adSLiam Girdwood 	.probe = cs42l51_i2c_probe,
630f0fba2adSLiam Girdwood 	.remove = cs42l51_i2c_remove,
631f0fba2adSLiam Girdwood };
63272ed5a8cSapatard@mandriva.com 
63372ed5a8cSapatard@mandriva.com static int __init cs42l51_init(void)
63472ed5a8cSapatard@mandriva.com {
63572ed5a8cSapatard@mandriva.com 	int ret;
63672ed5a8cSapatard@mandriva.com 
63772ed5a8cSapatard@mandriva.com 	ret = i2c_add_driver(&cs42l51_i2c_driver);
63872ed5a8cSapatard@mandriva.com 	if (ret != 0) {
63972ed5a8cSapatard@mandriva.com 		printk(KERN_ERR "%s: can't add i2c driver\n", __func__);
64072ed5a8cSapatard@mandriva.com 		return ret;
64172ed5a8cSapatard@mandriva.com 	}
64272ed5a8cSapatard@mandriva.com 	return 0;
64372ed5a8cSapatard@mandriva.com }
64472ed5a8cSapatard@mandriva.com module_init(cs42l51_init);
64572ed5a8cSapatard@mandriva.com 
64672ed5a8cSapatard@mandriva.com static void __exit cs42l51_exit(void)
64772ed5a8cSapatard@mandriva.com {
64872ed5a8cSapatard@mandriva.com 	i2c_del_driver(&cs42l51_i2c_driver);
64972ed5a8cSapatard@mandriva.com }
65072ed5a8cSapatard@mandriva.com module_exit(cs42l51_exit);
65172ed5a8cSapatard@mandriva.com 
65272ed5a8cSapatard@mandriva.com MODULE_AUTHOR("Arnaud Patard <apatard@mandriva.com>");
65372ed5a8cSapatard@mandriva.com MODULE_DESCRIPTION("Cirrus Logic CS42L51 ALSA SoC Codec Driver");
65472ed5a8cSapatard@mandriva.com MODULE_LICENSE("GPL");
655