From 800b18c7283e03e9c2a91d2348bebd1ed7ab037d Mon Sep 17 00:00:00 2001 From: Jingdong Lu Date: Thu, 27 Oct 2011 09:51:23 +0800 Subject: [PATCH 55/60] Temporarily revert some files to make audio work. Because many deferences between 3.0 and linux-omap-audio-2.6.38, temporarily rebase the following patches to make audio work welll. git://dev.omapzoom.org/pub/scm/misael/kernel-audio.git branch: misael/topic/linux-omap-audio-2.6.38 e1130bce9f8e5fc357c3523376bc980e221ae6f1 ASoC: core: Add support for platform DAPM components d4b31d2b781943ec160be82094e035d4b661a722 ASoC: core: Add support for back end DAIs 15780e7b83d264008c928fe4b8130fbe459e681b ASoC: dapm: stream event completion callback 4d2edee16c2f1a84f194d13b66f41f3d395451bf ASoC: core: Add backend DAI PCM operations routing e11ab2aa34a9b3d14b37c63d7264439067cd55f7 ASoC: core: Make PCM ops per rtd 39c0d679fa516c181398ea5ae4f1b77e99a2c9ba ASoC: core: make pcm_new() pass only snd_soc_pcm_runtime * 28bdb9388ab32c561f1617f9f8f7cad8ddcaece1 ASoC: core: Add FE playback and capture channels config 7246cd431ca3169a3e0a3188bd1182b80b26b38d ASoC: core: Add support for driver and longnames 0cc0ad13b6581bbb88317abaf82cee3aa37f8b7b ASoC: core: make BE ref counting per stream c653ee0abe1a9231d5a905c3e2566132207d53c4 ASoC: core: Add fixup function for BE hw_params 38ade0560db87cdbc89ab1422cc50c3a683cf942 ASoC: core: Add initial host-less DAI support e40644edbbdbf4608eaf81ed9569eaf89252dd9c ASoC: dapm: Fix race condition for widgets power list creation Integrated-by: Jingdong Lu --- include/sound/pcm.h | 96 ++-- include/sound/soc-dai.h | 49 ++ include/sound/soc-dapm.h | 127 +++--- sound/soc/soc-cache.c | 954 ++++++++++++++++++++++++------------ sound/soc/soc-dapm.c | 1211 ++++++++++++++++++++++++---------------------- sound/soc/soc-jack.c | 60 +--- 6 files changed, 1440 insertions(+), 1057 deletions(-) diff --git a/include/sound/pcm.h b/include/sound/pcm.h index e1bad11..a62171d 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -136,49 +136,48 @@ struct snd_pcm_ops { SNDRV_PCM_RATE_88200|SNDRV_PCM_RATE_96000) #define SNDRV_PCM_RATE_8000_192000 (SNDRV_PCM_RATE_8000_96000|SNDRV_PCM_RATE_176400|\ SNDRV_PCM_RATE_192000) -#define _SNDRV_PCM_FMTBIT(fmt) (1ULL << (__force int)SNDRV_PCM_FORMAT_##fmt) -#define SNDRV_PCM_FMTBIT_S8 _SNDRV_PCM_FMTBIT(S8) -#define SNDRV_PCM_FMTBIT_U8 _SNDRV_PCM_FMTBIT(U8) -#define SNDRV_PCM_FMTBIT_S16_LE _SNDRV_PCM_FMTBIT(S16_LE) -#define SNDRV_PCM_FMTBIT_S16_BE _SNDRV_PCM_FMTBIT(S16_BE) -#define SNDRV_PCM_FMTBIT_U16_LE _SNDRV_PCM_FMTBIT(U16_LE) -#define SNDRV_PCM_FMTBIT_U16_BE _SNDRV_PCM_FMTBIT(U16_BE) -#define SNDRV_PCM_FMTBIT_S24_LE _SNDRV_PCM_FMTBIT(S24_LE) -#define SNDRV_PCM_FMTBIT_S24_BE _SNDRV_PCM_FMTBIT(S24_BE) -#define SNDRV_PCM_FMTBIT_U24_LE _SNDRV_PCM_FMTBIT(U24_LE) -#define SNDRV_PCM_FMTBIT_U24_BE _SNDRV_PCM_FMTBIT(U24_BE) -#define SNDRV_PCM_FMTBIT_S32_LE _SNDRV_PCM_FMTBIT(S32_LE) -#define SNDRV_PCM_FMTBIT_S32_BE _SNDRV_PCM_FMTBIT(S32_BE) -#define SNDRV_PCM_FMTBIT_U32_LE _SNDRV_PCM_FMTBIT(U32_LE) -#define SNDRV_PCM_FMTBIT_U32_BE _SNDRV_PCM_FMTBIT(U32_BE) -#define SNDRV_PCM_FMTBIT_FLOAT_LE _SNDRV_PCM_FMTBIT(FLOAT_LE) -#define SNDRV_PCM_FMTBIT_FLOAT_BE _SNDRV_PCM_FMTBIT(FLOAT_BE) -#define SNDRV_PCM_FMTBIT_FLOAT64_LE _SNDRV_PCM_FMTBIT(FLOAT64_LE) -#define SNDRV_PCM_FMTBIT_FLOAT64_BE _SNDRV_PCM_FMTBIT(FLOAT64_BE) -#define SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE _SNDRV_PCM_FMTBIT(IEC958_SUBFRAME_LE) -#define SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_BE _SNDRV_PCM_FMTBIT(IEC958_SUBFRAME_BE) -#define SNDRV_PCM_FMTBIT_MU_LAW _SNDRV_PCM_FMTBIT(MU_LAW) -#define SNDRV_PCM_FMTBIT_A_LAW _SNDRV_PCM_FMTBIT(A_LAW) -#define SNDRV_PCM_FMTBIT_IMA_ADPCM _SNDRV_PCM_FMTBIT(IMA_ADPCM) -#define SNDRV_PCM_FMTBIT_MPEG _SNDRV_PCM_FMTBIT(MPEG) -#define SNDRV_PCM_FMTBIT_GSM _SNDRV_PCM_FMTBIT(GSM) -#define SNDRV_PCM_FMTBIT_SPECIAL _SNDRV_PCM_FMTBIT(SPECIAL) -#define SNDRV_PCM_FMTBIT_S24_3LE _SNDRV_PCM_FMTBIT(S24_3LE) -#define SNDRV_PCM_FMTBIT_U24_3LE _SNDRV_PCM_FMTBIT(U24_3LE) -#define SNDRV_PCM_FMTBIT_S24_3BE _SNDRV_PCM_FMTBIT(S24_3BE) -#define SNDRV_PCM_FMTBIT_U24_3BE _SNDRV_PCM_FMTBIT(U24_3BE) -#define SNDRV_PCM_FMTBIT_S20_3LE _SNDRV_PCM_FMTBIT(S20_3LE) -#define SNDRV_PCM_FMTBIT_U20_3LE _SNDRV_PCM_FMTBIT(U20_3LE) -#define SNDRV_PCM_FMTBIT_S20_3BE _SNDRV_PCM_FMTBIT(S20_3BE) -#define SNDRV_PCM_FMTBIT_U20_3BE _SNDRV_PCM_FMTBIT(U20_3BE) -#define SNDRV_PCM_FMTBIT_S18_3LE _SNDRV_PCM_FMTBIT(S18_3LE) -#define SNDRV_PCM_FMTBIT_U18_3LE _SNDRV_PCM_FMTBIT(U18_3LE) -#define SNDRV_PCM_FMTBIT_S18_3BE _SNDRV_PCM_FMTBIT(S18_3BE) -#define SNDRV_PCM_FMTBIT_U18_3BE _SNDRV_PCM_FMTBIT(U18_3BE) -#define SNDRV_PCM_FMTBIT_G723_24 _SNDRV_PCM_FMTBIT(G723_24) -#define SNDRV_PCM_FMTBIT_G723_24_1B _SNDRV_PCM_FMTBIT(G723_24_1B) -#define SNDRV_PCM_FMTBIT_G723_40 _SNDRV_PCM_FMTBIT(G723_40) -#define SNDRV_PCM_FMTBIT_G723_40_1B _SNDRV_PCM_FMTBIT(G723_40_1B) +#define SNDRV_PCM_FMTBIT_S8 (1ULL << SNDRV_PCM_FORMAT_S8) +#define SNDRV_PCM_FMTBIT_U8 (1ULL << SNDRV_PCM_FORMAT_U8) +#define SNDRV_PCM_FMTBIT_S16_LE (1ULL << SNDRV_PCM_FORMAT_S16_LE) +#define SNDRV_PCM_FMTBIT_S16_BE (1ULL << SNDRV_PCM_FORMAT_S16_BE) +#define SNDRV_PCM_FMTBIT_U16_LE (1ULL << SNDRV_PCM_FORMAT_U16_LE) +#define SNDRV_PCM_FMTBIT_U16_BE (1ULL << SNDRV_PCM_FORMAT_U16_BE) +#define SNDRV_PCM_FMTBIT_S24_LE (1ULL << SNDRV_PCM_FORMAT_S24_LE) +#define SNDRV_PCM_FMTBIT_S24_BE (1ULL << SNDRV_PCM_FORMAT_S24_BE) +#define SNDRV_PCM_FMTBIT_U24_LE (1ULL << SNDRV_PCM_FORMAT_U24_LE) +#define SNDRV_PCM_FMTBIT_U24_BE (1ULL << SNDRV_PCM_FORMAT_U24_BE) +#define SNDRV_PCM_FMTBIT_S32_LE (1ULL << SNDRV_PCM_FORMAT_S32_LE) +#define SNDRV_PCM_FMTBIT_S32_BE (1ULL << SNDRV_PCM_FORMAT_S32_BE) +#define SNDRV_PCM_FMTBIT_U32_LE (1ULL << SNDRV_PCM_FORMAT_U32_LE) +#define SNDRV_PCM_FMTBIT_U32_BE (1ULL << SNDRV_PCM_FORMAT_U32_BE) +#define SNDRV_PCM_FMTBIT_FLOAT_LE (1ULL << SNDRV_PCM_FORMAT_FLOAT_LE) +#define SNDRV_PCM_FMTBIT_FLOAT_BE (1ULL << SNDRV_PCM_FORMAT_FLOAT_BE) +#define SNDRV_PCM_FMTBIT_FLOAT64_LE (1ULL << SNDRV_PCM_FORMAT_FLOAT64_LE) +#define SNDRV_PCM_FMTBIT_FLOAT64_BE (1ULL << SNDRV_PCM_FORMAT_FLOAT64_BE) +#define SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE (1ULL << SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE) +#define SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_BE (1ULL << SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE) +#define SNDRV_PCM_FMTBIT_MU_LAW (1ULL << SNDRV_PCM_FORMAT_MU_LAW) +#define SNDRV_PCM_FMTBIT_A_LAW (1ULL << SNDRV_PCM_FORMAT_A_LAW) +#define SNDRV_PCM_FMTBIT_IMA_ADPCM (1ULL << SNDRV_PCM_FORMAT_IMA_ADPCM) +#define SNDRV_PCM_FMTBIT_MPEG (1ULL << SNDRV_PCM_FORMAT_MPEG) +#define SNDRV_PCM_FMTBIT_GSM (1ULL << SNDRV_PCM_FORMAT_GSM) +#define SNDRV_PCM_FMTBIT_SPECIAL (1ULL << SNDRV_PCM_FORMAT_SPECIAL) +#define SNDRV_PCM_FMTBIT_S24_3LE (1ULL << SNDRV_PCM_FORMAT_S24_3LE) +#define SNDRV_PCM_FMTBIT_U24_3LE (1ULL << SNDRV_PCM_FORMAT_U24_3LE) +#define SNDRV_PCM_FMTBIT_S24_3BE (1ULL << SNDRV_PCM_FORMAT_S24_3BE) +#define SNDRV_PCM_FMTBIT_U24_3BE (1ULL << SNDRV_PCM_FORMAT_U24_3BE) +#define SNDRV_PCM_FMTBIT_S20_3LE (1ULL << SNDRV_PCM_FORMAT_S20_3LE) +#define SNDRV_PCM_FMTBIT_U20_3LE (1ULL << SNDRV_PCM_FORMAT_U20_3LE) +#define SNDRV_PCM_FMTBIT_S20_3BE (1ULL << SNDRV_PCM_FORMAT_S20_3BE) +#define SNDRV_PCM_FMTBIT_U20_3BE (1ULL << SNDRV_PCM_FORMAT_U20_3BE) +#define SNDRV_PCM_FMTBIT_S18_3LE (1ULL << SNDRV_PCM_FORMAT_S18_3LE) +#define SNDRV_PCM_FMTBIT_U18_3LE (1ULL << SNDRV_PCM_FORMAT_U18_3LE) +#define SNDRV_PCM_FMTBIT_S18_3BE (1ULL << SNDRV_PCM_FORMAT_S18_3BE) +#define SNDRV_PCM_FMTBIT_U18_3BE (1ULL << SNDRV_PCM_FORMAT_U18_3BE) +#define SNDRV_PCM_FMTBIT_G723_24 (1ULL << SNDRV_PCM_FORMAT_G723_24) +#define SNDRV_PCM_FMTBIT_G723_24_1B (1ULL << SNDRV_PCM_FORMAT_G723_24_1B) +#define SNDRV_PCM_FMTBIT_G723_40 (1ULL << SNDRV_PCM_FORMAT_G723_40) +#define SNDRV_PCM_FMTBIT_G723_40_1B (1ULL << SNDRV_PCM_FORMAT_G723_40_1B) #ifdef SNDRV_LITTLE_ENDIAN #define SNDRV_PCM_FMTBIT_S16 SNDRV_PCM_FMTBIT_S16_LE @@ -413,6 +412,7 @@ struct snd_pcm_substream { #endif /* misc flags */ unsigned int hw_opened: 1; + unsigned int hw_no_buffer: 1; /* substream may not have a buffer */ }; #define SUBSTREAM_BUSY(substream) ((substream)->ref_count > 0) @@ -491,7 +491,7 @@ int snd_pcm_info_user(struct snd_pcm_substream *substream, int snd_pcm_status(struct snd_pcm_substream *substream, struct snd_pcm_status *status); int snd_pcm_start(struct snd_pcm_substream *substream); -int snd_pcm_stop(struct snd_pcm_substream *substream, snd_pcm_state_t status); +int snd_pcm_stop(struct snd_pcm_substream *substream, int status); int snd_pcm_drain_done(struct snd_pcm_substream *substream); #ifdef CONFIG_PM int snd_pcm_suspend(struct snd_pcm_substream *substream); @@ -749,8 +749,8 @@ static inline const struct snd_interval *hw_param_interval_c(const struct snd_pc return ¶ms->intervals[var - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL]; } -#define params_access(p) ((__force snd_pcm_access_t)snd_mask_min(hw_param_mask((p), SNDRV_PCM_HW_PARAM_ACCESS))) -#define params_format(p) ((__force snd_pcm_format_t)snd_mask_min(hw_param_mask((p), SNDRV_PCM_HW_PARAM_FORMAT))) +#define params_access(p) snd_mask_min(hw_param_mask((p), SNDRV_PCM_HW_PARAM_ACCESS)) +#define params_format(p) snd_mask_min(hw_param_mask((p), SNDRV_PCM_HW_PARAM_FORMAT)) #define params_subformat(p) snd_mask_min(hw_param_mask((p), SNDRV_PCM_HW_PARAM_SUBFORMAT)) #define params_channels(p) hw_param_interval((p), SNDRV_PCM_HW_PARAM_CHANNELS)->min #define params_rate(p) hw_param_interval((p), SNDRV_PCM_HW_PARAM_RATE)->min @@ -1031,7 +1031,9 @@ int snd_pcm_lib_mmap_iomem(struct snd_pcm_substream *substream, struct vm_area_s #define snd_pcm_lib_mmap_iomem NULL #endif -#define snd_pcm_lib_mmap_vmalloc NULL +int snd_pcm_lib_mmap_noncached(struct snd_pcm_substream *substream, + struct vm_area_struct *area); +#define snd_pcm_lib_mmap_vmalloc snd_pcm_lib_mmap_noncached static inline void snd_pcm_limit_isa_dma_size(int dma, size_t *max) { diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h index 1bafe95..af9a551 100644 --- a/include/sound/soc-dai.h +++ b/include/sound/soc-dai.h @@ -277,4 +277,53 @@ static inline void *snd_soc_dai_get_drvdata(struct snd_soc_dai *dai) return dev_get_drvdata(dai->dev); } +/* Backend DAI PCM ops */ +static inline int snd_soc_dai_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + if (dai->driver->ops->startup) + return dai->driver->ops->startup(substream, dai); + return 0; +} + +static inline void snd_soc_dai_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + if (dai->driver->ops->shutdown) + dai->driver->ops->shutdown(substream, dai); +} + +static inline int snd_soc_dai_hw_params(struct snd_pcm_substream * substream, + struct snd_pcm_hw_params *hw_params, struct snd_soc_dai *dai) +{ + if (dai->driver->ops->hw_params) + return dai->driver->ops->hw_params(substream, hw_params, dai); + return 0; +} + +static inline int snd_soc_dai_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + if (dai->driver->ops->hw_free) + return dai->driver->ops->hw_free(substream, dai); + return 0; +} + +static inline int snd_soc_dai_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + if (dai->driver->ops->prepare) + return dai->driver->ops->prepare(substream, dai); + return 0; +} + +static inline int snd_soc_dai_trigger(struct snd_pcm_substream *substream, + int cmd, struct snd_soc_dai *dai) +{ + if (dai->driver->ops->trigger) + return dai->driver->ops->trigger(substream, cmd, dai); + return 0; +} + + #endif diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 4b577b7..3245687 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -23,8 +23,8 @@ /* * SoC dynamic audio power management * - * We can have up to 4 power domains - * 1. Codec domain - VREF, VMID + * We can have upto 4 power domains + * 1. Codec domain - VREF, VMID * Usually controlled at codec probe/remove, although can be set * at stream time if power is not needed for sidetone, etc. * 2. Platform/Machine domain - physically connected inputs and outputs @@ -39,161 +39,149 @@ /* codec domain */ #define SND_SOC_DAPM_VMID(wname) \ -{ .id = snd_soc_dapm_vmid, .name = wname, .kcontrol_news = NULL, \ +{ .id = snd_soc_dapm_vmid, .name = wname, .kcontrols = NULL, \ .num_kcontrols = 0} /* platform domain */ #define SND_SOC_DAPM_INPUT(wname) \ -{ .id = snd_soc_dapm_input, .name = wname, .kcontrol_news = NULL, \ - .num_kcontrols = 0, .reg = SND_SOC_NOPM } +{ .id = snd_soc_dapm_input, .name = wname, .kcontrols = NULL, \ + .num_kcontrols = 0} #define SND_SOC_DAPM_OUTPUT(wname) \ -{ .id = snd_soc_dapm_output, .name = wname, .kcontrol_news = NULL, \ - .num_kcontrols = 0, .reg = SND_SOC_NOPM } +{ .id = snd_soc_dapm_output, .name = wname, .kcontrols = NULL, \ + .num_kcontrols = 0} #define SND_SOC_DAPM_MIC(wname, wevent) \ -{ .id = snd_soc_dapm_mic, .name = wname, .kcontrol_news = NULL, \ - .num_kcontrols = 0, .reg = SND_SOC_NOPM, .event = wevent, \ +{ .id = snd_soc_dapm_mic, .name = wname, .kcontrols = NULL, \ + .num_kcontrols = 0, .event = wevent, \ .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD} #define SND_SOC_DAPM_HP(wname, wevent) \ -{ .id = snd_soc_dapm_hp, .name = wname, .kcontrol_news = NULL, \ - .num_kcontrols = 0, .reg = SND_SOC_NOPM, .event = wevent, \ +{ .id = snd_soc_dapm_hp, .name = wname, .kcontrols = NULL, \ + .num_kcontrols = 0, .event = wevent, \ .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD} #define SND_SOC_DAPM_SPK(wname, wevent) \ -{ .id = snd_soc_dapm_spk, .name = wname, .kcontrol_news = NULL, \ - .num_kcontrols = 0, .reg = SND_SOC_NOPM, .event = wevent, \ +{ .id = snd_soc_dapm_spk, .name = wname, .kcontrols = NULL, \ + .num_kcontrols = 0, .event = wevent, \ .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD} #define SND_SOC_DAPM_LINE(wname, wevent) \ -{ .id = snd_soc_dapm_line, .name = wname, .kcontrol_news = NULL, \ - .num_kcontrols = 0, .reg = SND_SOC_NOPM, .event = wevent, \ +{ .id = snd_soc_dapm_line, .name = wname, .kcontrols = NULL, \ + .num_kcontrols = 0, .event = wevent, \ .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD} /* path domain */ #define SND_SOC_DAPM_PGA(wname, wreg, wshift, winvert,\ wcontrols, wncontrols) \ { .id = snd_soc_dapm_pga, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = wncontrols} + .invert = winvert, .kcontrols = wcontrols, .num_kcontrols = wncontrols} #define SND_SOC_DAPM_OUT_DRV(wname, wreg, wshift, winvert,\ wcontrols, wncontrols) \ { .id = snd_soc_dapm_out_drv, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = wncontrols} + .invert = winvert, .kcontrols = wcontrols, .num_kcontrols = wncontrols} #define SND_SOC_DAPM_MIXER(wname, wreg, wshift, winvert, \ wcontrols, wncontrols)\ { .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = wncontrols} + .invert = winvert, .kcontrols = wcontrols, .num_kcontrols = wncontrols} #define SND_SOC_DAPM_MIXER_NAMED_CTL(wname, wreg, wshift, winvert, \ wcontrols, wncontrols)\ { .id = snd_soc_dapm_mixer_named_ctl, .name = wname, .reg = wreg, \ - .shift = wshift, .invert = winvert, .kcontrol_news = wcontrols, \ + .shift = wshift, .invert = winvert, .kcontrols = wcontrols, \ .num_kcontrols = wncontrols} #define SND_SOC_DAPM_MICBIAS(wname, wreg, wshift, winvert) \ { .id = snd_soc_dapm_micbias, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrol_news = NULL, .num_kcontrols = 0} + .invert = winvert, .kcontrols = NULL, .num_kcontrols = 0} #define SND_SOC_DAPM_SWITCH(wname, wreg, wshift, winvert, wcontrols) \ { .id = snd_soc_dapm_switch, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = 1} + .invert = winvert, .kcontrols = wcontrols, .num_kcontrols = 1} #define SND_SOC_DAPM_MUX(wname, wreg, wshift, winvert, wcontrols) \ { .id = snd_soc_dapm_mux, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = 1} + .invert = winvert, .kcontrols = wcontrols, .num_kcontrols = 1} #define SND_SOC_DAPM_VIRT_MUX(wname, wreg, wshift, winvert, wcontrols) \ { .id = snd_soc_dapm_virt_mux, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = 1} + .invert = winvert, .kcontrols = wcontrols, .num_kcontrols = 1} #define SND_SOC_DAPM_VALUE_MUX(wname, wreg, wshift, winvert, wcontrols) \ { .id = snd_soc_dapm_value_mux, .name = wname, .reg = wreg, \ - .shift = wshift, .invert = winvert, .kcontrol_news = wcontrols, \ + .shift = wshift, .invert = winvert, .kcontrols = wcontrols, \ .num_kcontrols = 1} /* Simplified versions of above macros, assuming wncontrols = ARRAY_SIZE(wcontrols) */ #define SOC_PGA_ARRAY(wname, wreg, wshift, winvert,\ wcontrols) \ { .id = snd_soc_dapm_pga, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols)} + .invert = winvert, .kcontrols = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols)} #define SOC_MIXER_ARRAY(wname, wreg, wshift, winvert, \ wcontrols)\ { .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols)} + .invert = winvert, .kcontrols = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols)} #define SOC_MIXER_NAMED_CTL_ARRAY(wname, wreg, wshift, winvert, \ wcontrols)\ { .id = snd_soc_dapm_mixer_named_ctl, .name = wname, .reg = wreg, \ - .shift = wshift, .invert = winvert, .kcontrol_news = wcontrols, \ + .shift = wshift, .invert = winvert, .kcontrols = wcontrols, \ .num_kcontrols = ARRAY_SIZE(wcontrols)} /* path domain with event - event handler must return 0 for success */ #define SND_SOC_DAPM_PGA_E(wname, wreg, wshift, winvert, wcontrols, \ wncontrols, wevent, wflags) \ { .id = snd_soc_dapm_pga, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = wncontrols, \ + .invert = winvert, .kcontrols = wcontrols, .num_kcontrols = wncontrols, \ .event = wevent, .event_flags = wflags} #define SND_SOC_DAPM_OUT_DRV_E(wname, wreg, wshift, winvert, wcontrols, \ wncontrols, wevent, wflags) \ { .id = snd_soc_dapm_out_drv, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = wncontrols, \ + .invert = winvert, .kcontrols = wcontrols, .num_kcontrols = wncontrols, \ .event = wevent, .event_flags = wflags} #define SND_SOC_DAPM_MIXER_E(wname, wreg, wshift, winvert, wcontrols, \ wncontrols, wevent, wflags) \ { .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = wncontrols, \ + .invert = winvert, .kcontrols = wcontrols, .num_kcontrols = wncontrols, \ .event = wevent, .event_flags = wflags} #define SND_SOC_DAPM_MIXER_NAMED_CTL_E(wname, wreg, wshift, winvert, \ wcontrols, wncontrols, wevent, wflags) \ { .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrol_news = wcontrols, \ + .invert = winvert, .kcontrols = wcontrols, \ .num_kcontrols = wncontrols, .event = wevent, .event_flags = wflags} #define SND_SOC_DAPM_MICBIAS_E(wname, wreg, wshift, winvert, wevent, wflags) \ { .id = snd_soc_dapm_micbias, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrol_news = NULL, .num_kcontrols = 0, \ + .invert = winvert, .kcontrols = NULL, .num_kcontrols = 0, \ .event = wevent, .event_flags = wflags} #define SND_SOC_DAPM_SWITCH_E(wname, wreg, wshift, winvert, wcontrols, \ wevent, wflags) \ { .id = snd_soc_dapm_switch, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = 1, \ + .invert = winvert, .kcontrols = wcontrols, .num_kcontrols = 1, \ .event = wevent, .event_flags = wflags} #define SND_SOC_DAPM_MUX_E(wname, wreg, wshift, winvert, wcontrols, \ wevent, wflags) \ { .id = snd_soc_dapm_mux, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = 1, \ + .invert = winvert, .kcontrols = wcontrols, .num_kcontrols = 1, \ .event = wevent, .event_flags = wflags} #define SND_SOC_DAPM_VIRT_MUX_E(wname, wreg, wshift, winvert, wcontrols, \ wevent, wflags) \ { .id = snd_soc_dapm_virt_mux, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = 1, \ + .invert = winvert, .kcontrols = wcontrols, .num_kcontrols = 1, \ .event = wevent, .event_flags = wflags} -/* additional sequencing control within an event type */ -#define SND_SOC_DAPM_PGA_S(wname, wsubseq, wreg, wshift, winvert, \ - wevent, wflags) \ -{ .id = snd_soc_dapm_pga, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .event = wevent, .event_flags = wflags, \ - .subseq = wsubseq} -#define SND_SOC_DAPM_SUPPLY_S(wname, wsubseq, wreg, wshift, winvert, wevent, \ - wflags) \ -{ .id = snd_soc_dapm_supply, .name = wname, .reg = wreg, \ - .shift = wshift, .invert = winvert, .event = wevent, \ - .event_flags = wflags, .subseq = wsubseq} - /* Simplified versions of above macros, assuming wncontrols = ARRAY_SIZE(wcontrols) */ #define SOC_PGA_E_ARRAY(wname, wreg, wshift, winvert, wcontrols, \ wevent, wflags) \ { .id = snd_soc_dapm_pga, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols), \ + .invert = winvert, .kcontrols = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols), \ .event = wevent, .event_flags = wflags} #define SOC_MIXER_E_ARRAY(wname, wreg, wshift, winvert, wcontrols, \ wevent, wflags) \ { .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols), \ + .invert = winvert, .kcontrols = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols), \ .event = wevent, .event_flags = wflags} #define SOC_MIXER_NAMED_CTL_E_ARRAY(wname, wreg, wshift, winvert, \ wcontrols, wevent, wflags) \ { .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrol_news = wcontrols, \ + .invert = winvert, .kcontrols = wcontrols, \ .num_kcontrols = ARRAY_SIZE(wcontrols), .event = wevent, .event_flags = wflags} /* events that are pre and post DAPM */ #define SND_SOC_DAPM_PRE(wname, wevent) \ -{ .id = snd_soc_dapm_pre, .name = wname, .kcontrol_news = NULL, \ - .num_kcontrols = 0, .reg = SND_SOC_NOPM, .event = wevent, \ +{ .id = snd_soc_dapm_pre, .name = wname, .kcontrols = NULL, \ + .num_kcontrols = 0, .event = wevent, \ .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD} #define SND_SOC_DAPM_POST(wname, wevent) \ -{ .id = snd_soc_dapm_post, .name = wname, .kcontrol_news = NULL, \ - .num_kcontrols = 0, .reg = SND_SOC_NOPM, .event = wevent, \ +{ .id = snd_soc_dapm_post, .name = wname, .kcontrols = NULL, \ + .num_kcontrols = 0, .event = wevent, \ .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD} /* stream domain */ @@ -232,7 +220,7 @@ /* generic widgets */ #define SND_SOC_DAPM_REG(wid, wname, wreg, wshift, wmask, won_val, woff_val) \ -{ .id = wid, .name = wname, .kcontrol_news = NULL, .num_kcontrols = 0, \ +{ .id = wid, .name = wname, .kcontrols = NULL, .num_kcontrols = 0, \ .reg = -((wreg) + 1), .shift = wshift, .mask = wmask, \ .on_val = won_val, .off_val = woff_val, .event = dapm_reg_event, \ .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD} @@ -354,10 +342,14 @@ int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm); void snd_soc_dapm_free(struct snd_soc_dapm_context *dapm); int snd_soc_dapm_add_routes(struct snd_soc_dapm_context *dapm, const struct snd_soc_dapm_route *route, int num); +int snd_soc_scenario_set_path(struct snd_soc_dapm_context *dapm, + const char *source_name, const char *sink_name, int stream); +const char *snd_soc_dapm_get_aif(struct snd_soc_dapm_context *dapm, + const char *stream_name, enum snd_soc_dapm_type type); /* dapm events */ int snd_soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd, - const char *stream, int event); + int dir, const char *stream, int event); void snd_soc_dapm_shutdown(struct snd_soc_card *card); /* external DAPM widget events */ @@ -369,8 +361,7 @@ int snd_soc_dapm_mux_update_power(struct snd_soc_dapm_widget *widget, /* dapm sys fs - used by the core */ int snd_soc_dapm_sys_add(struct device *dev); -void snd_soc_dapm_debugfs_init(struct snd_soc_dapm_context *dapm, - struct dentry *parent); +void snd_soc_dapm_debugfs_init(struct snd_soc_dapm_context *dapm); /* dapm audio pin control and status */ int snd_soc_dapm_enable_pin(struct snd_soc_dapm_context *dapm, @@ -442,6 +433,7 @@ struct snd_soc_dapm_path { /* status */ u32 connect:1; /* source and sink widgets are connected */ u32 walked:1; /* path has been walked */ + u32 length:6; /* path length - used by route mapper */ int (*connected)(struct snd_soc_dapm_widget *source, struct snd_soc_dapm_widget *sink); @@ -457,6 +449,7 @@ struct snd_soc_dapm_widget { char *name; /* widget name */ char *sname; /* stream name */ struct snd_soc_codec *codec; + struct snd_soc_platform *platform; struct list_head list; struct snd_soc_dapm_context *dapm; @@ -465,6 +458,8 @@ struct snd_soc_dapm_widget { unsigned char shift; /* bits to shift */ unsigned int saved_value; /* widget saved value */ unsigned int value; /* widget current value */ + unsigned int path_idx; + unsigned int hops; unsigned int mask; /* non-shifted mask */ unsigned int on_val; /* on state value */ unsigned int off_val; /* off state value */ @@ -476,7 +471,6 @@ struct snd_soc_dapm_widget { unsigned char ext:1; /* has external widgets */ unsigned char force:1; /* force state */ unsigned char ignore_suspend:1; /* kept enabled over suspend */ - int subseq; /* sort within widget type */ int (*power_check)(struct snd_soc_dapm_widget *w); @@ -486,8 +480,7 @@ struct snd_soc_dapm_widget { /* kcontrols that relate to this widget */ int num_kcontrols; - const struct snd_kcontrol_new *kcontrol_news; - struct snd_kcontrol **kcontrols; + const struct snd_kcontrol_new *kcontrols; /* widget input and outputs */ struct list_head sources; @@ -508,6 +501,7 @@ struct snd_soc_dapm_update { /* DAPM context */ struct snd_soc_dapm_context { int n_widgets; /* number of widgets in this context */ + int num_valid_paths; enum snd_soc_bias_level bias_level; enum snd_soc_bias_level suspend_bias_level; struct delayed_work delayed_work; @@ -515,11 +509,10 @@ struct snd_soc_dapm_context { struct snd_soc_dapm_update *update; - void (*seq_notifier)(struct snd_soc_dapm_context *, - enum snd_soc_dapm_type, int); - struct device *dev; /* from parent - for debug */ struct snd_soc_codec *codec; /* parent codec */ + struct snd_soc_platform *platform; /*parent platform */ + int (*stream_event)(struct snd_soc_dapm_context *dapm); struct snd_soc_card *card; /* parent card */ /* used during DAPM updates */ @@ -531,10 +524,4 @@ struct snd_soc_dapm_context { #endif }; -/* A list of widgets associated with an object, typically a snd_kcontrol */ -struct snd_soc_dapm_widget_list { - int num_widgets; - struct snd_soc_dapm_widget *widgets[0]; -}; - #endif diff --git a/sound/soc/soc-cache.c b/sound/soc/soc-cache.c index 039b953..8c2a21a 100644 --- a/sound/soc/soc-cache.c +++ b/sound/soc/soc-cache.c @@ -18,30 +18,38 @@ #include #include -#include - -#ifdef CONFIG_SPI_MASTER -static int do_spi_write(void *control, const char *data, int len) +static unsigned int snd_soc_4_12_read(struct snd_soc_codec *codec, + unsigned int reg) { - struct spi_device *spi = control; int ret; + unsigned int val; - ret = spi_write(spi, data, len); - if (ret < 0) - return ret; + if (reg >= codec->driver->reg_cache_size || + snd_soc_codec_volatile_register(codec, reg)) { + if (codec->cache_only) + return -1; - return len; + BUG_ON(!codec->hw_read); + return codec->hw_read(codec, reg); + } + + ret = snd_soc_cache_read(codec, reg, &val); + if (ret < 0) + return -1; + return val; } -#endif -static int do_hw_write(struct snd_soc_codec *codec, unsigned int reg, - unsigned int value, const void *data, int len) +static int snd_soc_4_12_write(struct snd_soc_codec *codec, unsigned int reg, + unsigned int value) { + u8 data[2]; int ret; + data[0] = (reg << 4) | ((value >> 8) & 0x000f); + data[1] = value & 0x00ff; + if (!snd_soc_codec_volatile_register(codec, reg) && - reg < codec->driver->reg_cache_size && - !codec->cache_bypass) { + reg < codec->driver->reg_cache_size) { ret = snd_soc_cache_write(codec, reg, value); if (ret < 0) return -1; @@ -52,8 +60,8 @@ static int do_hw_write(struct snd_soc_codec *codec, unsigned int reg, return 0; } - ret = codec->hw_write(codec->control_data, data, len); - if (ret == len) + ret = codec->hw_write(codec->control_data, data, 2); + if (ret == 2) return 0; if (ret < 0) return ret; @@ -61,19 +69,49 @@ static int do_hw_write(struct snd_soc_codec *codec, unsigned int reg, return -EIO; } -static unsigned int do_hw_read(struct snd_soc_codec *codec, unsigned int reg) +#if defined(CONFIG_SPI_MASTER) +static int snd_soc_4_12_spi_write(void *control_data, const char *data, + int len) +{ + struct spi_device *spi = control_data; + struct spi_transfer t; + struct spi_message m; + u8 msg[2]; + + if (len <= 0) + return 0; + + msg[0] = data[1]; + msg[1] = data[0]; + + spi_message_init(&m); + memset(&t, 0, sizeof t); + + t.tx_buf = &msg[0]; + t.len = len; + + spi_message_add_tail(&t, &m); + spi_sync(spi, &m); + + return len; +} +#else +#define snd_soc_4_12_spi_write NULL +#endif + +static unsigned int snd_soc_7_9_read(struct snd_soc_codec *codec, + unsigned int reg) { int ret; unsigned int val; if (reg >= codec->driver->reg_cache_size || - snd_soc_codec_volatile_register(codec, reg) || - codec->cache_bypass) { - if (codec->cache_only) - return -1; + snd_soc_codec_volatile_register(codec, reg)) { + if (codec->cache_only) + return -1; - BUG_ON(!codec->hw_read); - return codec->hw_read(codec, reg); + BUG_ON(!codec->hw_read); + return codec->hw_read(codec, reg); } ret = snd_soc_cache_read(codec, reg, &val); @@ -82,117 +120,254 @@ static unsigned int do_hw_read(struct snd_soc_codec *codec, unsigned int reg) return val; } -static unsigned int snd_soc_4_12_read(struct snd_soc_codec *codec, - unsigned int reg) +static int snd_soc_7_9_write(struct snd_soc_codec *codec, unsigned int reg, + unsigned int value) { - return do_hw_read(codec, reg); -} + u8 data[2]; + int ret; -static int snd_soc_4_12_write(struct snd_soc_codec *codec, unsigned int reg, - unsigned int value) -{ - u16 data; + data[0] = (reg << 1) | ((value >> 8) & 0x0001); + data[1] = value & 0x00ff; + + if (!snd_soc_codec_volatile_register(codec, reg) && + reg < codec->driver->reg_cache_size) { + ret = snd_soc_cache_write(codec, reg, value); + if (ret < 0) + return -1; + } - data = cpu_to_be16((reg << 12) | (value & 0xffffff)); + if (codec->cache_only) { + codec->cache_sync = 1; + return 0; + } - return do_hw_write(codec, reg, value, &data, 2); + ret = codec->hw_write(codec->control_data, data, 2); + if (ret == 2) + return 0; + if (ret < 0) + return ret; + else + return -EIO; } -static unsigned int snd_soc_7_9_read(struct snd_soc_codec *codec, - unsigned int reg) +#if defined(CONFIG_SPI_MASTER) +static int snd_soc_7_9_spi_write(void *control_data, const char *data, + int len) { - return do_hw_read(codec, reg); -} + struct spi_device *spi = control_data; + struct spi_transfer t; + struct spi_message m; + u8 msg[2]; -static int snd_soc_7_9_write(struct snd_soc_codec *codec, unsigned int reg, - unsigned int value) -{ - u8 data[2]; + if (len <= 0) + return 0; - data[0] = (reg << 1) | ((value >> 8) & 0x0001); - data[1] = value & 0x00ff; + msg[0] = data[0]; + msg[1] = data[1]; + + spi_message_init(&m); + memset(&t, 0, sizeof t); + + t.tx_buf = &msg[0]; + t.len = len; + + spi_message_add_tail(&t, &m); + spi_sync(spi, &m); - return do_hw_write(codec, reg, value, data, 2); + return len; } +#else +#define snd_soc_7_9_spi_write NULL +#endif static int snd_soc_8_8_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int value) { u8 data[2]; + int ret; reg &= 0xff; data[0] = reg; data[1] = value & 0xff; - return do_hw_write(codec, reg, value, data, 2); + if (!snd_soc_codec_volatile_register(codec, reg) && + reg < codec->driver->reg_cache_size) { + ret = snd_soc_cache_write(codec, reg, value); + if (ret < 0) + return -1; + } + + if (codec->cache_only) { + codec->cache_sync = 1; + return 0; + } + + if (codec->hw_write(codec->control_data, data, 2) == 2) + return 0; + else + return -EIO; } static unsigned int snd_soc_8_8_read(struct snd_soc_codec *codec, unsigned int reg) { - return do_hw_read(codec, reg); + int ret; + unsigned int val; + + reg &= 0xff; + if (reg >= codec->driver->reg_cache_size || + snd_soc_codec_volatile_register(codec, reg)) { + if (codec->cache_only) + return -1; + + BUG_ON(!codec->hw_read); + return codec->hw_read(codec, reg); + } + + ret = snd_soc_cache_read(codec, reg, &val); + if (ret < 0) + return -1; + return val; } +#if defined(CONFIG_SPI_MASTER) +static int snd_soc_8_8_spi_write(void *control_data, const char *data, + int len) +{ + struct spi_device *spi = control_data; + struct spi_transfer t; + struct spi_message m; + u8 msg[2]; + + if (len <= 0) + return 0; + + msg[0] = data[0]; + msg[1] = data[1]; + + spi_message_init(&m); + memset(&t, 0, sizeof t); + + t.tx_buf = &msg[0]; + t.len = len; + + spi_message_add_tail(&t, &m); + spi_sync(spi, &m); + + return len; +} +#else +#define snd_soc_8_8_spi_write NULL +#endif + static int snd_soc_8_16_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int value) { u8 data[3]; + int ret; data[0] = reg; data[1] = (value >> 8) & 0xff; data[2] = value & 0xff; - return do_hw_write(codec, reg, value, data, 3); + if (!snd_soc_codec_volatile_register(codec, reg) && + reg < codec->driver->reg_cache_size) { + ret = snd_soc_cache_write(codec, reg, value); + if (ret < 0) + return -1; + } + + if (codec->cache_only) { + codec->cache_sync = 1; + return 0; + } + + if (codec->hw_write(codec->control_data, data, 3) == 3) + return 0; + else + return -EIO; } static unsigned int snd_soc_8_16_read(struct snd_soc_codec *codec, unsigned int reg) { - return do_hw_read(codec, reg); + int ret; + unsigned int val; + + if (reg >= codec->driver->reg_cache_size || + snd_soc_codec_volatile_register(codec, reg)) { + if (codec->cache_only) + return -1; + + BUG_ON(!codec->hw_read); + return codec->hw_read(codec, reg); + } + + ret = snd_soc_cache_read(codec, reg, &val); + if (ret < 0) + return -1; + return val; +} + +#if defined(CONFIG_SPI_MASTER) +static int snd_soc_8_16_spi_write(void *control_data, const char *data, + int len) +{ + struct spi_device *spi = control_data; + struct spi_transfer t; + struct spi_message m; + u8 msg[3]; + + if (len <= 0) + return 0; + + msg[0] = data[0]; + msg[1] = data[1]; + msg[2] = data[2]; + + spi_message_init(&m); + memset(&t, 0, sizeof t); + + t.tx_buf = &msg[0]; + t.len = len; + + spi_message_add_tail(&t, &m); + spi_sync(spi, &m); + + return len; } +#else +#define snd_soc_8_16_spi_write NULL +#endif #if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE)) -static unsigned int do_i2c_read(struct snd_soc_codec *codec, - void *reg, int reglen, - void *data, int datalen) +static unsigned int snd_soc_8_8_read_i2c(struct snd_soc_codec *codec, + unsigned int r) { struct i2c_msg xfer[2]; + u8 reg = r; + u8 data; int ret; struct i2c_client *client = codec->control_data; /* Write register */ xfer[0].addr = client->addr; xfer[0].flags = 0; - xfer[0].len = reglen; - xfer[0].buf = reg; + xfer[0].len = 1; + xfer[0].buf = ® /* Read data */ xfer[1].addr = client->addr; xfer[1].flags = I2C_M_RD; - xfer[1].len = datalen; - xfer[1].buf = data; + xfer[1].len = 1; + xfer[1].buf = &data; ret = i2c_transfer(client->adapter, xfer, 2); - if (ret == 2) + if (ret != 2) { + dev_err(&client->dev, "i2c_transfer() returned %d\n", ret); return 0; - else if (ret < 0) - return ret; - else - return -EIO; -} -#endif - -#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE)) -static unsigned int snd_soc_8_8_read_i2c(struct snd_soc_codec *codec, - unsigned int r) -{ - u8 reg = r; - u8 data; - int ret; + } - ret = do_i2c_read(codec, ®, 1, &data, 1); - if (ret < 0) - return 0; return data; } #else @@ -203,13 +378,30 @@ static unsigned int snd_soc_8_8_read_i2c(struct snd_soc_codec *codec, static unsigned int snd_soc_8_16_read_i2c(struct snd_soc_codec *codec, unsigned int r) { + struct i2c_msg xfer[2]; u8 reg = r; u16 data; int ret; + struct i2c_client *client = codec->control_data; - ret = do_i2c_read(codec, ®, 1, &data, 2); - if (ret < 0) + /* Write register */ + xfer[0].addr = client->addr; + xfer[0].flags = 0; + xfer[0].len = 1; + xfer[0].buf = ® + + /* Read data */ + xfer[1].addr = client->addr; + xfer[1].flags = I2C_M_RD; + xfer[1].len = 2; + xfer[1].buf = (u8 *)&data; + + ret = i2c_transfer(client->adapter, xfer, 2); + if (ret != 2) { + dev_err(&client->dev, "i2c_transfer() returned %d\n", ret); return 0; + } + return (data >> 8) | ((data & 0xff) << 8); } #else @@ -220,13 +412,30 @@ static unsigned int snd_soc_8_16_read_i2c(struct snd_soc_codec *codec, static unsigned int snd_soc_16_8_read_i2c(struct snd_soc_codec *codec, unsigned int r) { + struct i2c_msg xfer[2]; u16 reg = r; u8 data; int ret; + struct i2c_client *client = codec->control_data; - ret = do_i2c_read(codec, ®, 2, &data, 1); - if (ret < 0) + /* Write register */ + xfer[0].addr = client->addr; + xfer[0].flags = 0; + xfer[0].len = 2; + xfer[0].buf = (u8 *)® + + /* Read data */ + xfer[1].addr = client->addr; + xfer[1].flags = I2C_M_RD; + xfer[1].len = 1; + xfer[1].buf = &data; + + ret = i2c_transfer(client->adapter, xfer, 2); + if (ret != 2) { + dev_err(&client->dev, "i2c_transfer() returned %d\n", ret); return 0; + } + return data; } #else @@ -234,34 +443,118 @@ static unsigned int snd_soc_16_8_read_i2c(struct snd_soc_codec *codec, #endif static unsigned int snd_soc_16_8_read(struct snd_soc_codec *codec, - unsigned int reg) + unsigned int reg) { - return do_hw_read(codec, reg); + int ret; + unsigned int val; + + reg &= 0xff; + if (reg >= codec->driver->reg_cache_size || + snd_soc_codec_volatile_register(codec, reg)) { + if (codec->cache_only) + return -1; + + BUG_ON(!codec->hw_read); + return codec->hw_read(codec, reg); + } + + ret = snd_soc_cache_read(codec, reg, &val); + if (ret < 0) + return -1; + return val; } static int snd_soc_16_8_write(struct snd_soc_codec *codec, unsigned int reg, - unsigned int value) + unsigned int value) { u8 data[3]; + int ret; data[0] = (reg >> 8) & 0xff; data[1] = reg & 0xff; data[2] = value; - return do_hw_write(codec, reg, value, data, 3); + reg &= 0xff; + if (!snd_soc_codec_volatile_register(codec, reg) && + reg < codec->driver->reg_cache_size) { + ret = snd_soc_cache_write(codec, reg, value); + if (ret < 0) + return -1; + } + + if (codec->cache_only) { + codec->cache_sync = 1; + return 0; + } + + ret = codec->hw_write(codec->control_data, data, 3); + if (ret == 3) + return 0; + if (ret < 0) + return ret; + else + return -EIO; +} + +#if defined(CONFIG_SPI_MASTER) +static int snd_soc_16_8_spi_write(void *control_data, const char *data, + int len) +{ + struct spi_device *spi = control_data; + struct spi_transfer t; + struct spi_message m; + u8 msg[3]; + + if (len <= 0) + return 0; + + msg[0] = data[0]; + msg[1] = data[1]; + msg[2] = data[2]; + + spi_message_init(&m); + memset(&t, 0, sizeof t); + + t.tx_buf = &msg[0]; + t.len = len; + + spi_message_add_tail(&t, &m); + spi_sync(spi, &m); + + return len; } +#else +#define snd_soc_16_8_spi_write NULL +#endif #if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE)) static unsigned int snd_soc_16_16_read_i2c(struct snd_soc_codec *codec, unsigned int r) { + struct i2c_msg xfer[2]; u16 reg = cpu_to_be16(r); u16 data; int ret; + struct i2c_client *client = codec->control_data; - ret = do_i2c_read(codec, ®, 2, &data, 2); - if (ret < 0) + /* Write register */ + xfer[0].addr = client->addr; + xfer[0].flags = 0; + xfer[0].len = 2; + xfer[0].buf = (u8 *)® + + /* Read data */ + xfer[1].addr = client->addr; + xfer[1].flags = I2C_M_RD; + xfer[1].len = 2; + xfer[1].buf = (u8 *)&data; + + ret = i2c_transfer(client->adapter, xfer, 2); + if (ret != 2) { + dev_err(&client->dev, "i2c_transfer() returned %d\n", ret); return 0; + } + return be16_to_cpu(data); } #else @@ -271,59 +564,50 @@ static unsigned int snd_soc_16_16_read_i2c(struct snd_soc_codec *codec, static unsigned int snd_soc_16_16_read(struct snd_soc_codec *codec, unsigned int reg) { - return do_hw_read(codec, reg); + int ret; + unsigned int val; + + if (reg >= codec->driver->reg_cache_size || + snd_soc_codec_volatile_register(codec, reg)) { + if (codec->cache_only) + return -1; + + BUG_ON(!codec->hw_read); + return codec->hw_read(codec, reg); + } + + ret = snd_soc_cache_read(codec, reg, &val); + if (ret < 0) + return -1; + + return val; } static int snd_soc_16_16_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int value) { u8 data[4]; + int ret; data[0] = (reg >> 8) & 0xff; data[1] = reg & 0xff; data[2] = (value >> 8) & 0xff; data[3] = value & 0xff; - return do_hw_write(codec, reg, value, data, 4); -} - -/* Primitive bulk write support for soc-cache. The data pointed to by - * `data' needs to already be in the form the hardware expects - * including any leading register specific data. Any data written - * through this function will not go through the cache as it only - * handles writing to volatile or out of bounds registers. - */ -static int snd_soc_hw_bulk_write_raw(struct snd_soc_codec *codec, unsigned int reg, - const void *data, size_t len) -{ - int ret; - - /* To ensure that we don't get out of sync with the cache, check - * whether the base register is volatile or if we've directly asked - * to bypass the cache. Out of bounds registers are considered - * volatile. - */ - if (!codec->cache_bypass - && !snd_soc_codec_volatile_register(codec, reg) - && reg < codec->driver->reg_cache_size) - return -EINVAL; + if (!snd_soc_codec_volatile_register(codec, reg) && + reg < codec->driver->reg_cache_size) { + ret = snd_soc_cache_write(codec, reg, value); + if (ret < 0) + return -1; + } - switch (codec->control_type) { -#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE)) - case SND_SOC_I2C: - ret = i2c_master_send(codec->control_data, data, len); - break; -#endif -#if defined(CONFIG_SPI_MASTER) - case SND_SOC_SPI: - ret = spi_write(codec->control_data, data, len); - break; -#endif - default: - BUG(); + if (codec->cache_only) { + codec->cache_sync = 1; + return 0; } - if (ret == len) + ret = codec->hw_write(codec->control_data, data, 4); + if (ret == 4) return 0; if (ret < 0) return ret; @@ -331,40 +615,79 @@ static int snd_soc_hw_bulk_write_raw(struct snd_soc_codec *codec, unsigned int r return -EIO; } +#if defined(CONFIG_SPI_MASTER) +static int snd_soc_16_16_spi_write(void *control_data, const char *data, + int len) +{ + struct spi_device *spi = control_data; + struct spi_transfer t; + struct spi_message m; + u8 msg[4]; + + if (len <= 0) + return 0; + + msg[0] = data[0]; + msg[1] = data[1]; + msg[2] = data[2]; + msg[3] = data[3]; + + spi_message_init(&m); + memset(&t, 0, sizeof t); + + t.tx_buf = &msg[0]; + t.len = len; + + spi_message_add_tail(&t, &m); + spi_sync(spi, &m); + + return len; +} +#else +#define snd_soc_16_16_spi_write NULL +#endif + static struct { int addr_bits; int data_bits; int (*write)(struct snd_soc_codec *codec, unsigned int, unsigned int); + int (*spi_write)(void *, const char *, int); unsigned int (*read)(struct snd_soc_codec *, unsigned int); unsigned int (*i2c_read)(struct snd_soc_codec *, unsigned int); } io_types[] = { { .addr_bits = 4, .data_bits = 12, .write = snd_soc_4_12_write, .read = snd_soc_4_12_read, + .spi_write = snd_soc_4_12_spi_write, }, { .addr_bits = 7, .data_bits = 9, .write = snd_soc_7_9_write, .read = snd_soc_7_9_read, + .spi_write = snd_soc_7_9_spi_write, }, { .addr_bits = 8, .data_bits = 8, .write = snd_soc_8_8_write, .read = snd_soc_8_8_read, .i2c_read = snd_soc_8_8_read_i2c, + .spi_write = snd_soc_8_8_spi_write, }, { .addr_bits = 8, .data_bits = 16, .write = snd_soc_8_16_write, .read = snd_soc_8_16_read, .i2c_read = snd_soc_8_16_read_i2c, + .spi_write = snd_soc_8_16_spi_write, }, { .addr_bits = 16, .data_bits = 8, .write = snd_soc_16_8_write, .read = snd_soc_16_8_read, .i2c_read = snd_soc_16_8_read_i2c, + .spi_write = snd_soc_16_8_spi_write, }, { .addr_bits = 16, .data_bits = 16, .write = snd_soc_16_16_write, .read = snd_soc_16_16_read, .i2c_read = snd_soc_16_16_read_i2c, + .spi_write = snd_soc_16_16_spi_write, }, }; @@ -372,6 +695,7 @@ static struct { * snd_soc_codec_set_cache_io: Set up standard I/O functions. * * @codec: CODEC to configure. + * @type: Type of cache. * @addr_bits: Number of bits of register address data. * @data_bits: Number of bits of data per register. * @control: Control bus used. @@ -406,9 +730,11 @@ int snd_soc_codec_set_cache_io(struct snd_soc_codec *codec, codec->write = io_types[i].write; codec->read = io_types[i].read; - codec->bulk_write_raw = snd_soc_hw_bulk_write_raw; switch (control) { + case SND_SOC_CUSTOM: + break; + case SND_SOC_I2C: #if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE)) codec->hw_write = (hw_write_t)i2c_master_send; @@ -422,9 +748,8 @@ int snd_soc_codec_set_cache_io(struct snd_soc_codec *codec, break; case SND_SOC_SPI: -#ifdef CONFIG_SPI_MASTER - codec->hw_write = do_spi_write; -#endif + if (io_types[i].spi_write) + codec->hw_write = io_types[i].spi_write; codec->control_data = container_of(codec->dev, struct spi_device, @@ -436,52 +761,6 @@ int snd_soc_codec_set_cache_io(struct snd_soc_codec *codec, } EXPORT_SYMBOL_GPL(snd_soc_codec_set_cache_io); -static bool snd_soc_set_cache_val(void *base, unsigned int idx, - unsigned int val, unsigned int word_size) -{ - switch (word_size) { - case 1: { - u8 *cache = base; - if (cache[idx] == val) - return true; - cache[idx] = val; - break; - } - case 2: { - u16 *cache = base; - if (cache[idx] == val) - return true; - cache[idx] = val; - break; - } - default: - BUG(); - } - return false; -} - -static unsigned int snd_soc_get_cache_val(const void *base, unsigned int idx, - unsigned int word_size) -{ - if (!base) - return -1; - - switch (word_size) { - case 1: { - const u8 *cache = base; - return cache[idx]; - } - case 2: { - const u16 *cache = base; - return cache[idx]; - } - default: - BUG(); - } - /* unreachable */ - return -1; -} - struct snd_soc_rbtree_node { struct rb_node node; unsigned int reg; @@ -553,14 +832,10 @@ static int snd_soc_rbtree_cache_sync(struct snd_soc_codec *codec) rbnode = rb_entry(node, struct snd_soc_rbtree_node, node); if (rbnode->value == rbnode->defval) continue; - WARN_ON(codec->writable_register && - codec->writable_register(codec, rbnode->reg)); 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", @@ -649,12 +924,7 @@ static int snd_soc_rbtree_cache_exit(struct snd_soc_codec *codec) static int snd_soc_rbtree_cache_init(struct snd_soc_codec *codec) { - struct snd_soc_rbtree_node *rbtree_node; struct snd_soc_rbtree_ctx *rbtree_ctx; - unsigned int val; - unsigned int word_size; - int i; - int ret; codec->reg_cache = kmalloc(sizeof *rbtree_ctx, GFP_KERNEL); if (!codec->reg_cache) @@ -666,25 +936,53 @@ static int snd_soc_rbtree_cache_init(struct snd_soc_codec *codec) if (!codec->reg_def_copy) return 0; - /* - * populate the rbtree with the initialized registers. All other - * registers will be inserted when they are first modified. - */ - word_size = codec->driver->reg_word_size; - for (i = 0; i < codec->driver->reg_cache_size; ++i) { - val = snd_soc_get_cache_val(codec->reg_def_copy, i, word_size); - if (!val) - continue; - rbtree_node = kzalloc(sizeof *rbtree_node, GFP_KERNEL); - if (!rbtree_node) { - ret = -ENOMEM; - snd_soc_cache_exit(codec); - break; - } - rbtree_node->reg = i; - rbtree_node->value = val; - rbtree_node->defval = val; - snd_soc_rbtree_insert(&rbtree_ctx->root, rbtree_node); +/* + * populate the rbtree with the initialized registers. All other + * registers will be inserted into the tree when they are first written. + * + * The reasoning behind this, is that we need to step through and + * dereference the cache in u8/u16 increments without sacrificing + * portability. This could also be done using memcpy() but that would + * be slightly more cryptic. + */ +#define snd_soc_rbtree_populate(cache) \ +({ \ + int ret, i; \ + struct snd_soc_rbtree_node *rbtree_node; \ + \ + ret = 0; \ + cache = codec->reg_def_copy; \ + for (i = 0; i < codec->driver->reg_cache_size; ++i) { \ + if (!cache[i]) \ + continue; \ + rbtree_node = kzalloc(sizeof *rbtree_node, GFP_KERNEL); \ + if (!rbtree_node) { \ + ret = -ENOMEM; \ + snd_soc_cache_exit(codec); \ + break; \ + } \ + rbtree_node->reg = i; \ + rbtree_node->value = cache[i]; \ + rbtree_node->defval = cache[i]; \ + snd_soc_rbtree_insert(&rbtree_ctx->root, \ + rbtree_node); \ + } \ + ret; \ +}) + + switch (codec->driver->reg_word_size) { + case 1: { + const u8 *cache; + + return snd_soc_rbtree_populate(cache); + } + case 2: { + const u16 *cache; + + return snd_soc_rbtree_populate(cache); + } + default: + BUG(); } return 0; @@ -782,28 +1080,34 @@ static inline int snd_soc_lzo_get_blkindex(struct snd_soc_codec *codec, unsigned int reg) { const struct snd_soc_codec_driver *codec_drv; + size_t reg_size; codec_drv = codec->driver; + reg_size = codec_drv->reg_cache_size * codec_drv->reg_word_size; return (reg * codec_drv->reg_word_size) / - DIV_ROUND_UP(codec->reg_size, snd_soc_lzo_block_count()); + DIV_ROUND_UP(reg_size, snd_soc_lzo_block_count()); } static inline int snd_soc_lzo_get_blkpos(struct snd_soc_codec *codec, unsigned int reg) { const struct snd_soc_codec_driver *codec_drv; + size_t reg_size; codec_drv = codec->driver; - return reg % (DIV_ROUND_UP(codec->reg_size, snd_soc_lzo_block_count()) / + reg_size = codec_drv->reg_cache_size * codec_drv->reg_word_size; + return reg % (DIV_ROUND_UP(reg_size, snd_soc_lzo_block_count()) / codec_drv->reg_word_size); } static inline int snd_soc_lzo_get_blksize(struct snd_soc_codec *codec) { const struct snd_soc_codec_driver *codec_drv; + size_t reg_size; codec_drv = codec->driver; - return DIV_ROUND_UP(codec->reg_size, snd_soc_lzo_block_count()); + reg_size = codec_drv->reg_cache_size * codec_drv->reg_word_size; + return DIV_ROUND_UP(reg_size, snd_soc_lzo_block_count()); } static int snd_soc_lzo_cache_sync(struct snd_soc_codec *codec) @@ -815,14 +1119,10 @@ static int snd_soc_lzo_cache_sync(struct snd_soc_codec *codec) lzo_blocks = codec->reg_cache; for_each_set_bit(i, lzo_blocks[0]->sync_bmp, lzo_blocks[0]->sync_bmp_nbits) { - WARN_ON(codec->writable_register && - codec->writable_register(codec, i)); 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", @@ -865,10 +1165,29 @@ static int snd_soc_lzo_cache_write(struct snd_soc_codec *codec, } /* write the new value to the cache */ - if (snd_soc_set_cache_val(lzo_block->dst, blkpos, value, - codec->driver->reg_word_size)) { - kfree(lzo_block->dst); - goto out; + switch (codec->driver->reg_word_size) { + case 1: { + u8 *cache; + cache = lzo_block->dst; + if (cache[blkpos] == value) { + kfree(lzo_block->dst); + goto out; + } + cache[blkpos] = value; + } + break; + case 2: { + u16 *cache; + cache = lzo_block->dst; + if (cache[blkpos] == value) { + kfree(lzo_block->dst); + goto out; + } + cache[blkpos] = value; + } + break; + default: + BUG(); } /* prepare the source to be the decompressed block */ @@ -922,10 +1241,25 @@ static int snd_soc_lzo_cache_read(struct snd_soc_codec *codec, /* decompress the block */ ret = snd_soc_lzo_decompress_cache_block(codec, lzo_block); - if (ret >= 0) + if (ret >= 0) { /* fetch the value from the cache */ - *value = snd_soc_get_cache_val(lzo_block->dst, blkpos, - codec->driver->reg_word_size); + switch (codec->driver->reg_word_size) { + case 1: { + u8 *cache; + cache = lzo_block->dst; + *value = cache[blkpos]; + } + break; + case 2: { + u16 *cache; + cache = lzo_block->dst; + *value = cache[blkpos]; + } + break; + default: + BUG(); + } + } kfree(lzo_block->dst); /* restore the pointer and length of the compressed block */ @@ -967,7 +1301,7 @@ static int snd_soc_lzo_cache_exit(struct snd_soc_codec *codec) static int snd_soc_lzo_cache_init(struct snd_soc_codec *codec) { struct snd_soc_lzo_ctx **lzo_blocks; - size_t bmp_size; + size_t reg_size, bmp_size; const struct snd_soc_codec_driver *codec_drv; int ret, tofree, i, blksize, blkcount; const char *p, *end; @@ -975,6 +1309,7 @@ static int snd_soc_lzo_cache_init(struct snd_soc_codec *codec) ret = 0; codec_drv = codec->driver; + reg_size = codec_drv->reg_cache_size * codec_drv->reg_word_size; /* * If we have not been given a default register cache @@ -986,7 +1321,8 @@ static int snd_soc_lzo_cache_init(struct snd_soc_codec *codec) tofree = 1; if (!codec->reg_def_copy) { - codec->reg_def_copy = kzalloc(codec->reg_size, GFP_KERNEL); + codec->reg_def_copy = kzalloc(reg_size, + GFP_KERNEL); if (!codec->reg_def_copy) return -ENOMEM; } @@ -1034,7 +1370,7 @@ static int snd_soc_lzo_cache_init(struct snd_soc_codec *codec) blksize = snd_soc_lzo_get_blksize(codec); p = codec->reg_def_copy; - end = codec->reg_def_copy + codec->reg_size; + end = codec->reg_def_copy + reg_size; /* compress the register map and fill the lzo blocks */ for (i = 0; i < blkcount; ++i, p += blksize) { lzo_blocks[i]->src = p; @@ -1075,15 +1411,31 @@ static int snd_soc_flat_cache_sync(struct snd_soc_codec *codec) codec_drv = codec->driver; for (i = 0; i < codec_drv->reg_cache_size; ++i) { - WARN_ON(codec->writable_register && - codec->writable_register(codec, i)); ret = snd_soc_cache_read(codec, i, &val); if (ret) return ret; - if (codec->reg_def_copy) - if (snd_soc_get_cache_val(codec->reg_def_copy, - i, codec_drv->reg_word_size) == val) - continue; + if (codec_drv->reg_cache_default) { + switch (codec_drv->reg_word_size) { + case 1: { + const u8 *cache; + + cache = codec_drv->reg_cache_default; + if (cache[i] == val) + continue; + } + break; + case 2: { + const u16 *cache; + + cache = codec_drv->reg_cache_default; + if (cache[i] == val) + continue; + } + break; + default: + BUG(); + } + } ret = snd_soc_write(codec, i, val); if (ret) return ret; @@ -1096,16 +1448,50 @@ static int snd_soc_flat_cache_sync(struct snd_soc_codec *codec) static int snd_soc_flat_cache_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int value) { - snd_soc_set_cache_val(codec->reg_cache, reg, value, - codec->driver->reg_word_size); + switch (codec->driver->reg_word_size) { + case 1: { + u8 *cache; + + cache = codec->reg_cache; + cache[reg] = value; + } + break; + case 2: { + u16 *cache; + + cache = codec->reg_cache; + cache[reg] = value; + } + break; + default: + BUG(); + } + return 0; } static int snd_soc_flat_cache_read(struct snd_soc_codec *codec, unsigned int reg, unsigned int *value) { - *value = snd_soc_get_cache_val(codec->reg_cache, reg, - codec->driver->reg_word_size); + switch (codec->driver->reg_word_size) { + case 1: { + u8 *cache; + + cache = codec->reg_cache; + *value = cache[reg]; + } + break; + case 2: { + u16 *cache; + + cache = codec->reg_cache; + *value = cache[reg]; + } + break; + default: + BUG(); + } + return 0; } @@ -1121,14 +1507,24 @@ static int snd_soc_flat_cache_exit(struct snd_soc_codec *codec) static int snd_soc_flat_cache_init(struct snd_soc_codec *codec) { const struct snd_soc_codec_driver *codec_drv; + size_t reg_size; codec_drv = codec->driver; + reg_size = codec_drv->reg_cache_size * codec_drv->reg_word_size; + + /* + * for flat compression, we don't need to keep a copy of the + * original defaults register cache as it will definitely not + * be marked as __devinitconst + */ + kfree(codec->reg_def_copy); + codec->reg_def_copy = NULL; - if (codec->reg_def_copy) - codec->reg_cache = kmemdup(codec->reg_def_copy, - codec->reg_size, GFP_KERNEL); + if (codec_drv->reg_cache_default) + codec->reg_cache = kmemdup(codec_drv->reg_cache_default, + reg_size, GFP_KERNEL); else - codec->reg_cache = kzalloc(codec->reg_size, GFP_KERNEL); + codec->reg_cache = kzalloc(reg_size, GFP_KERNEL); if (!codec->reg_cache) return -ENOMEM; @@ -1193,7 +1589,7 @@ int snd_soc_cache_init(struct snd_soc_codec *codec) codec->cache_ops->name, codec->name); return codec->cache_ops->init(codec); } - return -ENOSYS; + return -EINVAL; } /* @@ -1208,7 +1604,7 @@ int snd_soc_cache_exit(struct snd_soc_codec *codec) codec->cache_ops->name, codec->name); return codec->cache_ops->exit(codec); } - return -ENOSYS; + return -EINVAL; } /** @@ -1232,7 +1628,7 @@ int snd_soc_cache_read(struct snd_soc_codec *codec, } mutex_unlock(&codec->cache_rw_mutex); - return -ENOSYS; + return -EINVAL; } EXPORT_SYMBOL_GPL(snd_soc_cache_read); @@ -1257,7 +1653,7 @@ int snd_soc_cache_write(struct snd_soc_codec *codec, } mutex_unlock(&codec->cache_rw_mutex); - return -ENOSYS; + return -EINVAL; } EXPORT_SYMBOL_GPL(snd_soc_cache_write); @@ -1273,91 +1669,21 @@ 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 || !codec->cache_ops->sync) - return -ENOSYS; + 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); + ret = codec->cache_ops->sync(codec); + if (!ret) + codec->cache_sync = 0; + return ret; + } - if (codec->cache_ops->name) - name = codec->cache_ops->name; - else - name = "unknown"; - - 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; + return -EINVAL; } EXPORT_SYMBOL_GPL(snd_soc_cache_sync); - -static int snd_soc_get_reg_access_index(struct snd_soc_codec *codec, - unsigned int reg) -{ - const struct snd_soc_codec_driver *codec_drv; - unsigned int min, max, index; - - codec_drv = codec->driver; - min = 0; - max = codec_drv->reg_access_size - 1; - do { - index = (min + max) / 2; - if (codec_drv->reg_access_default[index].reg == reg) - return index; - if (codec_drv->reg_access_default[index].reg < reg) - min = index + 1; - else - max = index; - } while (min <= max); - return -1; -} - -int snd_soc_default_volatile_register(struct snd_soc_codec *codec, - unsigned int reg) -{ - int index; - - if (reg >= codec->driver->reg_cache_size) - return 1; - index = snd_soc_get_reg_access_index(codec, reg); - if (index < 0) - return 0; - return codec->driver->reg_access_default[index].vol; -} -EXPORT_SYMBOL_GPL(snd_soc_default_volatile_register); - -int snd_soc_default_readable_register(struct snd_soc_codec *codec, - unsigned int reg) -{ - int index; - - if (reg >= codec->driver->reg_cache_size) - return 1; - index = snd_soc_get_reg_access_index(codec, reg); - if (index < 0) - return 0; - return codec->driver->reg_access_default[index].read; -} -EXPORT_SYMBOL_GPL(snd_soc_default_readable_register); - -int snd_soc_default_writable_register(struct snd_soc_codec *codec, - unsigned int reg) -{ - int index; - - if (reg >= codec->driver->reg_cache_size) - return 1; - index = snd_soc_get_reg_access_index(codec, reg); - if (index < 0) - return 0; - return codec->driver->reg_access_default[index].write; -} -EXPORT_SYMBOL_GPL(snd_soc_default_writable_register); diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 440743b..7e9d63c 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -32,7 +32,6 @@ #include #include #include -#include #include #include #include @@ -48,6 +47,8 @@ #include +static inline void dapm_clear_walk(struct snd_soc_dapm_context *dapm); + /* dapm power sequences - make this per codec in the future */ static int dapm_up_seq[] = { [snd_soc_dapm_pre] = 0, @@ -124,19 +125,256 @@ static inline struct snd_soc_dapm_widget *dapm_cnew_widget( return kmemdup(_widget, sizeof(*_widget), GFP_KERNEL); } +static int soc_widget_read(struct snd_soc_dapm_widget *w, int reg) +{ + if (w->codec) + return snd_soc_read(w->codec, reg); + else if (w->platform) + return snd_soc_platform_read(w->platform, reg); + return 0; +} + +static int soc_widget_write(struct snd_soc_dapm_widget *w, int reg, int val) +{ + if (w->codec) + return snd_soc_write(w->codec, reg, val); + else if (w->platform) + return snd_soc_platform_write(w->platform, reg, val); + return 0; +} + +int soc_widget_update_bits(struct snd_soc_dapm_widget *w, unsigned short reg, + unsigned int mask, unsigned int value) +{ + int change; + unsigned int old, new; + + old = soc_widget_read(w, reg); + new = (old & ~mask) | value; + change = old != new; +// if (change) + soc_widget_write(w, reg, new); + + return change; +} + +int soc_widget_test_bits(struct snd_soc_dapm_widget *w, unsigned short reg, + unsigned int mask, unsigned int value) +{ + int change; + unsigned int old, new; + + old = soc_widget_read(w, reg); + new = (old & ~mask) | value; + change = old != new; + + return change; +} + +#define MAX_HOPS 16 + +static void scenario_clear_paths(struct snd_soc_dapm_context *dapm) +{ + struct snd_soc_dapm_path *p; + struct snd_soc_dapm_widget *w; + struct list_head *l; + + list_for_each(l, &dapm->card->paths) { + p = list_entry(l, struct snd_soc_dapm_path, list); + p->length = 0; + } + list_for_each(l, &dapm->card->widgets) { + w = list_entry(l, struct snd_soc_dapm_widget, list); + w->hops = 0; + } + dapm_clear_walk(dapm); +} + +/* + * find all the paths between source and sink + */ +static int scenario_find_playback_paths(struct snd_soc_dapm_context *dapm, + struct snd_soc_dapm_widget *source, struct snd_soc_dapm_widget *sink, + int hops) +{ + struct list_head *lp; + struct snd_soc_dapm_path *path; + int dist = 0; + + if (hops > MAX_HOPS) + return 0; + + if (source == sink) { + dev_dbg(dapm->dev,"** found route with length %d\n", hops); + dapm->num_valid_paths++; + return 1; + } + + if (source->hops && source->hops <= hops) + return 0; + source->hops = hops; + + /* + * check all the output paths on this source widget + * by walking from source to sink + */ + list_for_each(lp, &source->sinks) { + path = list_entry(lp, struct snd_soc_dapm_path, list_source); + + dev_dbg(dapm->dev,"%d:try source %s path %s to %s len %d connect %d\n", + hops, source->name, path->name, path->sink->name, + path->length, path->connect); + + /* been here before ? */ + if (path->length && path->length <= hops) + continue; + + /* check down the next path if connected */ + if (path->sink && path->connect && + scenario_find_playback_paths(dapm, path->sink, sink, hops + 1)) { + path->length = hops; + if (!dist || dist > path->length) + dist = path->length; + } + } + + return dist; +} + +static int scenario_find_capture_paths (struct snd_soc_dapm_context *dapm, + struct snd_soc_dapm_widget *source, struct snd_soc_dapm_widget *sink, + int hops) +{ + struct list_head *lp; + struct snd_soc_dapm_path *path; + int dist = 0; + + if (hops > MAX_HOPS) + return 0; + + if (source == sink) { + dev_dbg(dapm->dev,"** found route with length %d\n", hops); + dapm->num_valid_paths++; + return 1; + } + + if (sink->hops && sink->hops <= hops) + return 0; + sink->hops = hops; + + /* + * check all the output paths on this source widget + * by walking from sink to source + */ + list_for_each(lp, &sink->sources) { + path = list_entry(lp, struct snd_soc_dapm_path, list_sink); + + dev_dbg(dapm->dev,"%d:try sink %s path %s to %s len %d connect %d\n", + hops, sink->name, path->name, path->source->name, + path->length, path->connect); + + /* been here before ? */ + if (path->length && path->length <= hops) + continue; + + /* check down the next path if connected */ + if (path->source && path->connect && + scenario_find_capture_paths(dapm, source, path->source, hops + 1)) { + path->length = hops; + if (!dist || dist > path->length) + dist = path->length; + } + } + + return dist; +} + +/* + * traverse the tree from sink to source via the shortest path + */ +static int scenario_get_playback_paths(struct snd_soc_dapm_context *dapm, + struct snd_soc_dapm_widget *source, struct snd_soc_dapm_widget *sink) +{ + dapm->num_valid_paths = 0; + + dev_dbg(dapm->dev, "check playback path from %s to %s\n", + source->name, sink->name); + scenario_find_playback_paths(dapm, source, sink, 1); + return dapm->num_valid_paths; +} + +static int scenario_get_capture_paths(struct snd_soc_dapm_context *dapm, + struct snd_soc_dapm_widget *source, struct snd_soc_dapm_widget *sink) +{ + dapm->num_valid_paths = 0; + dev_dbg(dapm->dev, "check capture path to %s from %s\n", + source->name, sink->name); + scenario_find_capture_paths(dapm, sink, source, 1); + return dapm->num_valid_paths; +} + +/** + * snd_soc_scenario_set_path - set new scenario path + * @codec: the soc codec + * @scenario: the sceanrio path + * + * Sets up a new audio path within the audio susbsytem. + * + * Returns 0 for success. + */ +int snd_soc_scenario_set_path(struct snd_soc_dapm_context *dapm, + const char *source_name, const char *sink_name, int stream) +{ + struct snd_soc_dapm_widget *sink = NULL, *source = NULL; + struct list_head *l = NULL; + int routes; + + /* find source */ + list_for_each(l, &dapm->card->widgets) { + struct snd_soc_dapm_widget *w; + w = list_entry(l, struct snd_soc_dapm_widget, list); + + if(!source && !strncmp(w->name, source_name, 16)) { + source = w; + continue; + } + if(!sink && !strncmp(w->name, sink_name, 16)) { + sink = w; + } + } + + if(!source) { + printk(KERN_ERR "soc: invalid scenario source %s\n", source_name); + return -EINVAL; + } + if(!sink) { + printk(KERN_ERR "soc: invalid scenario sink %s\n", sink_name); + return -EINVAL; + } + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + routes = scenario_get_playback_paths(dapm, source, sink); + else + routes = scenario_get_capture_paths(dapm, source, sink); + scenario_clear_paths(dapm); + + return routes; +} +EXPORT_SYMBOL_GPL(snd_soc_scenario_set_path); + /** * snd_soc_dapm_set_bias_level - set the bias level for the system - * @dapm: DAPM context + * @card: audio device * @level: level to configure * * Configure the bias (power) levels for the SoC audio device. * * Returns 0 for success else error. */ -static int snd_soc_dapm_set_bias_level(struct snd_soc_dapm_context *dapm, +static int snd_soc_dapm_set_bias_level(struct snd_soc_card *card, + struct snd_soc_dapm_context *dapm, enum snd_soc_bias_level level) { - struct snd_soc_card *card = dapm->card; int ret = 0; switch (level) { @@ -187,14 +425,14 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w, case snd_soc_dapm_mixer_named_ctl: { int val; struct soc_mixer_control *mc = (struct soc_mixer_control *) - w->kcontrol_news[i].private_value; + w->kcontrols[i].private_value; unsigned int reg = mc->reg; unsigned int shift = mc->shift; int max = mc->max; unsigned int mask = (1 << fls(max)) - 1; unsigned int invert = mc->invert; - val = snd_soc_read(w->codec, reg); + val = soc_widget_read(w, reg); val = (val >> shift) & mask; if ((invert && !val) || (!invert && val)) @@ -204,13 +442,12 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w, } break; case snd_soc_dapm_mux: { - struct soc_enum *e = (struct soc_enum *) - w->kcontrol_news[i].private_value; + struct soc_enum *e = (struct soc_enum *)w->kcontrols[i].private_value; int val, item, bitmask; for (bitmask = 1; bitmask < e->max; bitmask <<= 1) ; - val = snd_soc_read(w->codec, e->reg); + val = soc_widget_read(w, e->reg); item = (val >> e->shift_l) & (bitmask - 1); p->connect = 0; @@ -221,8 +458,7 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w, } break; case snd_soc_dapm_virt_mux: { - struct soc_enum *e = (struct soc_enum *) - w->kcontrol_news[i].private_value; + struct soc_enum *e = (struct soc_enum *)w->kcontrols[i].private_value; p->connect = 0; /* since a virtual mux has no backing registers to @@ -237,10 +473,10 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w, break; case snd_soc_dapm_value_mux: { struct soc_enum *e = (struct soc_enum *) - w->kcontrol_news[i].private_value; + w->kcontrols[i].private_value; int val, item; - val = snd_soc_read(w->codec, e->reg); + val = soc_widget_read(w, e->reg); val = (val >> e->shift_l) & e->mask; for (item = 0; item < e->max; item++) { if (val == e->values[item]) @@ -312,11 +548,11 @@ static int dapm_connect_mixer(struct snd_soc_dapm_context *dapm, /* search for mixer kcontrol */ for (i = 0; i < dest->num_kcontrols; i++) { - if (!strcmp(control_name, dest->kcontrol_news[i].name)) { + if (!strcmp(control_name, dest->kcontrols[i].name)) { list_add(&path->list, &dapm->card->paths); list_add(&path->list_sink, &dest->sources); list_add(&path->list_source, &src->sinks); - path->name = dest->kcontrol_news[i].name; + path->name = dest->kcontrols[i].name; dapm_set_path_status(dest, path, i); return 0; } @@ -324,52 +560,59 @@ static int dapm_connect_mixer(struct snd_soc_dapm_context *dapm, return -ENODEV; } -static int dapm_is_shared_kcontrol(struct snd_soc_dapm_context *dapm, - struct snd_soc_dapm_widget *kcontrolw, - const struct snd_kcontrol_new *kcontrol_new, - struct snd_kcontrol **kcontrol) +/* update dapm codec register bits */ +static int dapm_update_bits(struct snd_soc_dapm_widget *widget) { - struct snd_soc_dapm_widget *w; - int i; + int change, power; + unsigned int old, new; + struct snd_soc_dapm_context *dapm = widget->dapm; + struct snd_soc_card *card = dapm->card; - *kcontrol = NULL; + /* check for valid widgets */ + if (widget->reg < 0 || widget->id == snd_soc_dapm_input || + widget->id == snd_soc_dapm_output || + widget->id == snd_soc_dapm_hp || + widget->id == snd_soc_dapm_mic || + widget->id == snd_soc_dapm_line || + widget->id == snd_soc_dapm_spk) + return 0; - list_for_each_entry(w, &dapm->card->widgets, list) { - if (w == kcontrolw || w->dapm != kcontrolw->dapm) - continue; - for (i = 0; i < w->num_kcontrols; i++) { - if (&w->kcontrol_news[i] == kcontrol_new) { - if (w->kcontrols) - *kcontrol = w->kcontrols[i]; - return 1; - } - } - } + power = widget->power; + if (widget->invert) + power = (power ? 0:1); - return 0; + old = soc_widget_read(widget, widget->reg); + new = (old & ~(0x1 << widget->shift)) | (power << widget->shift); + + change = old != new; + if (change) { + pop_dbg(dapm->dev, card->pop_time, + "pop test %s : %s in %d ms\n", + widget->name, widget->power ? "on" : "off", + card->pop_time); + pop_wait(card->pop_time); + soc_widget_write(widget, widget->reg, new); + } + dev_dbg(dapm->dev, "reg %x old %x new %x change %d\n", widget->reg, + old, new, change); + return change; } /* create new dapm mixer control */ -static int dapm_new_mixer(struct snd_soc_dapm_widget *w) +static int dapm_new_mixer(struct snd_soc_dapm_context *dapm, + struct snd_soc_dapm_widget *w) { - struct snd_soc_dapm_context *dapm = w->dapm; int i, ret = 0; - size_t name_len, prefix_len; + size_t name_len; struct snd_soc_dapm_path *path; - struct snd_card *card = dapm->card->snd_card; - const char *prefix; - struct snd_soc_dapm_widget_list *wlist; - size_t wlistsize; + struct snd_card *card; if (dapm->codec) - prefix = dapm->codec->name_prefix; - else - prefix = NULL; - - if (prefix) - prefix_len = strlen(prefix) + 1; - else - prefix_len = 0; + card = dapm->codec->card->snd_card; + if (dapm->platform) + card = dapm->platform->card->snd_card; + if (!card) + return -ENODEV; /* add kcontrol */ for (i = 0; i < w->num_kcontrols; i++) { @@ -378,164 +621,93 @@ static int dapm_new_mixer(struct snd_soc_dapm_widget *w) list_for_each_entry(path, &w->sources, list_sink) { /* mixer/mux paths name must match control name */ - if (path->name != (char *)w->kcontrol_news[i].name) + if (path->name != (char*)w->kcontrols[i].name) continue; - wlistsize = sizeof(struct snd_soc_dapm_widget_list) + - sizeof(struct snd_soc_dapm_widget *), - wlist = kzalloc(wlistsize, GFP_KERNEL); - if (wlist == NULL) { - dev_err(dapm->dev, - "asoc: can't allocate widget list for %s\n", - w->name); - return -ENOMEM; - } - wlist->num_widgets = 1; - wlist->widgets[0] = w; - /* add dapm control with long name. * for dapm_mixer this is the concatenation of the * mixer and kcontrol name. * for dapm_mixer_named_ctl this is simply the * kcontrol name. */ - name_len = strlen(w->kcontrol_news[i].name) + 1; + name_len = strlen(w->kcontrols[i].name) + 1; if (w->id != snd_soc_dapm_mixer_named_ctl) name_len += 1 + strlen(w->name); path->long_name = kmalloc(name_len, GFP_KERNEL); - if (path->long_name == NULL) { - kfree(wlist); + if (path->long_name == NULL) return -ENOMEM; - } switch (w->id) { default: - /* The control will get a prefix from - * the control creation process but - * we're also using the same prefix - * for widgets so cut the prefix off - * the front of the widget name. - */ snprintf(path->long_name, name_len, "%s %s", - w->name + prefix_len, - w->kcontrol_news[i].name); + w->name, w->kcontrols[i].name); break; case snd_soc_dapm_mixer_named_ctl: snprintf(path->long_name, name_len, "%s", - w->kcontrol_news[i].name); + w->kcontrols[i].name); break; } path->long_name[name_len - 1] = '\0'; - path->kcontrol = snd_soc_cnew(&w->kcontrol_news[i], - wlist, path->long_name, - prefix); + path->kcontrol = snd_soc_cnew(&w->kcontrols[i], w, + path->long_name); ret = snd_ctl_add(card, path->kcontrol); if (ret < 0) { dev_err(dapm->dev, "asoc: failed to add dapm kcontrol %s: %d\n", path->long_name, ret); - kfree(wlist); kfree(path->long_name); path->long_name = NULL; return ret; } - w->kcontrols[i] = path->kcontrol; } } return ret; } /* create new dapm mux control */ -static int dapm_new_mux(struct snd_soc_dapm_widget *w) +static int dapm_new_mux(struct snd_soc_dapm_context *dapm, + struct snd_soc_dapm_widget *w) { - struct snd_soc_dapm_context *dapm = w->dapm; struct snd_soc_dapm_path *path = NULL; struct snd_kcontrol *kcontrol; - struct snd_card *card = dapm->card->snd_card; - const char *prefix; - size_t prefix_len; - int ret; - struct snd_soc_dapm_widget_list *wlist; - int shared, wlistentries; - size_t wlistsize; - char *name; - - if (w->num_kcontrols != 1) { - dev_err(dapm->dev, - "asoc: mux %s has incorrect number of controls\n", - w->name); - return -EINVAL; - } - - shared = dapm_is_shared_kcontrol(dapm, w, &w->kcontrol_news[0], - &kcontrol); - if (kcontrol) { - wlist = kcontrol->private_data; - wlistentries = wlist->num_widgets + 1; - } else { - wlist = NULL; - wlistentries = 1; - } - wlistsize = sizeof(struct snd_soc_dapm_widget_list) + - wlistentries * sizeof(struct snd_soc_dapm_widget *), - wlist = krealloc(wlist, wlistsize, GFP_KERNEL); - if (wlist == NULL) { - dev_err(dapm->dev, - "asoc: can't allocate widget list for %s\n", w->name); - return -ENOMEM; - } - wlist->num_widgets = wlistentries; - wlist->widgets[wlistentries - 1] = w; + struct snd_card *card; + int ret = 0; - if (!kcontrol) { - if (dapm->codec) - prefix = dapm->codec->name_prefix; - else - prefix = NULL; - - if (shared) { - name = w->kcontrol_news[0].name; - prefix_len = 0; - } else { - name = w->name; - if (prefix) - prefix_len = strlen(prefix) + 1; - else - prefix_len = 0; - } + if (dapm->codec) + card = dapm->codec->card->snd_card; + if (dapm->platform) + card = dapm->platform->card->snd_card; + if (!card) + return -ENODEV; - /* - * The control will get a prefix from the control creation - * process but we're also using the same prefix for widgets so - * cut the prefix off the front of the widget name. - */ - kcontrol = snd_soc_cnew(&w->kcontrol_news[0], wlist, - name + prefix_len, prefix); - ret = snd_ctl_add(card, kcontrol); - if (ret < 0) { - dev_err(dapm->dev, - "asoc: failed to add kcontrol %s\n", w->name); - kfree(wlist); - return ret; - } + if (!w->num_kcontrols) { + dev_err(dapm->dev, "asoc: mux %s has no controls\n", w->name); + return -EINVAL; } - kcontrol->private_data = wlist; + kcontrol = snd_soc_cnew(&w->kcontrols[0], w, w->name); + ret = snd_ctl_add(card, kcontrol); - w->kcontrols[0] = kcontrol; + if (ret < 0) + goto err; list_for_each_entry(path, &w->sources, list_sink) path->kcontrol = kcontrol; - return 0; + return ret; + +err: + dev_err(dapm->dev, "asoc: failed to add kcontrol %s\n", w->name); + return ret; } /* create new dapm volume control */ -static int dapm_new_pga(struct snd_soc_dapm_widget *w) +static int dapm_new_pga(struct snd_soc_dapm_context *dapm, + struct snd_soc_dapm_widget *w) { if (w->num_kcontrols) dev_err(w->dapm->dev, @@ -559,7 +731,17 @@ static inline void dapm_clear_walk(struct snd_soc_dapm_context *dapm) */ static int snd_soc_dapm_suspend_check(struct snd_soc_dapm_widget *widget) { - int level = snd_power_get_state(widget->dapm->card->snd_card); + struct snd_card *card; + int level; + + if (widget->codec) + card = widget->codec->card->snd_card; + else if (widget->platform) + card = widget->platform->card->snd_card; + else + return 0; + + level = snd_power_get_state(card); switch (level) { case SNDRV_CTL_POWER_D3hot: @@ -681,13 +863,64 @@ int dapm_reg_event(struct snd_soc_dapm_widget *w, else val = w->off_val; - snd_soc_update_bits(w->codec, -(w->reg + 1), + soc_widget_update_bits(w, -(w->reg + 1), w->mask << w->shift, val << w->shift); return 0; } EXPORT_SYMBOL_GPL(dapm_reg_event); +/* Standard power change method, used to apply power changes to most + * widgets. + */ +static int dapm_generic_apply_power(struct snd_soc_dapm_widget *w) +{ + int ret; + + /* call any power change event handlers */ + if (w->event) + dev_dbg(w->dapm->dev, "power %s event for %s flags %x\n", + w->power ? "on" : "off", + w->name, w->event_flags); + + /* power up pre event */ + if (w->power && w->event && + (w->event_flags & SND_SOC_DAPM_PRE_PMU)) { + ret = w->event(w, NULL, SND_SOC_DAPM_PRE_PMU); + if (ret < 0) + return ret; + } + + /* power down pre event */ + if (!w->power && w->event && + (w->event_flags & SND_SOC_DAPM_PRE_PMD)) { + ret = w->event(w, NULL, SND_SOC_DAPM_PRE_PMD); + if (ret < 0) + return ret; + } + + dapm_update_bits(w); + + /* power up post event */ + if (w->power && w->event && + (w->event_flags & SND_SOC_DAPM_POST_PMU)) { + ret = w->event(w, + NULL, SND_SOC_DAPM_POST_PMU); + if (ret < 0) + return ret; + } + + /* power down post event */ + if (!w->power && w->event && + (w->event_flags & SND_SOC_DAPM_POST_PMD)) { + ret = w->event(w, NULL, SND_SOC_DAPM_POST_PMD); + if (ret < 0) + return ret; + } + + return 0; +} + /* Generic check to see if a widget should be powered. */ static int dapm_generic_check_power(struct snd_soc_dapm_widget *w) @@ -741,15 +974,7 @@ static int dapm_supply_check_power(struct snd_soc_dapm_widget *w) !path->connected(path->source, path->sink)) continue; - if (!path->sink) - continue; - - if (path->sink->force) { - power = 1; - break; - } - - if (path->sink->power_check && + if (path->sink && path->sink->power_check && path->sink->power_check(path->sink)) { power = 1; break; @@ -763,23 +988,10 @@ 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, - bool power_up) + int sort[]) { - 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) @@ -791,12 +1003,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, - bool power_up) + int sort[]) { struct snd_soc_dapm_widget *w; list_for_each_entry(w, list, power_list) - if (dapm_seq_compare(new_widget, w, power_up) < 0) { + if (dapm_seq_compare(new_widget, w, sort) < 0) { list_add_tail(&new_widget->power_list, &w->power_list); return; } @@ -853,7 +1065,7 @@ static void dapm_seq_run_coalesced(struct snd_soc_dapm_context *dapm, struct list_head *pending) { struct snd_soc_card *card = dapm->card; - struct snd_soc_dapm_widget *w; + struct snd_soc_dapm_widget *w, *wf; int reg, power; unsigned int value = 0; unsigned int mask = 0; @@ -861,6 +1073,8 @@ static void dapm_seq_run_coalesced(struct snd_soc_dapm_context *dapm, reg = list_first_entry(pending, struct snd_soc_dapm_widget, power_list)->reg; + wf = list_first_entry(pending, struct snd_soc_dapm_widget, + power_list); list_for_each_entry(w, pending, power_list) { cur_mask = 1 << w->shift; @@ -889,7 +1103,7 @@ static void dapm_seq_run_coalesced(struct snd_soc_dapm_context *dapm, "pop test : Applying 0x%x/0x%x to %x in %dms\n", value, mask, reg, card->pop_time); pop_wait(card->pop_time); - snd_soc_update_bits(dapm->codec, reg, mask, value); + soc_widget_update_bits(wf, reg, mask, value); } list_for_each_entry(w, pending, power_list) { @@ -907,42 +1121,26 @@ 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, bool power_up) + struct list_head *list, int event, int sort[]) { 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, i; - int *sort; - - if (power_up) - sort = dapm_up_seq; - else - sort = dapm_down_seq; + int ret; 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->subseq != cur_subseq) { + w->dapm != cur_dapm) { 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; } @@ -974,10 +1172,19 @@ static void dapm_seq_run(struct snd_soc_dapm_context *dapm, NULL, SND_SOC_DAPM_POST_PMD); break; + case snd_soc_dapm_input: + case snd_soc_dapm_output: + case snd_soc_dapm_hp: + case snd_soc_dapm_mic: + case snd_soc_dapm_line: + case snd_soc_dapm_spk: + /* No register support currently */ + ret = dapm_generic_apply_power(w); + break; + 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); @@ -990,14 +1197,7 @@ static void dapm_seq_run(struct snd_soc_dapm_context *dapm, } 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); - } + dapm_seq_run_coalesced(dapm, &pending); } static void dapm_widget_update(struct snd_soc_dapm_context *dapm) @@ -1033,62 +1233,7 @@ static void dapm_widget_update(struct snd_soc_dapm_context *dapm) } } -/* Async callback run prior to DAPM sequences - brings to _PREPARE if - * they're changing state. - */ -static void dapm_pre_sequence_async(void *data, async_cookie_t cookie) -{ - struct snd_soc_dapm_context *d = data; - int ret; - - if (d->dev_power && d->bias_level == SND_SOC_BIAS_OFF) { - ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_STANDBY); - if (ret != 0) - dev_err(d->dev, - "Failed to turn on bias: %d\n", ret); - } - - /* If we're changing to all on or all off then prepare */ - if ((d->dev_power && d->bias_level == SND_SOC_BIAS_STANDBY) || - (!d->dev_power && d->bias_level == SND_SOC_BIAS_ON)) { - ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_PREPARE); - if (ret != 0) - dev_err(d->dev, - "Failed to prepare bias: %d\n", ret); - } -} - -/* Async callback run prior to DAPM sequences - brings to their final - * state. - */ -static void dapm_post_sequence_async(void *data, async_cookie_t cookie) -{ - struct snd_soc_dapm_context *d = data; - int ret; - - /* If we just powered the last thing off drop to standby bias */ - if (d->bias_level == SND_SOC_BIAS_PREPARE && !d->dev_power) { - ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_STANDBY); - if (ret != 0) - dev_err(d->dev, "Failed to apply standby bias: %d\n", - ret); - } - /* If we're in standby and can support bias off then do that */ - if (d->bias_level == SND_SOC_BIAS_STANDBY && d->idle_bias_off) { - ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_OFF); - if (ret != 0) - dev_err(d->dev, "Failed to turn off bias: %d\n", ret); - } - - /* If we just powered up then move to active bias */ - if (d->bias_level == SND_SOC_BIAS_PREPARE && d->dev_power) { - ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_ON); - if (ret != 0) - dev_err(d->dev, "Failed to apply active bias: %d\n", - ret); - } -} /* * Scan each dapm widget for complete audio path. @@ -1101,30 +1246,38 @@ static void dapm_post_sequence_async(void *data, async_cookie_t cookie) */ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event) { - struct snd_soc_card *card = dapm->card; + struct snd_soc_card *card = NULL; struct snd_soc_dapm_widget *w; struct snd_soc_dapm_context *d; LIST_HEAD(up_list); LIST_HEAD(down_list); - LIST_HEAD(async_domain); + int ret = 0; int power; + if (dapm->codec) + card = dapm->codec->card; + if (dapm->platform) + card = dapm->platform->card; + if (!card) + return -ENODEV; + trace_snd_soc_dapm_start(card); list_for_each_entry(d, &card->dapm_list, list) - if (d->n_widgets || d->codec == NULL) + if (d->n_widgets) d->dev_power = 0; /* Check which widgets we need to power and store them in * lists indicating if they should be powered up or down. */ + mutex_lock(&card->dapm_mutex); list_for_each_entry(w, &card->widgets, list) { switch (w->id) { case snd_soc_dapm_pre: - dapm_seq_insert(w, &down_list, false); + dapm_seq_insert(w, &down_list, dapm_down_seq); break; case snd_soc_dapm_post: - dapm_seq_insert(w, &up_list, true); + dapm_seq_insert(w, &up_list, dapm_up_seq); break; default: @@ -1144,14 +1297,15 @@ 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, true); + dapm_seq_insert(w, &up_list, dapm_up_seq); else - dapm_seq_insert(w, &down_list, false); + dapm_seq_insert(w, &down_list, dapm_down_seq); w->power = power; break; } } + mutex_unlock(&card->dapm_mutex); /* If there are no DAPM widgets then try to figure out power from the * event type. @@ -1184,34 +1338,65 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event) } } - /* Force all contexts in the card to the same bias state */ - power = 0; - list_for_each_entry(d, &card->dapm_list, list) - if (d->dev_power) - power = 1; - list_for_each_entry(d, &card->dapm_list, list) - d->dev_power = power; - + list_for_each_entry(d, &dapm->card->dapm_list, list) { + if (d->dev_power && d->bias_level == SND_SOC_BIAS_OFF) { + ret = snd_soc_dapm_set_bias_level(card, d, + SND_SOC_BIAS_STANDBY); + if (ret != 0) + dev_err(d->dev, + "Failed to turn on bias: %d\n", ret); + } - /* Run all the bias changes in parallel */ - list_for_each_entry(d, &dapm->card->dapm_list, list) - async_schedule_domain(dapm_pre_sequence_async, d, - &async_domain); - async_synchronize_full_domain(&async_domain); + /* If we're changing to all on or all off then prepare */ + if ((d->dev_power && d->bias_level == SND_SOC_BIAS_STANDBY) || + (!d->dev_power && d->bias_level == SND_SOC_BIAS_ON)) { + ret = snd_soc_dapm_set_bias_level(card, d, + SND_SOC_BIAS_PREPARE); + if (ret != 0) + dev_err(d->dev, + "Failed to prepare bias: %d\n", ret); + } + } /* Power down widgets first; try to avoid amplifying pops. */ - dapm_seq_run(dapm, &down_list, event, false); + dapm_seq_run(dapm, &down_list, event, dapm_down_seq); dapm_widget_update(dapm); /* Now power up. */ - dapm_seq_run(dapm, &up_list, event, true); + dapm_seq_run(dapm, &up_list, event, dapm_up_seq); + + list_for_each_entry(d, &dapm->card->dapm_list, list) { + /* If we just powered the last thing off drop to standby bias */ + if (d->bias_level == SND_SOC_BIAS_PREPARE && !d->dev_power) { + ret = snd_soc_dapm_set_bias_level(card, d, + SND_SOC_BIAS_STANDBY); + if (ret != 0) + dev_err(d->dev, + "Failed to apply standby bias: %d\n", + ret); + } - /* Run all the bias changes in parallel */ - list_for_each_entry(d, &dapm->card->dapm_list, list) - async_schedule_domain(dapm_post_sequence_async, d, - &async_domain); - async_synchronize_full_domain(&async_domain); + /* If we're in standby and can support bias off then do that */ + if (d->bias_level == SND_SOC_BIAS_STANDBY && + d->idle_bias_off) { + ret = snd_soc_dapm_set_bias_level(card, d, + SND_SOC_BIAS_OFF); + if (ret != 0) + dev_err(d->dev, + "Failed to turn off bias: %d\n", ret); + } + + /* If we just powered up then move to active bias */ + if (d->bias_level == SND_SOC_BIAS_PREPARE && d->dev_power) { + ret = snd_soc_dapm_set_bias_level(card, d, + SND_SOC_BIAS_ON); + if (ret != 0) + dev_err(d->dev, + "Failed to apply active bias: %d\n", + ret); + } + } pop_dbg(dapm->dev, card->pop_time, "DAPM sequencing finished, waiting %dms\n", card->pop_time); @@ -1269,7 +1454,7 @@ static ssize_t dapm_widget_power_read_file(struct file *file, if (p->connect) ret += snprintf(buf + ret, PAGE_SIZE - ret, - " in \"%s\" \"%s\"\n", + " in %s %s\n", p->name ? p->name : "static", p->source->name); } @@ -1279,7 +1464,7 @@ static ssize_t dapm_widget_power_read_file(struct file *file, if (p->connect) ret += snprintf(buf + ret, PAGE_SIZE - ret, - " out \"%s\" \"%s\"\n", + " out %s %s\n", p->name ? p->name : "static", p->sink->name); } @@ -1296,104 +1481,31 @@ static const struct file_operations dapm_widget_power_fops = { .llseek = default_llseek, }; -static int dapm_bias_open_file(struct inode *inode, struct file *file) -{ - file->private_data = inode->i_private; - return 0; -} - -static ssize_t dapm_bias_read_file(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct snd_soc_dapm_context *dapm = file->private_data; - char *level; - - switch (dapm->bias_level) { - case SND_SOC_BIAS_ON: - level = "On\n"; - break; - case SND_SOC_BIAS_PREPARE: - level = "Prepare\n"; - break; - case SND_SOC_BIAS_STANDBY: - level = "Standby\n"; - break; - case SND_SOC_BIAS_OFF: - level = "Off\n"; - break; - default: - BUG(); - level = "Unknown\n"; - break; - } - - return simple_read_from_buffer(user_buf, count, ppos, level, - strlen(level)); -} - -static const struct file_operations dapm_bias_fops = { - .open = dapm_bias_open_file, - .read = dapm_bias_read_file, - .llseek = default_llseek, -}; - -void snd_soc_dapm_debugfs_init(struct snd_soc_dapm_context *dapm, - struct dentry *parent) +void snd_soc_dapm_debugfs_init(struct snd_soc_dapm_context *dapm) { + struct snd_soc_dapm_widget *w; struct dentry *d; - dapm->debugfs_dapm = debugfs_create_dir("dapm", parent); - - if (!dapm->debugfs_dapm) { - printk(KERN_WARNING - "Failed to create DAPM debugfs directory\n"); - return; - } - - d = debugfs_create_file("bias_level", 0444, - dapm->debugfs_dapm, dapm, - &dapm_bias_fops); - if (!d) - dev_warn(dapm->dev, - "ASoC: Failed to create bias level debugfs file\n"); -} - -static void dapm_debugfs_add_widget(struct snd_soc_dapm_widget *w) -{ - struct snd_soc_dapm_context *dapm = w->dapm; - struct dentry *d; - - if (!dapm->debugfs_dapm || !w->name) + if (!dapm->debugfs_dapm) return; - d = debugfs_create_file(w->name, 0444, - dapm->debugfs_dapm, w, - &dapm_widget_power_fops); - if (!d) - dev_warn(w->dapm->dev, - "ASoC: Failed to create %s debugfs file\n", - w->name); -} + list_for_each_entry(w, &dapm->card->widgets, list) { + if (!w->name || w->dapm != dapm) + continue; -static void dapm_debugfs_cleanup(struct snd_soc_dapm_context *dapm) -{ - debugfs_remove_recursive(dapm->debugfs_dapm); + d = debugfs_create_file(w->name, 0444, + dapm->debugfs_dapm, w, + &dapm_widget_power_fops); + if (!d) + dev_warn(w->dapm->dev, + "ASoC: Failed to create %s debugfs file\n", + w->name); + } } - #else -void snd_soc_dapm_debugfs_init(struct snd_soc_dapm_context *dapm, - struct dentry *parent) -{ -} - -static inline void dapm_debugfs_add_widget(struct snd_soc_dapm_widget *w) +void snd_soc_dapm_debugfs_init(struct snd_soc_dapm_context *dapm) { } - -static inline void dapm_debugfs_cleanup(struct snd_soc_dapm_context *dapm) -{ -} - #endif /* test and update the power status of a mux widget */ @@ -1563,49 +1675,32 @@ static void dapm_free_widgets(struct snd_soc_dapm_context *dapm) kfree(p->long_name); kfree(p); } - kfree(w->kcontrols); kfree(w->name); kfree(w); } } -static struct snd_soc_dapm_widget *dapm_find_widget( - struct snd_soc_dapm_context *dapm, const char *pin, - bool search_other_contexts) +static int snd_soc_dapm_set_pin(struct snd_soc_dapm_context *dapm, + const char *pin, int status) { struct snd_soc_dapm_widget *w; - struct snd_soc_dapm_widget *fallback = NULL; list_for_each_entry(w, &dapm->card->widgets, list) { + if (w->dapm != dapm) + continue; if (!strcmp(w->name, pin)) { - if (w->dapm == dapm) - return w; - else - fallback = w; + dev_dbg(w->dapm->dev, "dapm: pin %s = %d\n", + pin, status); + w->connected = status; + /* Allow disabling of forced pins */ + if (status == 0) + w->force = 0; + return 0; } } - if (search_other_contexts) - return fallback; - - return NULL; -} - -static int snd_soc_dapm_set_pin(struct snd_soc_dapm_context *dapm, - const char *pin, int status) -{ - struct snd_soc_dapm_widget *w = dapm_find_widget(dapm, pin, true); - - if (!w) { - dev_err(dapm->dev, "dapm: unknown pin %s\n", pin); - return -EINVAL; - } - - w->connected = status; - if (status == 0) - w->force = 0; - - return 0; + dev_err(dapm->dev, "dapm: unknown pin %s\n", pin); + return -EINVAL; } /** @@ -1711,7 +1806,7 @@ static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm, } /* connect dynamic paths */ - switch (wsink->id) { + switch(wsink->id) { case snd_soc_dapm_adc: case snd_soc_dapm_dac: case snd_soc_dapm_pga: @@ -1734,7 +1829,7 @@ static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm, case snd_soc_dapm_virt_mux: case snd_soc_dapm_value_mux: ret = dapm_connect_mux(dapm, wsource, wsink, path, control, - &wsink->kcontrol_news[0]); + &wsink->kcontrols[0]); if (ret != 0) goto err; break; @@ -1807,33 +1902,24 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_add_routes); int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm) { struct snd_soc_dapm_widget *w; - unsigned int val; list_for_each_entry(w, &dapm->card->widgets, list) { if (w->new) continue; - if (w->num_kcontrols) { - w->kcontrols = kzalloc(w->num_kcontrols * - sizeof(struct snd_kcontrol *), - GFP_KERNEL); - if (!w->kcontrols) - return -ENOMEM; - } - switch(w->id) { case snd_soc_dapm_switch: case snd_soc_dapm_mixer: case snd_soc_dapm_mixer_named_ctl: w->power_check = dapm_generic_check_power; - dapm_new_mixer(w); + dapm_new_mixer(dapm, w); break; case snd_soc_dapm_mux: case snd_soc_dapm_virt_mux: case snd_soc_dapm_value_mux: w->power_check = dapm_generic_check_power; - dapm_new_mux(w); + dapm_new_mux(dapm, w); break; case snd_soc_dapm_adc: case snd_soc_dapm_aif_out: @@ -1846,7 +1932,7 @@ int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm) case snd_soc_dapm_pga: case snd_soc_dapm_out_drv: w->power_check = dapm_generic_check_power; - dapm_new_pga(w); + dapm_new_pga(dapm, w); break; case snd_soc_dapm_input: case snd_soc_dapm_output: @@ -1864,21 +1950,7 @@ int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm) case snd_soc_dapm_post: break; } - - /* Read the initial power state from the device */ - if (w->reg >= 0) { - val = snd_soc_read(w->codec, w->reg); - val &= 1 << w->shift; - if (w->invert) - val = !val; - - if (val) - w->power = 1; - } - w->new = 1; - - dapm_debugfs_add_widget(w); } dapm_power_widgets(dapm, SND_SOC_DAPM_STREAM_NOP); @@ -1886,6 +1958,23 @@ int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm) } EXPORT_SYMBOL_GPL(snd_soc_dapm_new_widgets); +const char *snd_soc_dapm_get_aif(struct snd_soc_dapm_context *dapm, + const char *stream_name, enum snd_soc_dapm_type type) +{ + struct snd_soc_dapm_widget *w; + + list_for_each_entry(w, &dapm->card->widgets, list) { + + if (!w->sname) + continue; + + if (w->id == type && strstr(w->sname, stream_name)) + return w->name; + } + return NULL; +} +EXPORT_SYMBOL_GPL(snd_soc_dapm_get_aif); + /** * snd_soc_dapm_get_volsw - dapm mixer get callback * @kcontrol: mixer control @@ -1898,8 +1987,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_new_widgets); int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); - struct snd_soc_dapm_widget *widget = wlist->widgets[0]; + struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol); struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; unsigned int reg = mc->reg; @@ -1910,10 +1998,10 @@ int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol, unsigned int mask = (1 << fls(max)) - 1; ucontrol->value.integer.value[0] = - (snd_soc_read(widget->codec, reg) >> shift) & mask; + (soc_widget_read(widget, reg) >> shift) & mask; if (shift != rshift) ucontrol->value.integer.value[1] = - (snd_soc_read(widget->codec, reg) >> rshift) & mask; + (soc_widget_read(widget, reg) >> rshift) & mask; if (invert) { ucontrol->value.integer.value[0] = max - ucontrol->value.integer.value[0]; @@ -1938,9 +2026,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_get_volsw); int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); - struct snd_soc_dapm_widget *widget = wlist->widgets[0]; - struct snd_soc_codec *codec = widget->codec; + struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol); struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; unsigned int reg = mc->reg; @@ -1951,7 +2037,6 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, unsigned int val; int connect, change; struct snd_soc_dapm_update update; - int wi; val = (ucontrol->value.integer.value[0] & mask); @@ -1960,36 +2045,31 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, mask = mask << shift; val = val << shift; - if (val) - /* new connection */ - connect = invert ? 0 : 1; - else - /* old connection must be powered down */ - connect = invert ? 1 : 0; - - mutex_lock(&codec->mutex); + mutex_lock(&widget->codec->mutex); + widget->value = val; - change = snd_soc_test_bits(widget->codec, reg, mask, val); + change = soc_widget_test_bits(widget, reg, mask, val); if (change) { - for (wi = 0; wi < wlist->num_widgets; wi++) { - widget = wlist->widgets[wi]; - - widget->value = val; + if (val) + /* new connection */ + connect = invert ? 0:1; + else + /* old connection must be powered down */ + connect = invert ? 1:0; - update.kcontrol = kcontrol; - update.widget = widget; - update.reg = reg; - update.mask = mask; - update.val = val; - widget->dapm->update = &update; + update.kcontrol = kcontrol; + update.widget = widget; + update.reg = reg; + update.mask = mask; + update.val = val; + widget->dapm->update = &update; - snd_soc_dapm_mixer_update_power(widget, kcontrol, connect); + snd_soc_dapm_mixer_update_power(widget, kcontrol, connect); - widget->dapm->update = NULL; - } + widget->dapm->update = NULL; } - mutex_unlock(&codec->mutex); + mutex_unlock(&widget->codec->mutex); return 0; } EXPORT_SYMBOL_GPL(snd_soc_dapm_put_volsw); @@ -2006,14 +2086,13 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_put_volsw); int snd_soc_dapm_get_enum_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); - struct snd_soc_dapm_widget *widget = wlist->widgets[0]; + struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol); struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; unsigned int val, bitmask; for (bitmask = 1; bitmask < e->max; bitmask <<= 1) ; - val = snd_soc_read(widget->codec, e->reg); + val = soc_widget_read(widget, e->reg); ucontrol->value.enumerated.item[0] = (val >> e->shift_l) & (bitmask - 1); if (e->shift_l != e->shift_r) ucontrol->value.enumerated.item[1] = @@ -2035,14 +2114,11 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_get_enum_double); int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); - struct snd_soc_dapm_widget *widget = wlist->widgets[0]; - struct snd_soc_codec *codec = widget->codec; + struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol); struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; unsigned int val, mux, change; unsigned int mask, bitmask; struct snd_soc_dapm_update update; - int wi; for (bitmask = 1; bitmask < e->max; bitmask <<= 1) ; @@ -2058,29 +2134,22 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, mask |= (bitmask - 1) << e->shift_r; } - mutex_lock(&codec->mutex); + mutex_lock(&widget->codec->mutex); + widget->value = val; + change = soc_widget_test_bits(widget, e->reg, mask, val); - change = snd_soc_test_bits(widget->codec, e->reg, mask, val); - if (change) { - for (wi = 0; wi < wlist->num_widgets; wi++) { - widget = wlist->widgets[wi]; - - widget->value = val; - - update.kcontrol = kcontrol; - update.widget = widget; - update.reg = e->reg; - update.mask = mask; - update.val = val; - widget->dapm->update = &update; + update.kcontrol = kcontrol; + update.widget = widget; + update.reg = e->reg; + update.mask = mask; + update.val = val; + widget->dapm->update = &update; - snd_soc_dapm_mux_update_power(widget, kcontrol, change, mux, e); + snd_soc_dapm_mux_update_power(widget, kcontrol, change, mux, e); - widget->dapm->update = NULL; - } - } + widget->dapm->update = NULL; - mutex_unlock(&codec->mutex); + mutex_unlock(&widget->codec->mutex); return change; } EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_double); @@ -2095,8 +2164,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_double); int snd_soc_dapm_get_enum_virt(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); - struct snd_soc_dapm_widget *widget = wlist->widgets[0]; + struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol); ucontrol->value.enumerated.item[0] = widget->value; @@ -2114,33 +2182,22 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_get_enum_virt); int snd_soc_dapm_put_enum_virt(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); - struct snd_soc_dapm_widget *widget = wlist->widgets[0]; - struct snd_soc_codec *codec = widget->codec; + struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol); struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; int change; int ret = 0; - int wi; if (ucontrol->value.enumerated.item[0] >= e->max) return -EINVAL; - mutex_lock(&codec->mutex); + mutex_lock(&widget->codec->mutex); change = widget->value != ucontrol->value.enumerated.item[0]; - if (change) { - for (wi = 0; wi < wlist->num_widgets; wi++) { - widget = wlist->widgets[wi]; + widget->value = ucontrol->value.enumerated.item[0]; + snd_soc_dapm_mux_update_power(widget, kcontrol, change, widget->value, e); - widget->value = ucontrol->value.enumerated.item[0]; - - snd_soc_dapm_mux_update_power(widget, kcontrol, change, - widget->value, e); - } - } - - mutex_unlock(&codec->mutex); + mutex_unlock(&widget->codec->mutex); return ret; } EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_virt); @@ -2161,12 +2218,11 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_virt); int snd_soc_dapm_get_value_enum_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); - struct snd_soc_dapm_widget *widget = wlist->widgets[0]; + struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol); struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; unsigned int reg_val, val, mux; - reg_val = snd_soc_read(widget->codec, e->reg); + reg_val = soc_widget_read(widget, e->reg); val = (reg_val >> e->shift_l) & e->mask; for (mux = 0; mux < e->max; mux++) { if (val == e->values[mux]) @@ -2202,14 +2258,11 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_get_value_enum_double); int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); - struct snd_soc_dapm_widget *widget = wlist->widgets[0]; - struct snd_soc_codec *codec = widget->codec; + struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol); struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; unsigned int val, mux, change; unsigned int mask; struct snd_soc_dapm_update update; - int wi; if (ucontrol->value.enumerated.item[0] > e->max - 1) return -EINVAL; @@ -2223,29 +2276,22 @@ int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol, mask |= e->mask << e->shift_r; } - mutex_lock(&codec->mutex); - - change = snd_soc_test_bits(widget->codec, e->reg, mask, val); - if (change) { - for (wi = 0; wi < wlist->num_widgets; wi++) { - widget = wlist->widgets[wi]; + mutex_lock(&widget->codec->mutex); + widget->value = val; + change = soc_widget_test_bits(widget, e->reg, mask, val); - widget->value = val; + update.kcontrol = kcontrol; + update.widget = widget; + update.reg = e->reg; + update.mask = mask; + update.val = val; + widget->dapm->update = &update; - update.kcontrol = kcontrol; - update.widget = widget; - update.reg = e->reg; - update.mask = mask; - update.val = val; - widget->dapm->update = &update; + snd_soc_dapm_mux_update_power(widget, kcontrol, change, mux, e); - snd_soc_dapm_mux_update_power(widget, kcontrol, change, mux, e); - - widget->dapm->update = NULL; - } - } + widget->dapm->update = NULL; - mutex_unlock(&codec->mutex); + mutex_unlock(&widget->codec->mutex); return change; } EXPORT_SYMBOL_GPL(snd_soc_dapm_put_value_enum_double); @@ -2355,6 +2401,7 @@ int snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm, dapm->n_widgets++; w->dapm = dapm; w->codec = dapm->codec; + w->platform = dapm->platform; INIT_LIST_HEAD(&w->sources); INIT_LIST_HEAD(&w->sinks); INIT_LIST_HEAD(&w->list); @@ -2425,6 +2472,10 @@ static void soc_dapm_stream_event(struct snd_soc_dapm_context *dapm, } dapm_power_widgets(dapm, event); + + /* do we need to notify any clients that DAPM stream is complete */ + if (dapm->stream_event) + dapm->stream_event(dapm); } /** @@ -2439,18 +2490,30 @@ static void soc_dapm_stream_event(struct snd_soc_dapm_context *dapm, * Returns 0 for success else error. */ int snd_soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd, - const char *stream, int event) + int dir, const char *stream, int event) { - struct snd_soc_codec *codec = rtd->codec; + int i; if (stream == NULL) return 0; - mutex_lock(&codec->mutex); - soc_dapm_stream_event(&codec->dapm, stream, event); - mutex_unlock(&codec->mutex); + if (rtd->dai_link->dynamic) { + for (i = 0; i < rtd->num_be[dir]; i++) { + struct snd_soc_platform *platform = rtd->be_rtd[i][dir]->platform; + + soc_dapm_stream_event(&platform->dapm, stream, event); + } + } else { + struct snd_soc_codec *codec = rtd->codec; + + mutex_lock(&codec->mutex); + soc_dapm_stream_event(&(codec->dapm), stream, event); + mutex_unlock(&codec->mutex); + } + return 0; } +EXPORT_SYMBOL_GPL(snd_soc_dapm_stream_event); /** * snd_soc_dapm_enable_pin - enable pin. @@ -2483,18 +2546,22 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_enable_pin); int snd_soc_dapm_force_enable_pin(struct snd_soc_dapm_context *dapm, const char *pin) { - struct snd_soc_dapm_widget *w = dapm_find_widget(dapm, pin, true); + struct snd_soc_dapm_widget *w; - if (!w) { - dev_err(dapm->dev, "dapm: unknown pin %s\n", pin); - return -EINVAL; + list_for_each_entry(w, &dapm->card->widgets, list) { + if (w->dapm != dapm) + continue; + if (!strcmp(w->name, pin)) { + dev_dbg(w->dapm->dev, + "dapm: force enable pin %s\n", pin); + w->connected = 1; + w->force = 1; + return 0; + } } - dev_dbg(w->dapm->dev, "dapm: force enable pin %s\n", pin); - w->connected = 1; - w->force = 1; - - return 0; + dev_err(dapm->dev, "dapm: unknown pin %s\n", pin); + return -EINVAL; } EXPORT_SYMBOL_GPL(snd_soc_dapm_force_enable_pin); @@ -2546,10 +2613,14 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_nc_pin); int snd_soc_dapm_get_pin_status(struct snd_soc_dapm_context *dapm, const char *pin) { - struct snd_soc_dapm_widget *w = dapm_find_widget(dapm, pin, true); + struct snd_soc_dapm_widget *w; - if (w) - return w->connected; + list_for_each_entry(w, &dapm->card->widgets, list) { + if (w->dapm != dapm) + continue; + if (!strcmp(w->name, pin)) + return w->connected; + } return 0; } @@ -2569,16 +2640,19 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_get_pin_status); int snd_soc_dapm_ignore_suspend(struct snd_soc_dapm_context *dapm, const char *pin) { - struct snd_soc_dapm_widget *w = dapm_find_widget(dapm, pin, false); + struct snd_soc_dapm_widget *w; - if (!w) { - dev_err(dapm->dev, "dapm: unknown pin %s\n", pin); - return -EINVAL; + list_for_each_entry(w, &dapm->card->widgets, list) { + if (w->dapm != dapm) + continue; + if (!strcmp(w->name, pin)) { + w->ignore_suspend = 1; + return 0; + } } - w->ignore_suspend = 1; - - return 0; + dev_err(dapm->dev, "dapm: unknown pin %s\n", pin); + return -EINVAL; } EXPORT_SYMBOL_GPL(snd_soc_dapm_ignore_suspend); @@ -2591,7 +2665,6 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_ignore_suspend); void snd_soc_dapm_free(struct snd_soc_dapm_context *dapm) { snd_soc_dapm_sys_remove(dapm->dev); - dapm_debugfs_cleanup(dapm); dapm_free_widgets(dapm); list_del(&dapm->list); } @@ -2607,7 +2680,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, false); + dapm_seq_insert(w, &down_list, dapm_down_seq); w->power = 0; powerdown = 1; } @@ -2617,9 +2690,9 @@ static void soc_dapm_shutdown_codec(struct snd_soc_dapm_context *dapm) * standby. */ if (powerdown) { - snd_soc_dapm_set_bias_level(dapm, SND_SOC_BIAS_PREPARE); - dapm_seq_run(dapm, &down_list, 0, false); - snd_soc_dapm_set_bias_level(dapm, SND_SOC_BIAS_STANDBY); + snd_soc_dapm_set_bias_level(NULL, dapm, SND_SOC_BIAS_PREPARE); + dapm_seq_run(dapm, &down_list, 0, dapm_down_seq); + snd_soc_dapm_set_bias_level(NULL, dapm, SND_SOC_BIAS_STANDBY); } } @@ -2632,7 +2705,7 @@ void snd_soc_dapm_shutdown(struct snd_soc_card *card) list_for_each_entry(codec, &card->codec_dev_list, list) { soc_dapm_shutdown_codec(&codec->dapm); - snd_soc_dapm_set_bias_level(&codec->dapm, SND_SOC_BIAS_OFF); + snd_soc_dapm_set_bias_level(card, &codec->dapm, SND_SOC_BIAS_OFF); } } diff --git a/sound/soc/soc-jack.c b/sound/soc/soc-jack.c index fa31d9c..04b7999 100644 --- a/sound/soc/soc-jack.c +++ b/sound/soc/soc-jack.c @@ -37,7 +37,6 @@ int snd_soc_jack_new(struct snd_soc_codec *codec, const char *id, int type, { jack->codec = codec; INIT_LIST_HEAD(&jack->pins); - INIT_LIST_HEAD(&jack->jack_zones); BLOCKING_INIT_NOTIFIER_HEAD(&jack->notifier); return snd_jack_new(codec->card->snd_card, id, type, &jack->jack); @@ -101,7 +100,7 @@ void snd_soc_jack_report(struct snd_soc_jack *jack, int status, int mask) } /* Report before the DAPM sync to help users updating micbias status */ - blocking_notifier_call_chain(&jack->notifier, status, jack); + blocking_notifier_call_chain(&jack->notifier, status, NULL); snd_soc_dapm_sync(dapm); @@ -113,51 +112,6 @@ out: EXPORT_SYMBOL_GPL(snd_soc_jack_report); /** - * snd_soc_jack_add_zones - Associate voltage zones with jack - * - * @jack: ASoC jack - * @count: Number of zones - * @zone: Array of zones - * - * After this function has been called the zones specified in the - * array will be associated with the jack. - */ -int snd_soc_jack_add_zones(struct snd_soc_jack *jack, int count, - struct snd_soc_jack_zone *zones) -{ - int i; - - for (i = 0; i < count; i++) { - INIT_LIST_HEAD(&zones[i].list); - list_add(&(zones[i].list), &jack->jack_zones); - } - return 0; -} -EXPORT_SYMBOL_GPL(snd_soc_jack_add_zones); - -/** - * snd_soc_jack_get_type - Based on the mic bias value, this function returns - * the type of jack from the zones delcared in the jack type - * - * @micbias_voltage: mic bias voltage at adc channel when jack is plugged in - * - * Based on the mic bias value passed, this function helps identify - * the type of jack from the already delcared jack zones - */ -int snd_soc_jack_get_type(struct snd_soc_jack *jack, int micbias_voltage) -{ - struct snd_soc_jack_zone *zone; - - list_for_each_entry(zone, &jack->jack_zones, list) { - if (micbias_voltage >= zone->min_mv && - micbias_voltage < zone->max_mv) - return zone->jack_type; - } - return 0; -} -EXPORT_SYMBOL_GPL(snd_soc_jack_get_type); - -/** * snd_soc_jack_add_pins - Associate DAPM pins with an ASoC jack * * @jack: ASoC jack @@ -240,7 +194,7 @@ static void snd_soc_jack_gpio_detect(struct snd_soc_jack_gpio *gpio) int enable; int report; - enable = gpio_get_value_cansleep(gpio->gpio); + enable = gpio_get_value(gpio->gpio); if (gpio->invert) enable = !enable; @@ -325,19 +279,11 @@ int snd_soc_jack_add_gpios(struct snd_soc_jack *jack, int count, gpio_handler, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, - gpios[i].name, + jack->codec->dev->driver->name, &gpios[i]); if (ret < 0) goto err; - if (gpios[i].wake) { - ret = irq_set_irq_wake(gpio_to_irq(gpios[i].gpio), 1); - if (ret != 0) - printk(KERN_ERR - "Failed to mark GPIO %d as wake source: %d\n", - gpios[i].gpio, ret); - } - #ifdef CONFIG_GPIO_SYSFS /* Expose GPIO value over sysfs for diagnostic purposes */ gpio_export(gpios[i].gpio, false); -- 1.7.4.1