diff options
Diffstat (limited to 'sound/soc')
46 files changed, 1870 insertions, 332 deletions
diff --git a/sound/soc/atmel/snd-soc-afeb9260.c b/sound/soc/atmel/snd-soc-afeb9260.c index da2208e06b0d..5e4d499d8434 100644 --- a/sound/soc/atmel/snd-soc-afeb9260.c +++ b/sound/soc/atmel/snd-soc-afeb9260.c @@ -129,7 +129,7 @@ static struct snd_soc_dai_link afeb9260_dai = { .cpu_dai_name = "atmel-ssc-dai.0", .codec_dai_name = "tlv320aic23-hifi", .platform_name = "atmel_pcm-audio", - .codec_name = "tlv320aic23-codec.0-0x1a", + .codec_name = "tlv320aic23-codec.0-001a", .init = afeb9260_tlv320aic23_init, .ops = &afeb9260_ops, }; diff --git a/sound/soc/blackfin/bf5xx-ssm2602.c b/sound/soc/blackfin/bf5xx-ssm2602.c index e902b24c1856..ad28663f5bbd 100644 --- a/sound/soc/blackfin/bf5xx-ssm2602.c +++ b/sound/soc/blackfin/bf5xx-ssm2602.c @@ -119,7 +119,7 @@ static struct snd_soc_dai_link bf5xx_ssm2602_dai = { .cpu_dai_name = "bf5xx-i2s", .codec_dai_name = "ssm2602-hifi", .platform_name = "bf5xx-pcm-audio", - .codec_name = "ssm2602-codec.0-0x1b", + .codec_name = "ssm2602-codec.0-001b", .ops = &bf5xx_ssm2602_ops, }; diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index a9cb2a04ad56..e239345a4d5d 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -26,6 +26,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_CQ0093VC if MFD_DAVINCI_VOICECODEC select SND_SOC_CS42L51 if I2C select SND_SOC_CS4270 if I2C + select SND_SOC_CS4271 if SND_SOC_I2C_AND_SPI select SND_SOC_CX20442 select SND_SOC_DA7210 if I2C select SND_SOC_JZ4740_CODEC if SOC_JZ4740 @@ -157,6 +158,9 @@ config SND_SOC_CS4270_VD33_ERRATA bool depends on SND_SOC_CS4270 +config SND_SOC_CS4271 + tristate + config SND_SOC_CX20442 tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 68e76af894b9..83b7accd7037 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -12,6 +12,7 @@ snd-soc-ak4671-objs := ak4671.o snd-soc-cq93vc-objs := cq93vc.o snd-soc-cs42l51-objs := cs42l51.o snd-soc-cs4270-objs := cs4270.o +snd-soc-cs4271-objs := cs4271.o snd-soc-cx20442-objs := cx20442.o snd-soc-da7210-objs := da7210.o snd-soc-dmic-objs := dmic.o @@ -93,6 +94,7 @@ obj-$(CONFIG_SND_SOC_AK4671) += snd-soc-ak4671.o obj-$(CONFIG_SND_SOC_CQ0093VC) += snd-soc-cq93vc.o obj-$(CONFIG_SND_SOC_CS42L51) += snd-soc-cs42l51.o obj-$(CONFIG_SND_SOC_CS4270) += snd-soc-cs4270.o +obj-$(CONFIG_SND_SOC_CS4271) += snd-soc-cs4271.o obj-$(CONFIG_SND_SOC_CX20442) += snd-soc-cx20442.o obj-$(CONFIG_SND_SOC_DA7210) += snd-soc-da7210.o obj-$(CONFIG_SND_SOC_DMIC) += snd-soc-dmic.o diff --git a/sound/soc/codecs/ak4642.c b/sound/soc/codecs/ak4642.c index f00eba313dfd..4be0570e3f1f 100644 --- a/sound/soc/codecs/ak4642.c +++ b/sound/soc/codecs/ak4642.c @@ -116,6 +116,12 @@ #define BCKO_MASK (1 << 3) #define BCKO_64 BCKO_MASK +#define DIF_MASK (3 << 0) +#define DSP (0 << 0) +#define RIGHT_J (1 << 0) +#define LEFT_J (2 << 0) +#define I2S (3 << 0) + /* MD_CTL2 */ #define FS0 (1 << 0) #define FS1 (1 << 1) @@ -354,6 +360,24 @@ static int ak4642_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) snd_soc_update_bits(codec, PW_MGMT2, MS, data); snd_soc_update_bits(codec, MD_CTL1, BCKO_MASK, bcko); + /* format type */ + data = 0; + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_LEFT_J: + data = LEFT_J; + break; + case SND_SOC_DAIFMT_I2S: + data = I2S; + break; + /* FIXME + * Please add RIGHT_J / DSP support here + */ + default: + return -EINVAL; + break; + } + snd_soc_update_bits(codec, MD_CTL1, DIF_MASK, data); + return 0; } diff --git a/sound/soc/codecs/cq93vc.c b/sound/soc/codecs/cq93vc.c index 46dbfd067f79..347a567b01e1 100644 --- a/sound/soc/codecs/cq93vc.c +++ b/sound/soc/codecs/cq93vc.c @@ -153,7 +153,7 @@ static int cq93vc_resume(struct snd_soc_codec *codec) static int cq93vc_probe(struct snd_soc_codec *codec) { - struct davinci_vc *davinci_vc = codec->dev->platform_data; + struct davinci_vc *davinci_vc = snd_soc_codec_get_drvdata(codec); davinci_vc->cq93vc.codec = codec; codec->control_data = davinci_vc; diff --git a/sound/soc/codecs/cs4271.c b/sound/soc/codecs/cs4271.c new file mode 100644 index 000000000000..9c5b7db0ce6a --- /dev/null +++ b/sound/soc/codecs/cs4271.c @@ -0,0 +1,659 @@ +/* + * CS4271 ASoC codec driver + * + * Copyright (c) 2010 Alexander Sverdlin <subaparts@yandex.ru> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * This driver support CS4271 codec being master or slave, working + * in control port mode, connected either via SPI or I2C. + * The data format accepted is I2S or left-justified. + * DAPM support not implemented. + */ + +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <sound/pcm.h> +#include <sound/soc.h> +#include <sound/tlv.h> +#include <linux/gpio.h> +#include <linux/i2c.h> +#include <linux/spi/spi.h> +#include <sound/cs4271.h> + +#define CS4271_PCM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +/* + * CS4271 registers + * High byte represents SPI chip address (0x10) + write command (0) + * Low byte - codec register address + */ +#define CS4271_MODE1 0x2001 /* Mode Control 1 */ +#define CS4271_DACCTL 0x2002 /* DAC Control */ +#define CS4271_DACVOL 0x2003 /* DAC Volume & Mixing Control */ +#define CS4271_VOLA 0x2004 /* DAC Channel A Volume Control */ +#define CS4271_VOLB 0x2005 /* DAC Channel B Volume Control */ +#define CS4271_ADCCTL 0x2006 /* ADC Control */ +#define CS4271_MODE2 0x2007 /* Mode Control 2 */ +#define CS4271_CHIPID 0x2008 /* Chip ID */ + +#define CS4271_FIRSTREG CS4271_MODE1 +#define CS4271_LASTREG CS4271_MODE2 +#define CS4271_NR_REGS ((CS4271_LASTREG & 0xFF) + 1) + +/* Bit masks for the CS4271 registers */ +#define CS4271_MODE1_MODE_MASK 0xC0 +#define CS4271_MODE1_MODE_1X 0x00 +#define CS4271_MODE1_MODE_2X 0x80 +#define CS4271_MODE1_MODE_4X 0xC0 + +#define CS4271_MODE1_DIV_MASK 0x30 +#define CS4271_MODE1_DIV_1 0x00 +#define CS4271_MODE1_DIV_15 0x10 +#define CS4271_MODE1_DIV_2 0x20 +#define CS4271_MODE1_DIV_3 0x30 + +#define CS4271_MODE1_MASTER 0x08 + +#define CS4271_MODE1_DAC_DIF_MASK 0x07 +#define CS4271_MODE1_DAC_DIF_LJ 0x00 +#define CS4271_MODE1_DAC_DIF_I2S 0x01 +#define CS4271_MODE1_DAC_DIF_RJ16 0x02 +#define CS4271_MODE1_DAC_DIF_RJ24 0x03 +#define CS4271_MODE1_DAC_DIF_RJ20 0x04 +#define CS4271_MODE1_DAC_DIF_RJ18 0x05 + +#define CS4271_DACCTL_AMUTE 0x80 +#define CS4271_DACCTL_IF_SLOW 0x40 + +#define CS4271_DACCTL_DEM_MASK 0x30 +#define CS4271_DACCTL_DEM_DIS 0x00 +#define CS4271_DACCTL_DEM_441 0x10 +#define CS4271_DACCTL_DEM_48 0x20 +#define CS4271_DACCTL_DEM_32 0x30 + +#define CS4271_DACCTL_SVRU 0x08 +#define CS4271_DACCTL_SRD 0x04 +#define CS4271_DACCTL_INVA 0x02 +#define CS4271_DACCTL_INVB 0x01 + +#define CS4271_DACVOL_BEQUA 0x40 +#define CS4271_DACVOL_SOFT 0x20 +#define CS4271_DACVOL_ZEROC 0x10 + +#define CS4271_DACVOL_ATAPI_MASK 0x0F +#define CS4271_DACVOL_ATAPI_M_M 0x00 +#define CS4271_DACVOL_ATAPI_M_BR 0x01 +#define CS4271_DACVOL_ATAPI_M_BL 0x02 +#define CS4271_DACVOL_ATAPI_M_BLR2 0x03 +#define CS4271_DACVOL_ATAPI_AR_M 0x04 +#define CS4271_DACVOL_ATAPI_AR_BR 0x05 +#define CS4271_DACVOL_ATAPI_AR_BL 0x06 +#define CS4271_DACVOL_ATAPI_AR_BLR2 0x07 +#define CS4271_DACVOL_ATAPI_AL_M 0x08 +#define CS4271_DACVOL_ATAPI_AL_BR 0x09 +#define CS4271_DACVOL_ATAPI_AL_BL 0x0A +#define CS4271_DACVOL_ATAPI_AL_BLR2 0x0B +#define CS4271_DACVOL_ATAPI_ALR2_M 0x0C +#define CS4271_DACVOL_ATAPI_ALR2_BR 0x0D +#define CS4271_DACVOL_ATAPI_ALR2_BL 0x0E +#define CS4271_DACVOL_ATAPI_ALR2_BLR2 0x0F + +#define CS4271_VOLA_MUTE 0x80 +#define CS4271_VOLA_VOL_MASK 0x7F +#define CS4271_VOLB_MUTE 0x80 +#define CS4271_VOLB_VOL_MASK 0x7F + +#define CS4271_ADCCTL_DITHER16 0x20 + +#define CS4271_ADCCTL_ADC_DIF_MASK 0x10 +#define CS4271_ADCCTL_ADC_DIF_LJ 0x00 +#define CS4271_ADCCTL_ADC_DIF_I2S 0x10 + +#define CS4271_ADCCTL_MUTEA 0x08 +#define CS4271_ADCCTL_MUTEB 0x04 +#define CS4271_ADCCTL_HPFDA 0x02 +#define CS4271_ADCCTL_HPFDB 0x01 + +#define CS4271_MODE2_LOOP 0x10 +#define CS4271_MODE2_MUTECAEQUB 0x08 +#define CS4271_MODE2_FREEZE 0x04 +#define CS4271_MODE2_CPEN 0x02 +#define CS4271_MODE2_PDN 0x01 + +#define CS4271_CHIPID_PART_MASK 0xF0 +#define CS4271_CHIPID_REV_MASK 0x0F + +/* + * Default CS4271 power-up configuration + * Array contains non-existing in hw register at address 0 + * Array do not include Chip ID, as codec driver does not use + * registers read operations at all + */ +static const u8 cs4271_dflt_reg[CS4271_NR_REGS] = { + 0, + 0, + CS4271_DACCTL_AMUTE, + CS4271_DACVOL_SOFT | CS4271_DACVOL_ATAPI_AL_BR, + 0, + 0, + 0, + 0, +}; + +struct cs4271_private { + /* SND_SOC_I2C or SND_SOC_SPI */ + enum snd_soc_control_type bus_type; + void *control_data; + unsigned int mclk; + bool master; + bool deemph; + /* Current sample rate for de-emphasis control */ + int rate; + /* GPIO driving Reset pin, if any */ + int gpio_nreset; + /* GPIO that disable serial bus, if any */ + int gpio_disable; +}; + +struct cs4271_clk_cfg { + unsigned int ratio; /* MCLK / sample rate */ + u8 speed_mode; /* codec speed mode: 1x, 2x, 4x */ + u8 mclk_master; /* ratio bit mask for Master mode */ + u8 mclk_slave; /* ratio bit mask for Slave mode */ +}; + +static struct cs4271_clk_cfg cs4271_clk_tab[] = { + {64, CS4271_MODE1_MODE_4X, CS4271_MODE1_DIV_1, CS4271_MODE1_DIV_1}, + {96, CS4271_MODE1_MODE_4X, CS4271_MODE1_DIV_15, CS4271_MODE1_DIV_1}, + {128, CS4271_MODE1_MODE_2X, CS4271_MODE1_DIV_1, CS4271_MODE1_DIV_1}, + {192, CS4271_MODE1_MODE_2X, CS4271_MODE1_DIV_15, CS4271_MODE1_DIV_1}, + {256, CS4271_MODE1_MODE_1X, CS4271_MODE1_DIV_1, CS4271_MODE1_DIV_1}, + {384, CS4271_MODE1_MODE_1X, CS4271_MODE1_DIV_15, CS4271_MODE1_DIV_1}, + {512, CS4271_MODE1_MODE_1X, CS4271_MODE1_DIV_2, CS4271_MODE1_DIV_1}, + {768, CS4271_MODE1_MODE_1X, CS4271_MODE1_DIV_3, CS4271_MODE1_DIV_3}, + {1024, CS4271_MODE1_MODE_1X, CS4271_MODE1_DIV_3, CS4271_MODE1_DIV_3} +}; + +#define CS4171_NR_RATIOS ARRAY_SIZE(cs4271_clk_tab) + +/* + * @freq is the desired MCLK rate + * MCLK rate should (c) be the sample rate, multiplied by one of the + * ratios listed in cs4271_mclk_fs_ratios table + */ +static int cs4271_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec); + + cs4271->mclk = freq; + return 0; +} + +static int cs4271_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int format) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec); + unsigned int val = 0; + int ret; + + switch (format & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + cs4271->master = 0; + break; + case SND_SOC_DAIFMT_CBM_CFM: + cs4271->master = 1; + val |= CS4271_MODE1_MASTER; + break; + default: + dev_err(codec->dev, "Invalid DAI format\n"); + return -EINVAL; + } + + switch (format & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_LEFT_J: + val |= CS4271_MODE1_DAC_DIF_LJ; + ret = snd_soc_update_bits(codec, CS4271_ADCCTL, + CS4271_ADCCTL_ADC_DIF_MASK, CS4271_ADCCTL_ADC_DIF_LJ); + if (ret < 0) + return ret; + break; + case SND_SOC_DAIFMT_I2S: + val |= CS4271_MODE1_DAC_DIF_I2S; + ret = snd_soc_update_bits(codec, CS4271_ADCCTL, + CS4271_ADCCTL_ADC_DIF_MASK, CS4271_ADCCTL_ADC_DIF_I2S); + if (ret < 0) + return ret; + break; + default: + dev_err(codec->dev, "Invalid DAI format\n"); + return -EINVAL; + } + + ret = snd_soc_update_bits(codec, CS4271_MODE1, + CS4271_MODE1_DAC_DIF_MASK | CS4271_MODE1_MASTER, val); + if (ret < 0) + return ret; + return 0; +} + +static int cs4271_deemph[] = {0, 44100, 48000, 32000}; + +static int cs4271_set_deemph(struct snd_soc_codec *codec) +{ + struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec); + int i, ret; + int val = CS4271_DACCTL_DEM_DIS; + + if (cs4271->deemph) { + /* Find closest de-emphasis freq */ + val = 1; + for (i = 2; i < ARRAY_SIZE(cs4271_deemph); i++) + if (abs(cs4271_deemph[i] - cs4271->rate) < + abs(cs4271_deemph[val] - cs4271->rate)) + val = i; + val <<= 4; + } + + ret = snd_soc_update_bits(codec, CS4271_DACCTL, + CS4271_DACCTL_DEM_MASK, val); + if (ret < 0) + return ret; + return 0; +} + +static int cs4271_get_deemph(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.enumerated.item[0] = cs4271->deemph; + return 0; +} + +static int cs4271_put_deemph(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec); + + cs4271->deemph = ucontrol->value.enumerated.item[0]; + return cs4271_set_deemph(codec); +} + +static int cs4271_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_codec *codec = rtd->codec; + struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec); + int i, ret; + unsigned int ratio, val; + + cs4271->rate = params_rate(params); + ratio = cs4271->mclk / cs4271->rate; + for (i = 0; i < CS4171_NR_RATIOS; i++) + if (cs4271_clk_tab[i].ratio == ratio) + break; + + if ((i == CS4171_NR_RATIOS) || ((ratio == 1024) && cs4271->master)) { + dev_err(codec->dev, "Invalid sample rate\n"); + return -EINVAL; + } + + /* Configure DAC */ + val = cs4271_clk_tab[i].speed_mode; + + if (cs4271->master) + val |= cs4271_clk_tab[i].mclk_master; + else + val |= cs4271_clk_tab[i].mclk_slave; + + ret = snd_soc_update_bits(codec, CS4271_MODE1, + CS4271_MODE1_MODE_MASK | CS4271_MODE1_DIV_MASK, val); + if (ret < 0) + return ret; + + return cs4271_set_deemph(codec); +} + +static int cs4271_digital_mute(struct snd_soc_dai *dai, int mute) +{ + struct snd_soc_codec *codec = dai->codec; + int ret; + int val_a = 0; + int val_b = 0; + + if (mute) { + val_a = CS4271_VOLA_MUTE; + val_b = CS4271_VOLB_MUTE; + } + + ret = snd_soc_update_bits(codec, CS4271_VOLA, CS4271_VOLA_MUTE, val_a); + if (ret < 0) + return ret; + ret = snd_soc_update_bits(codec, CS4271_VOLB, CS4271_VOLB_MUTE, val_b); + if (ret < 0) + return ret; + + return 0; +} + +/* CS4271 controls */ +static DECLARE_TLV_DB_SCALE(cs4271_dac_tlv, -12700, 100, 0); + +static const struct snd_kcontrol_new cs4271_snd_controls[] = { + SOC_DOUBLE_R_TLV("Master Playback Volume", CS4271_VOLA, CS4271_VOLB, + 0, 0x7F, 1, cs4271_dac_tlv), + SOC_SINGLE("Digital Loopback Switch", CS4271_MODE2, 4, 1, 0), + SOC_SINGLE("Soft Ramp Switch", CS4271_DACVOL, 5, 1, 0), + SOC_SINGLE("Zero Cross Switch", CS4271_DACVOL, 4, 1, 0), + SOC_SINGLE_BOOL_EXT("De-emphasis Switch", 0, + cs4271_get_deemph, cs4271_put_deemph), + SOC_SINGLE("Auto-Mute Switch", CS4271_DACCTL, 7, 1, 0), + SOC_SINGLE("Slow Roll Off Filter Switch", CS4271_DACCTL, 6, 1, 0), + SOC_SINGLE("Soft Volume Ramp-Up Switch", CS4271_DACCTL, 3, 1, 0), + SOC_SINGLE("Soft Ramp-Down Switch", CS4271_DACCTL, 2, 1, 0), + SOC_SINGLE("Left Channel Inversion Switch", CS4271_DACCTL, 1, 1, 0), + SOC_SINGLE("Right Channel Inversion Switch", CS4271_DACCTL, 0, 1, 0), + SOC_DOUBLE("Master Capture Switch", CS4271_ADCCTL, 3, 2, 1, 1), + SOC_SINGLE("Dither 16-Bit Data Switch", CS4271_ADCCTL, 5, 1, 0), + SOC_DOUBLE("High Pass Filter Switch", CS4271_ADCCTL, 1, 0, 1, 1), + SOC_DOUBLE_R("Master Playback Switch", CS4271_VOLA, CS4271_VOLB, + 7, 1, 1), +}; + +static struct snd_soc_dai_ops cs4271_dai_ops = { + .hw_params = cs4271_hw_params, + .set_sysclk = cs4271_set_dai_sysclk, + .set_fmt = cs4271_set_dai_fmt, + .digital_mute = cs4271_digital_mute, +}; + +static struct snd_soc_dai_driver cs4271_dai = { + .name = "cs4271-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = CS4271_PCM_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = CS4271_PCM_FORMATS, + }, + .ops = &cs4271_dai_ops, + .symmetric_rates = 1, +}; + +#ifdef CONFIG_PM +static int cs4271_soc_suspend(struct snd_soc_codec *codec, pm_message_t mesg) +{ + int ret; + /* Set power-down bit */ + ret = snd_soc_update_bits(codec, CS4271_MODE2, 0, CS4271_MODE2_PDN); + if (ret < 0) + return ret; + return 0; +} + +static int cs4271_soc_resume(struct snd_soc_codec *codec) +{ + int ret; + /* Restore codec state */ + ret = snd_soc_cache_sync(codec); + if (ret < 0) + return ret; + /* then disable the power-down bit */ + ret = snd_soc_update_bits(codec, CS4271_MODE2, CS4271_MODE2_PDN, 0); + if (ret < 0) + return ret; + return 0; +} +#else +#define cs4271_soc_suspend NULL +#define cs4271_soc_resume NULL +#endif /* CONFIG_PM */ + +static int cs4271_probe(struct snd_soc_codec *codec) +{ + struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec); + struct cs4271_platform_data *cs4271plat = codec->dev->platform_data; + int ret; + int gpio_nreset = -EINVAL; + int gpio_disable = -EINVAL; + + codec->control_data = cs4271->control_data; + + if (cs4271plat) { + if (gpio_is_valid(cs4271plat->gpio_nreset)) + gpio_nreset = cs4271plat->gpio_nreset; + if (gpio_is_valid(cs4271plat->gpio_disable)) + gpio_disable = cs4271plat->gpio_disable; + } + + if (gpio_disable >= 0) + if (gpio_request(gpio_disable, "CS4271 Disable")) + gpio_disable = -EINVAL; + if (gpio_disable >= 0) + gpio_direction_output(gpio_disable, 0); + + if (gpio_nreset >= 0) + if (gpio_request(gpio_nreset, "CS4271 Reset")) + gpio_nreset = -EINVAL; + if (gpio_nreset >= 0) { + /* Reset codec */ + gpio_direction_output(gpio_nreset, 0); + udelay(1); + gpio_set_value(gpio_nreset, 1); + /* Give the codec time to wake up */ + udelay(1); + } + + cs4271->gpio_nreset = gpio_nreset; + cs4271->gpio_disable = gpio_disable; + + /* + * In case of I2C, chip address specified in board data. + * So cache IO operations use 8 bit codec register address. + * In case of SPI, chip address and register address + * passed together as 16 bit value. + * Anyway, register address is masked with 0xFF inside + * soc-cache code. + */ + if (cs4271->bus_type == SND_SOC_SPI) + ret = snd_soc_codec_set_cache_io(codec, 16, 8, + cs4271->bus_type); + else + ret = snd_soc_codec_set_cache_io(codec, 8, 8, + cs4271->bus_type); + if (ret) { + dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); + return ret; + } + + ret = snd_soc_update_bits(codec, CS4271_MODE2, 0, + CS4271_MODE2_PDN | CS4271_MODE2_CPEN); + if (ret < 0) + return ret; + ret = snd_soc_update_bits(codec, CS4271_MODE2, CS4271_MODE2_PDN, 0); + if (ret < 0) + return ret; + /* Power-up sequence requires 85 uS */ + udelay(85); + + return snd_soc_add_controls(codec, cs4271_snd_controls, + ARRAY_SIZE(cs4271_snd_controls)); +} + +static int cs4271_remove(struct snd_soc_codec *codec) +{ + struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec); + int gpio_nreset, gpio_disable; + + gpio_nreset = cs4271->gpio_nreset; + gpio_disable = cs4271->gpio_disable; + + if (gpio_is_valid(gpio_nreset)) { + /* Set codec to the reset state */ + gpio_set_value(gpio_nreset, 0); + gpio_free(gpio_nreset); + } + + if (gpio_is_valid(gpio_disable)) + gpio_free(gpio_disable); + + return 0; +}; + +static struct snd_soc_codec_driver soc_codec_dev_cs4271 = { + .probe = cs4271_probe, + .remove = cs4271_remove, + .suspend = cs4271_soc_suspend, + .resume = cs4271_soc_resume, + .reg_cache_default = cs4271_dflt_reg, + .reg_cache_size = ARRAY_SIZE(cs4271_dflt_reg), + .reg_word_size = sizeof(cs4271_dflt_reg[0]), + .compress_type = SND_SOC_FLAT_COMPRESSION, +}; + +#if defined(CONFIG_SPI_MASTER) +static int __devinit cs4271_spi_probe(struct spi_device *spi) +{ + struct cs4271_private *cs4271; + + cs4271 = devm_kzalloc(&spi->dev, sizeof(*cs4271), GFP_KERNEL); + if (!cs4271) + return -ENOMEM; + + spi_set_drvdata(spi, cs4271); + cs4271->control_data = spi; + cs4271->bus_type = SND_SOC_SPI; + + return snd_soc_register_codec(&spi->dev, &soc_codec_dev_cs4271, + &cs4271_dai, 1); +} + +static int __devexit cs4271_spi_remove(struct spi_device *spi) +{ + snd_soc_unregister_codec(&spi->dev); + return 0; +} + +static struct spi_driver cs4271_spi_driver = { + .driver = { + .name = "cs4271", + .owner = THIS_MODULE, + }, + .probe = cs4271_spi_probe, + .remove = __devexit_p(cs4271_spi_remove), +}; +#endif /* defined(CONFIG_SPI_MASTER) */ + +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +static struct i2c_device_id cs4271_i2c_id[] = { + {"cs4271", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, cs4271_i2c_id); + +static int __devinit cs4271_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct cs4271_private *cs4271; + + cs4271 = devm_kzalloc(&client->dev, sizeof(*cs4271), GFP_KERNEL); + if (!cs4271) + return -ENOMEM; + + i2c_set_clientdata(client, cs4271); + cs4271->control_data = client; + cs4271->bus_type = SND_SOC_I2C; + + return snd_soc_register_codec(&client->dev, &soc_codec_dev_cs4271, + &cs4271_dai, 1); +} + +static int __devexit cs4271_i2c_remove(struct i2c_client *client) +{ + snd_soc_unregister_codec(&client->dev); + return 0; +} + +static struct i2c_driver cs4271_i2c_driver = { + .driver = { + .name = "cs4271", + .owner = THIS_MODULE, + }, + .id_table = cs4271_i2c_id, + .probe = cs4271_i2c_probe, + .remove = __devexit_p(cs4271_i2c_remove), +}; +#endif /* defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) */ + +/* + * We only register our serial bus driver here without + * assignment to particular chip. So if any of the below + * fails, there is some problem with I2C or SPI subsystem. + * In most cases this module will be compiled with support + * of only one serial bus. + */ +static int __init cs4271_modinit(void) +{ + int ret; + +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) + ret = i2c_add_driver(&cs4271_i2c_driver); + if (ret) { + pr_err("Failed to register CS4271 I2C driver: %d\n", ret); + return ret; + } +#endif + +#if defined(CONFIG_SPI_MASTER) + ret = spi_register_driver(&cs4271_spi_driver); + if (ret) { + pr_err("Failed to register CS4271 SPI driver: %d\n", ret); + return ret; + } +#endif + + return 0; +} +module_init(cs4271_modinit); + +static void __exit cs4271_modexit(void) +{ +#if defined(CONFIG_SPI_MASTER) + spi_unregister_driver(&cs4271_spi_driver); +#endif + +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) + i2c_del_driver(&cs4271_i2c_driver); +#endif +} +module_exit(cs4271_modexit); + +MODULE_AUTHOR("Alexander Sverdlin <subaparts@yandex.ru>"); +MODULE_DESCRIPTION("Cirrus Logic CS4271 ALSA SoC Codec Driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cx20442.c b/sound/soc/codecs/cx20442.c index 03d1e860d229..bb4bf65b9e7e 100644 --- a/sound/soc/codecs/cx20442.c +++ b/sound/soc/codecs/cx20442.c @@ -367,9 +367,12 @@ static int cx20442_codec_remove(struct snd_soc_codec *codec) return 0; } +static const u8 cx20442_reg = CX20442_TELOUT | CX20442_MIC; + static struct snd_soc_codec_driver cx20442_codec_dev = { .probe = cx20442_codec_probe, .remove = cx20442_codec_remove, + .reg_cache_default = &cx20442_reg, .reg_cache_size = 1, .reg_word_size = sizeof(u8), .read = cx20442_read_reg_cache, diff --git a/sound/soc/codecs/sn95031.c b/sound/soc/codecs/sn95031.c index 593632cf791d..40e285df9ae5 100644 --- a/sound/soc/codecs/sn95031.c +++ b/sound/soc/codecs/sn95031.c @@ -33,6 +33,7 @@ #include <sound/soc.h> #include <sound/soc-dapm.h> #include <sound/initval.h> +#include <sound/tlv.h> #include "sn95031.h" #define SN95031_RATES (SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_44100) @@ -145,6 +146,129 @@ static int sn95031_vihf_event(struct snd_soc_dapm_widget *w, return 0; } +static int sn95031_dmic12_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + unsigned int ldo = 0, clk_dir = 0, data_dir = 0; + + if (SND_SOC_DAPM_EVENT_ON(event)) { + ldo = BIT(5)|BIT(4); + clk_dir = BIT(0); + data_dir = BIT(7); + } + /* program DMIC LDO, clock and set clock */ + snd_soc_update_bits(w->codec, SN95031_MICBIAS, BIT(5)|BIT(4), ldo); + snd_soc_update_bits(w->codec, SN95031_DMICBUF0123, BIT(0), clk_dir); + snd_soc_update_bits(w->codec, SN95031_DMICBUF0123, BIT(7), data_dir); + return 0; +} + +static int sn95031_dmic34_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + unsigned int ldo = 0, clk_dir = 0, data_dir = 0; + + if (SND_SOC_DAPM_EVENT_ON(event)) { + ldo = BIT(5)|BIT(4); + clk_dir = BIT(2); + data_dir = BIT(1); + } + /* program DMIC LDO, clock and set clock */ + snd_soc_update_bits(w->codec, SN95031_MICBIAS, BIT(5)|BIT(4), ldo); + snd_soc_update_bits(w->codec, SN95031_DMICBUF0123, BIT(2), clk_dir); + snd_soc_update_bits(w->codec, SN95031_DMICBUF45, BIT(1), data_dir); + return 0; +} + +static int sn95031_dmic56_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + unsigned int ldo = 0; + + if (SND_SOC_DAPM_EVENT_ON(event)) + ldo = BIT(7)|BIT(6); + + /* program DMIC LDO */ + snd_soc_update_bits(w->codec, SN95031_MICBIAS, BIT(7)|BIT(6), ldo); + return 0; +} + +/* mux controls */ +static const char *sn95031_mic_texts[] = { "AMIC", "LineIn" }; + +static const struct soc_enum sn95031_micl_enum = + SOC_ENUM_SINGLE(SN95031_ADCCONFIG, 1, 2, sn95031_mic_texts); + +static const struct snd_kcontrol_new sn95031_micl_mux_control = + SOC_DAPM_ENUM("Route", sn95031_micl_enum); + +static const struct soc_enum sn95031_micr_enum = + SOC_ENUM_SINGLE(SN95031_ADCCONFIG, 3, 2, sn95031_mic_texts); + +static const struct snd_kcontrol_new sn95031_micr_mux_control = + SOC_DAPM_ENUM("Route", sn95031_micr_enum); + +static const char *sn95031_input_texts[] = { "DMIC1", "DMIC2", "DMIC3", + "DMIC4", "DMIC5", "DMIC6", + "ADC Left", "ADC Right" }; + +static const struct soc_enum sn95031_input1_enum = + SOC_ENUM_SINGLE(SN95031_AUDIOMUX12, 0, 8, sn95031_input_texts); + +static const struct snd_kcontrol_new sn95031_input1_mux_control = + SOC_DAPM_ENUM("Route", sn95031_input1_enum); + +static const struct soc_enum sn95031_input2_enum = + SOC_ENUM_SINGLE(SN95031_AUDIOMUX12, 4, 8, sn95031_input_texts); + +static const struct snd_kcontrol_new sn95031_input2_mux_control = + SOC_DAPM_ENUM("Route", sn95031_input2_enum); + +static const struct soc_enum sn95031_input3_enum = + SOC_ENUM_SINGLE(SN95031_AUDIOMUX34, 0, 8, sn95031_input_texts); + +static const struct snd_kcontrol_new sn95031_input3_mux_control = + SOC_DAPM_ENUM("Route", sn95031_input3_enum); + +static const struct soc_enum sn95031_input4_enum = + SOC_ENUM_SINGLE(SN95031_AUDIOMUX34, 4, 8, sn95031_input_texts); + +static const struct snd_kcontrol_new sn95031_input4_mux_control = + SOC_DAPM_ENUM("Route", sn95031_input4_enum); + +/* capture path controls */ + +static const char *sn95031_micmode_text[] = {"Single Ended", "Differential"}; + +/* 0dB to 30dB in 10dB steps */ +static const DECLARE_TLV_DB_SCALE(mic_tlv, 0, 10, 30); + +static const struct soc_enum sn95031_micmode1_enum = + SOC_ENUM_SINGLE(SN95031_MICAMP1, 1, 2, sn95031_micmode_text); +static const struct soc_enum sn95031_micmode2_enum = + SOC_ENUM_SINGLE(SN95031_MICAMP2, 1, 2, sn95031_micmode_text); + +static const char *sn95031_dmic_cfg_text[] = {"GPO", "DMIC"}; + +static const struct soc_enum sn95031_dmic12_cfg_enum = + SOC_ENUM_SINGLE(SN95031_DMICMUX, 0, 2, sn95031_dmic_cfg_text); +static const struct soc_enum sn95031_dmic34_cfg_enum = + SOC_ENUM_SINGLE(SN95031_DMICMUX, 1, 2, sn95031_dmic_cfg_text); +static const struct soc_enum sn95031_dmic56_cfg_enum = + SOC_ENUM_SINGLE(SN95031_DMICMUX, 2, 2, sn95031_dmic_cfg_text); + +static const struct snd_kcontrol_new sn95031_snd_controls[] = { + SOC_ENUM("Mic1Mode Capture Route", sn95031_micmode1_enum), + SOC_ENUM("Mic2Mode Capture Route", sn95031_micmode2_enum), + SOC_ENUM("DMIC12 Capture Route", sn95031_dmic12_cfg_enum), + SOC_ENUM("DMIC34 Capture Route", sn95031_dmic34_cfg_enum), + SOC_ENUM("DMIC56 Capture Route", sn95031_dmic56_cfg_enum), + SOC_SINGLE_TLV("Mic1 Capture Volume", SN95031_MICAMP1, + 2, 4, 0, mic_tlv), + SOC_SINGLE_TLV("Mic2 Capture Volume", SN95031_MICAMP2, + 2, 4, 0, mic_tlv), +}; + /* DAPM widgets */ static const struct snd_soc_dapm_widget sn95031_dapm_widgets[] = { @@ -159,6 +283,36 @@ static const struct snd_soc_dapm_widget sn95031_dapm_widgets[] = { SND_SOC_DAPM_OUTPUT("VIB1OUT"), SND_SOC_DAPM_OUTPUT("VIB2OUT"), + SND_SOC_DAPM_INPUT("AMIC1"), /* headset mic */ + SND_SOC_DAPM_INPUT("AMIC2"), + SND_SOC_DAPM_INPUT("DMIC1"), + SND_SOC_DAPM_INPUT("DMIC2"), + SND_SOC_DAPM_INPUT("DMIC3"), + SND_SOC_DAPM_INPUT("DMIC4"), + SND_SOC_DAPM_INPUT("DMIC5"), + SND_SOC_DAPM_INPUT("DMIC6"), + SND_SOC_DAPM_INPUT("LINEINL"), + SND_SOC_DAPM_INPUT("LINEINR"), + + SND_SOC_DAPM_MICBIAS("AMIC1Bias", SN95031_MICBIAS, 2, 0), + SND_SOC_DAPM_MICBIAS("AMIC2Bias", SN95031_MICBIAS, 3, 0), + SND_SOC_DAPM_MICBIAS("DMIC12Bias", SN95031_DMICMUX, 3, 0), + SND_SOC_DAPM_MICBIAS("DMIC34Bias", SN95031_DMICMUX, 4, 0), + SND_SOC_DAPM_MICBIAS("DMIC56Bias", SN95031_DMICMUX, 5, 0), + + SND_SOC_DAPM_SUPPLY("DMIC12supply", SN95031_DMICLK, 0, 0, + sn95031_dmic12_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("DMIC34supply", SN95031_DMICLK, 1, 0, + sn95031_dmic34_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("DMIC56supply", SN95031_DMICLK, 2, 0, + sn95031_dmic56_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_AIF_OUT("PCM_Out", "Capture", 0, + SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_SUPPLY("Headset Rail", SND_SOC_NOPM, 0, 0, sn95031_vhs_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), @@ -209,6 +363,40 @@ static const struct snd_soc_dapm_widget sn95031_dapm_widgets[] = { SN95031_VIB1C5, 1, 0), SND_SOC_DAPM_DAC("Vibra2 DAC", "Vibra2", SN95031_VIB2C5, 1, 0), + + /* capture widgets */ + SND_SOC_DAPM_PGA("LineIn Enable Left", SN95031_MICAMP1, + 7, 0, NULL, 0), + SND_SOC_DAPM_PGA("LineIn Enable Right", SN95031_MICAMP2, + 7, 0, NULL, 0), + + SND_SOC_DAPM_PGA("MIC1 Enable", SN95031_MICAMP1, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("MIC2 Enable", SN95031_MICAMP2, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("TX1 Enable", SN95031_AUDIOTXEN, 2, 0, NULL, 0), + SND_SOC_DAPM_PGA("TX2 Enable", SN95031_AUDIOTXEN, 3, 0, NULL, 0), + SND_SOC_DAPM_PGA("TX3 Enable", SN95031_AUDIOTXEN, 4, 0, NULL, 0), + SND_SOC_DAPM_PGA("TX4 Enable", SN95031_AUDIOTXEN, 5, 0, NULL, 0), + + /* ADC have null stream as they will be turned ON by TX path */ + SND_SOC_DAPM_ADC("ADC Left", NULL, + SN95031_ADCCONFIG, 0, 0), + SND_SOC_DAPM_ADC("ADC Right", NULL, + SN95031_ADCCONFIG, 2, 0), + + SND_SOC_DAPM_MUX("Mic_InputL Capture Route", + SND_SOC_NOPM, 0, 0, &sn95031_micl_mux_control), + SND_SOC_DAPM_MUX("Mic_InputR Capture Route", + SND_SOC_NOPM, 0, 0, &sn95031_micr_mux_control), + + SND_SOC_DAPM_MUX("Txpath1 Capture Route", + SND_SOC_NOPM, 0, 0, &sn95031_input1_mux_control), + SND_SOC_DAPM_MUX("Txpath2 Capture Route", + SND_SOC_NOPM, 0, 0, &sn95031_input2_mux_control), + SND_SOC_DAPM_MUX("Txpath3 Capture Route", + SND_SOC_NOPM, 0, 0, &sn95031_input3_mux_control), + SND_SOC_DAPM_MUX("Txpath4 Capture Route", + SND_SOC_NOPM, 0, 0, &sn95031_input4_mux_control), + }; static const struct snd_soc_dapm_route sn95031_audio_map[] = { @@ -250,6 +438,87 @@ static const struct snd_soc_dapm_route sn95031_audio_map[] = { { "Lineout Right Playback", NULL, "Headset Right Filter"}, { "Lineout Right Playback", NULL, "Speaker Right Filter"}, { "Lineout Right Playback", NULL, "Vibra2 DAC"}, + + /* Headset (AMIC1) mic */ + { "AMIC1Bias", NULL, "AMIC1"}, + { "MIC1 Enable", NULL, "AMIC1Bias"}, + { "Mic_InputL Capture Route", "AMIC", "MIC1 Enable"}, + + /* AMIC2 */ + { "AMIC2Bias", NULL, "AMIC2"}, + { "MIC2 Enable", NULL, "AMIC2Bias"}, + { "Mic_InputR Capture Route", "AMIC", "MIC2 Enable"}, + + + /* Linein */ + { "LineIn Enable Left", NULL, "LINEINL"}, + { "LineIn Enable Right", NULL, "LINEINR"}, + { "Mic_InputL Capture Route", "LineIn", "LineIn Enable Left"}, + { "Mic_InputR Capture Route", "LineIn", "LineIn Enable Right"}, + + /* ADC connection */ + { "ADC Left", NULL, "Mic_InputL Capture Route"}, + { "ADC Right", NULL, "Mic_InputR Capture Route"}, + + /*DMIC connections */ + { "DMIC1", NULL, "DMIC12supply"}, + { "DMIC2", NULL, "DMIC12supply"}, + { "DMIC3", NULL, "DMIC34supply"}, + { "DMIC4", NULL, "DMIC34supply"}, + { "DMIC5", NULL, "DMIC56supply"}, + { "DMIC6", NULL, "DMIC56supply"}, + + { "DMIC12Bias", NULL, "DMIC1"}, + { "DMIC12Bias", NULL, "DMIC2"}, + { "DMIC34Bias", NULL, "DMIC3"}, + { "DMIC34Bias", NULL, "DMIC4"}, + { "DMIC56Bias", NULL, "DMIC5"}, + { "DMIC56Bias", NULL, "DMIC6"}, + + /*TX path inputs*/ + { "Txpath1 Capture Route", "ADC Left", "ADC Left"}, + { "Txpath2 Capture Route", "ADC Left", "ADC Left"}, + { "Txpath3 Capture Route", "ADC Left", "ADC Left"}, + { "Txpath4 Capture Route", "ADC Left", "ADC Left"}, + { "Txpath1 Capture Route", "ADC Right", "ADC Right"}, + { "Txpath2 Capture Route", "ADC Right", "ADC Right"}, + { "Txpath3 Capture Route", "ADC Right", "ADC Right"}, + { "Txpath4 Capture Route", "ADC Right", "ADC Right"}, + { "Txpath1 Capture Route", NULL, "DMIC1"}, + { "Txpath2 Capture Route", NULL, "DMIC1"}, + { "Txpath3 Capture Route", NULL, "DMIC1"}, + { "Txpath4 Capture Route", NULL, "DMIC1"}, + { "Txpath1 Capture Route", NULL, "DMIC2"}, + { "Txpath2 Capture Route", NULL, "DMIC2"}, + { "Txpath3 Capture Route", NULL, "DMIC2"}, + { "Txpath4 Capture Route", NULL, "DMIC2"}, + { "Txpath1 Capture Route", NULL, "DMIC3"}, + { "Txpath2 Capture Route", NULL, "DMIC3"}, + { "Txpath3 Capture Route", NULL, "DMIC3"}, + { "Txpath4 Capture Route", NULL, "DMIC3"}, + { "Txpath1 Capture Route", NULL, "DMIC4"}, + { "Txpath2 Capture Route", NULL, "DMIC4"}, + { "Txpath3 Capture Route", NULL, "DMIC4"}, + { "Txpath4 Capture Route", NULL, "DMIC4"}, + { "Txpath1 Capture Route", NULL, "DMIC5"}, + { "Txpath2 Capture Route", NULL, "DMIC5"}, + { "Txpath3 Capture Route", NULL, "DMIC5"}, + { "Txpath4 Capture Route", NULL, "DMIC5"}, + { "Txpath1 Capture Route", NULL, "DMIC6"}, + { "Txpath2 Capture Route", NULL, "DMIC6"}, + { "Txpath3 Capture Route", NULL, "DMIC6"}, + { "Txpath4 Capture Route", NULL, "DMIC6"}, + + /* tx path */ + { "TX1 Enable", NULL, "Txpath1 Capture Route"}, + { "TX2 Enable", NULL, "Txpath2 Capture Route"}, + { "TX3 Enable", NULL, "Txpath3 Capture Route"}, + { "TX4 Enable", NULL, "Txpath4 Capture Route"}, + { "PCM_Out", NULL, "TX1 Enable"}, + { "PCM_Out", NULL, "TX2 Enable"}, + { "PCM_Out", NULL, "TX3 Enable"}, + { "PCM_Out", NULL, "TX4 Enable"}, + }; /* speaker and headset mutes, for audio pops and clicks */ @@ -339,6 +608,13 @@ struct snd_soc_dai_driver sn95031_dais[] = { .rates = SN95031_RATES, .formats = SN95031_FORMATS, }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 5, + .rates = SN95031_RATES, + .formats = SN95031_FORMATS, + }, .ops = &sn95031_headset_dai_ops, }, { .name = "SN95031 Speaker", @@ -390,6 +666,8 @@ static int sn95031_codec_probe(struct snd_soc_codec *codec) snd_soc_write(codec, SN95031_PCM2RXSLOT01, 0x10); snd_soc_write(codec, SN95031_PCM2RXSLOT23, 0x32); snd_soc_write(codec, SN95031_PCM2RXSLOT45, 0x54); + snd_soc_write(codec, SN95031_PCM2TXSLOT01, 0x10); + snd_soc_write(codec, SN95031_PCM2TXSLOT23, 0x32); /* pcm port setting * This sets the pcm port to slave and clock at 19.2Mhz which * can support 6slots, sampling rate set per stream in hw-params @@ -423,6 +701,9 @@ static int sn95031_codec_probe(struct snd_soc_codec *codec) snd_soc_write(codec, SN95031_SSR2, 0x10); snd_soc_write(codec, SN95031_SSR3, 0x40); + snd_soc_add_controls(codec, sn95031_snd_controls, + ARRAY_SIZE(sn95031_snd_controls)); + ret = snd_soc_dapm_new_controls(&codec->dapm, sn95031_dapm_widgets, ARRAY_SIZE(sn95031_dapm_widgets)); if (ret) diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c index a2a446cb1807..3d4c55f3c7b5 100644 --- a/sound/soc/codecs/wm8903.c +++ b/sound/soc/codecs/wm8903.c @@ -2,6 +2,7 @@ * wm8903.c -- WM8903 ALSA SoC Audio driver * * Copyright 2008 Wolfson Microelectronics + * Copyright 2011 NVIDIA, Inc. * * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> * @@ -19,6 +20,7 @@ #include <linux/init.h> #include <linux/completion.h> #include <linux/delay.h> +#include <linux/gpio.h> #include <linux/pm.h> #include <linux/i2c.h> #include <linux/platform_device.h> @@ -213,6 +215,7 @@ static u16 wm8903_reg_defaults[] = { }; struct wm8903_priv { + struct snd_soc_codec *codec; int sysclk; int irq; @@ -230,6 +233,10 @@ struct wm8903_priv { int mic_short; int mic_last_report; int mic_delay; + +#ifdef CONFIG_GPIOLIB + struct gpio_chip gpio_chip; +#endif }; static int wm8903_volatile_register(struct snd_soc_codec *codec, unsigned int reg) @@ -1635,6 +1642,120 @@ static int wm8903_resume(struct snd_soc_codec *codec) return 0; } +#ifdef CONFIG_GPIOLIB +static inline struct wm8903_priv *gpio_to_wm8903(struct gpio_chip *chip) +{ + return container_of(chip, struct wm8903_priv, gpio_chip); +} + +static int wm8903_gpio_request(struct gpio_chip *chip, unsigned offset) +{ + if (offset >= WM8903_NUM_GPIO) + return -EINVAL; + + return 0; +} + +static int wm8903_gpio_direction_in(struct gpio_chip *chip, unsigned offset) +{ + struct wm8903_priv *wm8903 = gpio_to_wm8903(chip); + struct snd_soc_codec *codec = wm8903->codec; + unsigned int mask, val; + + mask = WM8903_GP1_FN_MASK | WM8903_GP1_DIR_MASK; + val = (WM8903_GPn_FN_GPIO_INPUT << WM8903_GP1_FN_SHIFT) | + WM8903_GP1_DIR; + + return snd_soc_update_bits(codec, WM8903_GPIO_CONTROL_1 + offset, + mask, val); +} + +static int wm8903_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + struct wm8903_priv *wm8903 = gpio_to_wm8903(chip); + struct snd_soc_codec *codec = wm8903->codec; + int reg; + + reg = snd_soc_read(codec, WM8903_GPIO_CONTROL_1 + offset); + + return (reg & WM8903_GP1_LVL_MASK) >> WM8903_GP1_LVL_SHIFT; +} + +static int wm8903_gpio_direction_out(struct gpio_chip *chip, + unsigned offset, int value) +{ + struct wm8903_priv *wm8903 = gpio_to_wm8903(chip); + struct snd_soc_codec *codec = wm8903->codec; + unsigned int mask, val; + + mask = WM8903_GP1_FN_MASK | WM8903_GP1_DIR_MASK | WM8903_GP1_LVL_MASK; + val = (WM8903_GPn_FN_GPIO_OUTPUT << WM8903_GP1_FN_SHIFT) | + (value << WM8903_GP2_LVL_SHIFT); + + return snd_soc_update_bits(codec, WM8903_GPIO_CONTROL_1 + offset, + mask, val); +} + +static void wm8903_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +{ + struct wm8903_priv *wm8903 = gpio_to_wm8903(chip); + struct snd_soc_codec *codec = wm8903->codec; + + snd_soc_update_bits(codec, WM8903_GPIO_CONTROL_1 + offset, + WM8903_GP1_LVL_MASK, + !!value << WM8903_GP1_LVL_SHIFT); +} + +static struct gpio_chip wm8903_template_chip = { + .label = "wm8903", + .owner = THIS_MODULE, + .request = wm8903_gpio_request, + .direction_input = wm8903_gpio_direction_in, + .get = wm8903_gpio_get, + .direction_output = wm8903_gpio_direction_out, + .set = wm8903_gpio_set, + .can_sleep = 1, +}; + +static void wm8903_init_gpio(struct snd_soc_codec *codec) +{ + struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec); + struct wm8903_platform_data *pdata = dev_get_platdata(codec->dev); + int ret; + + wm8903->gpio_chip = wm8903_template_chip; + wm8903->gpio_chip.ngpio = WM8903_NUM_GPIO; + wm8903->gpio_chip.dev = codec->dev; + + if (pdata && pdata->gpio_base) + wm8903->gpio_chip.base = pdata->gpio_base; + else + wm8903->gpio_chip.base = -1; + + ret = gpiochip_add(&wm8903->gpio_chip); + if (ret != 0) + dev_err(codec->dev, "Failed to add GPIOs: %d\n", ret); +} + +static void wm8903_free_gpio(struct snd_soc_codec *codec) +{ + struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec); + int ret; + + ret = gpiochip_remove(&wm8903->gpio_chip); + if (ret != 0) + dev_err(codec->dev, "Failed to remove GPIOs: %d\n", ret); +} +#else +static void wm8903_init_gpio(struct snd_soc_codec *codec) +{ +} + +static void wm8903_free_gpio(struct snd_soc_codec *codec) +{ +} +#endif + static int wm8903_probe(struct snd_soc_codec *codec) { struct wm8903_platform_data *pdata = dev_get_platdata(codec->dev); @@ -1643,6 +1764,7 @@ static int wm8903_probe(struct snd_soc_codec *codec) int trigger, irq_pol; u16 val; + wm8903->codec = codec; init_completion(&wm8903->wseq); ret = snd_soc_codec_set_cache_io(codec, 8, 16, SND_SOC_I2C); @@ -1667,7 +1789,7 @@ static int wm8903_probe(struct snd_soc_codec *codec) /* Set up GPIOs and microphone detection */ if (pdata) { for (i = 0; i < ARRAY_SIZE(pdata->gpio_cfg); i++) { - if (!pdata->gpio_cfg[i]) + if (pdata->gpio_cfg[i] == WM8903_GPIO_NO_CONFIG) continue; snd_soc_write(codec, WM8903_GPIO_CONTROL_1 + i, @@ -1749,12 +1871,15 @@ static int wm8903_probe(struct snd_soc_codec *codec) ARRAY_SIZE(wm8903_snd_controls)); wm8903_add_widgets(codec); + wm8903_init_gpio(codec); + return ret; } /* power down chip */ static int wm8903_remove(struct snd_soc_codec *codec) { + wm8903_free_gpio(codec); wm8903_set_bias_level(codec, SND_SOC_BIAS_OFF); return 0; } diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index 5c7b730a864f..3b71dd65c966 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c @@ -3635,7 +3635,7 @@ static void wm8962_gpio_set(struct gpio_chip *chip, unsigned offset, int value) struct snd_soc_codec *codec = wm8962->codec; snd_soc_update_bits(codec, WM8962_GPIO_BASE + offset, - WM8962_GP2_LVL, value << WM8962_GP2_LVL_SHIFT); + WM8962_GP2_LVL, !!value << WM8962_GP2_LVL_SHIFT); } static int wm8962_gpio_direction_out(struct gpio_chip *chip, diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index 0bb0bb40b842..0ca81d3c64e8 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -2386,7 +2386,7 @@ static int wm8994_set_tristate(struct snd_soc_dai *codec_dai, int tristate) else val = 0; - return snd_soc_update_bits(codec, reg, mask, reg); + return snd_soc_update_bits(codec, reg, mask, val); } #define WM8994_RATES SNDRV_PCM_RATE_8000_96000 diff --git a/sound/soc/codecs/wm8995.c b/sound/soc/codecs/wm8995.c index 7d563413df3e..67eaaecbb42e 100644 --- a/sound/soc/codecs/wm8995.c +++ b/sound/soc/codecs/wm8995.c @@ -1265,7 +1265,7 @@ static int wm8995_set_tristate(struct snd_soc_dai *codec_dai, int tristate) else val = 0; - return snd_soc_update_bits(codec, reg, mask, reg); + return snd_soc_update_bits(codec, reg, mask, val); } /* The size in bits of the FLL divide multiplied by 10 diff --git a/sound/soc/codecs/wm_hubs.c b/sound/soc/codecs/wm_hubs.c index c466982eed23..613df5db0b32 100644 --- a/sound/soc/codecs/wm_hubs.c +++ b/sound/soc/codecs/wm_hubs.c @@ -91,6 +91,7 @@ static void wait_for_dc_servo(struct snd_soc_codec *codec, unsigned int op) static void calibrate_dc_servo(struct snd_soc_codec *codec) { struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec); + s8 offset; u16 reg, reg_l, reg_r, dcs_cfg; /* If we're using a digital only path and have a previously @@ -149,16 +150,14 @@ static void calibrate_dc_servo(struct snd_soc_codec *codec) hubs->dcs_codes); /* HPOUT1L */ - if (reg_l + hubs->dcs_codes > 0 && - reg_l + hubs->dcs_codes < 0xff) - reg_l += hubs->dcs_codes; - dcs_cfg = reg_l << WM8993_DCS_DAC_WR_VAL_1_SHIFT; + offset = reg_l; + offset += hubs->dcs_codes; + dcs_cfg = (u8)offset << WM8993_DCS_DAC_WR_VAL_1_SHIFT; /* HPOUT1R */ - if (reg_r + hubs->dcs_codes > 0 && - reg_r + hubs->dcs_codes < 0xff) - reg_r += hubs->dcs_codes; - dcs_cfg |= reg_r; + offset = reg_r; + offset += hubs->dcs_codes; + dcs_cfg |= (u8)offset; dev_dbg(codec->dev, "DCS result: %x\n", dcs_cfg); diff --git a/sound/soc/davinci/davinci-evm.c b/sound/soc/davinci/davinci-evm.c index 0c2d6bacc681..b36f0b39b090 100644 --- a/sound/soc/davinci/davinci-evm.c +++ b/sound/soc/davinci/davinci-evm.c @@ -223,7 +223,7 @@ static struct snd_soc_dai_link da8xx_evm_dai = { .stream_name = "AIC3X", .cpu_dai_name= "davinci-mcasp.0", .codec_dai_name = "tlv320aic3x-hifi", - .codec_name = "tlv320aic3x-codec.0-001a", + .codec_name = "tlv320aic3x-codec.1-0018", .platform_name = "davinci-pcm-audio", .init = evm_aic3x_init, .ops = &evm_ops, diff --git a/sound/soc/ep93xx/Kconfig b/sound/soc/ep93xx/Kconfig index 57429041189c..91a28de94109 100644 --- a/sound/soc/ep93xx/Kconfig +++ b/sound/soc/ep93xx/Kconfig @@ -30,3 +30,12 @@ config SND_EP93XX_SOC_SIMONE help Say Y or M here if you want to add support for AC97 audio on the Simplemachines Sim.One board. + +config SND_EP93XX_SOC_EDB93XX + tristate "SoC Audio support for Cirrus Logic EDB93xx boards" + depends on SND_EP93XX_SOC && (MACH_EDB9301 || MACH_EDB9302 || MACH_EDB9302A || MACH_EDB9307A || MACH_EDB9315A) + select SND_EP93XX_SOC_I2S + select SND_SOC_CS4271 + help + Say Y or M here if you want to add support for I2S audio on the + Cirrus Logic EDB93xx boards. diff --git a/sound/soc/ep93xx/Makefile b/sound/soc/ep93xx/Makefile index 8e7977fb6b7d..5514146cbdf0 100644 --- a/sound/soc/ep93xx/Makefile +++ b/sound/soc/ep93xx/Makefile @@ -10,6 +10,8 @@ obj-$(CONFIG_SND_EP93XX_SOC_AC97) += snd-soc-ep93xx-ac97.o # EP93XX Machine Support snd-soc-snappercl15-objs := snappercl15.o snd-soc-simone-objs := simone.o +snd-soc-edb93xx-objs := edb93xx.o obj-$(CONFIG_SND_EP93XX_SOC_SNAPPERCL15) += snd-soc-snappercl15.o obj-$(CONFIG_SND_EP93XX_SOC_SIMONE) += snd-soc-simone.o +obj-$(CONFIG_SND_EP93XX_SOC_EDB93XX) += snd-soc-edb93xx.o diff --git a/sound/soc/ep93xx/edb93xx.c b/sound/soc/ep93xx/edb93xx.c new file mode 100644 index 000000000000..b270085227f3 --- /dev/null +++ b/sound/soc/ep93xx/edb93xx.c @@ -0,0 +1,142 @@ +/* + * SoC audio for EDB93xx + * + * Copyright (c) 2010 Alexander Sverdlin <subaparts@yandex.ru> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * This driver support CS4271 codec being master or slave, working + * in control port mode, connected either via SPI or I2C. + * The data format accepted is I2S or left-justified. + * DAPM support not implemented. + */ + +#include <linux/platform_device.h> +#include <linux/gpio.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/soc.h> +#include <asm/mach-types.h> +#include <mach/hardware.h> +#include "ep93xx-pcm.h" + +#define edb93xx_has_audio() (machine_is_edb9301() || \ + machine_is_edb9302() || \ + machine_is_edb9302a() || \ + machine_is_edb9307a() || \ + machine_is_edb9315a()) + +static int edb93xx_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + int err; + unsigned int rate = params_rate(params); + /* + * We set LRCLK equal to `rate' and SCLK = LRCLK * 64, + * because our sample size is 32 bit * 2 channels. + * I2S standard permits us to transmit more bits than + * the codec uses. + * MCLK = SCLK * 4 is the best recommended value, + * but we have to fall back to ratio 2 for higher + * sample rates. + */ + unsigned int mclk_rate = rate * 64 * ((rate <= 48000) ? 4 : 2); + + err = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_IF | + SND_SOC_DAIFMT_CBS_CFS); + if (err) + return err; + + err = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_IF | + SND_SOC_DAIFMT_CBS_CFS); + if (err) + return err; + + err = snd_soc_dai_set_sysclk(codec_dai, 0, mclk_rate, + SND_SOC_CLOCK_IN); + if (err) + return err; + + return snd_soc_dai_set_sysclk(cpu_dai, 0, mclk_rate, + SND_SOC_CLOCK_OUT); +} + +static struct snd_soc_ops edb93xx_ops = { + .hw_params = edb93xx_hw_params, +}; + +static struct snd_soc_dai_link edb93xx_dai = { + .name = "CS4271", + .stream_name = "CS4271 HiFi", + .platform_name = "ep93xx-pcm-audio", + .cpu_dai_name = "ep93xx-i2s", + .codec_name = "spi0.0", + .codec_dai_name = "cs4271-hifi", + .ops = &edb93xx_ops, +}; + +static struct snd_soc_card snd_soc_edb93xx = { + .name = "EDB93XX", + .dai_link = &edb93xx_dai, + .num_links = 1, +}; + +static struct platform_device *edb93xx_snd_device; + +static int __init edb93xx_init(void) +{ + int ret; + + if (!edb93xx_has_audio()) + return -ENODEV; + + ret = ep93xx_i2s_acquire(EP93XX_SYSCON_DEVCFG_I2SONAC97, + EP93XX_SYSCON_I2SCLKDIV_ORIDE | + EP93XX_SYSCON_I2SCLKDIV_SPOL); + if (ret) + return ret; + + edb93xx_snd_device = platform_device_alloc("soc-audio", -1); + if (!edb93xx_snd_device) { + ret = -ENOMEM; + goto free_i2s; + } + + platform_set_drvdata(edb93xx_snd_device, &snd_soc_edb93xx); + ret = platform_device_add(edb93xx_snd_device); + if (ret) + goto device_put; + + return 0; + +device_put: + platform_device_put(edb93xx_snd_device); +free_i2s: + ep93xx_i2s_release(); + return ret; +} +module_init(edb93xx_init); + +static void __exit edb93xx_exit(void) +{ + platform_device_unregister(edb93xx_snd_device); + ep93xx_i2s_release(); +} +module_exit(edb93xx_exit); + +MODULE_AUTHOR("Alexander Sverdlin <subaparts@yandex.ru>"); +MODULE_DESCRIPTION("ALSA SoC EDB93xx"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/fsl/mpc8610_hpcd.c b/sound/soc/fsl/mpc8610_hpcd.c index 7d7847a1e66b..c16c6b2eff95 100644 --- a/sound/soc/fsl/mpc8610_hpcd.c +++ b/sound/soc/fsl/mpc8610_hpcd.c @@ -53,9 +53,8 @@ struct mpc8610_hpcd_data { * * Here we program the DMACR and PMUXCR registers. */ -static int mpc8610_hpcd_machine_probe(struct platform_device *sound_device) +static int mpc8610_hpcd_machine_probe(struct snd_soc_card *card) { - struct snd_soc_card *card = platform_get_drvdata(sound_device); struct mpc8610_hpcd_data *machine_data = container_of(card, struct mpc8610_hpcd_data, card); struct ccsr_guts_86xx __iomem *guts; @@ -138,9 +137,8 @@ static int mpc8610_hpcd_startup(struct snd_pcm_substream *substream) * This function is called to remove the sound device for one SSI. We * de-program the DMACR and PMUXCR register. */ -static int mpc8610_hpcd_machine_remove(struct platform_device *sound_device) +static int mpc8610_hpcd_machine_remove(struct snd_soc_card *card) { - struct snd_soc_card *card = platform_get_drvdata(sound_device); struct mpc8610_hpcd_data *machine_data = container_of(card, struct mpc8610_hpcd_data, card); struct ccsr_guts_86xx __iomem *guts; diff --git a/sound/soc/fsl/p1022_ds.c b/sound/soc/fsl/p1022_ds.c index 026b756961e0..66e0b68af147 100644 --- a/sound/soc/fsl/p1022_ds.c +++ b/sound/soc/fsl/p1022_ds.c @@ -85,9 +85,8 @@ struct machine_data { * * Here we program the DMACR and PMUXCR registers. */ -static int p1022_ds_machine_probe(struct platform_device *sound_device) +static int p1022_ds_machine_probe(struct snd_soc_card *card) { - struct snd_soc_card *card = platform_get_drvdata(sound_device); struct machine_data *mdata = container_of(card, struct machine_data, card); struct ccsr_guts_85xx __iomem *guts; @@ -160,9 +159,8 @@ static int p1022_ds_startup(struct snd_pcm_substream *substream) * This function is called to remove the sound device for one SSI. We * de-program the DMACR and PMUXCR register. */ -static int p1022_ds_machine_remove(struct platform_device *sound_device) +static int p1022_ds_machine_remove(struct snd_soc_card *card) { - struct snd_soc_card *card = platform_get_drvdata(sound_device); struct machine_data *mdata = container_of(card, struct machine_data, card); struct ccsr_guts_85xx __iomem *guts; diff --git a/sound/soc/mid-x86/mfld_machine.c b/sound/soc/mid-x86/mfld_machine.c index 1a330be1a01e..7925851a5de1 100644 --- a/sound/soc/mid-x86/mfld_machine.c +++ b/sound/soc/mid-x86/mfld_machine.c @@ -182,6 +182,9 @@ static int mfld_init(struct snd_soc_pcm_runtime *runtime) snd_soc_dapm_disable_pin(dapm, "LINEOUTR"); lo_dac = 3; hs_switch = 0; + /* we dont use linein in this so set to NC */ + snd_soc_dapm_disable_pin(dapm, "LINEINL"); + snd_soc_dapm_disable_pin(dapm, "LINEINR"); return snd_soc_dapm_sync(dapm); } diff --git a/sound/soc/mid-x86/sst_platform.c b/sound/soc/mid-x86/sst_platform.c index 1d1f5447b338..96e6e9c9c5f4 100644 --- a/sound/soc/mid-x86/sst_platform.c +++ b/sound/soc/mid-x86/sst_platform.c @@ -73,6 +73,12 @@ struct snd_soc_dai_driver sst_platform_dai[] = { .rates = SNDRV_PCM_RATE_48000, .formats = SNDRV_PCM_FMTBIT_S24_LE, }, + .capture = { + .channels_min = 1, + .channels_max = 5, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S24_LE, + }, }, { .name = "Speaker-cpu-dai", diff --git a/sound/soc/omap/ams-delta.c b/sound/soc/omap/ams-delta.c index 2101bdcee21f..3167be689621 100644 --- a/sound/soc/omap/ams-delta.c +++ b/sound/soc/omap/ams-delta.c @@ -507,8 +507,6 @@ static int ams_delta_cx20442_init(struct snd_soc_pcm_runtime *rtd) /* Set up digital mute if not provided by the codec */ if (!codec_dai->driver->ops) { codec_dai->driver->ops = &ams_delta_dai_ops; - } else if (!codec_dai->driver->ops->digital_mute) { - codec_dai->driver->ops->digital_mute = ams_delta_digital_mute; } else { ams_delta_ops.startup = ams_delta_startup; ams_delta_ops.shutdown = ams_delta_shutdown; diff --git a/sound/soc/pxa/corgi.c b/sound/soc/pxa/corgi.c index fc592f0d5fc7..784cff5f67e8 100644 --- a/sound/soc/pxa/corgi.c +++ b/sound/soc/pxa/corgi.c @@ -307,10 +307,10 @@ static int corgi_wm8731_init(struct snd_soc_pcm_runtime *rtd) static struct snd_soc_dai_link corgi_dai = { .name = "WM8731", .stream_name = "WM8731", - .cpu_dai_name = "pxa-is2-dai", + .cpu_dai_name = "pxa2xx-i2s", .codec_dai_name = "wm8731-hifi", .platform_name = "pxa-pcm-audio", - .codec_name = "wm8731-codec-0.001a", + .codec_name = "wm8731-codec-0.001b", .init = corgi_wm8731_init, .ops = &corgi_ops, }; diff --git a/sound/soc/pxa/poodle.c b/sound/soc/pxa/poodle.c index 6298ee115e27..a7d4999f9b24 100644 --- a/sound/soc/pxa/poodle.c +++ b/sound/soc/pxa/poodle.c @@ -276,7 +276,7 @@ static struct snd_soc_dai_link poodle_dai = { .cpu_dai_name = "pxa2xx-i2s", .codec_dai_name = "wm8731-hifi", .platform_name = "pxa-pcm-audio", - .codec_name = "wm8731-codec.0-001a", + .codec_name = "wm8731-codec.0-001b", .init = poodle_wm8731_init, .ops = &poodle_ops, }; diff --git a/sound/soc/pxa/raumfeld.c b/sound/soc/pxa/raumfeld.c index 0fd60f423036..db1dd560a585 100644 --- a/sound/soc/pxa/raumfeld.c +++ b/sound/soc/pxa/raumfeld.c @@ -151,13 +151,13 @@ static struct snd_soc_ops raumfeld_cs4270_ops = { .hw_params = raumfeld_cs4270_hw_params, }; -static int raumfeld_line_suspend(struct platform_device *pdev, pm_message_t state) +static int raumfeld_line_suspend(struct snd_soc_card *card) { raumfeld_enable_audio(false); return 0; } -static int raumfeld_line_resume(struct platform_device *pdev) +static int raumfeld_line_resume(struct snd_soc_card *card) { raumfeld_enable_audio(true); return 0; diff --git a/sound/soc/pxa/spitz.c b/sound/soc/pxa/spitz.c index c2acb69b957a..8e1571350630 100644 --- a/sound/soc/pxa/spitz.c +++ b/sound/soc/pxa/spitz.c @@ -315,10 +315,10 @@ static int spitz_wm8750_init(struct snd_soc_pcm_runtime *rtd) static struct snd_soc_dai_link spitz_dai = { .name = "wm8750", .stream_name = "WM8750", - .cpu_dai_name = "pxa-is2", + .cpu_dai_name = "pxa2xx-i2s", .codec_dai_name = "wm8750-hifi", .platform_name = "pxa-pcm-audio", - .codec_name = "wm8750-codec.0-001a", + .codec_name = "wm8750-codec.0-001b", .init = spitz_wm8750_init, .ops = &spitz_ops, }; diff --git a/sound/soc/pxa/tosa.c b/sound/soc/pxa/tosa.c index f75804ef0897..489139a31cf9 100644 --- a/sound/soc/pxa/tosa.c +++ b/sound/soc/pxa/tosa.c @@ -237,7 +237,7 @@ static struct snd_soc_dai_link tosa_dai[] = { }, }; -static int tosa_probe(struct platform_device *dev) +static int tosa_probe(struct snd_soc_card *card) { int ret; @@ -251,7 +251,7 @@ static int tosa_probe(struct platform_device *dev) return ret; } -static int tosa_remove(struct platform_device *dev) +static int tosa_remove(struct snd_soc_card *card) { gpio_free(TOSA_GPIO_L_MUTE); return 0; diff --git a/sound/soc/pxa/zylonite.c b/sound/soc/pxa/zylonite.c index b222a7d72027..c5858296b48a 100644 --- a/sound/soc/pxa/zylonite.c +++ b/sound/soc/pxa/zylonite.c @@ -189,7 +189,7 @@ static struct snd_soc_dai_link zylonite_dai[] = { }, }; -static int zylonite_probe(struct platform_device *pdev) +static int zylonite_probe(struct snd_soc_card *card) { int ret; @@ -216,7 +216,7 @@ static int zylonite_probe(struct platform_device *pdev) return 0; } -static int zylonite_remove(struct platform_device *pdev) +static int zylonite_remove(struct snd_soc_card *card) { if (clk_pout) { clk_disable(pout); @@ -226,8 +226,7 @@ static int zylonite_remove(struct platform_device *pdev) return 0; } -static int zylonite_suspend_post(struct platform_device *pdev, - pm_message_t state) +static int zylonite_suspend_post(struct snd_soc_card *card) { if (clk_pout) clk_disable(pout); @@ -235,7 +234,7 @@ static int zylonite_suspend_post(struct platform_device *pdev, return 0; } -static int zylonite_resume_pre(struct platform_device *pdev) +static int zylonite_resume_pre(struct snd_soc_card *card) { int ret = 0; diff --git a/sound/soc/samsung/neo1973_gta02_wm8753.c b/sound/soc/samsung/neo1973_gta02_wm8753.c index 69e08fd1ff00..95ebf812b146 100644 --- a/sound/soc/samsung/neo1973_gta02_wm8753.c +++ b/sound/soc/samsung/neo1973_gta02_wm8753.c @@ -387,11 +387,11 @@ static struct snd_soc_dai_link neo1973_gta02_dai[] = { { /* Hifi Playback - for similatious use with voice below */ .name = "WM8753", .stream_name = "WM8753 HiFi", - .cpu_dai_name = "s3c24xx-i2s", + .cpu_dai_name = "s3c24xx-iis", .codec_dai_name = "wm8753-hifi", .init = neo1973_gta02_wm8753_init, .platform_name = "samsung-audio", - .codec_name = "wm8753-codec.0-0x1a", + .codec_name = "wm8753-codec.0-001a", .ops = &neo1973_gta02_hifi_ops, }, { /* Voice via BT */ @@ -400,7 +400,7 @@ static struct snd_soc_dai_link neo1973_gta02_dai[] = { .cpu_dai_name = "bluetooth-dai", .codec_dai_name = "wm8753-voice", .ops = &neo1973_gta02_voice_ops, - .codec_name = "wm8753-codec.0-0x1a", + .codec_name = "wm8753-codec.0-001a", .platform_name = "samsung-audio", }, }; diff --git a/sound/soc/samsung/neo1973_wm8753.c b/sound/soc/samsung/neo1973_wm8753.c index c7a24514beb5..d3cd6888a810 100644 --- a/sound/soc/samsung/neo1973_wm8753.c +++ b/sound/soc/samsung/neo1973_wm8753.c @@ -24,7 +24,6 @@ #include <sound/tlv.h> #include <asm/mach-types.h> -#include <asm/hardware/scoop.h> #include <mach/regs-clock.h> #include <mach/regs-gpio.h> #include <mach/hardware.h> @@ -559,9 +558,9 @@ static struct snd_soc_dai_link neo1973_dai[] = { .name = "WM8753", .stream_name = "WM8753 HiFi", .platform_name = "samsung-audio", - .cpu_dai_name = "s3c24xx-i2s", + .cpu_dai_name = "s3c24xx-iis", .codec_dai_name = "wm8753-hifi", - .codec_name = "wm8753-codec.0-0x1a", + .codec_name = "wm8753-codec.0-001a", .init = neo1973_wm8753_init, .ops = &neo1973_hifi_ops, }, @@ -571,7 +570,7 @@ static struct snd_soc_dai_link neo1973_dai[] = { .platform_name = "samsung-audio", .cpu_dai_name = "bluetooth-dai", .codec_dai_name = "wm8753-voice", - .codec_name = "wm8753-codec.0-0x1a", + .codec_name = "wm8753-codec.0-001a", .ops = &neo1973_voice_ops, }, }; diff --git a/sound/soc/samsung/s3c24xx_simtec_hermes.c b/sound/soc/samsung/s3c24xx_simtec_hermes.c index d7b3e6e9d783..ce6aef604179 100644 --- a/sound/soc/samsung/s3c24xx_simtec_hermes.c +++ b/sound/soc/samsung/s3c24xx_simtec_hermes.c @@ -84,8 +84,8 @@ static int simtec_hermes_init(struct snd_soc_pcm_runtime *rtd) static struct snd_soc_dai_link simtec_dai_aic33 = { .name = "tlv320aic33", .stream_name = "TLV320AIC33", - .codec_name = "tlv320aic3x-codec.0-0x1a", - .cpu_dai_name = "s3c24xx-i2s", + .codec_name = "tlv320aic3x-codec.0-001a", + .cpu_dai_name = "s3c24xx-iis", .codec_dai_name = "tlv320aic3x-hifi", .platform_name = "samsung-audio", .init = simtec_hermes_init, diff --git a/sound/soc/samsung/s3c24xx_simtec_tlv320aic23.c b/sound/soc/samsung/s3c24xx_simtec_tlv320aic23.c index ff6168f5f2e0..a7ef7db54687 100644 --- a/sound/soc/samsung/s3c24xx_simtec_tlv320aic23.c +++ b/sound/soc/samsung/s3c24xx_simtec_tlv320aic23.c @@ -73,8 +73,8 @@ static int simtec_tlv320aic23_init(struct snd_soc_pcm_runtime *rtd) static struct snd_soc_dai_link simtec_dai_aic23 = { .name = "tlv320aic23", .stream_name = "TLV320AIC23", - .codec_name = "tlv320aic3x-codec.0-0x1a", - .cpu_dai_name = "s3c24xx-i2s", + .codec_name = "tlv320aic3x-codec.0-001a", + .cpu_dai_name = "s3c24xx-iis", .codec_dai_name = "tlv320aic3x-hifi", .platform_name = "samsung-audio", .init = simtec_tlv320aic23_init, diff --git a/sound/soc/samsung/s3c24xx_uda134x.c b/sound/soc/samsung/s3c24xx_uda134x.c index ce749a10ec07..3cb700751078 100644 --- a/sound/soc/samsung/s3c24xx_uda134x.c +++ b/sound/soc/samsung/s3c24xx_uda134x.c @@ -221,7 +221,7 @@ static struct snd_soc_dai_link s3c24xx_uda134x_dai_link = { .stream_name = "UDA134X", .codec_name = "uda134x-hifi", .codec_dai_name = "uda134x-hifi", - .cpu_dai_name = "s3c24xx-i2s", + .cpu_dai_name = "s3c24xx-iis", .ops = &s3c24xx_uda134x_ops, .platform_name = "samsung-audio", }; diff --git a/sound/soc/sh/fsi-ak4642.c b/sound/soc/sh/fsi-ak4642.c index 56cd34223100..d6f4703b3c07 100644 --- a/sound/soc/sh/fsi-ak4642.c +++ b/sound/soc/sh/fsi-ak4642.c @@ -23,14 +23,21 @@ struct fsi_ak4642_data { static int fsi_ak4642_dai_init(struct snd_soc_pcm_runtime *rtd) { - struct snd_soc_dai *dai = rtd->codec_dai; + struct snd_soc_dai *codec = rtd->codec_dai; + struct snd_soc_dai *cpu = rtd->cpu_dai; int ret; - ret = snd_soc_dai_set_fmt(dai, SND_SOC_DAIFMT_CBM_CFM); + ret = snd_soc_dai_set_fmt(codec, SND_SOC_DAIFMT_LEFT_J | + SND_SOC_DAIFMT_CBM_CFM); if (ret < 0) return ret; - ret = snd_soc_dai_set_sysclk(dai, 0, 11289600, 0); + ret = snd_soc_dai_set_sysclk(codec, 0, 11289600, 0); + if (ret < 0) + return ret; + + ret = snd_soc_dai_set_fmt(cpu, SND_SOC_DAIFMT_LEFT_J | + SND_SOC_DAIFMT_CBS_CFS); return ret; } diff --git a/sound/soc/sh/fsi-da7210.c b/sound/soc/sh/fsi-da7210.c index e8df9da92f71..dbafd7ac5590 100644 --- a/sound/soc/sh/fsi-da7210.c +++ b/sound/soc/sh/fsi-da7210.c @@ -15,11 +15,20 @@ static int fsi_da7210_init(struct snd_soc_pcm_runtime *rtd) { - struct snd_soc_dai *dai = rtd->codec_dai; + struct snd_soc_dai *codec = rtd->codec_dai; + struct snd_soc_dai *cpu = rtd->cpu_dai; + int ret; - return snd_soc_dai_set_fmt(dai, + ret = snd_soc_dai_set_fmt(codec, SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM); + if (ret < 0) + return ret; + + ret = snd_soc_dai_set_fmt(cpu, SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_CBS_CFS); + + return ret; } static struct snd_soc_dai_link fsi_da7210_dai = { diff --git a/sound/soc/sh/fsi-hdmi.c b/sound/soc/sh/fsi-hdmi.c index a52dd8ec71d3..9719985eb82d 100644 --- a/sound/soc/sh/fsi-hdmi.c +++ b/sound/soc/sh/fsi-hdmi.c @@ -12,31 +12,59 @@ #include <linux/platform_device.h> #include <sound/sh_fsi.h> +struct fsi_hdmi_data { + const char *cpu_dai; + const char *card; + int id; +}; + +static int fsi_hdmi_dai_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_dai *cpu = rtd->cpu_dai; + int ret; + + ret = snd_soc_dai_set_fmt(cpu, SND_SOC_DAIFMT_CBM_CFM); + + return ret; +} + static struct snd_soc_dai_link fsi_dai_link = { .name = "HDMI", .stream_name = "HDMI", - .cpu_dai_name = "fsib-dai", /* fsi B */ .codec_dai_name = "sh_mobile_hdmi-hifi", .platform_name = "sh_fsi2", .codec_name = "sh-mobile-hdmi", + .init = fsi_hdmi_dai_init, }; static struct snd_soc_card fsi_soc_card = { - .name = "FSI (SH MOBILE HDMI)", .dai_link = &fsi_dai_link, .num_links = 1, }; static struct platform_device *fsi_snd_device; -static int __init fsi_hdmi_init(void) +static int fsi_hdmi_probe(struct platform_device *pdev) { int ret = -ENOMEM; + const struct platform_device_id *id_entry; + struct fsi_hdmi_data *pdata; + + id_entry = pdev->id_entry; + if (!id_entry) { + dev_err(&pdev->dev, "unknown fsi hdmi\n"); + return -ENODEV; + } - fsi_snd_device = platform_device_alloc("soc-audio", FSI_PORT_B); + pdata = (struct fsi_hdmi_data *)id_entry->driver_data; + + fsi_snd_device = platform_device_alloc("soc-audio", pdata->id); if (!fsi_snd_device) goto out; + fsi_dai_link.cpu_dai_name = pdata->cpu_dai; + fsi_soc_card.name = pdata->card; + platform_set_drvdata(fsi_snd_device, &fsi_soc_card); ret = platform_device_add(fsi_snd_device); @@ -47,9 +75,48 @@ out: return ret; } -static void __exit fsi_hdmi_exit(void) +static int fsi_hdmi_remove(struct platform_device *pdev) { platform_device_unregister(fsi_snd_device); + return 0; +} + +static struct fsi_hdmi_data fsi2_a_hdmi = { + .cpu_dai = "fsia-dai", + .card = "FSI2A (SH MOBILE HDMI)", + .id = FSI_PORT_A, +}; + +static struct fsi_hdmi_data fsi2_b_hdmi = { + .cpu_dai = "fsib-dai", + .card = "FSI2B (SH MOBILE HDMI)", + .id = FSI_PORT_B, +}; + +static struct platform_device_id fsi_id_table[] = { + /* FSI 2 */ + { "sh_fsi2_a_hdmi", (kernel_ulong_t)&fsi2_a_hdmi }, + { "sh_fsi2_b_hdmi", (kernel_ulong_t)&fsi2_b_hdmi }, + {}, +}; + +static struct platform_driver fsi_hdmi = { + .driver = { + .name = "fsi-hdmi-audio", + }, + .probe = fsi_hdmi_probe, + .remove = fsi_hdmi_remove, + .id_table = fsi_id_table, +}; + +static int __init fsi_hdmi_init(void) +{ + return platform_driver_register(&fsi_hdmi); +} + +static void __exit fsi_hdmi_exit(void) +{ + platform_driver_unregister(&fsi_hdmi); } module_init(fsi_hdmi_init); diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c index 2b06402801ef..0c9997e2d8c0 100644 --- a/sound/soc/sh/fsi.c +++ b/sound/soc/sh/fsi.c @@ -78,6 +78,8 @@ /* CKG1 */ #define ACKMD_MASK 0x00007000 #define BPFMD_MASK 0x00000700 +#define DIMD (1 << 4) +#define DOMD (1 << 0) /* A/B MST_CTLR */ #define BP (1 << 4) /* Fix the signal of Biphase output */ @@ -111,6 +113,8 @@ #define FSI_FMTS (SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE) +typedef int (*set_rate_func)(struct device *dev, int is_porta, int rate, int enable); + /* * FSI driver use below type name for variable * @@ -128,7 +132,6 @@ struct fsi_stream { struct snd_pcm_substream *substream; int fifo_max_num; - int chan_num; int buff_offset; int buff_len; @@ -143,6 +146,7 @@ struct fsi_priv { void __iomem *base; struct fsi_master *master; + int chan_num; struct fsi_stream playback; struct fsi_stream capture; @@ -252,9 +256,8 @@ static struct snd_soc_dai *fsi_get_dai(struct snd_pcm_substream *substream) return rtd->cpu_dai; } -static struct fsi_priv *fsi_get_priv(struct snd_pcm_substream *substream) +static struct fsi_priv *fsi_get_priv_frm_dai(struct snd_soc_dai *dai) { - struct snd_soc_dai *dai = fsi_get_dai(substream); struct fsi_master *master = snd_soc_dai_get_drvdata(dai); if (dai->id == 0) @@ -263,11 +266,27 @@ static struct fsi_priv *fsi_get_priv(struct snd_pcm_substream *substream) return &master->fsib; } +static struct fsi_priv *fsi_get_priv(struct snd_pcm_substream *substream) +{ + return fsi_get_priv_frm_dai(fsi_get_dai(substream)); +} + +static set_rate_func fsi_get_info_set_rate(struct fsi_master *master) +{ + if (!master->info) + return NULL; + + return master->info->set_rate; +} + static u32 fsi_get_info_flags(struct fsi_priv *fsi) { int is_porta = fsi_is_port_a(fsi); struct fsi_master *master = fsi_get_master(fsi); + if (!master->info) + return 0; + return is_porta ? master->info->porta_flags : master->info->portb_flags; } @@ -288,21 +307,6 @@ static inline struct fsi_stream *fsi_get_stream(struct fsi_priv *fsi, return is_play ? &fsi->playback : &fsi->capture; } -static int fsi_is_master_mode(struct fsi_priv *fsi, int is_play) -{ - u32 mode; - u32 flags = fsi_get_info_flags(fsi); - - mode = is_play ? SH_FSI_OUT_SLAVE_MODE : SH_FSI_IN_SLAVE_MODE; - - /* return - * 1 : master mode - * 0 : slave mode - */ - - return (mode & flags) != mode; -} - static u32 fsi_get_port_shift(struct fsi_priv *fsi, int is_play) { int is_porta = fsi_is_port_a(fsi); @@ -357,7 +361,6 @@ static void fsi_stream_pop(struct fsi_priv *fsi, int is_play) static int fsi_get_fifo_data_num(struct fsi_priv *fsi, int is_play) { u32 status; - struct fsi_stream *io = fsi_get_stream(fsi, is_play); int data_num; status = is_play ? @@ -365,7 +368,7 @@ static int fsi_get_fifo_data_num(struct fsi_priv *fsi, int is_play) fsi_reg_read(fsi, DIFF_ST); data_num = 0x1ff & (status >> 8); - data_num *= io->chan_num; + data_num *= fsi->chan_num; return data_num; } @@ -387,7 +390,7 @@ static int fsi_get_frame_width(struct fsi_priv *fsi, int is_play) struct snd_pcm_substream *substream = io->substream; struct snd_pcm_runtime *runtime = substream->runtime; - return frames_to_bytes(runtime, 1) / io->chan_num; + return frames_to_bytes(runtime, 1) / fsi->chan_num; } static void fsi_count_fifo_err(struct fsi_priv *fsi) @@ -580,10 +583,10 @@ static void fsi_fifo_init(struct fsi_priv *fsi, * 7 channels: 32 ( 32 x 7 = 224) * 8 channels: 32 ( 32 x 8 = 256) */ - for (i = 1; i < io->chan_num; i <<= 1) + for (i = 1; i < fsi->chan_num; i <<= 1) io->fifo_max_num >>= 1; dev_dbg(dai->dev, "%d channel %d store\n", - io->chan_num, io->fifo_max_num); + fsi->chan_num, io->fifo_max_num); /* * set interrupt generation factor @@ -659,7 +662,7 @@ static int fsi_fifo_data_ctrl(struct fsi_priv *fsi, int stream) * data_num_max : number of FSI fifo free space * data_num : number of ALSA residue data */ - data_num_max = io->fifo_max_num * io->chan_num; + data_num_max = io->fifo_max_num * fsi->chan_num; data_num_max -= fsi_get_fifo_data_num(fsi, is_play); data_num = data_residue_num; @@ -754,25 +757,12 @@ static int fsi_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct fsi_priv *fsi = fsi_get_priv(substream); - struct fsi_master *master = fsi_get_master(fsi); - struct fsi_stream *io; u32 flags = fsi_get_info_flags(fsi); - u32 fmt; u32 data; int is_play = fsi_is_play(substream); - int is_master; - - io = fsi_get_stream(fsi, is_play); pm_runtime_get_sync(dai->dev); - /* CKG1 */ - data = is_play ? (1 << 0) : (1 << 4); - is_master = fsi_is_master_mode(fsi, is_play); - if (is_master) - fsi_reg_mask_set(fsi, CKG1, data, data); - else - fsi_reg_mask_set(fsi, CKG1, data, 0); /* clock inversion (CKG2) */ data = 0; @@ -787,54 +777,6 @@ static int fsi_dai_startup(struct snd_pcm_substream *substream, fsi_reg_write(fsi, CKG2, data); - /* do fmt, di fmt */ - data = 0; - fmt = is_play ? SH_FSI_GET_OFMT(flags) : SH_FSI_GET_IFMT(flags); - switch (fmt) { - case SH_FSI_FMT_MONO: - data = CR_MONO; - io->chan_num = 1; - break; - case SH_FSI_FMT_MONO_DELAY: - data = CR_MONO_D; - io->chan_num = 1; - break; - case SH_FSI_FMT_PCM: - data = CR_PCM; - io->chan_num = 2; - break; - case SH_FSI_FMT_I2S: - data = CR_I2S; - io->chan_num = 2; - break; - case SH_FSI_FMT_TDM: - io->chan_num = is_play ? - SH_FSI_GET_CH_O(flags) : SH_FSI_GET_CH_I(flags); - data = CR_TDM | (io->chan_num - 1); - break; - case SH_FSI_FMT_TDM_DELAY: - io->chan_num = is_play ? - SH_FSI_GET_CH_O(flags) : SH_FSI_GET_CH_I(flags); - data = CR_TDM_D | (io->chan_num - 1); - break; - case SH_FSI_FMT_SPDIF: - if (master->core->ver < 2) { - dev_err(dai->dev, "This FSI can not use SPDIF\n"); - return -EINVAL; - } - data = CR_BWS_16 | CR_DTMD_SPDIF_PCM | CR_PCM; - io->chan_num = 2; - fsi_spdif_clk_ctrl(fsi, 1); - fsi_reg_mask_set(fsi, OUT_SEL, DMMD, DMMD); - break; - default: - dev_err(dai->dev, "unknown format.\n"); - return -EINVAL; - } - is_play ? - fsi_reg_write(fsi, DO_FMT, data) : - fsi_reg_write(fsi, DI_FMT, data); - /* irq clear */ fsi_irq_disable(fsi, is_play); fsi_irq_clear_status(fsi); @@ -851,12 +793,12 @@ static void fsi_dai_shutdown(struct snd_pcm_substream *substream, struct fsi_priv *fsi = fsi_get_priv(substream); int is_play = fsi_is_play(substream); struct fsi_master *master = fsi_get_master(fsi); - int (*set_rate)(struct device *dev, int is_porta, int rate, int enable); + set_rate_func set_rate; fsi_irq_disable(fsi, is_play); fsi_clk_ctrl(fsi, 0); - set_rate = master->info->set_rate; + set_rate = fsi_get_info_set_rate(master); if (set_rate && fsi->rate) set_rate(dai->dev, fsi_is_port_a(fsi), fsi->rate, 0); fsi->rate = 0; @@ -889,18 +831,100 @@ static int fsi_dai_trigger(struct snd_pcm_substream *substream, int cmd, return ret; } +static int fsi_set_fmt_dai(struct fsi_priv *fsi, unsigned int fmt) +{ + u32 data = 0; + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + data = CR_I2S; + fsi->chan_num = 2; + break; + case SND_SOC_DAIFMT_LEFT_J: + data = CR_PCM; + fsi->chan_num = 2; + break; + default: + return -EINVAL; + } + + fsi_reg_write(fsi, DO_FMT, data); + fsi_reg_write(fsi, DI_FMT, data); + + return 0; +} + +static int fsi_set_fmt_spdif(struct fsi_priv *fsi) +{ + struct fsi_master *master = fsi_get_master(fsi); + u32 data = 0; + + if (master->core->ver < 2) + return -EINVAL; + + data = CR_BWS_16 | CR_DTMD_SPDIF_PCM | CR_PCM; + fsi->chan_num = 2; + fsi_spdif_clk_ctrl(fsi, 1); + fsi_reg_mask_set(fsi, OUT_SEL, DMMD, DMMD); + + fsi_reg_write(fsi, DO_FMT, data); + fsi_reg_write(fsi, DI_FMT, data); + + return 0; +} + +static int fsi_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct fsi_priv *fsi = fsi_get_priv_frm_dai(dai); + u32 flags = fsi_get_info_flags(fsi); + u32 data = 0; + int ret; + + pm_runtime_get_sync(dai->dev); + + /* set master/slave audio interface */ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + data = DIMD | DOMD; + break; + case SND_SOC_DAIFMT_CBS_CFS: + break; + default: + ret = -EINVAL; + goto set_fmt_exit; + } + fsi_reg_mask_set(fsi, CKG1, (DIMD | DOMD), data); + + /* set format */ + switch (flags & SH_FSI_FMT_MASK) { + case SH_FSI_FMT_DAI: + ret = fsi_set_fmt_dai(fsi, fmt & SND_SOC_DAIFMT_FORMAT_MASK); + break; + case SH_FSI_FMT_SPDIF: + ret = fsi_set_fmt_spdif(fsi); + break; + default: + ret = -EINVAL; + } + +set_fmt_exit: + pm_runtime_put_sync(dai->dev); + + return ret; +} + static int fsi_dai_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { struct fsi_priv *fsi = fsi_get_priv(substream); struct fsi_master *master = fsi_get_master(fsi); - int (*set_rate)(struct device *dev, int is_porta, int rate, int enable); + set_rate_func set_rate; int fsi_ver = master->core->ver; long rate = params_rate(params); int ret; - set_rate = master->info->set_rate; + set_rate = fsi_get_info_set_rate(master); if (!set_rate) return 0; @@ -975,6 +999,7 @@ static struct snd_soc_dai_ops fsi_dai_ops = { .startup = fsi_dai_startup, .shutdown = fsi_dai_shutdown, .trigger = fsi_dai_trigger, + .set_fmt = fsi_dai_set_fmt, .hw_params = fsi_dai_hw_params, }; diff --git a/sound/soc/soc-cache.c b/sound/soc/soc-cache.c index d97a59f6a249..db66dc44add2 100644 --- a/sound/soc/soc-cache.c +++ b/sound/soc/soc-cache.c @@ -18,6 +18,8 @@ #include <linux/bitmap.h> #include <linux/rbtree.h> +#include <trace/events/asoc.h> + static unsigned int snd_soc_4_12_read(struct snd_soc_codec *codec, unsigned int reg) { @@ -25,7 +27,8 @@ static unsigned int snd_soc_4_12_read(struct snd_soc_codec *codec, unsigned int val; if (reg >= codec->driver->reg_cache_size || - snd_soc_codec_volatile_register(codec, reg)) { + snd_soc_codec_volatile_register(codec, reg) || + codec->cache_bypass) { if (codec->cache_only) return -1; @@ -49,7 +52,8 @@ static int snd_soc_4_12_write(struct snd_soc_codec *codec, unsigned int reg, data[1] = value & 0x00ff; if (!snd_soc_codec_volatile_register(codec, reg) && - reg < codec->driver->reg_cache_size) { + reg < codec->driver->reg_cache_size && + !codec->cache_bypass) { ret = snd_soc_cache_write(codec, reg, value); if (ret < 0) return -1; @@ -106,7 +110,8 @@ static unsigned int snd_soc_7_9_read(struct snd_soc_codec *codec, unsigned int val; if (reg >= codec->driver->reg_cache_size || - snd_soc_codec_volatile_register(codec, reg)) { + snd_soc_codec_volatile_register(codec, reg) || + codec->cache_bypass) { if (codec->cache_only) return -1; @@ -130,7 +135,8 @@ static int snd_soc_7_9_write(struct snd_soc_codec *codec, unsigned int reg, data[1] = value & 0x00ff; if (!snd_soc_codec_volatile_register(codec, reg) && - reg < codec->driver->reg_cache_size) { + reg < codec->driver->reg_cache_size && + !codec->cache_bypass) { ret = snd_soc_cache_write(codec, reg, value); if (ret < 0) return -1; @@ -191,7 +197,8 @@ static int snd_soc_8_8_write(struct snd_soc_codec *codec, unsigned int reg, data[1] = value & 0xff; if (!snd_soc_codec_volatile_register(codec, reg) && - reg < codec->driver->reg_cache_size) { + reg < codec->driver->reg_cache_size && + !codec->cache_bypass) { ret = snd_soc_cache_write(codec, reg, value); if (ret < 0) return -1; @@ -216,7 +223,8 @@ static unsigned int snd_soc_8_8_read(struct snd_soc_codec *codec, reg &= 0xff; if (reg >= codec->driver->reg_cache_size || - snd_soc_codec_volatile_register(codec, reg)) { + snd_soc_codec_volatile_register(codec, reg) || + codec->cache_bypass) { if (codec->cache_only) return -1; @@ -271,7 +279,8 @@ static int snd_soc_8_16_write(struct snd_soc_codec *codec, unsigned int reg, data[2] = value & 0xff; if (!snd_soc_codec_volatile_register(codec, reg) && - reg < codec->driver->reg_cache_size) { + reg < codec->driver->reg_cache_size && + !codec->cache_bypass) { ret = snd_soc_cache_write(codec, reg, value); if (ret < 0) return -1; @@ -295,7 +304,8 @@ static unsigned int snd_soc_8_16_read(struct snd_soc_codec *codec, unsigned int val; if (reg >= codec->driver->reg_cache_size || - snd_soc_codec_volatile_register(codec, reg)) { + snd_soc_codec_volatile_register(codec, reg) || + codec->cache_bypass) { if (codec->cache_only) return -1; @@ -450,7 +460,8 @@ static unsigned int snd_soc_16_8_read(struct snd_soc_codec *codec, reg &= 0xff; if (reg >= codec->driver->reg_cache_size || - snd_soc_codec_volatile_register(codec, reg)) { + snd_soc_codec_volatile_register(codec, reg) || + codec->cache_bypass) { if (codec->cache_only) return -1; @@ -476,7 +487,8 @@ static int snd_soc_16_8_write(struct snd_soc_codec *codec, unsigned int reg, reg &= 0xff; if (!snd_soc_codec_volatile_register(codec, reg) && - reg < codec->driver->reg_cache_size) { + reg < codec->driver->reg_cache_size && + !codec->cache_bypass) { ret = snd_soc_cache_write(codec, reg, value); if (ret < 0) return -1; @@ -568,7 +580,8 @@ static unsigned int snd_soc_16_16_read(struct snd_soc_codec *codec, unsigned int val; if (reg >= codec->driver->reg_cache_size || - snd_soc_codec_volatile_register(codec, reg)) { + snd_soc_codec_volatile_register(codec, reg) || + codec->cache_bypass) { if (codec->cache_only) return -1; @@ -595,7 +608,8 @@ static int snd_soc_16_16_write(struct snd_soc_codec *codec, unsigned int reg, data[3] = value & 0xff; if (!snd_soc_codec_volatile_register(codec, reg) && - reg < codec->driver->reg_cache_size) { + reg < codec->driver->reg_cache_size && + !codec->cache_bypass) { ret = snd_soc_cache_write(codec, reg, value); if (ret < 0) return -1; @@ -878,7 +892,9 @@ static int snd_soc_rbtree_cache_sync(struct snd_soc_codec *codec) ret = snd_soc_cache_read(codec, rbnode->reg, &val); if (ret) return ret; + codec->cache_bypass = 1; ret = snd_soc_write(codec, rbnode->reg, val); + codec->cache_bypass = 0; if (ret) return ret; dev_dbg(codec->dev, "Synced register %#x, value = %#x\n", @@ -1136,7 +1152,9 @@ static int snd_soc_lzo_cache_sync(struct snd_soc_codec *codec) ret = snd_soc_cache_read(codec, i, &val); if (ret) return ret; + codec->cache_bypass = 1; ret = snd_soc_write(codec, i, val); + codec->cache_bypass = 0; if (ret) return ret; dev_dbg(codec->dev, "Synced register %#x, value = %#x\n", @@ -1585,18 +1603,26 @@ EXPORT_SYMBOL_GPL(snd_soc_cache_write); int snd_soc_cache_sync(struct snd_soc_codec *codec) { int ret; + const char *name; if (!codec->cache_sync) { return 0; } + if (codec->cache_ops->name) + name = codec->cache_ops->name; + else + name = "unknown"; + if (codec->cache_ops && codec->cache_ops->sync) { if (codec->cache_ops->name) dev_dbg(codec->dev, "Syncing %s cache for %s codec\n", codec->cache_ops->name, codec->name); + trace_snd_soc_cache_sync(codec, name, "start"); ret = codec->cache_ops->sync(codec); if (!ret) codec->cache_sync = 0; + trace_snd_soc_cache_sync(codec, name, "end"); return ret; } diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 9e68984423b2..205cbd7b149f 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -69,10 +69,32 @@ static int pmdown_time = 5000; module_param(pmdown_time, int, 0); MODULE_PARM_DESC(pmdown_time, "DAPM stream powerdown time (msecs)"); +/* returns the minimum number of bytes needed to represent + * a particular given value */ +static int min_bytes_needed(unsigned long val) +{ + int c = 0; + int i; + + for (i = (sizeof val * 8) - 1; i >= 0; --i, ++c) + if (val & (1UL << i)) + break; + c = (sizeof val * 8) - c; + if (!c || (c % 8)) + c = (c + 8) / 8; + else + c /= 8; + return c; +} + /* codec register dump */ static ssize_t soc_codec_reg_show(struct snd_soc_codec *codec, char *buf) { int ret, i, step = 1, count = 0; + int wordsize, regsize; + + wordsize = codec->driver->reg_word_size * 2; + regsize = min_bytes_needed(codec->driver->reg_cache_size) * 2; if (!codec->driver->reg_cache_size) return 0; @@ -80,12 +102,11 @@ static ssize_t soc_codec_reg_show(struct snd_soc_codec *codec, char *buf) if (codec->driver->reg_cache_step) step = codec->driver->reg_cache_step; - count += sprintf(buf, "%s registers\n", codec->name); for (i = 0; i < codec->driver->reg_cache_size; i += step) { if (codec->readable_register && !codec->readable_register(codec, i)) continue; - count += sprintf(buf + count, "%2x: ", i); + count += sprintf(buf + count, "%.*x: ", regsize, i); if (count >= PAGE_SIZE - 1) break; @@ -101,7 +122,7 @@ static ssize_t soc_codec_reg_show(struct snd_soc_codec *codec, char *buf) if (ret >= 0) count += snprintf(buf + count, PAGE_SIZE - count, - "%4x", ret); + "%.*x", wordsize, ret); else count += snprintf(buf + count, PAGE_SIZE - count, @@ -235,6 +256,11 @@ static void soc_init_codec_debugfs(struct snd_soc_codec *codec) return; } + debugfs_create_bool("cache_sync", 0444, codec->debugfs_codec_root, + &codec->cache_sync); + debugfs_create_bool("cache_only", 0444, codec->debugfs_codec_root, + &codec->cache_only); + codec->debugfs_reg = debugfs_create_file("codec_reg", 0644, codec->debugfs_codec_root, codec, &codec_reg_fops); @@ -965,12 +991,11 @@ static struct snd_pcm_ops soc_pcm_ops = { .pointer = soc_pcm_pointer, }; -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP /* powers down audio subsystem for suspend */ -static int soc_suspend(struct device *dev) +int snd_soc_suspend(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct snd_soc_card *card = platform_get_drvdata(pdev); + struct snd_soc_card *card = dev_get_drvdata(dev); struct snd_soc_codec *codec; int i; @@ -1011,7 +1036,7 @@ static int soc_suspend(struct device *dev) } if (card->suspend_pre) - card->suspend_pre(pdev, PMSG_SUSPEND); + card->suspend_pre(card); for (i = 0; i < card->num_rtd; i++) { struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai; @@ -1078,10 +1103,11 @@ static int soc_suspend(struct device *dev) } if (card->suspend_post) - card->suspend_post(pdev, PMSG_SUSPEND); + card->suspend_post(card); return 0; } +EXPORT_SYMBOL_GPL(snd_soc_suspend); /* deferred resume work, so resume can complete before we finished * setting our codec back up, which can be very slow on I2C @@ -1090,7 +1116,6 @@ static void soc_resume_deferred(struct work_struct *work) { struct snd_soc_card *card = container_of(work, struct snd_soc_card, deferred_resume_work); - struct platform_device *pdev = to_platform_device(card->dev); struct snd_soc_codec *codec; int i; @@ -1104,7 +1129,7 @@ static void soc_resume_deferred(struct work_struct *work) snd_power_change_state(card->snd_card, SNDRV_CTL_POWER_D2); if (card->resume_pre) - card->resume_pre(pdev); + card->resume_pre(card); /* resume AC97 DAIs */ for (i = 0; i < card->num_rtd; i++) { @@ -1179,7 +1204,7 @@ static void soc_resume_deferred(struct work_struct *work) } if (card->resume_post) - card->resume_post(pdev); + card->resume_post(card); dev_dbg(card->dev, "resume work completed\n"); @@ -1188,10 +1213,9 @@ static void soc_resume_deferred(struct work_struct *work) } /* powers up audio subsystem after a suspend */ -static int soc_resume(struct device *dev) +int snd_soc_resume(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct snd_soc_card *card = platform_get_drvdata(pdev); + struct snd_soc_card *card = dev_get_drvdata(dev); int i; /* AC97 devices might have other drivers hanging off them so @@ -1213,9 +1237,10 @@ static int soc_resume(struct device *dev) return 0; } +EXPORT_SYMBOL_GPL(snd_soc_resume); #else -#define soc_suspend NULL -#define soc_resume NULL +#define snd_soc_suspend NULL +#define snd_soc_resume NULL #endif static struct snd_soc_dai_ops null_dai_ops = { @@ -1408,26 +1433,31 @@ static int soc_probe_codec(struct snd_soc_card *card, codec->dapm.card = card; soc_set_name_prefix(card, codec); + if (!try_module_get(codec->dev->driver->owner)) + return -ENODEV; + if (codec->driver->probe) { ret = codec->driver->probe(codec); if (ret < 0) { dev_err(codec->dev, "asoc: failed to probe CODEC %s: %d\n", codec->name, ret); - return ret; + goto err_probe; } } soc_init_codec_debugfs(codec); /* mark codec as probed and add to card codec list */ - if (!try_module_get(codec->dev->driver->owner)) - return -ENODEV; - codec->probed = 1; list_add(&codec->card_list, &card->codec_dev_list); list_add(&codec->dapm.list, &card->dapm_list); + return 0; + +err_probe: + module_put(codec->dev->driver->owner); + return ret; } @@ -1545,19 +1575,19 @@ static int soc_probe_dai_link(struct snd_soc_card *card, int num) /* probe the platform */ if (!platform->probed) { + if (!try_module_get(platform->dev->driver->owner)) + return -ENODEV; + if (platform->driver->probe) { ret = platform->driver->probe(platform); if (ret < 0) { printk(KERN_ERR "asoc: failed to probe platform %s\n", platform->name); + module_put(platform->dev->driver->owner); return ret; } } /* mark platform as probed and add to card platform list */ - - if (!try_module_get(platform->dev->driver->owner)) - return -ENODEV; - platform->probed = 1; list_add(&platform->card_list, &card->platform_dev_list); } @@ -1666,9 +1696,6 @@ static int soc_probe_aux_dev(struct snd_soc_card *card, int num) goto out; found: - if (!try_module_get(codec->dev->driver->owner)) - return -ENODEV; - ret = soc_probe_codec(card, codec); if (ret < 0) return ret; @@ -1718,7 +1745,6 @@ static int snd_soc_init_codec_cache(struct snd_soc_codec *codec, static void snd_soc_instantiate_card(struct snd_soc_card *card) { - struct platform_device *pdev = to_platform_device(card->dev); struct snd_soc_codec *codec; struct snd_soc_codec_conf *codec_conf; enum snd_soc_compress_type compress_type; @@ -1782,7 +1808,7 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card) /* initialise the sound card only once */ if (card->probe) { - ret = card->probe(pdev); + ret = card->probe(card); if (ret < 0) goto card_probe_error; } @@ -1843,7 +1869,7 @@ probe_dai_err: card_probe_error: if (card->remove) - card->remove(pdev); + card->remove(card); snd_card_free(card->snd_card); @@ -1876,7 +1902,6 @@ static int soc_probe(struct platform_device *pdev) /* Bodge while we unpick instantiation */ card->dev = &pdev->dev; - snd_soc_initialize_card_lists(card); ret = snd_soc_register_card(card); if (ret != 0) { @@ -1889,7 +1914,6 @@ static int soc_probe(struct platform_device *pdev) static int soc_cleanup_card_resources(struct snd_soc_card *card) { - struct platform_device *pdev = to_platform_device(card->dev); int i; /* make sure any delayed work runs */ @@ -1910,7 +1934,7 @@ static int soc_cleanup_card_resources(struct snd_soc_card *card) /* remove the card */ if (card->remove) - card->remove(pdev); + card->remove(card); kfree(card->rtd); snd_card_free(card->snd_card); @@ -1927,10 +1951,9 @@ static int soc_remove(struct platform_device *pdev) return 0; } -static int soc_poweroff(struct device *dev) +int snd_soc_poweroff(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct snd_soc_card *card = platform_get_drvdata(pdev); + struct snd_soc_card *card = dev_get_drvdata(dev); int i; if (!card->instantiated) @@ -1947,11 +1970,12 @@ static int soc_poweroff(struct device *dev) return 0; } +EXPORT_SYMBOL_GPL(snd_soc_poweroff); -static const struct dev_pm_ops soc_pm_ops = { - .suspend = soc_suspend, - .resume = soc_resume, - .poweroff = soc_poweroff, +const struct dev_pm_ops snd_soc_pm_ops = { + .suspend = snd_soc_suspend, + .resume = snd_soc_resume, + .poweroff = snd_soc_poweroff, }; /* ASoC platform driver */ @@ -1959,7 +1983,7 @@ static struct platform_driver soc_driver = { .driver = { .name = "soc-audio", .owner = THIS_MODULE, - .pm = &soc_pm_ops, + .pm = &snd_soc_pm_ops, }, .probe = soc_probe, .remove = soc_remove, @@ -2029,7 +2053,8 @@ static int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) * * Boolean function indiciating if a CODEC register is volatile. */ -int snd_soc_codec_volatile_register(struct snd_soc_codec *codec, int reg) +int snd_soc_codec_volatile_register(struct snd_soc_codec *codec, + unsigned int reg) { if (codec->volatile_register) return codec->volatile_register(codec, reg); @@ -3117,6 +3142,8 @@ int snd_soc_register_card(struct snd_soc_card *card) if (!card->name || !card->dev) return -EINVAL; + snd_soc_initialize_card_lists(card); + soc_init_card_debugfs(card); card->rtd = kzalloc(sizeof(struct snd_soc_pcm_runtime) * @@ -3496,6 +3523,7 @@ int snd_soc_register_codec(struct device *dev, codec->dapm.bias_level = SND_SOC_BIAS_OFF; codec->dapm.dev = dev; codec->dapm.codec = codec; + codec->dapm.seq_notifier = codec_drv->seq_notifier; codec->dev = dev; codec->driver = codec_drv; codec->num_dai = num_dai; diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 499730ab5638..d0342aab2c15 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -726,10 +726,23 @@ static int dapm_supply_check_power(struct snd_soc_dapm_widget *w) static int dapm_seq_compare(struct snd_soc_dapm_widget *a, struct snd_soc_dapm_widget *b, - int sort[]) + bool power_up) { + int *sort; + + if (power_up) + sort = dapm_up_seq; + else + sort = dapm_down_seq; + if (sort[a->id] != sort[b->id]) return sort[a->id] - sort[b->id]; + if (a->subseq != b->subseq) { + if (power_up) + return a->subseq - b->subseq; + else + return b->subseq - a->subseq; + } if (a->reg != b->reg) return a->reg - b->reg; if (a->dapm != b->dapm) @@ -741,12 +754,12 @@ static int dapm_seq_compare(struct snd_soc_dapm_widget *a, /* Insert a widget in order into a DAPM power sequence. */ static void dapm_seq_insert(struct snd_soc_dapm_widget *new_widget, struct list_head *list, - int sort[]) + bool power_up) { struct snd_soc_dapm_widget *w; list_for_each_entry(w, list, power_list) - if (dapm_seq_compare(new_widget, w, sort) < 0) { + if (dapm_seq_compare(new_widget, w, power_up) < 0) { list_add_tail(&new_widget->power_list, &w->power_list); return; } @@ -857,26 +870,42 @@ static void dapm_seq_run_coalesced(struct snd_soc_dapm_context *dapm, * handled. */ static void dapm_seq_run(struct snd_soc_dapm_context *dapm, - struct list_head *list, int event, int sort[]) + struct list_head *list, int event, bool power_up) { struct snd_soc_dapm_widget *w, *n; LIST_HEAD(pending); int cur_sort = -1; + int cur_subseq = -1; int cur_reg = SND_SOC_NOPM; struct snd_soc_dapm_context *cur_dapm = NULL; - int ret; + int ret, i; + int *sort; + + if (power_up) + sort = dapm_up_seq; + else + sort = dapm_down_seq; list_for_each_entry_safe(w, n, list, power_list) { ret = 0; /* Do we need to apply any queued changes? */ if (sort[w->id] != cur_sort || w->reg != cur_reg || - w->dapm != cur_dapm) { + w->dapm != cur_dapm || w->subseq != cur_subseq) { if (!list_empty(&pending)) dapm_seq_run_coalesced(cur_dapm, &pending); + if (cur_dapm && cur_dapm->seq_notifier) { + for (i = 0; i < ARRAY_SIZE(dapm_up_seq); i++) + if (sort[i] == cur_sort) + cur_dapm->seq_notifier(cur_dapm, + i, + cur_subseq); + } + INIT_LIST_HEAD(&pending); cur_sort = -1; + cur_subseq = -1; cur_reg = SND_SOC_NOPM; cur_dapm = NULL; } @@ -921,6 +950,7 @@ static void dapm_seq_run(struct snd_soc_dapm_context *dapm, default: /* Queue it up for application */ cur_sort = sort[w->id]; + cur_subseq = w->subseq; cur_reg = w->reg; cur_dapm = w->dapm; list_move(&w->power_list, &pending); @@ -934,6 +964,13 @@ static void dapm_seq_run(struct snd_soc_dapm_context *dapm, if (!list_empty(&pending)) dapm_seq_run_coalesced(dapm, &pending); + + if (cur_dapm && cur_dapm->seq_notifier) { + for (i = 0; i < ARRAY_SIZE(dapm_up_seq); i++) + if (sort[i] == cur_sort) + cur_dapm->seq_notifier(cur_dapm, + i, cur_subseq); + } } static void dapm_widget_update(struct snd_soc_dapm_context *dapm) @@ -1002,10 +1039,10 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event) list_for_each_entry(w, &card->widgets, list) { switch (w->id) { case snd_soc_dapm_pre: - dapm_seq_insert(w, &down_list, dapm_down_seq); + dapm_seq_insert(w, &down_list, false); break; case snd_soc_dapm_post: - dapm_seq_insert(w, &up_list, dapm_up_seq); + dapm_seq_insert(w, &up_list, true); break; default: @@ -1025,9 +1062,9 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event) trace_snd_soc_dapm_widget_power(w, power); if (power) - dapm_seq_insert(w, &up_list, dapm_up_seq); + dapm_seq_insert(w, &up_list, true); else - dapm_seq_insert(w, &down_list, dapm_down_seq); + dapm_seq_insert(w, &down_list, false); w->power = power; break; @@ -1086,12 +1123,12 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event) } /* Power down widgets first; try to avoid amplifying pops. */ - dapm_seq_run(dapm, &down_list, event, dapm_down_seq); + dapm_seq_run(dapm, &down_list, event, false); dapm_widget_update(dapm); /* Now power up. */ - dapm_seq_run(dapm, &up_list, event, dapm_up_seq); + dapm_seq_run(dapm, &up_list, event, true); list_for_each_entry(d, &dapm->card->dapm_list, list) { /* If we just powered the last thing off drop to standby bias */ @@ -1742,7 +1779,7 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, int max = mc->max; unsigned int mask = (1 << fls(max)) - 1; unsigned int invert = mc->invert; - unsigned int val, val_mask; + unsigned int val; int connect, change; struct snd_soc_dapm_update update; @@ -1750,13 +1787,13 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, if (invert) val = max - val; - val_mask = mask << shift; + mask = mask << shift; val = val << shift; mutex_lock(&widget->codec->mutex); widget->value = val; - change = snd_soc_test_bits(widget->codec, reg, val_mask, val); + change = snd_soc_test_bits(widget->codec, reg, mask, val); if (change) { if (val) /* new connection */ @@ -2372,7 +2409,7 @@ static void soc_dapm_shutdown_codec(struct snd_soc_dapm_context *dapm) if (w->dapm != dapm) continue; if (w->power) { - dapm_seq_insert(w, &down_list, dapm_down_seq); + dapm_seq_insert(w, &down_list, false); w->power = 0; powerdown = 1; } @@ -2383,7 +2420,7 @@ static void soc_dapm_shutdown_codec(struct snd_soc_dapm_context *dapm) */ if (powerdown) { snd_soc_dapm_set_bias_level(NULL, dapm, SND_SOC_BIAS_PREPARE); - dapm_seq_run(dapm, &down_list, 0, dapm_down_seq); + dapm_seq_run(dapm, &down_list, 0, false); snd_soc_dapm_set_bias_level(NULL, dapm, SND_SOC_BIAS_STANDBY); } } diff --git a/sound/soc/soc-utils.c b/sound/soc/soc-utils.c index 1d07b931f3d8..3f45e6a439bf 100644 --- a/sound/soc/soc-utils.c +++ b/sound/soc/soc-utils.c @@ -28,26 +28,9 @@ int snd_soc_params_to_frame_size(struct snd_pcm_hw_params *params) { int sample_size; - switch (params_format(params)) { - case SNDRV_PCM_FORMAT_S16_LE: - case SNDRV_PCM_FORMAT_S16_BE: - sample_size = 16; - break; - case SNDRV_PCM_FORMAT_S20_3LE: - case SNDRV_PCM_FORMAT_S20_3BE: - sample_size = 20; - break; - case SNDRV_PCM_FORMAT_S24_LE: - case SNDRV_PCM_FORMAT_S24_BE: - sample_size = 24; - break; - case SNDRV_PCM_FORMAT_S32_LE: - case SNDRV_PCM_FORMAT_S32_BE: - sample_size = 32; - break; - default: - return -ENOTSUPP; - } + sample_size = snd_pcm_format_width(params_format(params)); + if (sample_size < 0) + return sample_size; return snd_soc_calc_frame_size(sample_size, params_channels(params), 1); diff --git a/sound/soc/tegra/harmony.c b/sound/soc/tegra/harmony.c index b160b7113f45..11e2cb825664 100644 --- a/sound/soc/tegra/harmony.c +++ b/sound/soc/tegra/harmony.c @@ -2,7 +2,7 @@ * harmony.c - Harmony machine ASoC driver * * Author: Stephen Warren <swarren@nvidia.com> - * Copyright (C) 2010 - NVIDIA, Inc. + * Copyright (C) 2010-2011 - NVIDIA, Inc. * * Based on code copyright/by: * @@ -29,7 +29,14 @@ */ #include <asm/mach-types.h> + #include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/gpio.h> + +#include <mach/harmony_audio.h> + #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> @@ -40,9 +47,13 @@ #include "tegra_pcm.h" #include "tegra_asoc_utils.h" -#define PREFIX "ASoC Harmony: " +#define DRV_NAME "tegra-snd-harmony" -static struct platform_device *harmony_snd_device; +struct tegra_harmony { + struct tegra_asoc_utils_data util_data; + struct harmony_audio_platform_data *pdata; + int gpio_spkr_en_requested; +}; static int harmony_asoc_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) @@ -50,6 +61,9 @@ static int harmony_asoc_hw_params(struct snd_pcm_substream *substream, struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *codec_dai = rtd->codec_dai; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_card *card = codec->card; + struct tegra_harmony *harmony = snd_soc_card_get_drvdata(card); int srate, mclk, mclk_change; int err; @@ -68,9 +82,10 @@ static int harmony_asoc_hw_params(struct snd_pcm_substream *substream, while (mclk < 6000000) mclk *= 2; - err = tegra_asoc_utils_set_rate(srate, mclk, &mclk_change); + err = tegra_asoc_utils_set_rate(&harmony->util_data, srate, mclk, + &mclk_change); if (err < 0) { - pr_err(PREFIX "Can't configure clocks\n"); + dev_err(card->dev, "Can't configure clocks\n"); return err; } @@ -79,7 +94,7 @@ static int harmony_asoc_hw_params(struct snd_pcm_substream *substream, SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); if (err < 0) { - pr_err(PREFIX "codec_dai fmt not set\n"); + dev_err(card->dev, "codec_dai fmt not set\n"); return err; } @@ -88,16 +103,17 @@ static int harmony_asoc_hw_params(struct snd_pcm_substream *substream, SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); if (err < 0) { - pr_err(PREFIX "cpu_dai fmt not set\n"); + dev_err(card->dev, "cpu_dai fmt not set\n"); return err; } if (mclk_change) { - err = snd_soc_dai_set_sysclk(codec_dai, 0, mclk, SND_SOC_CLOCK_IN); - if (err < 0) { - pr_err(PREFIX "codec_dai clock not set\n"); - return err; - } + err = snd_soc_dai_set_sysclk(codec_dai, 0, mclk, + SND_SOC_CLOCK_IN); + if (err < 0) { + dev_err(card->dev, "codec_dai clock not set\n"); + return err; + } } return 0; @@ -107,7 +123,22 @@ static struct snd_soc_ops harmony_asoc_ops = { .hw_params = harmony_asoc_hw_params, }; +static int harmony_event_int_spk(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_codec *codec = w->codec; + struct snd_soc_card *card = codec->card; + struct tegra_harmony *harmony = snd_soc_card_get_drvdata(card); + struct harmony_audio_platform_data *pdata = harmony->pdata; + + gpio_set_value_cansleep(pdata->gpio_spkr_en, + SND_SOC_DAPM_EVENT_ON(event)); + + return 0; +} + static const struct snd_soc_dapm_widget harmony_dapm_widgets[] = { + SND_SOC_DAPM_SPK("Int Spk", harmony_event_int_spk), SND_SOC_DAPM_HP("Headphone Jack", NULL), SND_SOC_DAPM_MIC("Mic Jack", NULL), }; @@ -115,6 +146,10 @@ static const struct snd_soc_dapm_widget harmony_dapm_widgets[] = { static const struct snd_soc_dapm_route harmony_audio_map[] = { {"Headphone Jack", NULL, "HPOUTR"}, {"Headphone Jack", NULL, "HPOUTL"}, + {"Int Spk", NULL, "ROP"}, + {"Int Spk", NULL, "RON"}, + {"Int Spk", NULL, "LOP"}, + {"Int Spk", NULL, "LON"}, {"Mic Bias", NULL, "Mic Jack"}, {"IN1L", NULL, "Mic Bias"}, }; @@ -123,6 +158,19 @@ static int harmony_asoc_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_codec *codec = rtd->codec; struct snd_soc_dapm_context *dapm = &codec->dapm; + struct snd_soc_card *card = codec->card; + struct tegra_harmony *harmony = snd_soc_card_get_drvdata(card); + struct harmony_audio_platform_data *pdata = harmony->pdata; + int ret; + + ret = gpio_request(pdata->gpio_spkr_en, "spkr_en"); + if (ret) { + dev_err(card->dev, "cannot get spkr_en gpio\n"); + return ret; + } + harmony->gpio_spkr_en_requested = 1; + + gpio_direction_output(pdata->gpio_spkr_en, 0); snd_soc_dapm_new_controls(dapm, harmony_dapm_widgets, ARRAY_SIZE(harmony_dapm_widgets)); @@ -131,6 +179,7 @@ static int harmony_asoc_init(struct snd_soc_pcm_runtime *rtd) ARRAY_SIZE(harmony_audio_map)); snd_soc_dapm_enable_pin(dapm, "Headphone Jack"); + snd_soc_dapm_enable_pin(dapm, "Int Spk"); snd_soc_dapm_enable_pin(dapm, "Mic Jack"); snd_soc_dapm_sync(dapm); @@ -154,56 +203,101 @@ static struct snd_soc_card snd_soc_harmony = { .num_links = 1, }; -static int __init harmony_soc_modinit(void) +static __devinit int tegra_snd_harmony_probe(struct platform_device *pdev) { + struct snd_soc_card *card = &snd_soc_harmony; + struct tegra_harmony *harmony; + struct harmony_audio_platform_data *pdata; int ret; if (!machine_is_harmony()) { - pr_err(PREFIX "Not running on Tegra Harmony!\n"); + dev_err(&pdev->dev, "Not running on Tegra Harmony!\n"); return -ENODEV; } - ret = tegra_asoc_utils_init(); - if (ret) { - return ret; + pdata = pdev->dev.platform_data; + if (!pdata) { + dev_err(&pdev->dev, "no platform data supplied\n"); + return -EINVAL; } - /* - * Create and register platform device - */ - harmony_snd_device = platform_device_alloc("soc-audio", -1); - if (harmony_snd_device == NULL) { - pr_err(PREFIX "platform_device_alloc failed\n"); - ret = -ENOMEM; - goto err_clock_utils; + harmony = kzalloc(sizeof(struct tegra_harmony), GFP_KERNEL); + if (!harmony) { + dev_err(&pdev->dev, "Can't allocate tegra_harmony\n"); + return -ENOMEM; } - platform_set_drvdata(harmony_snd_device, &snd_soc_harmony); + harmony->pdata = pdata; + + ret = tegra_asoc_utils_init(&harmony->util_data, &pdev->dev); + if (ret) + goto err_free_harmony; + + card->dev = &pdev->dev; + platform_set_drvdata(pdev, card); + snd_soc_card_set_drvdata(card, harmony); - ret = platform_device_add(harmony_snd_device); + ret = snd_soc_register_card(card); if (ret) { - pr_err(PREFIX "platform_device_add failed (%d)\n", + dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret); - goto err_device_put; + goto err_clear_drvdata; } return 0; -err_device_put: - platform_device_put(harmony_snd_device); -err_clock_utils: - tegra_asoc_utils_fini(); +err_clear_drvdata: + snd_soc_card_set_drvdata(card, NULL); + platform_set_drvdata(pdev, NULL); + card->dev = NULL; + tegra_asoc_utils_fini(&harmony->util_data); +err_free_harmony: + kfree(harmony); return ret; } -module_init(harmony_soc_modinit); -static void __exit harmony_soc_modexit(void) +static int __devexit tegra_snd_harmony_remove(struct platform_device *pdev) { - platform_device_unregister(harmony_snd_device); + struct snd_soc_card *card = platform_get_drvdata(pdev); + struct tegra_harmony *harmony = snd_soc_card_get_drvdata(card); + struct harmony_audio_platform_data *pdata = harmony->pdata; + + snd_soc_unregister_card(card); + + snd_soc_card_set_drvdata(card, NULL); + platform_set_drvdata(pdev, NULL); + card->dev = NULL; - tegra_asoc_utils_fini(); + tegra_asoc_utils_fini(&harmony->util_data); + + if (harmony->gpio_spkr_en_requested) + gpio_free(pdata->gpio_spkr_en); + + kfree(harmony); + + return 0; +} + +static struct platform_driver tegra_snd_harmony_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + }, + .probe = tegra_snd_harmony_probe, + .remove = __devexit_p(tegra_snd_harmony_remove), +}; + +static int __init snd_tegra_harmony_init(void) +{ + return platform_driver_register(&tegra_snd_harmony_driver); +} +module_init(snd_tegra_harmony_init); + +static void __exit snd_tegra_harmony_exit(void) +{ + platform_driver_unregister(&tegra_snd_harmony_driver); } -module_exit(harmony_soc_modexit); +module_exit(snd_tegra_harmony_exit); MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>"); MODULE_DESCRIPTION("Harmony machine ASoC driver"); diff --git a/sound/soc/tegra/tegra_asoc_utils.c b/sound/soc/tegra/tegra_asoc_utils.c index cfe2ea890dc0..cb4fc13c7d22 100644 --- a/sound/soc/tegra/tegra_asoc_utils.c +++ b/sound/soc/tegra/tegra_asoc_utils.c @@ -21,20 +21,14 @@ */ #include <linux/clk.h> +#include <linux/device.h> #include <linux/err.h> #include <linux/kernel.h> #include "tegra_asoc_utils.h" -#define PREFIX "ASoC Tegra: " - -static struct clk *clk_pll_a; -static struct clk *clk_pll_a_out0; -static struct clk *clk_cdev1; - -static int set_baseclock, set_mclk; - -int tegra_asoc_utils_set_rate(int srate, int mclk, int *mclk_change) +int tegra_asoc_utils_set_rate(struct tegra_asoc_utils_data *data, int srate, + int mclk, int *mclk_change) { int new_baseclock; int err; @@ -58,95 +52,98 @@ int tegra_asoc_utils_set_rate(int srate, int mclk, int *mclk_change) return -EINVAL; } - *mclk_change = ((new_baseclock != set_baseclock) || - (mclk != set_mclk)); + *mclk_change = ((new_baseclock != data->set_baseclock) || + (mclk != data->set_mclk)); if (!*mclk_change) return 0; - set_baseclock = 0; - set_mclk = 0; + data->set_baseclock = 0; + data->set_mclk = 0; - clk_disable(clk_cdev1); - clk_disable(clk_pll_a_out0); - clk_disable(clk_pll_a); + clk_disable(data->clk_cdev1); + clk_disable(data->clk_pll_a_out0); + clk_disable(data->clk_pll_a); - err = clk_set_rate(clk_pll_a, new_baseclock); + err = clk_set_rate(data->clk_pll_a, new_baseclock); if (err) { - pr_err(PREFIX "Can't set pll_a rate: %d\n", err); + dev_err(data->dev, "Can't set pll_a rate: %d\n", err); return err; } - err = clk_set_rate(clk_pll_a_out0, mclk); + err = clk_set_rate(data->clk_pll_a_out0, mclk); if (err) { - pr_err(PREFIX "Can't set pll_a_out0 rate: %d\n", err); + dev_err(data->dev, "Can't set pll_a_out0 rate: %d\n", err); return err; } /* Don't set cdev1 rate; its locked to pll_a_out0 */ - err = clk_enable(clk_pll_a); + err = clk_enable(data->clk_pll_a); if (err) { - pr_err(PREFIX "Can't enable pll_a: %d\n", err); + dev_err(data->dev, "Can't enable pll_a: %d\n", err); return err; } - err = clk_enable(clk_pll_a_out0); + err = clk_enable(data->clk_pll_a_out0); if (err) { - pr_err(PREFIX "Can't enable pll_a_out0: %d\n", err); + dev_err(data->dev, "Can't enable pll_a_out0: %d\n", err); return err; } - err = clk_enable(clk_cdev1); + err = clk_enable(data->clk_cdev1); if (err) { - pr_err(PREFIX "Can't enable cdev1: %d\n", err); + dev_err(data->dev, "Can't enable cdev1: %d\n", err); return err; } - set_baseclock = new_baseclock; - set_mclk = mclk; + data->set_baseclock = new_baseclock; + data->set_mclk = mclk; return 0; } -int tegra_asoc_utils_init(void) +int tegra_asoc_utils_init(struct tegra_asoc_utils_data *data, + struct device *dev) { int ret; - clk_pll_a = clk_get_sys(NULL, "pll_a"); - if (IS_ERR(clk_pll_a)) { - pr_err(PREFIX "Can't retrieve clk pll_a\n"); - ret = PTR_ERR(clk_pll_a); + data->dev = dev; + + data->clk_pll_a = clk_get_sys(NULL, "pll_a"); + if (IS_ERR(data->clk_pll_a)) { + dev_err(data->dev, "Can't retrieve clk pll_a\n"); + ret = PTR_ERR(data->clk_pll_a); goto err; } - clk_pll_a_out0 = clk_get_sys(NULL, "pll_a_out0"); - if (IS_ERR(clk_pll_a_out0)) { - pr_err(PREFIX "Can't retrieve clk pll_a_out0\n"); - ret = PTR_ERR(clk_pll_a_out0); + data->clk_pll_a_out0 = clk_get_sys(NULL, "pll_a_out0"); + if (IS_ERR(data->clk_pll_a_out0)) { + dev_err(data->dev, "Can't retrieve clk pll_a_out0\n"); + ret = PTR_ERR(data->clk_pll_a_out0); goto err_put_pll_a; } - clk_cdev1 = clk_get_sys(NULL, "cdev1"); - if (IS_ERR(clk_cdev1)) { - pr_err(PREFIX "Can't retrieve clk cdev1\n"); - ret = PTR_ERR(clk_cdev1); + data->clk_cdev1 = clk_get_sys(NULL, "cdev1"); + if (IS_ERR(data->clk_cdev1)) { + dev_err(data->dev, "Can't retrieve clk cdev1\n"); + ret = PTR_ERR(data->clk_cdev1); goto err_put_pll_a_out0; } return 0; err_put_pll_a_out0: - clk_put(clk_pll_a_out0); + clk_put(data->clk_pll_a_out0); err_put_pll_a: - clk_put(clk_pll_a); + clk_put(data->clk_pll_a); err: return ret; } -void tegra_asoc_utils_fini(void) +void tegra_asoc_utils_fini(struct tegra_asoc_utils_data *data) { - clk_put(clk_cdev1); - clk_put(clk_pll_a_out0); - clk_put(clk_pll_a); + clk_put(data->clk_cdev1); + clk_put(data->clk_pll_a_out0); + clk_put(data->clk_pll_a); } diff --git a/sound/soc/tegra/tegra_asoc_utils.h b/sound/soc/tegra/tegra_asoc_utils.h index 855f8f6e44ca..bbba7afdfc2c 100644 --- a/sound/soc/tegra/tegra_asoc_utils.h +++ b/sound/soc/tegra/tegra_asoc_utils.h @@ -23,9 +23,23 @@ #ifndef __TEGRA_ASOC_UTILS_H__ #define __TEGRA_ASOC_UTILS_H_ -int tegra_asoc_utils_set_rate(int srate, int mclk_rate, int *mclk_change); -int tegra_asoc_utils_init(void); -void tegra_asoc_utils_fini(void); +struct clk; +struct device; + +struct tegra_asoc_utils_data { + struct device *dev; + struct clk *clk_pll_a; + struct clk *clk_pll_a_out0; + struct clk *clk_cdev1; + int set_baseclock; + int set_mclk; +}; + +int tegra_asoc_utils_set_rate(struct tegra_asoc_utils_data *data, int srate, + int mclk, int *mclk_change); +int tegra_asoc_utils_init(struct tegra_asoc_utils_data *data, + struct device *dev); +void tegra_asoc_utils_fini(struct tegra_asoc_utils_data *data); #endif diff --git a/sound/soc/tegra/tegra_i2s.c b/sound/soc/tegra/tegra_i2s.c index 6d668785e9af..870ee361f757 100644 --- a/sound/soc/tegra/tegra_i2s.c +++ b/sound/soc/tegra/tegra_i2s.c @@ -386,7 +386,7 @@ static __devinit int tegra_i2s_platform_probe(struct platform_device *pdev) snprintf(clk_name, sizeof(clk_name), DRV_NAME ".%d", pdev->id); i2s->clk_i2s = clk_get_sys(clk_name, NULL); if (IS_ERR(i2s->clk_i2s)) { - pr_err("Can't retrieve i2s clock\n"); + dev_err(&pdev->dev, "Can't retrieve i2s clock\n"); ret = PTR_ERR(i2s->clk_i2s); goto err_free; } |