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