Merge branch 'for-2.6.40' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound-2.6 into topic/asoc

This commit is contained in:
Takashi Iwai 2011-05-10 09:24:50 +02:00
commit 1209842af4
14 changed files with 783 additions and 327 deletions

View file

@ -39,30 +39,30 @@
/* codec domain */ /* codec domain */
#define SND_SOC_DAPM_VMID(wname) \ #define SND_SOC_DAPM_VMID(wname) \
{ .id = snd_soc_dapm_vmid, .name = wname, .kcontrols = NULL, \ { .id = snd_soc_dapm_vmid, .name = wname, .kcontrol_news = NULL, \
.num_kcontrols = 0} .num_kcontrols = 0}
/* platform domain */ /* platform domain */
#define SND_SOC_DAPM_INPUT(wname) \ #define SND_SOC_DAPM_INPUT(wname) \
{ .id = snd_soc_dapm_input, .name = wname, .kcontrols = NULL, \ { .id = snd_soc_dapm_input, .name = wname, .kcontrol_news = NULL, \
.num_kcontrols = 0, .reg = SND_SOC_NOPM } .num_kcontrols = 0, .reg = SND_SOC_NOPM }
#define SND_SOC_DAPM_OUTPUT(wname) \ #define SND_SOC_DAPM_OUTPUT(wname) \
{ .id = snd_soc_dapm_output, .name = wname, .kcontrols = NULL, \ { .id = snd_soc_dapm_output, .name = wname, .kcontrol_news = NULL, \
.num_kcontrols = 0, .reg = SND_SOC_NOPM } .num_kcontrols = 0, .reg = SND_SOC_NOPM }
#define SND_SOC_DAPM_MIC(wname, wevent) \ #define SND_SOC_DAPM_MIC(wname, wevent) \
{ .id = snd_soc_dapm_mic, .name = wname, .kcontrols = NULL, \ { .id = snd_soc_dapm_mic, .name = wname, .kcontrol_news = NULL, \
.num_kcontrols = 0, .reg = SND_SOC_NOPM, .event = wevent, \ .num_kcontrols = 0, .reg = SND_SOC_NOPM, .event = wevent, \
.event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD} .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD}
#define SND_SOC_DAPM_HP(wname, wevent) \ #define SND_SOC_DAPM_HP(wname, wevent) \
{ .id = snd_soc_dapm_hp, .name = wname, .kcontrols = NULL, \ { .id = snd_soc_dapm_hp, .name = wname, .kcontrol_news = NULL, \
.num_kcontrols = 0, .reg = SND_SOC_NOPM, .event = wevent, \ .num_kcontrols = 0, .reg = SND_SOC_NOPM, .event = wevent, \
.event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD} .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD}
#define SND_SOC_DAPM_SPK(wname, wevent) \ #define SND_SOC_DAPM_SPK(wname, wevent) \
{ .id = snd_soc_dapm_spk, .name = wname, .kcontrols = NULL, \ { .id = snd_soc_dapm_spk, .name = wname, .kcontrol_news = NULL, \
.num_kcontrols = 0, .reg = SND_SOC_NOPM, .event = wevent, \ .num_kcontrols = 0, .reg = SND_SOC_NOPM, .event = wevent, \
.event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD} .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD}
#define SND_SOC_DAPM_LINE(wname, wevent) \ #define SND_SOC_DAPM_LINE(wname, wevent) \
{ .id = snd_soc_dapm_line, .name = wname, .kcontrols = NULL, \ { .id = snd_soc_dapm_line, .name = wname, .kcontrol_news = NULL, \
.num_kcontrols = 0, .reg = SND_SOC_NOPM, .event = wevent, \ .num_kcontrols = 0, .reg = SND_SOC_NOPM, .event = wevent, \
.event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD} .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD}
@ -70,91 +70,91 @@
#define SND_SOC_DAPM_PGA(wname, wreg, wshift, winvert,\ #define SND_SOC_DAPM_PGA(wname, wreg, wshift, winvert,\
wcontrols, wncontrols) \ wcontrols, wncontrols) \
{ .id = snd_soc_dapm_pga, .name = wname, .reg = wreg, .shift = wshift, \ { .id = snd_soc_dapm_pga, .name = wname, .reg = wreg, .shift = wshift, \
.invert = winvert, .kcontrols = wcontrols, .num_kcontrols = wncontrols} .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = wncontrols}
#define SND_SOC_DAPM_OUT_DRV(wname, wreg, wshift, winvert,\ #define SND_SOC_DAPM_OUT_DRV(wname, wreg, wshift, winvert,\
wcontrols, wncontrols) \ wcontrols, wncontrols) \
{ .id = snd_soc_dapm_out_drv, .name = wname, .reg = wreg, .shift = wshift, \ { .id = snd_soc_dapm_out_drv, .name = wname, .reg = wreg, .shift = wshift, \
.invert = winvert, .kcontrols = wcontrols, .num_kcontrols = wncontrols} .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = wncontrols}
#define SND_SOC_DAPM_MIXER(wname, wreg, wshift, winvert, \ #define SND_SOC_DAPM_MIXER(wname, wreg, wshift, winvert, \
wcontrols, wncontrols)\ wcontrols, wncontrols)\
{ .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \ { .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \
.invert = winvert, .kcontrols = wcontrols, .num_kcontrols = wncontrols} .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = wncontrols}
#define SND_SOC_DAPM_MIXER_NAMED_CTL(wname, wreg, wshift, winvert, \ #define SND_SOC_DAPM_MIXER_NAMED_CTL(wname, wreg, wshift, winvert, \
wcontrols, wncontrols)\ wcontrols, wncontrols)\
{ .id = snd_soc_dapm_mixer_named_ctl, .name = wname, .reg = wreg, \ { .id = snd_soc_dapm_mixer_named_ctl, .name = wname, .reg = wreg, \
.shift = wshift, .invert = winvert, .kcontrols = wcontrols, \ .shift = wshift, .invert = winvert, .kcontrol_news = wcontrols, \
.num_kcontrols = wncontrols} .num_kcontrols = wncontrols}
#define SND_SOC_DAPM_MICBIAS(wname, wreg, wshift, winvert) \ #define SND_SOC_DAPM_MICBIAS(wname, wreg, wshift, winvert) \
{ .id = snd_soc_dapm_micbias, .name = wname, .reg = wreg, .shift = wshift, \ { .id = snd_soc_dapm_micbias, .name = wname, .reg = wreg, .shift = wshift, \
.invert = winvert, .kcontrols = NULL, .num_kcontrols = 0} .invert = winvert, .kcontrol_news = NULL, .num_kcontrols = 0}
#define SND_SOC_DAPM_SWITCH(wname, wreg, wshift, winvert, wcontrols) \ #define SND_SOC_DAPM_SWITCH(wname, wreg, wshift, winvert, wcontrols) \
{ .id = snd_soc_dapm_switch, .name = wname, .reg = wreg, .shift = wshift, \ { .id = snd_soc_dapm_switch, .name = wname, .reg = wreg, .shift = wshift, \
.invert = winvert, .kcontrols = wcontrols, .num_kcontrols = 1} .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = 1}
#define SND_SOC_DAPM_MUX(wname, wreg, wshift, winvert, wcontrols) \ #define SND_SOC_DAPM_MUX(wname, wreg, wshift, winvert, wcontrols) \
{ .id = snd_soc_dapm_mux, .name = wname, .reg = wreg, .shift = wshift, \ { .id = snd_soc_dapm_mux, .name = wname, .reg = wreg, .shift = wshift, \
.invert = winvert, .kcontrols = wcontrols, .num_kcontrols = 1} .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = 1}
#define SND_SOC_DAPM_VIRT_MUX(wname, wreg, wshift, winvert, wcontrols) \ #define SND_SOC_DAPM_VIRT_MUX(wname, wreg, wshift, winvert, wcontrols) \
{ .id = snd_soc_dapm_virt_mux, .name = wname, .reg = wreg, .shift = wshift, \ { .id = snd_soc_dapm_virt_mux, .name = wname, .reg = wreg, .shift = wshift, \
.invert = winvert, .kcontrols = wcontrols, .num_kcontrols = 1} .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = 1}
#define SND_SOC_DAPM_VALUE_MUX(wname, wreg, wshift, winvert, wcontrols) \ #define SND_SOC_DAPM_VALUE_MUX(wname, wreg, wshift, winvert, wcontrols) \
{ .id = snd_soc_dapm_value_mux, .name = wname, .reg = wreg, \ { .id = snd_soc_dapm_value_mux, .name = wname, .reg = wreg, \
.shift = wshift, .invert = winvert, .kcontrols = wcontrols, \ .shift = wshift, .invert = winvert, .kcontrol_news = wcontrols, \
.num_kcontrols = 1} .num_kcontrols = 1}
/* Simplified versions of above macros, assuming wncontrols = ARRAY_SIZE(wcontrols) */ /* Simplified versions of above macros, assuming wncontrols = ARRAY_SIZE(wcontrols) */
#define SOC_PGA_ARRAY(wname, wreg, wshift, winvert,\ #define SOC_PGA_ARRAY(wname, wreg, wshift, winvert,\
wcontrols) \ wcontrols) \
{ .id = snd_soc_dapm_pga, .name = wname, .reg = wreg, .shift = wshift, \ { .id = snd_soc_dapm_pga, .name = wname, .reg = wreg, .shift = wshift, \
.invert = winvert, .kcontrols = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols)} .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols)}
#define SOC_MIXER_ARRAY(wname, wreg, wshift, winvert, \ #define SOC_MIXER_ARRAY(wname, wreg, wshift, winvert, \
wcontrols)\ wcontrols)\
{ .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \ { .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \
.invert = winvert, .kcontrols = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols)} .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols)}
#define SOC_MIXER_NAMED_CTL_ARRAY(wname, wreg, wshift, winvert, \ #define SOC_MIXER_NAMED_CTL_ARRAY(wname, wreg, wshift, winvert, \
wcontrols)\ wcontrols)\
{ .id = snd_soc_dapm_mixer_named_ctl, .name = wname, .reg = wreg, \ { .id = snd_soc_dapm_mixer_named_ctl, .name = wname, .reg = wreg, \
.shift = wshift, .invert = winvert, .kcontrols = wcontrols, \ .shift = wshift, .invert = winvert, .kcontrol_news = wcontrols, \
.num_kcontrols = ARRAY_SIZE(wcontrols)} .num_kcontrols = ARRAY_SIZE(wcontrols)}
/* path domain with event - event handler must return 0 for success */ /* path domain with event - event handler must return 0 for success */
#define SND_SOC_DAPM_PGA_E(wname, wreg, wshift, winvert, wcontrols, \ #define SND_SOC_DAPM_PGA_E(wname, wreg, wshift, winvert, wcontrols, \
wncontrols, wevent, wflags) \ wncontrols, wevent, wflags) \
{ .id = snd_soc_dapm_pga, .name = wname, .reg = wreg, .shift = wshift, \ { .id = snd_soc_dapm_pga, .name = wname, .reg = wreg, .shift = wshift, \
.invert = winvert, .kcontrols = wcontrols, .num_kcontrols = wncontrols, \ .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = wncontrols, \
.event = wevent, .event_flags = wflags} .event = wevent, .event_flags = wflags}
#define SND_SOC_DAPM_OUT_DRV_E(wname, wreg, wshift, winvert, wcontrols, \ #define SND_SOC_DAPM_OUT_DRV_E(wname, wreg, wshift, winvert, wcontrols, \
wncontrols, wevent, wflags) \ wncontrols, wevent, wflags) \
{ .id = snd_soc_dapm_out_drv, .name = wname, .reg = wreg, .shift = wshift, \ { .id = snd_soc_dapm_out_drv, .name = wname, .reg = wreg, .shift = wshift, \
.invert = winvert, .kcontrols = wcontrols, .num_kcontrols = wncontrols, \ .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = wncontrols, \
.event = wevent, .event_flags = wflags} .event = wevent, .event_flags = wflags}
#define SND_SOC_DAPM_MIXER_E(wname, wreg, wshift, winvert, wcontrols, \ #define SND_SOC_DAPM_MIXER_E(wname, wreg, wshift, winvert, wcontrols, \
wncontrols, wevent, wflags) \ wncontrols, wevent, wflags) \
{ .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \ { .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \
.invert = winvert, .kcontrols = wcontrols, .num_kcontrols = wncontrols, \ .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = wncontrols, \
.event = wevent, .event_flags = wflags} .event = wevent, .event_flags = wflags}
#define SND_SOC_DAPM_MIXER_NAMED_CTL_E(wname, wreg, wshift, winvert, \ #define SND_SOC_DAPM_MIXER_NAMED_CTL_E(wname, wreg, wshift, winvert, \
wcontrols, wncontrols, wevent, wflags) \ wcontrols, wncontrols, wevent, wflags) \
{ .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \ { .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \
.invert = winvert, .kcontrols = wcontrols, \ .invert = winvert, .kcontrol_news = wcontrols, \
.num_kcontrols = wncontrols, .event = wevent, .event_flags = wflags} .num_kcontrols = wncontrols, .event = wevent, .event_flags = wflags}
#define SND_SOC_DAPM_MICBIAS_E(wname, wreg, wshift, winvert, wevent, wflags) \ #define SND_SOC_DAPM_MICBIAS_E(wname, wreg, wshift, winvert, wevent, wflags) \
{ .id = snd_soc_dapm_micbias, .name = wname, .reg = wreg, .shift = wshift, \ { .id = snd_soc_dapm_micbias, .name = wname, .reg = wreg, .shift = wshift, \
.invert = winvert, .kcontrols = NULL, .num_kcontrols = 0, \ .invert = winvert, .kcontrol_news = NULL, .num_kcontrols = 0, \
.event = wevent, .event_flags = wflags} .event = wevent, .event_flags = wflags}
#define SND_SOC_DAPM_SWITCH_E(wname, wreg, wshift, winvert, wcontrols, \ #define SND_SOC_DAPM_SWITCH_E(wname, wreg, wshift, winvert, wcontrols, \
wevent, wflags) \ wevent, wflags) \
{ .id = snd_soc_dapm_switch, .name = wname, .reg = wreg, .shift = wshift, \ { .id = snd_soc_dapm_switch, .name = wname, .reg = wreg, .shift = wshift, \
.invert = winvert, .kcontrols = wcontrols, .num_kcontrols = 1, \ .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = 1, \
.event = wevent, .event_flags = wflags} .event = wevent, .event_flags = wflags}
#define SND_SOC_DAPM_MUX_E(wname, wreg, wshift, winvert, wcontrols, \ #define SND_SOC_DAPM_MUX_E(wname, wreg, wshift, winvert, wcontrols, \
wevent, wflags) \ wevent, wflags) \
{ .id = snd_soc_dapm_mux, .name = wname, .reg = wreg, .shift = wshift, \ { .id = snd_soc_dapm_mux, .name = wname, .reg = wreg, .shift = wshift, \
.invert = winvert, .kcontrols = wcontrols, .num_kcontrols = 1, \ .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = 1, \
.event = wevent, .event_flags = wflags} .event = wevent, .event_flags = wflags}
#define SND_SOC_DAPM_VIRT_MUX_E(wname, wreg, wshift, winvert, wcontrols, \ #define SND_SOC_DAPM_VIRT_MUX_E(wname, wreg, wshift, winvert, wcontrols, \
wevent, wflags) \ wevent, wflags) \
{ .id = snd_soc_dapm_virt_mux, .name = wname, .reg = wreg, .shift = wshift, \ { .id = snd_soc_dapm_virt_mux, .name = wname, .reg = wreg, .shift = wshift, \
.invert = winvert, .kcontrols = wcontrols, .num_kcontrols = 1, \ .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = 1, \
.event = wevent, .event_flags = wflags} .event = wevent, .event_flags = wflags}
/* additional sequencing control within an event type */ /* additional sequencing control within an event type */
@ -173,26 +173,26 @@
#define SOC_PGA_E_ARRAY(wname, wreg, wshift, winvert, wcontrols, \ #define SOC_PGA_E_ARRAY(wname, wreg, wshift, winvert, wcontrols, \
wevent, wflags) \ wevent, wflags) \
{ .id = snd_soc_dapm_pga, .name = wname, .reg = wreg, .shift = wshift, \ { .id = snd_soc_dapm_pga, .name = wname, .reg = wreg, .shift = wshift, \
.invert = winvert, .kcontrols = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols), \ .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols), \
.event = wevent, .event_flags = wflags} .event = wevent, .event_flags = wflags}
#define SOC_MIXER_E_ARRAY(wname, wreg, wshift, winvert, wcontrols, \ #define SOC_MIXER_E_ARRAY(wname, wreg, wshift, winvert, wcontrols, \
wevent, wflags) \ wevent, wflags) \
{ .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \ { .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \
.invert = winvert, .kcontrols = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols), \ .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols), \
.event = wevent, .event_flags = wflags} .event = wevent, .event_flags = wflags}
#define SOC_MIXER_NAMED_CTL_E_ARRAY(wname, wreg, wshift, winvert, \ #define SOC_MIXER_NAMED_CTL_E_ARRAY(wname, wreg, wshift, winvert, \
wcontrols, wevent, wflags) \ wcontrols, wevent, wflags) \
{ .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \ { .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \
.invert = winvert, .kcontrols = wcontrols, \ .invert = winvert, .kcontrol_news = wcontrols, \
.num_kcontrols = ARRAY_SIZE(wcontrols), .event = wevent, .event_flags = wflags} .num_kcontrols = ARRAY_SIZE(wcontrols), .event = wevent, .event_flags = wflags}
/* events that are pre and post DAPM */ /* events that are pre and post DAPM */
#define SND_SOC_DAPM_PRE(wname, wevent) \ #define SND_SOC_DAPM_PRE(wname, wevent) \
{ .id = snd_soc_dapm_pre, .name = wname, .kcontrols = NULL, \ { .id = snd_soc_dapm_pre, .name = wname, .kcontrol_news = NULL, \
.num_kcontrols = 0, .reg = SND_SOC_NOPM, .event = wevent, \ .num_kcontrols = 0, .reg = SND_SOC_NOPM, .event = wevent, \
.event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD} .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD}
#define SND_SOC_DAPM_POST(wname, wevent) \ #define SND_SOC_DAPM_POST(wname, wevent) \
{ .id = snd_soc_dapm_post, .name = wname, .kcontrols = NULL, \ { .id = snd_soc_dapm_post, .name = wname, .kcontrol_news = NULL, \
.num_kcontrols = 0, .reg = SND_SOC_NOPM, .event = wevent, \ .num_kcontrols = 0, .reg = SND_SOC_NOPM, .event = wevent, \
.event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD} .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD}
@ -232,7 +232,7 @@
/* generic widgets */ /* generic widgets */
#define SND_SOC_DAPM_REG(wid, wname, wreg, wshift, wmask, won_val, woff_val) \ #define SND_SOC_DAPM_REG(wid, wname, wreg, wshift, wmask, won_val, woff_val) \
{ .id = wid, .name = wname, .kcontrols = NULL, .num_kcontrols = 0, \ { .id = wid, .name = wname, .kcontrol_news = NULL, .num_kcontrols = 0, \
.reg = -((wreg) + 1), .shift = wshift, .mask = wmask, \ .reg = -((wreg) + 1), .shift = wshift, .mask = wmask, \
.on_val = won_val, .off_val = woff_val, .event = dapm_reg_event, \ .on_val = won_val, .off_val = woff_val, .event = dapm_reg_event, \
.event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD} .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD}
@ -356,7 +356,8 @@ void snd_soc_dapm_shutdown(struct snd_soc_card *card);
/* dapm sys fs - used by the core */ /* dapm sys fs - used by the core */
int snd_soc_dapm_sys_add(struct device *dev); int snd_soc_dapm_sys_add(struct device *dev);
void snd_soc_dapm_debugfs_init(struct snd_soc_dapm_context *dapm); void snd_soc_dapm_debugfs_init(struct snd_soc_dapm_context *dapm,
struct dentry *parent);
/* dapm audio pin control and status */ /* dapm audio pin control and status */
int snd_soc_dapm_enable_pin(struct snd_soc_dapm_context *dapm, int snd_soc_dapm_enable_pin(struct snd_soc_dapm_context *dapm,
@ -472,7 +473,8 @@ struct snd_soc_dapm_widget {
/* kcontrols that relate to this widget */ /* kcontrols that relate to this widget */
int num_kcontrols; int num_kcontrols;
const struct snd_kcontrol_new *kcontrols; const struct snd_kcontrol_new *kcontrol_news;
struct snd_kcontrol **kcontrols;
/* widget input and outputs */ /* widget input and outputs */
struct list_head sources; struct list_head sources;
@ -516,4 +518,10 @@ struct snd_soc_dapm_context {
#endif #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 #endif

View file

@ -120,7 +120,7 @@
*/ */
#define PM860X_DAPM_OUTPUT(wname, wevent) \ #define PM860X_DAPM_OUTPUT(wname, wevent) \
{ .id = snd_soc_dapm_pga, .name = wname, .reg = SND_SOC_NOPM, \ { .id = snd_soc_dapm_pga, .name = wname, .reg = SND_SOC_NOPM, \
.shift = 0, .invert = 0, .kcontrols = NULL, \ .shift = 0, .invert = 0, .kcontrol_news = NULL, \
.num_kcontrols = 0, .event = wevent, \ .num_kcontrols = 0, .event = wevent, \
.event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD, } .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD, }

View file

@ -40,17 +40,25 @@
#include <sound/pcm_params.h> #include <sound/pcm_params.h>
#include <sound/soc.h> #include <sound/soc.h>
#include <sound/initval.h> #include <sound/initval.h>
#include <sound/tlv.h>
#include "ssm2602.h" #include "ssm2602.h"
#define SSM2602_VERSION "0.1" #define SSM2602_VERSION "0.1"
enum ssm2602_type {
SSM2602,
SSM2604,
};
/* codec private data */ /* codec private data */
struct ssm2602_priv { struct ssm2602_priv {
unsigned int sysclk; unsigned int sysclk;
enum snd_soc_control_type control_type; enum snd_soc_control_type control_type;
struct snd_pcm_substream *master_substream; struct snd_pcm_substream *master_substream;
struct snd_pcm_substream *slave_substream; struct snd_pcm_substream *slave_substream;
enum ssm2602_type type;
}; };
/* /*
@ -60,8 +68,8 @@ struct ssm2602_priv {
* There is no point in caching the reset register * There is no point in caching the reset register
*/ */
static const u16 ssm2602_reg[SSM2602_CACHEREGNUM] = { static const u16 ssm2602_reg[SSM2602_CACHEREGNUM] = {
0x0017, 0x0017, 0x0079, 0x0079, 0x0097, 0x0097, 0x0079, 0x0079,
0x0000, 0x0000, 0x0000, 0x000a, 0x000a, 0x0008, 0x009f, 0x000a,
0x0000, 0x0000 0x0000, 0x0000
}; };
@ -80,172 +88,186 @@ static const struct soc_enum ssm2602_enum[] = {
SOC_ENUM_SINGLE(SSM2602_APDIGI, 1, 4, ssm2602_deemph), SOC_ENUM_SINGLE(SSM2602_APDIGI, 1, 4, ssm2602_deemph),
}; };
static const struct snd_kcontrol_new ssm2602_snd_controls[] = { static const unsigned int ssm260x_outmix_tlv[] = {
TLV_DB_RANGE_HEAD(2),
0, 47, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 0),
48, 127, TLV_DB_SCALE_ITEM(-7400, 100, 0),
};
SOC_DOUBLE_R("Master Playback Volume", SSM2602_LOUT1V, SSM2602_ROUT1V, static const DECLARE_TLV_DB_SCALE(ssm260x_inpga_tlv, -3450, 150, 0);
0, 127, 0), static const DECLARE_TLV_DB_SCALE(ssm260x_sidetone_tlv, -1500, 300, 0);
SOC_DOUBLE_R("Master Playback ZC Switch", SSM2602_LOUT1V, SSM2602_ROUT1V,
7, 1, 0),
SOC_DOUBLE_R("Capture Volume", SSM2602_LINVOL, SSM2602_RINVOL, 0, 31, 0), static const struct snd_kcontrol_new ssm260x_snd_controls[] = {
SOC_DOUBLE_R_TLV("Capture Volume", SSM2602_LINVOL, SSM2602_RINVOL, 0, 45, 0,
ssm260x_inpga_tlv),
SOC_DOUBLE_R("Capture Switch", SSM2602_LINVOL, SSM2602_RINVOL, 7, 1, 1), SOC_DOUBLE_R("Capture Switch", SSM2602_LINVOL, SSM2602_RINVOL, 7, 1, 1),
SOC_SINGLE("Mic Boost (+20dB)", SSM2602_APANA, 0, 1, 0),
SOC_SINGLE("Mic Boost2 (+20dB)", SSM2602_APANA, 7, 1, 0),
SOC_SINGLE("Mic Switch", SSM2602_APANA, 1, 1, 1),
SOC_SINGLE("Sidetone Playback Volume", SSM2602_APANA, 6, 3, 1),
SOC_SINGLE("ADC High Pass Filter Switch", SSM2602_APDIGI, 0, 1, 1), SOC_SINGLE("ADC High Pass Filter Switch", SSM2602_APDIGI, 0, 1, 1),
SOC_SINGLE("Store DC Offset Switch", SSM2602_APDIGI, 4, 1, 0), SOC_SINGLE("Store DC Offset Switch", SSM2602_APDIGI, 4, 1, 0),
SOC_ENUM("Capture Source", ssm2602_enum[0]),
SOC_ENUM("Playback De-emphasis", ssm2602_enum[1]), SOC_ENUM("Playback De-emphasis", ssm2602_enum[1]),
}; };
static const struct snd_kcontrol_new ssm2602_snd_controls[] = {
SOC_DOUBLE_R_TLV("Master Playback Volume", SSM2602_LOUT1V, SSM2602_ROUT1V,
0, 127, 0, ssm260x_outmix_tlv),
SOC_DOUBLE_R("Master Playback ZC Switch", SSM2602_LOUT1V, SSM2602_ROUT1V,
7, 1, 0),
SOC_SINGLE_TLV("Sidetone Playback Volume", SSM2602_APANA, 6, 3, 1,
ssm260x_sidetone_tlv),
SOC_SINGLE("Mic Boost (+20dB)", SSM2602_APANA, 0, 1, 0),
SOC_SINGLE("Mic Boost2 (+20dB)", SSM2602_APANA, 8, 1, 0),
SOC_SINGLE("Mic Switch", SSM2602_APANA, 1, 1, 1),
};
/* Output Mixer */ /* Output Mixer */
static const struct snd_kcontrol_new ssm2602_output_mixer_controls[] = { static const struct snd_kcontrol_new ssm260x_output_mixer_controls[] = {
SOC_DAPM_SINGLE("Line Bypass Switch", SSM2602_APANA, 3, 1, 0), SOC_DAPM_SINGLE("Line Bypass Switch", SSM2602_APANA, 3, 1, 0),
SOC_DAPM_SINGLE("Mic Sidetone Switch", SSM2602_APANA, 5, 1, 0),
SOC_DAPM_SINGLE("HiFi Playback Switch", SSM2602_APANA, 4, 1, 0), SOC_DAPM_SINGLE("HiFi Playback Switch", SSM2602_APANA, 4, 1, 0),
SOC_DAPM_SINGLE("Mic Sidetone Switch", SSM2602_APANA, 5, 1, 0),
}; };
/* Input mux */ /* Input mux */
static const struct snd_kcontrol_new ssm2602_input_mux_controls = static const struct snd_kcontrol_new ssm2602_input_mux_controls =
SOC_DAPM_ENUM("Input Select", ssm2602_enum[0]); SOC_DAPM_ENUM("Input Select", ssm2602_enum[0]);
static const struct snd_soc_dapm_widget ssm2602_dapm_widgets[] = { static const struct snd_soc_dapm_widget ssm260x_dapm_widgets[] = {
SND_SOC_DAPM_MIXER("Output Mixer", SSM2602_PWR, 4, 1,
&ssm2602_output_mixer_controls[0],
ARRAY_SIZE(ssm2602_output_mixer_controls)),
SND_SOC_DAPM_DAC("DAC", "HiFi Playback", SSM2602_PWR, 3, 1), SND_SOC_DAPM_DAC("DAC", "HiFi Playback", SSM2602_PWR, 3, 1),
SND_SOC_DAPM_OUTPUT("LOUT"),
SND_SOC_DAPM_OUTPUT("LHPOUT"),
SND_SOC_DAPM_OUTPUT("ROUT"),
SND_SOC_DAPM_OUTPUT("RHPOUT"),
SND_SOC_DAPM_ADC("ADC", "HiFi Capture", SSM2602_PWR, 2, 1), SND_SOC_DAPM_ADC("ADC", "HiFi Capture", SSM2602_PWR, 2, 1),
SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0, &ssm2602_input_mux_controls),
SND_SOC_DAPM_PGA("Line Input", SSM2602_PWR, 0, 1, NULL, 0), SND_SOC_DAPM_PGA("Line Input", SSM2602_PWR, 0, 1, NULL, 0),
SND_SOC_DAPM_MICBIAS("Mic Bias", SSM2602_PWR, 1, 1),
SND_SOC_DAPM_INPUT("MICIN"), SND_SOC_DAPM_SUPPLY("Digital Core Power", SSM2602_ACTIVE, 0, 0, 0, 0),
SND_SOC_DAPM_OUTPUT("LOUT"),
SND_SOC_DAPM_OUTPUT("ROUT"),
SND_SOC_DAPM_INPUT("RLINEIN"), SND_SOC_DAPM_INPUT("RLINEIN"),
SND_SOC_DAPM_INPUT("LLINEIN"), SND_SOC_DAPM_INPUT("LLINEIN"),
}; };
static const struct snd_soc_dapm_route audio_conn[] = { static const struct snd_soc_dapm_widget ssm2602_dapm_widgets[] = {
/* output mixer */ SND_SOC_DAPM_MIXER("Output Mixer", SSM2602_PWR, 4, 1,
ssm260x_output_mixer_controls,
ARRAY_SIZE(ssm260x_output_mixer_controls)),
SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0, &ssm2602_input_mux_controls),
SND_SOC_DAPM_MICBIAS("Mic Bias", SSM2602_PWR, 1, 1),
SND_SOC_DAPM_OUTPUT("LHPOUT"),
SND_SOC_DAPM_OUTPUT("RHPOUT"),
SND_SOC_DAPM_INPUT("MICIN"),
};
static const struct snd_soc_dapm_widget ssm2604_dapm_widgets[] = {
SND_SOC_DAPM_MIXER("Output Mixer", SND_SOC_NOPM, 0, 0,
ssm260x_output_mixer_controls,
ARRAY_SIZE(ssm260x_output_mixer_controls) - 1), /* Last element is the mic */
};
static const struct snd_soc_dapm_route ssm260x_routes[] = {
{"DAC", NULL, "Digital Core Power"},
{"ADC", NULL, "Digital Core Power"},
{"Output Mixer", "Line Bypass Switch", "Line Input"}, {"Output Mixer", "Line Bypass Switch", "Line Input"},
{"Output Mixer", "HiFi Playback Switch", "DAC"}, {"Output Mixer", "HiFi Playback Switch", "DAC"},
{"Output Mixer", "Mic Sidetone Switch", "Mic Bias"},
/* outputs */
{"RHPOUT", NULL, "Output Mixer"},
{"ROUT", NULL, "Output Mixer"}, {"ROUT", NULL, "Output Mixer"},
{"LHPOUT", NULL, "Output Mixer"},
{"LOUT", NULL, "Output Mixer"}, {"LOUT", NULL, "Output Mixer"},
/* input mux */ {"Line Input", NULL, "LLINEIN"},
{"Line Input", NULL, "RLINEIN"},
};
static const struct snd_soc_dapm_route ssm2602_routes[] = {
{"Output Mixer", "Mic Sidetone Switch", "Mic Bias"},
{"RHPOUT", NULL, "Output Mixer"},
{"LHPOUT", NULL, "Output Mixer"},
{"Input Mux", "Line", "Line Input"}, {"Input Mux", "Line", "Line Input"},
{"Input Mux", "Mic", "Mic Bias"}, {"Input Mux", "Mic", "Mic Bias"},
{"ADC", NULL, "Input Mux"}, {"ADC", NULL, "Input Mux"},
/* inputs */
{"Line Input", NULL, "LLINEIN"},
{"Line Input", NULL, "RLINEIN"},
{"Mic Bias", NULL, "MICIN"}, {"Mic Bias", NULL, "MICIN"},
}; };
static int ssm2602_add_widgets(struct snd_soc_codec *codec) static const struct snd_soc_dapm_route ssm2604_routes[] = {
{ {"ADC", NULL, "Line Input"},
struct snd_soc_dapm_context *dapm = &codec->dapm; };
snd_soc_dapm_new_controls(dapm, ssm2602_dapm_widgets, struct ssm2602_coeff {
ARRAY_SIZE(ssm2602_dapm_widgets));
snd_soc_dapm_add_routes(dapm, audio_conn, ARRAY_SIZE(audio_conn));
return 0;
}
struct _coeff_div {
u32 mclk; u32 mclk;
u32 rate; u32 rate;
u16 fs; u8 srate;
u8 sr:4;
u8 bosr:1;
u8 usb:1;
}; };
/* codec mclk clock divider coefficients */ #define SSM2602_COEFF_SRATE(sr, bosr, usb) (((sr) << 2) | ((bosr) << 1) | (usb))
static const struct _coeff_div coeff_div[] = {
/* codec mclk clock coefficients */
static const struct ssm2602_coeff ssm2602_coeff_table[] = {
/* 48k */ /* 48k */
{12288000, 48000, 256, 0x0, 0x0, 0x0}, {12288000, 48000, SSM2602_COEFF_SRATE(0x0, 0x0, 0x0)},
{18432000, 48000, 384, 0x0, 0x1, 0x0}, {18432000, 48000, SSM2602_COEFF_SRATE(0x0, 0x1, 0x0)},
{12000000, 48000, 250, 0x0, 0x0, 0x1}, {12000000, 48000, SSM2602_COEFF_SRATE(0x0, 0x0, 0x1)},
/* 32k */ /* 32k */
{12288000, 32000, 384, 0x6, 0x0, 0x0}, {12288000, 32000, SSM2602_COEFF_SRATE(0x6, 0x0, 0x0)},
{18432000, 32000, 576, 0x6, 0x1, 0x0}, {18432000, 32000, SSM2602_COEFF_SRATE(0x6, 0x1, 0x0)},
{12000000, 32000, 375, 0x6, 0x0, 0x1}, {12000000, 32000, SSM2602_COEFF_SRATE(0x6, 0x0, 0x1)},
/* 8k */ /* 8k */
{12288000, 8000, 1536, 0x3, 0x0, 0x0}, {12288000, 8000, SSM2602_COEFF_SRATE(0x3, 0x0, 0x0)},
{18432000, 8000, 2304, 0x3, 0x1, 0x0}, {18432000, 8000, SSM2602_COEFF_SRATE(0x3, 0x1, 0x0)},
{11289600, 8000, 1408, 0xb, 0x0, 0x0}, {11289600, 8000, SSM2602_COEFF_SRATE(0xb, 0x0, 0x0)},
{16934400, 8000, 2112, 0xb, 0x1, 0x0}, {16934400, 8000, SSM2602_COEFF_SRATE(0xb, 0x1, 0x0)},
{12000000, 8000, 1500, 0x3, 0x0, 0x1}, {12000000, 8000, SSM2602_COEFF_SRATE(0x3, 0x0, 0x1)},
/* 96k */ /* 96k */
{12288000, 96000, 128, 0x7, 0x0, 0x0}, {12288000, 96000, SSM2602_COEFF_SRATE(0x7, 0x0, 0x0)},
{18432000, 96000, 192, 0x7, 0x1, 0x0}, {18432000, 96000, SSM2602_COEFF_SRATE(0x7, 0x1, 0x0)},
{12000000, 96000, 125, 0x7, 0x0, 0x1}, {12000000, 96000, SSM2602_COEFF_SRATE(0x7, 0x0, 0x1)},
/* 44.1k */ /* 44.1k */
{11289600, 44100, 256, 0x8, 0x0, 0x0}, {11289600, 44100, SSM2602_COEFF_SRATE(0x8, 0x0, 0x0)},
{16934400, 44100, 384, 0x8, 0x1, 0x0}, {16934400, 44100, SSM2602_COEFF_SRATE(0x8, 0x1, 0x0)},
{12000000, 44100, 272, 0x8, 0x1, 0x1}, {12000000, 44100, SSM2602_COEFF_SRATE(0x8, 0x1, 0x1)},
/* 88.2k */ /* 88.2k */
{11289600, 88200, 128, 0xf, 0x0, 0x0}, {11289600, 88200, SSM2602_COEFF_SRATE(0xf, 0x0, 0x0)},
{16934400, 88200, 192, 0xf, 0x1, 0x0}, {16934400, 88200, SSM2602_COEFF_SRATE(0xf, 0x1, 0x0)},
{12000000, 88200, 136, 0xf, 0x1, 0x1}, {12000000, 88200, SSM2602_COEFF_SRATE(0xf, 0x1, 0x1)},
}; };
static inline int get_coeff(int mclk, int rate) static inline int ssm2602_get_coeff(int mclk, int rate)
{ {
int i; int i;
for (i = 0; i < ARRAY_SIZE(coeff_div); i++) { for (i = 0; i < ARRAY_SIZE(ssm2602_coeff_table); i++) {
if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk) if (ssm2602_coeff_table[i].rate == rate &&
return i; ssm2602_coeff_table[i].mclk == mclk)
return ssm2602_coeff_table[i].srate;
} }
return i; return -EINVAL;
} }
static int ssm2602_hw_params(struct snd_pcm_substream *substream, static int ssm2602_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai) struct snd_soc_dai *dai)
{ {
u16 srate;
struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_codec *codec = rtd->codec; struct snd_soc_codec *codec = rtd->codec;
struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec); struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec);
u16 iface = snd_soc_read(codec, SSM2602_IFACE) & 0xfff3; u16 iface = snd_soc_read(codec, SSM2602_IFACE) & 0xfff3;
int i = get_coeff(ssm2602->sysclk, params_rate(params)); int srate = ssm2602_get_coeff(ssm2602->sysclk, params_rate(params));
if (substream == ssm2602->slave_substream) { if (substream == ssm2602->slave_substream) {
dev_dbg(codec->dev, "Ignoring hw_params for slave substream\n"); dev_dbg(codec->dev, "Ignoring hw_params for slave substream\n");
return 0; return 0;
} }
/*no match is found*/ if (srate < 0)
if (i == ARRAY_SIZE(coeff_div)) return srate;
return -EINVAL;
srate = (coeff_div[i].sr << 2) |
(coeff_div[i].bosr << 1) | coeff_div[i].usb;
snd_soc_write(codec, SSM2602_ACTIVE, 0);
snd_soc_write(codec, SSM2602_SRATE, srate); snd_soc_write(codec, SSM2602_SRATE, srate);
/* bit size */ /* bit size */
@ -263,7 +285,6 @@ static int ssm2602_hw_params(struct snd_pcm_substream *substream,
break; break;
} }
snd_soc_write(codec, SSM2602_IFACE, iface); snd_soc_write(codec, SSM2602_IFACE, iface);
snd_soc_write(codec, SSM2602_ACTIVE, ACTIVE_ACTIVATE_CODEC);
return 0; return 0;
} }
@ -305,17 +326,6 @@ static int ssm2602_startup(struct snd_pcm_substream *substream,
return 0; return 0;
} }
static int ssm2602_pcm_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_codec *codec = rtd->codec;
/* set active */
snd_soc_write(codec, SSM2602_ACTIVE, ACTIVE_ACTIVATE_CODEC);
return 0;
}
static void ssm2602_shutdown(struct snd_pcm_substream *substream, static void ssm2602_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai) struct snd_soc_dai *dai)
{ {
@ -323,16 +333,13 @@ static void ssm2602_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_codec *codec = rtd->codec; struct snd_soc_codec *codec = rtd->codec;
struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec); struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec);
/* deactivate */
if (!codec->active)
snd_soc_write(codec, SSM2602_ACTIVE, 0);
if (ssm2602->master_substream == substream) if (ssm2602->master_substream == substream)
ssm2602->master_substream = ssm2602->slave_substream; ssm2602->master_substream = ssm2602->slave_substream;
ssm2602->slave_substream = NULL; ssm2602->slave_substream = NULL;
} }
static int ssm2602_mute(struct snd_soc_dai *dai, int mute) static int ssm2602_mute(struct snd_soc_dai *dai, int mute)
{ {
struct snd_soc_codec *codec = dai->codec; struct snd_soc_codec *codec = dai->codec;
@ -439,7 +446,6 @@ static int ssm2602_set_bias_level(struct snd_soc_codec *codec,
break; break;
case SND_SOC_BIAS_OFF: case SND_SOC_BIAS_OFF:
/* everything off, dac mute, inactive */ /* everything off, dac mute, inactive */
snd_soc_write(codec, SSM2602_ACTIVE, 0);
snd_soc_write(codec, SSM2602_PWR, 0xffff); snd_soc_write(codec, SSM2602_PWR, 0xffff);
break; break;
@ -457,7 +463,6 @@ static int ssm2602_set_bias_level(struct snd_soc_codec *codec,
static struct snd_soc_dai_ops ssm2602_dai_ops = { static struct snd_soc_dai_ops ssm2602_dai_ops = {
.startup = ssm2602_startup, .startup = ssm2602_startup,
.prepare = ssm2602_pcm_prepare,
.hw_params = ssm2602_hw_params, .hw_params = ssm2602_hw_params,
.shutdown = ssm2602_shutdown, .shutdown = ssm2602_shutdown,
.digital_mute = ssm2602_mute, .digital_mute = ssm2602_mute,
@ -498,9 +503,47 @@ static int ssm2602_resume(struct snd_soc_codec *codec)
} }
static int ssm2602_probe(struct snd_soc_codec *codec) static int ssm2602_probe(struct snd_soc_codec *codec)
{
struct snd_soc_dapm_context *dapm = &codec->dapm;
int ret, reg;
reg = snd_soc_read(codec, SSM2602_LOUT1V);
snd_soc_write(codec, SSM2602_LOUT1V, reg | LOUT1V_LRHP_BOTH);
reg = snd_soc_read(codec, SSM2602_ROUT1V);
snd_soc_write(codec, SSM2602_ROUT1V, reg | ROUT1V_RLHP_BOTH);
ret = snd_soc_add_controls(codec, ssm2602_snd_controls,
ARRAY_SIZE(ssm2602_snd_controls));
if (ret)
return ret;
ret = snd_soc_dapm_new_controls(dapm, ssm2602_dapm_widgets,
ARRAY_SIZE(ssm2602_dapm_widgets));
if (ret)
return ret;
return snd_soc_dapm_add_routes(dapm, ssm2602_routes,
ARRAY_SIZE(ssm2602_routes));
}
static int ssm2604_probe(struct snd_soc_codec *codec)
{
struct snd_soc_dapm_context *dapm = &codec->dapm;
int ret;
ret = snd_soc_dapm_new_controls(dapm, ssm2604_dapm_widgets,
ARRAY_SIZE(ssm2604_dapm_widgets));
if (ret)
return ret;
return snd_soc_dapm_add_routes(dapm, ssm2604_routes,
ARRAY_SIZE(ssm2604_routes));
}
static int ssm260x_probe(struct snd_soc_codec *codec)
{ {
struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec); struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec);
int ret = 0, reg; int ret, reg;
pr_info("ssm2602 Audio Codec %s", SSM2602_VERSION); pr_info("ssm2602 Audio Codec %s", SSM2602_VERSION);
@ -516,27 +559,25 @@ static int ssm2602_probe(struct snd_soc_codec *codec)
return ret; return ret;
} }
/*power on device*/
snd_soc_write(codec, SSM2602_ACTIVE, 0);
/* set the update bits */ /* set the update bits */
reg = snd_soc_read(codec, SSM2602_LINVOL); reg = snd_soc_read(codec, SSM2602_LINVOL);
snd_soc_write(codec, SSM2602_LINVOL, reg | LINVOL_LRIN_BOTH); snd_soc_write(codec, SSM2602_LINVOL, reg | LINVOL_LRIN_BOTH);
reg = snd_soc_read(codec, SSM2602_RINVOL); reg = snd_soc_read(codec, SSM2602_RINVOL);
snd_soc_write(codec, SSM2602_RINVOL, reg | RINVOL_RLIN_BOTH); snd_soc_write(codec, SSM2602_RINVOL, reg | RINVOL_RLIN_BOTH);
reg = snd_soc_read(codec, SSM2602_LOUT1V);
snd_soc_write(codec, SSM2602_LOUT1V, reg | LOUT1V_LRHP_BOTH);
reg = snd_soc_read(codec, SSM2602_ROUT1V);
snd_soc_write(codec, SSM2602_ROUT1V, reg | ROUT1V_RLHP_BOTH);
/*select Line in as default input*/ /*select Line in as default input*/
snd_soc_write(codec, SSM2602_APANA, APANA_SELECT_DAC | snd_soc_write(codec, SSM2602_APANA, APANA_SELECT_DAC |
APANA_ENABLE_MIC_BOOST); APANA_ENABLE_MIC_BOOST);
snd_soc_write(codec, SSM2602_PWR, 0);
snd_soc_add_controls(codec, ssm2602_snd_controls, switch (ssm2602->type) {
ARRAY_SIZE(ssm2602_snd_controls)); case SSM2602:
ssm2602_add_widgets(codec); ret = ssm2602_probe(codec);
break;
case SSM2604:
ret = ssm2604_probe(codec);
break;
}
return 0; return ret;
} }
/* remove everything here */ /* remove everything here */
@ -547,14 +588,21 @@ static int ssm2602_remove(struct snd_soc_codec *codec)
} }
static struct snd_soc_codec_driver soc_codec_dev_ssm2602 = { static struct snd_soc_codec_driver soc_codec_dev_ssm2602 = {
.probe = ssm2602_probe, .probe = ssm260x_probe,
.remove = ssm2602_remove, .remove = ssm2602_remove,
.suspend = ssm2602_suspend, .suspend = ssm2602_suspend,
.resume = ssm2602_resume, .resume = ssm2602_resume,
.set_bias_level = ssm2602_set_bias_level, .set_bias_level = ssm2602_set_bias_level,
.reg_cache_size = sizeof(ssm2602_reg), .reg_cache_size = ARRAY_SIZE(ssm2602_reg),
.reg_word_size = sizeof(u16), .reg_word_size = sizeof(u16),
.reg_cache_default = ssm2602_reg, .reg_cache_default = ssm2602_reg,
.controls = ssm260x_snd_controls,
.num_controls = ARRAY_SIZE(ssm260x_snd_controls),
.dapm_widgets = ssm260x_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(ssm260x_dapm_widgets),
.dapm_routes = ssm260x_routes,
.num_dapm_routes = ARRAY_SIZE(ssm260x_routes),
}; };
#if defined(CONFIG_SPI_MASTER) #if defined(CONFIG_SPI_MASTER)
@ -569,6 +617,7 @@ static int __devinit ssm2602_spi_probe(struct spi_device *spi)
spi_set_drvdata(spi, ssm2602); spi_set_drvdata(spi, ssm2602);
ssm2602->control_type = SND_SOC_SPI; ssm2602->control_type = SND_SOC_SPI;
ssm2602->type = SSM2602;
ret = snd_soc_register_codec(&spi->dev, ret = snd_soc_register_codec(&spi->dev,
&soc_codec_dev_ssm2602, &ssm2602_dai, 1); &soc_codec_dev_ssm2602, &ssm2602_dai, 1);
@ -601,7 +650,7 @@ static struct spi_driver ssm2602_spi_driver = {
* low = 0x1a * low = 0x1a
* high = 0x1b * high = 0x1b
*/ */
static int ssm2602_i2c_probe(struct i2c_client *i2c, static int __devinit ssm2602_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id) const struct i2c_device_id *id)
{ {
struct ssm2602_priv *ssm2602; struct ssm2602_priv *ssm2602;
@ -613,6 +662,7 @@ static int ssm2602_i2c_probe(struct i2c_client *i2c,
i2c_set_clientdata(i2c, ssm2602); i2c_set_clientdata(i2c, ssm2602);
ssm2602->control_type = SND_SOC_I2C; ssm2602->control_type = SND_SOC_I2C;
ssm2602->type = id->driver_data;
ret = snd_soc_register_codec(&i2c->dev, ret = snd_soc_register_codec(&i2c->dev,
&soc_codec_dev_ssm2602, &ssm2602_dai, 1); &soc_codec_dev_ssm2602, &ssm2602_dai, 1);
@ -621,7 +671,7 @@ static int ssm2602_i2c_probe(struct i2c_client *i2c,
return ret; return ret;
} }
static int ssm2602_i2c_remove(struct i2c_client *client) static int __devexit ssm2602_i2c_remove(struct i2c_client *client)
{ {
snd_soc_unregister_codec(&client->dev); snd_soc_unregister_codec(&client->dev);
kfree(i2c_get_clientdata(client)); kfree(i2c_get_clientdata(client));
@ -629,7 +679,9 @@ static int ssm2602_i2c_remove(struct i2c_client *client)
} }
static const struct i2c_device_id ssm2602_i2c_id[] = { static const struct i2c_device_id ssm2602_i2c_id[] = {
{ "ssm2602", 0 }, { "ssm2602", SSM2602 },
{ "ssm2603", SSM2602 },
{ "ssm2604", SSM2604 },
{ } { }
}; };
MODULE_DEVICE_TABLE(i2c, ssm2602_i2c_id); MODULE_DEVICE_TABLE(i2c, ssm2602_i2c_id);
@ -641,7 +693,7 @@ static struct i2c_driver ssm2602_i2c_driver = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
}, },
.probe = ssm2602_i2c_probe, .probe = ssm2602_i2c_probe,
.remove = ssm2602_i2c_remove, .remove = __devexit_p(ssm2602_i2c_remove),
.id_table = ssm2602_i2c_id, .id_table = ssm2602_i2c_id,
}; };
#endif #endif
@ -679,6 +731,6 @@ static void __exit ssm2602_exit(void)
} }
module_exit(ssm2602_exit); module_exit(ssm2602_exit);
MODULE_DESCRIPTION("ASoC ssm2602 driver"); MODULE_DESCRIPTION("ASoC SSM2602/SSM2603/SSM2604 driver");
MODULE_AUTHOR("Cliff Cai"); MODULE_AUTHOR("Cliff Cai");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");

View file

@ -117,11 +117,5 @@
#define SSM2602_CACHEREGNUM 10 #define SSM2602_CACHEREGNUM 10
#define SSM2602_SYSCLK 0 #define SSM2602_SYSCLK 0
#define SSM2602_DAI 0
struct ssm2602_setup_data {
int i2c_bus;
unsigned short i2c_address;
};
#endif #endif

View file

@ -601,9 +601,7 @@ static struct snd_soc_codec_driver soc_codec_dev_uda134x = {
.reg_cache_step = 1, .reg_cache_step = 1,
.read = uda134x_read_reg_cache, .read = uda134x_read_reg_cache,
.write = uda134x_write, .write = uda134x_write,
#ifdef POWER_OFF_ON_STANDBY
.set_bias_level = uda134x_set_bias_level, .set_bias_level = uda134x_set_bias_level,
#endif
}; };
static int __devinit uda134x_codec_probe(struct platform_device *pdev) static int __devinit uda134x_codec_probe(struct platform_device *pdev)

View file

@ -991,17 +991,16 @@ SND_SOC_DAPM_MICBIAS("MICB1", WM8915_POWER_MANAGEMENT_1, 8, 0),
SND_SOC_DAPM_PGA("IN1L PGA", WM8915_POWER_MANAGEMENT_2, 5, 0, NULL, 0), SND_SOC_DAPM_PGA("IN1L PGA", WM8915_POWER_MANAGEMENT_2, 5, 0, NULL, 0),
SND_SOC_DAPM_PGA("IN1R PGA", WM8915_POWER_MANAGEMENT_2, 4, 0, NULL, 0), SND_SOC_DAPM_PGA("IN1R PGA", WM8915_POWER_MANAGEMENT_2, 4, 0, NULL, 0),
SND_SOC_DAPM_PGA("ADC", SND_SOC_NOPM, 0, 0, NULL, 0), SND_SOC_DAPM_MUX("IN1L Mux", SND_SOC_NOPM, 0, 0, &in1_mux),
SND_SOC_DAPM_MUX("IN1R Mux", SND_SOC_NOPM, 0, 0, &in1_mux),
SND_SOC_DAPM_MUX("IN1 Mux", SND_SOC_NOPM, 0, 0, &in1_mux), SND_SOC_DAPM_MUX("IN2L Mux", SND_SOC_NOPM, 0, 0, &in2_mux),
SND_SOC_DAPM_MUX("IN2 Mux", SND_SOC_NOPM, 0, 0, &in2_mux), SND_SOC_DAPM_MUX("IN2R Mux", SND_SOC_NOPM, 0, 0, &in2_mux),
SND_SOC_DAPM_PGA("IN1L", WM8915_POWER_MANAGEMENT_7, 2, 0, NULL, 0), SND_SOC_DAPM_PGA("IN1L", WM8915_POWER_MANAGEMENT_7, 2, 0, NULL, 0),
SND_SOC_DAPM_PGA("IN1R", WM8915_POWER_MANAGEMENT_7, 3, 0, NULL, 0), SND_SOC_DAPM_PGA("IN1R", WM8915_POWER_MANAGEMENT_7, 3, 0, NULL, 0),
SND_SOC_DAPM_PGA("IN2L", WM8915_POWER_MANAGEMENT_7, 6, 0, NULL, 0), SND_SOC_DAPM_PGA("IN2L", WM8915_POWER_MANAGEMENT_7, 6, 0, NULL, 0),
SND_SOC_DAPM_PGA("IN2R", WM8915_POWER_MANAGEMENT_7, 7, 0, NULL, 0), SND_SOC_DAPM_PGA("IN2R", WM8915_POWER_MANAGEMENT_7, 7, 0, NULL, 0),
/* FIXME - these need to be concentrator widgets */
SND_SOC_DAPM_SUPPLY("DMIC2", WM8915_POWER_MANAGEMENT_7, 9, 0, NULL, 0), SND_SOC_DAPM_SUPPLY("DMIC2", WM8915_POWER_MANAGEMENT_7, 9, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("DMIC1", WM8915_POWER_MANAGEMENT_7, 8, 0, NULL, 0), SND_SOC_DAPM_SUPPLY("DMIC1", WM8915_POWER_MANAGEMENT_7, 8, 0, NULL, 0),
@ -1172,28 +1171,33 @@ static const struct snd_soc_dapm_route wm8915_dapm_routes[] = {
{ "DMIC1L", NULL, "DMIC1" }, { "DMIC1L", NULL, "DMIC1" },
{ "DMIC1R", NULL, "DMIC1" }, { "DMIC1R", NULL, "DMIC1" },
{ "ADC", NULL, "ADCL" }, { "IN1L Mux", "ADC", "ADCL" },
{ "ADC", NULL, "ADCR" }, { "IN1L Mux", "DMIC1", "DMIC1L" },
{ "IN1L Mux", "DMIC2", "DMIC2L" },
{ "IN1 Mux", "ADC", "ADC" }, { "IN1R Mux", "ADC", "ADCR" },
{ "IN1 Mux", "DMIC1", "DMIC1" }, { "IN1R Mux", "DMIC1", "DMIC1R" },
{ "IN1 Mux", "DMIC2", "DMIC2" }, { "IN1R Mux", "DMIC2", "DMIC2R" },
{ "IN2 Mux", "ADC", "ADC" }, { "IN2L Mux", "ADC", "ADCL" },
{ "IN2 Mux", "DMIC1", "DMIC1" }, { "IN2L Mux", "DMIC1", "DMIC1L" },
{ "IN2 Mux", "DMIC2", "DMIC2" }, { "IN2L Mux", "DMIC2", "DMIC2L" },
{ "Left Sidetone", "IN1", "IN1 Mux" }, { "IN2R Mux", "ADC", "ADCR" },
{ "Left Sidetone", "IN2", "IN2 Mux" }, { "IN2R Mux", "DMIC1", "DMIC1R" },
{ "IN2R Mux", "DMIC2", "DMIC2R" },
{ "Right Sidetone", "IN1", "IN1 Mux" }, { "Left Sidetone", "IN1", "IN1L Mux" },
{ "Right Sidetone", "IN2", "IN2 Mux" }, { "Left Sidetone", "IN2", "IN2L Mux" },
{ "DSP1TXL", "IN1 Switch", "IN1 Mux" }, { "Right Sidetone", "IN1", "IN1R Mux" },
{ "DSP1TXR", "IN1 Switch", "IN1 Mux" }, { "Right Sidetone", "IN2", "IN2R Mux" },
{ "DSP2TXL", "IN1 Switch", "IN2 Mux" }, { "DSP1TXL", "IN1 Switch", "IN1L Mux" },
{ "DSP2TXR", "IN1 Switch", "IN2 Mux" }, { "DSP1TXR", "IN1 Switch", "IN1R Mux" },
{ "DSP2TXL", "IN1 Switch", "IN2L Mux" },
{ "DSP2TXR", "IN1 Switch", "IN2R Mux" },
{ "AIF1TX0", NULL, "DSP1TXL" }, { "AIF1TX0", NULL, "DSP1TXL" },
{ "AIF1TX1", NULL, "DSP1TXR" }, { "AIF1TX1", NULL, "DSP1TXR" },

View file

@ -133,7 +133,7 @@ static void jz4740_i2s_shutdown(struct snd_pcm_substream *substream,
struct jz4740_i2s *i2s = snd_soc_dai_get_drvdata(dai); struct jz4740_i2s *i2s = snd_soc_dai_get_drvdata(dai);
uint32_t conf; uint32_t conf;
if (!dai->active) if (dai->active)
return; return;
conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF); conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF);

View file

@ -380,6 +380,11 @@ static int sst_platform_pcm_hw_params(struct snd_pcm_substream *substream,
return 0; return 0;
} }
static int sst_platform_pcm_hw_free(struct snd_pcm_substream *substream)
{
return snd_pcm_lib_free_pages(substream);
}
static struct snd_pcm_ops sst_platform_ops = { static struct snd_pcm_ops sst_platform_ops = {
.open = sst_platform_open, .open = sst_platform_open,
.close = sst_platform_close, .close = sst_platform_close,
@ -388,6 +393,7 @@ static struct snd_pcm_ops sst_platform_ops = {
.trigger = sst_platform_pcm_trigger, .trigger = sst_platform_pcm_trigger,
.pointer = sst_platform_pcm_pointer, .pointer = sst_platform_pcm_pointer,
.hw_params = sst_platform_pcm_hw_params, .hw_params = sst_platform_pcm_hw_params,
.hw_free = sst_platform_pcm_hw_free,
}; };
static void sst_pcm_free(struct snd_pcm *pcm) static void sst_pcm_free(struct snd_pcm *pcm)

View file

@ -404,12 +404,13 @@ static int snd_soc_hw_bulk_write_raw(struct snd_soc_codec *codec, unsigned int r
{ {
int ret; int ret;
/* Ensure that the base register is volatile. Subsequently /* To ensure that we don't get out of sync with the cache, check
* any other register that is touched by this routine should be * whether the base register is volatile or if we've directly asked
* volatile as well to ensure that we don't get out of sync with * to bypass the cache. Out of bounds registers are considered
* the cache. * volatile.
*/ */
if (!snd_soc_codec_volatile_register(codec, reg) if (!codec->cache_bypass
&& !snd_soc_codec_volatile_register(codec, reg)
&& reg < codec->driver->reg_cache_size) && reg < codec->driver->reg_cache_size)
return -EINVAL; return -EINVAL;

View file

@ -302,13 +302,7 @@ static void soc_init_codec_debugfs(struct snd_soc_codec *codec)
printk(KERN_WARNING printk(KERN_WARNING
"ASoC: Failed to create codec register debugfs file\n"); "ASoC: Failed to create codec register debugfs file\n");
codec->dapm.debugfs_dapm = debugfs_create_dir("dapm", snd_soc_dapm_debugfs_init(&codec->dapm, codec->debugfs_codec_root);
codec->debugfs_codec_root);
if (!codec->dapm.debugfs_dapm)
printk(KERN_WARNING
"Failed to create DAPM debugfs directory\n");
snd_soc_dapm_debugfs_init(&codec->dapm);
} }
static void soc_cleanup_codec_debugfs(struct snd_soc_codec *codec) static void soc_cleanup_codec_debugfs(struct snd_soc_codec *codec)
@ -1499,6 +1493,12 @@ static int soc_probe_codec(struct snd_soc_card *card,
if (!try_module_get(codec->dev->driver->owner)) if (!try_module_get(codec->dev->driver->owner))
return -ENODEV; return -ENODEV;
soc_init_codec_debugfs(codec);
if (driver->dapm_widgets)
snd_soc_dapm_new_controls(&codec->dapm, driver->dapm_widgets,
driver->num_dapm_widgets);
if (driver->probe) { if (driver->probe) {
ret = driver->probe(codec); ret = driver->probe(codec);
if (ret < 0) { if (ret < 0) {
@ -1512,15 +1512,10 @@ static int soc_probe_codec(struct snd_soc_card *card,
if (driver->controls) if (driver->controls)
snd_soc_add_controls(codec, driver->controls, snd_soc_add_controls(codec, driver->controls,
driver->num_controls); driver->num_controls);
if (driver->dapm_widgets)
snd_soc_dapm_new_controls(&codec->dapm, driver->dapm_widgets,
driver->num_dapm_widgets);
if (driver->dapm_routes) if (driver->dapm_routes)
snd_soc_dapm_add_routes(&codec->dapm, driver->dapm_routes, snd_soc_dapm_add_routes(&codec->dapm, driver->dapm_routes,
driver->num_dapm_routes); driver->num_dapm_routes);
soc_init_codec_debugfs(codec);
/* mark codec as probed and add to card codec list */ /* mark codec as probed and add to card codec list */
codec->probed = 1; codec->probed = 1;
list_add(&codec->card_list, &card->codec_dev_list); list_add(&codec->card_list, &card->codec_dev_list);
@ -1529,6 +1524,7 @@ static int soc_probe_codec(struct snd_soc_card *card,
return 0; return 0;
err_probe: err_probe:
soc_cleanup_codec_debugfs(codec);
module_put(codec->dev->driver->owner); module_put(codec->dev->driver->owner);
return ret; return ret;
@ -1879,6 +1875,10 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card)
card->dapm.card = card; card->dapm.card = card;
list_add(&card->dapm.list, &card->dapm_list); list_add(&card->dapm.list, &card->dapm_list);
#ifdef CONFIG_DEBUG_FS
snd_soc_dapm_debugfs_init(&card->dapm, card->debugfs_card_root);
#endif
#ifdef CONFIG_PM_SLEEP #ifdef CONFIG_PM_SLEEP
/* deferred resume work */ /* deferred resume work */
INIT_WORK(&card->deferred_resume_work, soc_resume_deferred); INIT_WORK(&card->deferred_resume_work, soc_resume_deferred);
@ -1925,16 +1925,6 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card)
snd_soc_dapm_add_routes(&card->dapm, card->dapm_routes, snd_soc_dapm_add_routes(&card->dapm, card->dapm_routes,
card->num_dapm_routes); card->num_dapm_routes);
#ifdef CONFIG_DEBUG_FS
card->dapm.debugfs_dapm = debugfs_create_dir("dapm",
card->debugfs_card_root);
if (!card->dapm.debugfs_dapm)
printk(KERN_WARNING
"Failed to create card DAPM debugfs directory\n");
snd_soc_dapm_debugfs_init(&card->dapm);
#endif
snprintf(card->snd_card->shortname, sizeof(card->snd_card->shortname), snprintf(card->snd_card->shortname, sizeof(card->snd_card->shortname),
"%s", card->name); "%s", card->name);
snprintf(card->snd_card->longname, sizeof(card->snd_card->longname), snprintf(card->snd_card->longname, sizeof(card->snd_card->longname),
@ -2047,6 +2037,8 @@ static int soc_cleanup_card_resources(struct snd_soc_card *card)
if (card->remove) if (card->remove)
card->remove(card); card->remove(card);
snd_soc_dapm_free(&card->dapm);
kfree(card->rtd); kfree(card->rtd);
snd_card_free(card->snd_card); snd_card_free(card->snd_card);
return 0; return 0;
@ -3365,6 +3357,8 @@ int snd_soc_register_card(struct snd_soc_card *card)
if (!card->name || !card->dev) if (!card->name || !card->dev)
return -EINVAL; return -EINVAL;
dev_set_drvdata(card->dev, card);
snd_soc_initialize_card_lists(card); snd_soc_initialize_card_lists(card);
soc_init_card_debugfs(card); soc_init_card_debugfs(card);

View file

@ -187,7 +187,7 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w,
case snd_soc_dapm_mixer_named_ctl: { case snd_soc_dapm_mixer_named_ctl: {
int val; int val;
struct soc_mixer_control *mc = (struct soc_mixer_control *) struct soc_mixer_control *mc = (struct soc_mixer_control *)
w->kcontrols[i].private_value; w->kcontrol_news[i].private_value;
unsigned int reg = mc->reg; unsigned int reg = mc->reg;
unsigned int shift = mc->shift; unsigned int shift = mc->shift;
int max = mc->max; int max = mc->max;
@ -204,7 +204,8 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w,
} }
break; break;
case snd_soc_dapm_mux: { case snd_soc_dapm_mux: {
struct soc_enum *e = (struct soc_enum *)w->kcontrols[i].private_value; struct soc_enum *e = (struct soc_enum *)
w->kcontrol_news[i].private_value;
int val, item, bitmask; int val, item, bitmask;
for (bitmask = 1; bitmask < e->max; bitmask <<= 1) for (bitmask = 1; bitmask < e->max; bitmask <<= 1)
@ -220,7 +221,8 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w,
} }
break; break;
case snd_soc_dapm_virt_mux: { case snd_soc_dapm_virt_mux: {
struct soc_enum *e = (struct soc_enum *)w->kcontrols[i].private_value; struct soc_enum *e = (struct soc_enum *)
w->kcontrol_news[i].private_value;
p->connect = 0; p->connect = 0;
/* since a virtual mux has no backing registers to /* since a virtual mux has no backing registers to
@ -235,7 +237,7 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w,
break; break;
case snd_soc_dapm_value_mux: { case snd_soc_dapm_value_mux: {
struct soc_enum *e = (struct soc_enum *) struct soc_enum *e = (struct soc_enum *)
w->kcontrols[i].private_value; w->kcontrol_news[i].private_value;
int val, item; int val, item;
val = snd_soc_read(w->codec, e->reg); val = snd_soc_read(w->codec, e->reg);
@ -310,11 +312,11 @@ static int dapm_connect_mixer(struct snd_soc_dapm_context *dapm,
/* search for mixer kcontrol */ /* search for mixer kcontrol */
for (i = 0; i < dest->num_kcontrols; i++) { for (i = 0; i < dest->num_kcontrols; i++) {
if (!strcmp(control_name, dest->kcontrols[i].name)) { if (!strcmp(control_name, dest->kcontrol_news[i].name)) {
list_add(&path->list, &dapm->card->paths); list_add(&path->list, &dapm->card->paths);
list_add(&path->list_sink, &dest->sources); list_add(&path->list_sink, &dest->sources);
list_add(&path->list_source, &src->sinks); list_add(&path->list_source, &src->sinks);
path->name = dest->kcontrols[i].name; path->name = dest->kcontrol_news[i].name;
dapm_set_path_status(dest, path, i); dapm_set_path_status(dest, path, i);
return 0; return 0;
} }
@ -322,6 +324,28 @@ static int dapm_connect_mixer(struct snd_soc_dapm_context *dapm,
return -ENODEV; return -ENODEV;
} }
static int dapm_is_shared_kcontrol(struct snd_soc_dapm_context *dapm,
const struct snd_kcontrol_new *kcontrol_new,
struct snd_kcontrol **kcontrol)
{
struct snd_soc_dapm_widget *w;
int i;
*kcontrol = NULL;
list_for_each_entry(w, &dapm->card->widgets, list) {
for (i = 0; i < w->num_kcontrols; i++) {
if (&w->kcontrol_news[i] == kcontrol_new) {
if (w->kcontrols)
*kcontrol = w->kcontrols[i];
return 1;
}
}
}
return 0;
}
/* create new dapm mixer control */ /* create new dapm mixer control */
static int dapm_new_mixer(struct snd_soc_dapm_context *dapm, static int dapm_new_mixer(struct snd_soc_dapm_context *dapm,
struct snd_soc_dapm_widget *w) struct snd_soc_dapm_widget *w)
@ -331,6 +355,8 @@ static int dapm_new_mixer(struct snd_soc_dapm_context *dapm,
struct snd_soc_dapm_path *path; struct snd_soc_dapm_path *path;
struct snd_card *card = dapm->card->snd_card; struct snd_card *card = dapm->card->snd_card;
const char *prefix; const char *prefix;
struct snd_soc_dapm_widget_list *wlist;
size_t wlistsize;
if (dapm->codec) if (dapm->codec)
prefix = dapm->codec->name_prefix; prefix = dapm->codec->name_prefix;
@ -349,23 +375,37 @@ static int dapm_new_mixer(struct snd_soc_dapm_context *dapm,
list_for_each_entry(path, &w->sources, list_sink) { list_for_each_entry(path, &w->sources, list_sink) {
/* mixer/mux paths name must match control name */ /* mixer/mux paths name must match control name */
if (path->name != (char*)w->kcontrols[i].name) if (path->name != (char *)w->kcontrol_news[i].name)
continue; 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. /* add dapm control with long name.
* for dapm_mixer this is the concatenation of the * for dapm_mixer this is the concatenation of the
* mixer and kcontrol name. * mixer and kcontrol name.
* for dapm_mixer_named_ctl this is simply the * for dapm_mixer_named_ctl this is simply the
* kcontrol name. * kcontrol name.
*/ */
name_len = strlen(w->kcontrols[i].name) + 1; name_len = strlen(w->kcontrol_news[i].name) + 1;
if (w->id != snd_soc_dapm_mixer_named_ctl) if (w->id != snd_soc_dapm_mixer_named_ctl)
name_len += 1 + strlen(w->name); name_len += 1 + strlen(w->name);
path->long_name = kmalloc(name_len, GFP_KERNEL); path->long_name = kmalloc(name_len, GFP_KERNEL);
if (path->long_name == NULL) if (path->long_name == NULL) {
kfree(wlist);
return -ENOMEM; return -ENOMEM;
}
switch (w->id) { switch (w->id) {
default: default:
@ -377,27 +417,30 @@ static int dapm_new_mixer(struct snd_soc_dapm_context *dapm,
*/ */
snprintf(path->long_name, name_len, "%s %s", snprintf(path->long_name, name_len, "%s %s",
w->name + prefix_len, w->name + prefix_len,
w->kcontrols[i].name); w->kcontrol_news[i].name);
break; break;
case snd_soc_dapm_mixer_named_ctl: case snd_soc_dapm_mixer_named_ctl:
snprintf(path->long_name, name_len, "%s", snprintf(path->long_name, name_len, "%s",
w->kcontrols[i].name); w->kcontrol_news[i].name);
break; break;
} }
path->long_name[name_len - 1] = '\0'; path->long_name[name_len - 1] = '\0';
path->kcontrol = snd_soc_cnew(&w->kcontrols[i], w, path->kcontrol = snd_soc_cnew(&w->kcontrol_news[i],
path->long_name, prefix); wlist, path->long_name,
prefix);
ret = snd_ctl_add(card, path->kcontrol); ret = snd_ctl_add(card, path->kcontrol);
if (ret < 0) { if (ret < 0) {
dev_err(dapm->dev, dev_err(dapm->dev,
"asoc: failed to add dapm kcontrol %s: %d\n", "asoc: failed to add dapm kcontrol %s: %d\n",
path->long_name, ret); path->long_name, ret);
kfree(wlist);
kfree(path->long_name); kfree(path->long_name);
path->long_name = NULL; path->long_name = NULL;
return ret; return ret;
} }
w->kcontrols[i] = path->kcontrol;
} }
} }
return ret; return ret;
@ -412,42 +455,80 @@ static int dapm_new_mux(struct snd_soc_dapm_context *dapm,
struct snd_card *card = dapm->card->snd_card; struct snd_card *card = dapm->card->snd_card;
const char *prefix; const char *prefix;
size_t prefix_len; size_t prefix_len;
int ret = 0; int ret;
struct snd_soc_dapm_widget_list *wlist;
int shared, wlistentries;
size_t wlistsize;
char *name;
if (!w->num_kcontrols) { if (w->num_kcontrols != 1) {
dev_err(dapm->dev, "asoc: mux %s has no controls\n", w->name); dev_err(dapm->dev,
"asoc: mux %s has incorrect number of controls\n",
w->name);
return -EINVAL; return -EINVAL;
} }
if (dapm->codec) shared = dapm_is_shared_kcontrol(dapm, &w->kcontrol_news[0],
prefix = dapm->codec->name_prefix; &kcontrol);
else if (kcontrol) {
prefix = NULL; 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;
if (prefix) if (!kcontrol) {
prefix_len = strlen(prefix) + 1; if (dapm->codec)
else prefix = dapm->codec->name_prefix;
prefix_len = 0; else
prefix = NULL;
/* The control will get a prefix from the control creation if (shared) {
* process but we're also using the same prefix for widgets so name = w->kcontrol_news[0].name;
* cut the prefix off the front of the widget name. prefix_len = 0;
*/ } else {
kcontrol = snd_soc_cnew(&w->kcontrols[0], w, w->name + prefix_len, name = w->name;
prefix); if (prefix)
ret = snd_ctl_add(card, kcontrol); prefix_len = strlen(prefix) + 1;
else
prefix_len = 0;
}
if (ret < 0) /*
goto err; * 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;
}
}
kcontrol->private_data = wlist;
w->kcontrols[0] = kcontrol;
list_for_each_entry(path, &w->sources, list_sink) list_for_each_entry(path, &w->sources, list_sink)
path->kcontrol = kcontrol; path->kcontrol = kcontrol;
return ret; return 0;
err:
dev_err(dapm->dev, "asoc: failed to add kcontrol %s\n", w->name);
return ret;
} }
/* create new dapm volume control */ /* create new dapm volume control */
@ -1254,13 +1335,18 @@ static const struct file_operations dapm_bias_fops = {
.llseek = default_llseek, .llseek = default_llseek,
}; };
void snd_soc_dapm_debugfs_init(struct snd_soc_dapm_context *dapm) void snd_soc_dapm_debugfs_init(struct snd_soc_dapm_context *dapm,
struct dentry *parent)
{ {
struct snd_soc_dapm_widget *w;
struct dentry *d; struct dentry *d;
if (!dapm->debugfs_dapm) dapm->debugfs_dapm = debugfs_create_dir("dapm", parent);
if (!dapm->debugfs_dapm) {
printk(KERN_WARNING
"Failed to create DAPM debugfs directory\n");
return; return;
}
d = debugfs_create_file("bias_level", 0444, d = debugfs_create_file("bias_level", 0444,
dapm->debugfs_dapm, dapm, dapm->debugfs_dapm, dapm,
@ -1268,24 +1354,44 @@ void snd_soc_dapm_debugfs_init(struct snd_soc_dapm_context *dapm)
if (!d) if (!d)
dev_warn(dapm->dev, dev_warn(dapm->dev,
"ASoC: Failed to create bias level debugfs file\n"); "ASoC: Failed to create bias level debugfs file\n");
list_for_each_entry(w, &dapm->card->widgets, list) {
if (!w->name || w->dapm != dapm)
continue;
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);
}
} }
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)
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);
}
static void dapm_debugfs_cleanup(struct snd_soc_dapm_context *dapm)
{
debugfs_remove_recursive(dapm->debugfs_dapm);
}
#else #else
void snd_soc_dapm_debugfs_init(struct snd_soc_dapm_context *dapm) 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)
{
}
static inline void dapm_debugfs_cleanup(struct snd_soc_dapm_context *dapm)
{
}
#endif #endif
/* test and update the power status of a mux widget */ /* test and update the power status of a mux widget */
@ -1453,6 +1559,7 @@ static void dapm_free_widgets(struct snd_soc_dapm_context *dapm)
kfree(p->long_name); kfree(p->long_name);
kfree(p); kfree(p);
} }
kfree(w->kcontrols);
kfree(w->name); kfree(w->name);
kfree(w); kfree(w);
} }
@ -1623,7 +1730,7 @@ static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm,
case snd_soc_dapm_virt_mux: case snd_soc_dapm_virt_mux:
case snd_soc_dapm_value_mux: case snd_soc_dapm_value_mux:
ret = dapm_connect_mux(dapm, wsource, wsink, path, control, ret = dapm_connect_mux(dapm, wsource, wsink, path, control,
&wsink->kcontrols[0]); &wsink->kcontrol_news[0]);
if (ret != 0) if (ret != 0)
goto err; goto err;
break; break;
@ -1703,6 +1810,14 @@ int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm)
if (w->new) if (w->new)
continue; 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) { switch(w->id) {
case snd_soc_dapm_switch: case snd_soc_dapm_switch:
case snd_soc_dapm_mixer: case snd_soc_dapm_mixer:
@ -1758,6 +1873,8 @@ int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm)
} }
w->new = 1; w->new = 1;
dapm_debugfs_add_widget(w);
} }
dapm_power_widgets(dapm, SND_SOC_DAPM_STREAM_NOP); dapm_power_widgets(dapm, SND_SOC_DAPM_STREAM_NOP);
@ -1777,7 +1894,8 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_new_widgets);
int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol, int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol) struct snd_ctl_elem_value *ucontrol)
{ {
struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol); struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
struct snd_soc_dapm_widget *widget = wlist->widgets[0];
struct soc_mixer_control *mc = struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value; (struct soc_mixer_control *)kcontrol->private_value;
unsigned int reg = mc->reg; unsigned int reg = mc->reg;
@ -1816,7 +1934,9 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_get_volsw);
int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol) struct snd_ctl_elem_value *ucontrol)
{ {
struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol); 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 soc_mixer_control *mc = struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value; (struct soc_mixer_control *)kcontrol->private_value;
unsigned int reg = mc->reg; unsigned int reg = mc->reg;
@ -1827,6 +1947,7 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol,
unsigned int val; unsigned int val;
int connect, change; int connect, change;
struct snd_soc_dapm_update update; struct snd_soc_dapm_update update;
int wi;
val = (ucontrol->value.integer.value[0] & mask); val = (ucontrol->value.integer.value[0] & mask);
@ -1835,31 +1956,36 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol,
mask = mask << shift; mask = mask << shift;
val = val << shift; val = val << shift;
mutex_lock(&widget->codec->mutex); if (val)
widget->value = val; /* new connection */
connect = invert ? 0 : 1;
else
/* old connection must be powered down */
connect = invert ? 1 : 0;
mutex_lock(&codec->mutex);
change = snd_soc_test_bits(widget->codec, reg, mask, val); change = snd_soc_test_bits(widget->codec, reg, mask, val);
if (change) { if (change) {
if (val) for (wi = 0; wi < wlist->num_widgets; wi++) {
/* new connection */ widget = wlist->widgets[wi];
connect = invert ? 0:1;
else
/* old connection must be powered down */
connect = invert ? 1:0;
update.kcontrol = kcontrol; widget->value = val;
update.widget = widget;
update.reg = reg;
update.mask = mask;
update.val = val;
widget->dapm->update = &update;
dapm_mixer_update_power(widget, kcontrol, connect); update.kcontrol = kcontrol;
update.widget = widget;
update.reg = reg;
update.mask = mask;
update.val = val;
widget->dapm->update = &update;
widget->dapm->update = NULL; dapm_mixer_update_power(widget, kcontrol, connect);
widget->dapm->update = NULL;
}
} }
mutex_unlock(&widget->codec->mutex); mutex_unlock(&codec->mutex);
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(snd_soc_dapm_put_volsw); EXPORT_SYMBOL_GPL(snd_soc_dapm_put_volsw);
@ -1876,7 +2002,8 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_put_volsw);
int snd_soc_dapm_get_enum_double(struct snd_kcontrol *kcontrol, int snd_soc_dapm_get_enum_double(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol) struct snd_ctl_elem_value *ucontrol)
{ {
struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol); struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
struct snd_soc_dapm_widget *widget = wlist->widgets[0];
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
unsigned int val, bitmask; unsigned int val, bitmask;
@ -1904,11 +2031,14 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_get_enum_double);
int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol) struct snd_ctl_elem_value *ucontrol)
{ {
struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol); 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 soc_enum *e = (struct soc_enum *)kcontrol->private_value; struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
unsigned int val, mux, change; unsigned int val, mux, change;
unsigned int mask, bitmask; unsigned int mask, bitmask;
struct snd_soc_dapm_update update; struct snd_soc_dapm_update update;
int wi;
for (bitmask = 1; bitmask < e->max; bitmask <<= 1) for (bitmask = 1; bitmask < e->max; bitmask <<= 1)
; ;
@ -1924,22 +2054,29 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol,
mask |= (bitmask - 1) << e->shift_r; mask |= (bitmask - 1) << e->shift_r;
} }
mutex_lock(&widget->codec->mutex); mutex_lock(&codec->mutex);
widget->value = val;
change = snd_soc_test_bits(widget->codec, 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];
update.kcontrol = kcontrol; widget->value = val;
update.widget = widget;
update.reg = e->reg;
update.mask = mask;
update.val = val;
widget->dapm->update = &update;
dapm_mux_update_power(widget, kcontrol, change, mux, e); update.kcontrol = kcontrol;
update.widget = widget;
update.reg = e->reg;
update.mask = mask;
update.val = val;
widget->dapm->update = &update;
widget->dapm->update = NULL; dapm_mux_update_power(widget, kcontrol, change, mux, e);
mutex_unlock(&widget->codec->mutex); widget->dapm->update = NULL;
}
}
mutex_unlock(&codec->mutex);
return change; return change;
} }
EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_double); EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_double);
@ -1954,7 +2091,8 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_double);
int snd_soc_dapm_get_enum_virt(struct snd_kcontrol *kcontrol, int snd_soc_dapm_get_enum_virt(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol) struct snd_ctl_elem_value *ucontrol)
{ {
struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol); struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
struct snd_soc_dapm_widget *widget = wlist->widgets[0];
ucontrol->value.enumerated.item[0] = widget->value; ucontrol->value.enumerated.item[0] = widget->value;
@ -1972,22 +2110,33 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_get_enum_virt);
int snd_soc_dapm_put_enum_virt(struct snd_kcontrol *kcontrol, int snd_soc_dapm_put_enum_virt(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol) struct snd_ctl_elem_value *ucontrol)
{ {
struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol); 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 soc_enum *e = struct soc_enum *e =
(struct soc_enum *)kcontrol->private_value; (struct soc_enum *)kcontrol->private_value;
int change; int change;
int ret = 0; int ret = 0;
int wi;
if (ucontrol->value.enumerated.item[0] >= e->max) if (ucontrol->value.enumerated.item[0] >= e->max)
return -EINVAL; return -EINVAL;
mutex_lock(&widget->codec->mutex); mutex_lock(&codec->mutex);
change = widget->value != ucontrol->value.enumerated.item[0]; change = widget->value != ucontrol->value.enumerated.item[0];
widget->value = ucontrol->value.enumerated.item[0]; if (change) {
dapm_mux_update_power(widget, kcontrol, change, widget->value, e); for (wi = 0; wi < wlist->num_widgets; wi++) {
widget = wlist->widgets[wi];
mutex_unlock(&widget->codec->mutex); widget->value = ucontrol->value.enumerated.item[0];
dapm_mux_update_power(widget, kcontrol, change,
widget->value, e);
}
}
mutex_unlock(&codec->mutex);
return ret; return ret;
} }
EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_virt); EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_virt);
@ -2008,7 +2157,8 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_virt);
int snd_soc_dapm_get_value_enum_double(struct snd_kcontrol *kcontrol, int snd_soc_dapm_get_value_enum_double(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol) struct snd_ctl_elem_value *ucontrol)
{ {
struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol); struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
struct snd_soc_dapm_widget *widget = wlist->widgets[0];
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
unsigned int reg_val, val, mux; unsigned int reg_val, val, mux;
@ -2048,11 +2198,14 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_get_value_enum_double);
int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol, int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol) struct snd_ctl_elem_value *ucontrol)
{ {
struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol); 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 soc_enum *e = (struct soc_enum *)kcontrol->private_value; struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
unsigned int val, mux, change; unsigned int val, mux, change;
unsigned int mask; unsigned int mask;
struct snd_soc_dapm_update update; struct snd_soc_dapm_update update;
int wi;
if (ucontrol->value.enumerated.item[0] > e->max - 1) if (ucontrol->value.enumerated.item[0] > e->max - 1)
return -EINVAL; return -EINVAL;
@ -2066,22 +2219,29 @@ int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol,
mask |= e->mask << e->shift_r; mask |= e->mask << e->shift_r;
} }
mutex_lock(&widget->codec->mutex); mutex_lock(&codec->mutex);
widget->value = val;
change = snd_soc_test_bits(widget->codec, 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];
update.kcontrol = kcontrol; widget->value = val;
update.widget = widget;
update.reg = e->reg;
update.mask = mask;
update.val = val;
widget->dapm->update = &update;
dapm_mux_update_power(widget, kcontrol, change, mux, e); update.kcontrol = kcontrol;
update.widget = widget;
update.reg = e->reg;
update.mask = mask;
update.val = val;
widget->dapm->update = &update;
widget->dapm->update = NULL; dapm_mux_update_power(widget, kcontrol, change, mux, e);
mutex_unlock(&widget->codec->mutex); widget->dapm->update = NULL;
}
}
mutex_unlock(&codec->mutex);
return change; return change;
} }
EXPORT_SYMBOL_GPL(snd_soc_dapm_put_value_enum_double); EXPORT_SYMBOL_GPL(snd_soc_dapm_put_value_enum_double);
@ -2427,6 +2587,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_ignore_suspend);
void snd_soc_dapm_free(struct snd_soc_dapm_context *dapm) void snd_soc_dapm_free(struct snd_soc_dapm_context *dapm)
{ {
snd_soc_dapm_sys_remove(dapm->dev); snd_soc_dapm_sys_remove(dapm->dev);
dapm_debugfs_cleanup(dapm);
dapm_free_widgets(dapm); dapm_free_widgets(dapm);
list_del(&dapm->list); list_del(&dapm->list);
} }

View file

@ -30,3 +30,11 @@ config SND_SOC_TEGRA_WM8903
boards using the WM8093 codec. Currently, the supported boards are boards using the WM8093 codec. Currently, the supported boards are
Harmony, Ventana, Seaboard, Kaen, and Aebl. Harmony, Ventana, Seaboard, Kaen, and Aebl.
config SND_SOC_TEGRA_TRIMSLICE
tristate "SoC Audio support for TrimSlice board"
depends on SND_SOC_TEGRA && MACH_TRIMSLICE && I2C
select SND_SOC_TEGRA_I2S
select SND_SOC_TLV320AIC23
help
Say Y or M here if you want to add support for SoC audio on the
TrimSlice platform.

View file

@ -11,5 +11,7 @@ obj-$(CONFIG_SND_SOC_TEGRA_I2S) += snd-soc-tegra-i2s.o
# Tegra machine Support # Tegra machine Support
snd-soc-tegra-wm8903-objs := tegra_wm8903.o snd-soc-tegra-wm8903-objs := tegra_wm8903.o
snd-soc-tegra-trimslice-objs := trimslice.o
obj-$(CONFIG_SND_SOC_TEGRA_WM8903) += snd-soc-tegra-wm8903.o obj-$(CONFIG_SND_SOC_TEGRA_WM8903) += snd-soc-tegra-wm8903.o
obj-$(CONFIG_SND_SOC_TEGRA_TRIMSLICE) += snd-soc-tegra-trimslice.o

228
sound/soc/tegra/trimslice.c Normal file
View file

@ -0,0 +1,228 @@
/*
* trimslice.c - TrimSlice machine ASoC driver
*
* Copyright (C) 2011 - CompuLab, Ltd.
* Author: Mike Rapoport <mike@compulab.co.il>
*
* Based on code copyright/by:
* Author: Stephen Warren <swarren@nvidia.com>
* Copyright (C) 2010-2011 - NVIDIA, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#include <asm/mach-types.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/jack.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include "../codecs/tlv320aic23.h"
#include "tegra_das.h"
#include "tegra_i2s.h"
#include "tegra_pcm.h"
#include "tegra_asoc_utils.h"
#define DRV_NAME "tegra-snd-trimslice"
struct tegra_trimslice {
struct tegra_asoc_utils_data util_data;
};
static int trimslice_asoc_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *codec_dai = rtd->codec_dai;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
struct snd_soc_codec *codec = rtd->codec;
struct snd_soc_card *card = codec->card;
struct tegra_trimslice *trimslice = snd_soc_card_get_drvdata(card);
int srate, mclk;
int err;
srate = params_rate(params);
mclk = 128 * srate;
err = tegra_asoc_utils_set_rate(&trimslice->util_data, srate, mclk);
if (err < 0) {
dev_err(card->dev, "Can't configure clocks\n");
return err;
}
err = snd_soc_dai_set_fmt(codec_dai,
SND_SOC_DAIFMT_I2S |
SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBS_CFS);
if (err < 0) {
dev_err(card->dev, "codec_dai fmt not set\n");
return err;
}
err = snd_soc_dai_set_fmt(cpu_dai,
SND_SOC_DAIFMT_I2S |
SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBS_CFS);
if (err < 0) {
dev_err(card->dev, "cpu_dai fmt not set\n");
return err;
}
err = snd_soc_dai_set_sysclk(codec_dai, 0, mclk,
SND_SOC_CLOCK_IN);
if (err < 0) {
dev_err(card->dev, "codec_dai clock not set\n");
return err;
}
return 0;
}
static struct snd_soc_ops trimslice_asoc_ops = {
.hw_params = trimslice_asoc_hw_params,
};
static const struct snd_soc_dapm_widget trimslice_dapm_widgets[] = {
SND_SOC_DAPM_HP("Line Out", NULL),
SND_SOC_DAPM_LINE("Line In", NULL),
};
static const struct snd_soc_dapm_route trimslice_audio_map[] = {
{"Line Out", NULL, "LOUT"},
{"Line Out", NULL, "ROUT"},
{"LLINEIN", NULL, "Line In"},
{"RLINEIN", NULL, "Line In"},
};
static int trimslice_asoc_init(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_codec *codec = rtd->codec;
struct snd_soc_dapm_context *dapm = &codec->dapm;
snd_soc_dapm_nc_pin(dapm, "LHPOUT");
snd_soc_dapm_nc_pin(dapm, "RHPOUT");
snd_soc_dapm_nc_pin(dapm, "MICIN");
snd_soc_dapm_sync(dapm);
return 0;
}
static struct snd_soc_dai_link trimslice_tlv320aic23_dai = {
.name = "TLV320AIC23",
.stream_name = "AIC23",
.codec_name = "tlv320aic23-codec.2-001a",
.platform_name = "tegra-pcm-audio",
.cpu_dai_name = "tegra-i2s.0",
.codec_dai_name = "tlv320aic23-hifi",
.init = trimslice_asoc_init,
.ops = &trimslice_asoc_ops,
};
static struct snd_soc_card snd_soc_trimslice = {
.name = "tegra-trimslice",
.dai_link = &trimslice_tlv320aic23_dai,
.num_links = 1,
.dapm_widgets = trimslice_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(trimslice_dapm_widgets),
.dapm_routes = trimslice_audio_map,
.num_dapm_routes = ARRAY_SIZE(trimslice_audio_map),
};
static __devinit int tegra_snd_trimslice_probe(struct platform_device *pdev)
{
struct snd_soc_card *card = &snd_soc_trimslice;
struct tegra_trimslice *trimslice;
int ret;
trimslice = kzalloc(sizeof(struct tegra_trimslice), GFP_KERNEL);
if (!trimslice) {
dev_err(&pdev->dev, "Can't allocate tegra_trimslice\n");
return -ENOMEM;
}
ret = tegra_asoc_utils_init(&trimslice->util_data, &pdev->dev);
if (ret)
goto err_free_trimslice;
card->dev = &pdev->dev;
platform_set_drvdata(pdev, card);
snd_soc_card_set_drvdata(card, trimslice);
ret = snd_soc_register_card(card);
if (ret) {
dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n",
ret);
goto err_fini_utils;
}
return 0;
err_fini_utils:
tegra_asoc_utils_fini(&trimslice->util_data);
err_free_trimslice:
kfree(trimslice);
return ret;
}
static int __devexit tegra_snd_trimslice_remove(struct platform_device *pdev)
{
struct snd_soc_card *card = platform_get_drvdata(pdev);
struct tegra_trimslice *trimslice = snd_soc_card_get_drvdata(card);
snd_soc_unregister_card(card);
tegra_asoc_utils_fini(&trimslice->util_data);
kfree(trimslice);
return 0;
}
static struct platform_driver tegra_snd_trimslice_driver = {
.driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
},
.probe = tegra_snd_trimslice_probe,
.remove = __devexit_p(tegra_snd_trimslice_remove),
};
static int __init snd_tegra_trimslice_init(void)
{
return platform_driver_register(&tegra_snd_trimslice_driver);
}
module_init(snd_tegra_trimslice_init);
static void __exit snd_tegra_trimslice_exit(void)
{
platform_driver_unregister(&tegra_snd_trimslice_driver);
}
module_exit(snd_tegra_trimslice_exit);
MODULE_AUTHOR("Mike Rapoport <mike@compulab.co.il>");
MODULE_DESCRIPTION("Trimslice machine ASoC driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:" DRV_NAME);