From 1a39b5e1f932b0ab292c1737724f17bd6a73d630 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 4 Jul 2013 14:32:16 +0200 Subject: ALSA: hda - Add GPIO control to AD1884 HP fixup The AD1884 HP laptop/mobile quirks control GPIO1 bit as the primary mute as well. Add the similar control to ad1884 fixup for auto parser, too. Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index d97f0d6..2ae7dc5 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -3599,14 +3599,34 @@ static void ad1884_fixup_amp_override(struct hda_codec *codec, (1 << AC_AMPCAP_MUTE_SHIFT)); } +/* toggle GPIO1 according to the mute state */ +static void ad1884_vmaster_hp_gpio_hook(void *private_data, int enabled) +{ + struct hda_codec *codec = private_data; + struct ad198x_spec *spec = codec->spec; + + if (spec->eapd_nid) + ad_vmaster_eapd_hook(private_data, enabled); + snd_hda_codec_update_cache(codec, 0x01, 0, + AC_VERB_SET_GPIO_DATA, + enabled ? 0x00 : 0x02); +} + static void ad1884_fixup_hp_eapd(struct hda_codec *codec, const struct hda_fixup *fix, int action) { struct ad198x_spec *spec = codec->spec; + static const struct hda_verb gpio_init_verbs[] = { + {0x01, AC_VERB_SET_GPIO_MASK, 0x02}, + {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02}, + {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, + {}, + }; switch (action) { case HDA_FIXUP_ACT_PRE_PROBE: - spec->gen.vmaster_mute.hook = ad_vmaster_eapd_hook; + spec->gen.vmaster_mute.hook = ad1884_vmaster_hp_gpio_hook; + snd_hda_sequence_write_cache(codec, gpio_init_verbs); break; case HDA_FIXUP_ACT_PROBE: if (spec->gen.autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT) -- cgit v0.10.2 From 6a699bec88d5755c0f1be4e967649b3cfeac0205 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 4 Jul 2013 14:45:37 +0200 Subject: ALSA: hda - Add fixup for Lenovo Thinkpad with AD1984 codec Ported from the static quirk (model=thinkpad). Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index 2ae7dc5..0262ffb 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -3637,9 +3637,17 @@ static void ad1884_fixup_hp_eapd(struct hda_codec *codec, } } +/* set magic COEFs for dmic */ +static const struct hda_verb ad1884_dmic_init_verbs[] = { + {0x01, AC_VERB_SET_COEF_INDEX, 0x13f7}, + {0x01, AC_VERB_SET_PROC_COEF, 0x08}, + {} +}; + enum { AD1884_FIXUP_AMP_OVERRIDE, AD1884_FIXUP_HP_EAPD, + AD1884_FIXUP_DMIC_COEF, }; static const struct hda_fixup ad1884_fixups[] = { @@ -3653,10 +3661,15 @@ static const struct hda_fixup ad1884_fixups[] = { .chained = true, .chain_id = AD1884_FIXUP_AMP_OVERRIDE, }, + [AD1884_FIXUP_DMIC_COEF] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = ad1884_dmic_init_verbs, + }, }; static const struct snd_pci_quirk ad1884_fixup_tbl[] = { SND_PCI_QUIRK_VENDOR(0x103c, "HP", AD1884_FIXUP_HP_EAPD), + SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo Thinkpad", AD1884_FIXUP_DMIC_COEF), {} }; -- cgit v0.10.2 From f404627d27b27d79287dee7c6dba934790959ee3 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 4 Jul 2013 15:14:17 +0200 Subject: ALSA: hda - Add fixup for HP TouchSmart with AD1984A codec Ported from the static quirk. Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index 0262ffb..a667256 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -3648,6 +3648,7 @@ enum { AD1884_FIXUP_AMP_OVERRIDE, AD1884_FIXUP_HP_EAPD, AD1884_FIXUP_DMIC_COEF, + AD1884_FIXUP_HP_TOUCHSMART, }; static const struct hda_fixup ad1884_fixups[] = { @@ -3665,9 +3666,16 @@ static const struct hda_fixup ad1884_fixups[] = { .type = HDA_FIXUP_VERBS, .v.verbs = ad1884_dmic_init_verbs, }, + [AD1884_FIXUP_HP_TOUCHSMART] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = ad1884_dmic_init_verbs, + .chained = true, + .chain_id = AD1884_FIXUP_HP_EAPD, + }, }; static const struct snd_pci_quirk ad1884_fixup_tbl[] = { + SND_PCI_QUIRK(0x103c, 0x2a82, "HP Touchsmart", AD1884_FIXUP_HP_TOUCHSMART), SND_PCI_QUIRK_VENDOR(0x103c, "HP", AD1884_FIXUP_HP_EAPD), SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo Thinkpad", AD1884_FIXUP_DMIC_COEF), {} -- cgit v0.10.2 From aa95d61b43e0fcb0b2ce68e5efa37174fd9e5cd3 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 4 Jul 2013 15:16:31 +0200 Subject: ALSA: hda - Remove static quirks for AD1882 Now the generic parser can work stably enough, we can get rid of the static quirks. Let's start from AD1882. Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index a667256..876d836e 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -4891,299 +4891,7 @@ static int patch_ad1884a(struct hda_codec *codec) * port-G - rear clfe-out (6stack) */ -#ifdef ENABLE_AD_STATIC_QUIRKS -static const hda_nid_t ad1882_dac_nids[3] = { - 0x04, 0x03, 0x05 -}; - -static const hda_nid_t ad1882_adc_nids[2] = { - 0x08, 0x09, -}; - -static const hda_nid_t ad1882_capsrc_nids[2] = { - 0x0c, 0x0d, -}; - -#define AD1882_SPDIF_OUT 0x02 - -/* list: 0x11, 0x39, 0x3a, 0x18, 0x3c, 0x3b, 0x12, 0x20 */ -static const struct hda_input_mux ad1882_capture_source = { - .num_items = 5, - .items = { - { "Front Mic", 0x1 }, - { "Mic", 0x4 }, - { "Line", 0x2 }, - { "CD", 0x3 }, - { "Mix", 0x7 }, - }, -}; - -/* list: 0x11, 0x39, 0x3a, 0x3c, 0x18, 0x1f, 0x12, 0x20 */ -static const struct hda_input_mux ad1882a_capture_source = { - .num_items = 5, - .items = { - { "Front Mic", 0x1 }, - { "Mic", 0x4}, - { "Line", 0x2 }, - { "Digital Mic", 0x06 }, - { "Mix", 0x7 }, - }, -}; - -static const struct snd_kcontrol_new ad1882_base_mixers[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Surround Playback Volume", 0x03, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT), - - HDA_CODEC_VOLUME("Mic Boost Volume", 0x3c, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x39, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Line-In Boost Volume", 0x3a, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - /* The multiple "Capture Source" controls confuse alsamixer - * So call somewhat different.. - */ - /* .name = "Capture Source", */ - .name = "Input Source", - .count = 2, - .info = ad198x_mux_enum_info, - .get = ad198x_mux_enum_get, - .put = ad198x_mux_enum_put, - }, - /* SPDIF controls */ - HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source", - /* identical with ad1983 */ - .info = ad1983_spdif_route_info, - .get = ad1983_spdif_route_get, - .put = ad1983_spdif_route_put, - }, - { } /* end */ -}; - -static const struct snd_kcontrol_new ad1882_loopback_mixers[] = { - HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT), - HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT), - HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x04, HDA_INPUT), - HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x04, HDA_INPUT), - HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x06, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x06, HDA_INPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new ad1882a_loopback_mixers[] = { - HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT), - HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x04, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x04, HDA_INPUT), - HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x01, HDA_INPUT), - HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x01, HDA_INPUT), - HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x06, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x06, HDA_INPUT), - HDA_CODEC_VOLUME("Digital Mic Boost Volume", 0x1f, 0x0, HDA_INPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new ad1882_3stack_mixers[] = { - HDA_CODEC_MUTE("Surround Playback Switch", 0x15, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x17, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x17, 2, 0x0, HDA_OUTPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Channel Mode", - .info = ad198x_ch_mode_info, - .get = ad198x_ch_mode_get, - .put = ad198x_ch_mode_put, - }, - { } /* end */ -}; - -/* simple auto-mute control for AD1882 3-stack board */ -#define AD1882_HP_EVENT 0x01 - -static void ad1882_3stack_automute(struct hda_codec *codec) -{ - bool mute = snd_hda_jack_detect(codec, 0x11); - snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, - mute ? 0 : PIN_OUT); -} - -static int ad1882_3stack_automute_init(struct hda_codec *codec) -{ - ad198x_init(codec); - ad1882_3stack_automute(codec); - return 0; -} - -static void ad1882_3stack_unsol_event(struct hda_codec *codec, unsigned int res) -{ - switch (res >> 26) { - case AD1882_HP_EVENT: - ad1882_3stack_automute(codec); - break; - } -} - -static const struct snd_kcontrol_new ad1882_6stack_mixers[] = { - HDA_CODEC_MUTE("Surround Playback Switch", 0x16, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x24, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x24, 2, 0x0, HDA_OUTPUT), - { } /* end */ -}; - -static const struct hda_verb ad1882_ch2_init[] = { - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - { } /* end */ -}; - -static const struct hda_verb ad1882_ch4_init[] = { - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - { } /* end */ -}; - -static const struct hda_verb ad1882_ch6_init[] = { - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - { } /* end */ -}; - -static const struct hda_channel_mode ad1882_modes[3] = { - { 2, ad1882_ch2_init }, - { 4, ad1882_ch4_init }, - { 6, ad1882_ch6_init }, -}; - -/* - * initialization verbs - */ -static const struct hda_verb ad1882_init_verbs[] = { - /* DACs; mute as default */ - {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - /* Port-A (HP) mixer */ - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - /* Port-A pin */ - {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* HP selector - select DAC2 */ - {0x37, AC_VERB_SET_CONNECT_SEL, 0x1}, - /* Port-D (Line-out) mixer */ - {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - /* Port-D pin */ - {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* Mono-out mixer */ - {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - /* Mono-out pin */ - {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* Port-B (front mic) pin */ - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */ - /* Port-C (line-in) pin */ - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */ - /* Port-C mixer - mute as input */ - {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - /* Port-E (mic-in) pin */ - {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */ - /* Port-E mixer - mute as input */ - {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - /* Port-F (surround) */ - {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* Port-G (CLFE) */ - {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* Analog mixer; mute as default */ - /* list: 0x39, 0x3a, 0x11, 0x12, 0x3c, 0x3b, 0x18, 0x1a */ - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)}, - /* Analog Mix output amp */ - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */ - /* SPDIF output selector */ - {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */ - {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */ - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */ - { } /* end */ -}; - -static const struct hda_verb ad1882_3stack_automute_verbs[] = { - {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1882_HP_EVENT}, - { } /* end */ -}; - -#ifdef CONFIG_PM -static const struct hda_amp_list ad1882_loopbacks[] = { - { 0x20, HDA_INPUT, 0 }, /* Front Mic */ - { 0x20, HDA_INPUT, 1 }, /* Mic */ - { 0x20, HDA_INPUT, 4 }, /* Line */ - { 0x20, HDA_INPUT, 6 }, /* CD */ - { } /* end */ -}; -#endif - -/* models */ -enum { - AD1882_AUTO, - AD1882_3STACK, - AD1882_6STACK, - AD1882_3STACK_AUTOMUTE, - AD1882_MODELS -}; - -static const char * const ad1882_models[AD1986A_MODELS] = { - [AD1882_AUTO] = "auto", - [AD1882_3STACK] = "3stack", - [AD1882_6STACK] = "6stack", - [AD1882_3STACK_AUTOMUTE] = "3stack-automute", -}; -#endif /* ENABLE_AD_STATIC_QUIRKS */ - -static int ad1882_parse_auto_config(struct hda_codec *codec) +static int patch_ad1882(struct hda_codec *codec) { struct ad198x_spec *spec; int err; @@ -5210,96 +4918,6 @@ static int ad1882_parse_auto_config(struct hda_codec *codec) return err; } -#ifdef ENABLE_AD_STATIC_QUIRKS -static int patch_ad1882(struct hda_codec *codec) -{ - struct ad198x_spec *spec; - int err, board_config; - - board_config = snd_hda_check_board_config(codec, AD1882_MODELS, - ad1882_models, NULL); - if (board_config < 0) { - printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", - codec->chip_name); - board_config = AD1882_AUTO; - } - - if (board_config == AD1882_AUTO) - return ad1882_parse_auto_config(codec); - - err = alloc_ad_spec(codec); - if (err < 0) - return err; - spec = codec->spec; - - err = snd_hda_attach_beep_device(codec, 0x10); - if (err < 0) { - ad198x_free(codec); - return err; - } - set_beep_amp(spec, 0x10, 0, HDA_OUTPUT); - - spec->multiout.max_channels = 6; - spec->multiout.num_dacs = 3; - spec->multiout.dac_nids = ad1882_dac_nids; - spec->multiout.dig_out_nid = AD1882_SPDIF_OUT; - spec->num_adc_nids = ARRAY_SIZE(ad1882_adc_nids); - spec->adc_nids = ad1882_adc_nids; - spec->capsrc_nids = ad1882_capsrc_nids; - if (codec->vendor_id == 0x11d41882) - spec->input_mux = &ad1882_capture_source; - else - spec->input_mux = &ad1882a_capture_source; - spec->num_mixers = 2; - spec->mixers[0] = ad1882_base_mixers; - if (codec->vendor_id == 0x11d41882) - spec->mixers[1] = ad1882_loopback_mixers; - else - spec->mixers[1] = ad1882a_loopback_mixers; - spec->num_init_verbs = 1; - spec->init_verbs[0] = ad1882_init_verbs; - spec->spdif_route = 0; -#ifdef CONFIG_PM - spec->loopback.amplist = ad1882_loopbacks; -#endif - spec->vmaster_nid = 0x04; - - codec->patch_ops = ad198x_patch_ops; - - /* override some parameters */ - switch (board_config) { - default: - case AD1882_3STACK: - case AD1882_3STACK_AUTOMUTE: - spec->num_mixers = 3; - spec->mixers[2] = ad1882_3stack_mixers; - spec->channel_mode = ad1882_modes; - spec->num_channel_mode = ARRAY_SIZE(ad1882_modes); - spec->need_dac_fix = 1; - spec->multiout.max_channels = 2; - spec->multiout.num_dacs = 1; - if (board_config != AD1882_3STACK) { - spec->init_verbs[spec->num_init_verbs++] = - ad1882_3stack_automute_verbs; - codec->patch_ops.unsol_event = ad1882_3stack_unsol_event; - codec->patch_ops.init = ad1882_3stack_automute_init; - } - break; - case AD1882_6STACK: - spec->num_mixers = 3; - spec->mixers[2] = ad1882_6stack_mixers; - break; - } - - codec->no_trigger_sense = 1; - codec->no_sticky_stream = 1; - - return 0; -} -#else /* ENABLE_AD_STATIC_QUIRKS */ -#define patch_ad1882 ad1882_parse_auto_config -#endif /* ENABLE_AD_STATIC_QUIRKS */ - /* * patch entries -- cgit v0.10.2 From 5ccc618fee67f0f0b2122dd4b32a02fd2b6a1569 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 4 Jul 2013 15:36:56 +0200 Subject: ALSA: hda - Remove static quirks for AD1884/1984 & variants Since the necessary device-specific fixups for Thinkpad and HP devices have been already ported, we can remove all static quirks for AD1884, AD1984, AD1884A and AD1984A codecs. Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index 876d836e..bfa8f53 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -3423,167 +3423,19 @@ static int patch_ad1988(struct hda_codec *codec) * * AD1984 = AD1884 + two digital mic-ins * - * FIXME: - * For simplicity, we share the single DAC for both HP and line-outs - * right now. The inidividual playbacks could be easily implemented, - * but no build-up framework is given, so far. - */ - -#ifdef ENABLE_AD_STATIC_QUIRKS -static const hda_nid_t ad1884_dac_nids[1] = { - 0x04, -}; - -static const hda_nid_t ad1884_adc_nids[2] = { - 0x08, 0x09, -}; - -static const hda_nid_t ad1884_capsrc_nids[2] = { - 0x0c, 0x0d, -}; - -#define AD1884_SPDIF_OUT 0x02 - -static const struct hda_input_mux ad1884_capture_source = { - .num_items = 4, - .items = { - { "Front Mic", 0x0 }, - { "Mic", 0x1 }, - { "CD", 0x2 }, - { "Mix", 0x3 }, - }, -}; - -static const struct snd_kcontrol_new ad1884_base_mixers[] = { - HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT), - /* HDA_CODEC_VOLUME_IDX("PCM Playback Volume", 1, 0x03, 0x0, HDA_OUTPUT), */ - HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT), - HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT), - HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x02, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x02, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x15, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x14, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - /* The multiple "Capture Source" controls confuse alsamixer - * So call somewhat different.. - */ - /* .name = "Capture Source", */ - .name = "Input Source", - .count = 2, - .info = ad198x_mux_enum_info, - .get = ad198x_mux_enum_get, - .put = ad198x_mux_enum_put, - }, - /* SPDIF controls */ - HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source", - /* identical with ad1983 */ - .info = ad1983_spdif_route_info, - .get = ad1983_spdif_route_get, - .put = ad1983_spdif_route_put, - }, - { } /* end */ -}; - -static const struct snd_kcontrol_new ad1984_dmic_mixers[] = { - HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x05, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Digital Mic Capture Switch", 0x05, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME_IDX("Digital Mic Capture Volume", 1, 0x06, 0x0, - HDA_INPUT), - HDA_CODEC_MUTE_IDX("Digital Mic Capture Switch", 1, 0x06, 0x0, - HDA_INPUT), - { } /* end */ -}; - -/* - * initialization verbs + * AD1883 / AD1884A / AD1984A / AD1984B + * + * port-B (0x14) - front mic-in + * port-E (0x1c) - rear mic-in + * port-F (0x16) - CD / ext out + * port-C (0x15) - rear line-in + * port-D (0x12) - rear line-out + * port-A (0x11) - front hp-out + * + * AD1984A = AD1884A + digital-mic + * AD1883 = equivalent with AD1984A + * AD1984B = AD1984A + extra SPDIF-out */ -static const struct hda_verb ad1884_init_verbs[] = { - /* DACs; mute as default */ - {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - /* Port-A (HP) mixer */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - /* Port-A pin */ - {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* HP selector - select DAC2 */ - {0x22, AC_VERB_SET_CONNECT_SEL, 0x1}, - /* Port-D (Line-out) mixer */ - {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - /* Port-D pin */ - {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* Mono-out mixer */ - {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - /* Mono-out pin */ - {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* Mono selector */ - {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1}, - /* Port-B (front mic) pin */ - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - /* Port-C (rear mic) pin */ - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - /* Analog mixer; mute as default */ - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - /* Analog Mix output amp */ - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */ - /* SPDIF output selector */ - {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */ - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */ - { } /* end */ -}; - -#ifdef CONFIG_PM -static const struct hda_amp_list ad1884_loopbacks[] = { - { 0x20, HDA_INPUT, 0 }, /* Front Mic */ - { 0x20, HDA_INPUT, 1 }, /* Mic */ - { 0x20, HDA_INPUT, 2 }, /* CD */ - { 0x20, HDA_INPUT, 4 }, /* Docking */ - { } /* end */ -}; -#endif - -static const char * const ad1884_slave_vols[] = { - "PCM", "Mic", "Mono", "Front Mic", "Mic", "CD", - "Internal Mic", "Dock Mic", /* "Beep", */ "IEC958", - NULL -}; - -enum { - AD1884_AUTO, - AD1884_BASIC, - AD1884_MODELS -}; - -static const char * const ad1884_models[AD1884_MODELS] = { - [AD1884_AUTO] = "auto", - [AD1884_BASIC] = "basic", -}; -#endif /* ENABLE_AD_STATIC_QUIRKS */ - /* set the upper-limit for mixer amp to 0dB for avoiding the possible * damage by overloading @@ -3682,7 +3534,7 @@ static const struct snd_pci_quirk ad1884_fixup_tbl[] = { }; -static int ad1884_parse_auto_config(struct hda_codec *codec) +static int patch_ad1884(struct hda_codec *codec) { struct ad198x_spec *spec; int err; @@ -3715,1170 +3567,6 @@ static int ad1884_parse_auto_config(struct hda_codec *codec) return err; } -#ifdef ENABLE_AD_STATIC_QUIRKS -static int patch_ad1884_basic(struct hda_codec *codec) -{ - struct ad198x_spec *spec; - int err; - - err = alloc_ad_spec(codec); - if (err < 0) - return err; - spec = codec->spec; - - err = snd_hda_attach_beep_device(codec, 0x10); - if (err < 0) { - ad198x_free(codec); - return err; - } - set_beep_amp(spec, 0x10, 0, HDA_OUTPUT); - - spec->multiout.max_channels = 2; - spec->multiout.num_dacs = ARRAY_SIZE(ad1884_dac_nids); - spec->multiout.dac_nids = ad1884_dac_nids; - spec->multiout.dig_out_nid = AD1884_SPDIF_OUT; - spec->num_adc_nids = ARRAY_SIZE(ad1884_adc_nids); - spec->adc_nids = ad1884_adc_nids; - spec->capsrc_nids = ad1884_capsrc_nids; - spec->input_mux = &ad1884_capture_source; - spec->num_mixers = 1; - spec->mixers[0] = ad1884_base_mixers; - spec->num_init_verbs = 1; - spec->init_verbs[0] = ad1884_init_verbs; - spec->spdif_route = 0; -#ifdef CONFIG_PM - spec->loopback.amplist = ad1884_loopbacks; -#endif - spec->vmaster_nid = 0x04; - /* we need to cover all playback volumes */ - spec->slave_vols = ad1884_slave_vols; - /* slaves may contain input volumes, so we can't raise to 0dB blindly */ - spec->avoid_init_slave_vol = 1; - - codec->patch_ops = ad198x_patch_ops; - - codec->no_trigger_sense = 1; - codec->no_sticky_stream = 1; - - return 0; -} - -static int patch_ad1884(struct hda_codec *codec) -{ - int board_config; - - board_config = snd_hda_check_board_config(codec, AD1884_MODELS, - ad1884_models, NULL); - if (board_config < 0) { - printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", - codec->chip_name); - board_config = AD1884_AUTO; - } - - if (board_config == AD1884_AUTO) - return ad1884_parse_auto_config(codec); - else - return patch_ad1884_basic(codec); -} -#else /* ENABLE_AD_STATIC_QUIRKS */ -#define patch_ad1884 ad1884_parse_auto_config -#endif /* ENABLE_AD_STATIC_QUIRKS */ - - -#ifdef ENABLE_AD_STATIC_QUIRKS -/* - * Lenovo Thinkpad T61/X61 - */ -static const struct hda_input_mux ad1984_thinkpad_capture_source = { - .num_items = 4, - .items = { - { "Mic", 0x0 }, - { "Internal Mic", 0x1 }, - { "Mix", 0x3 }, - { "Dock Mic", 0x4 }, - }, -}; - - -/* - * Dell Precision T3400 - */ -static const struct hda_input_mux ad1984_dell_desktop_capture_source = { - .num_items = 3, - .items = { - { "Front Mic", 0x0 }, - { "Line-In", 0x1 }, - { "Mix", 0x3 }, - }, -}; - - -static const struct snd_kcontrol_new ad1984_thinkpad_mixers[] = { - HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT), - /* HDA_CODEC_VOLUME_IDX("PCM Playback Volume", 1, 0x03, 0x0, HDA_OUTPUT), */ - HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Speaker Playback Switch", 0x12, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT), - HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x20, 0x01, HDA_INPUT), - HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x20, 0x01, HDA_INPUT), - HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x03, HDA_INPUT), - HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x03, HDA_INPUT), - HDA_CODEC_VOLUME("Dock Mic Playback Volume", 0x20, 0x04, HDA_INPUT), - HDA_CODEC_MUTE("Dock Mic Playback Switch", 0x20, 0x04, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x14, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x15, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Dock Mic Boost Volume", 0x25, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - /* The multiple "Capture Source" controls confuse alsamixer - * So call somewhat different.. - */ - /* .name = "Capture Source", */ - .name = "Input Source", - .count = 2, - .info = ad198x_mux_enum_info, - .get = ad198x_mux_enum_get, - .put = ad198x_mux_enum_put, - }, - /* SPDIF controls */ - HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source", - /* identical with ad1983 */ - .info = ad1983_spdif_route_info, - .get = ad1983_spdif_route_get, - .put = ad1983_spdif_route_put, - }, - { } /* end */ -}; - -/* additional verbs */ -static const struct hda_verb ad1984_thinkpad_init_verbs[] = { - /* Port-E (docking station mic) pin */ - {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* docking mic boost */ - {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - /* Analog PC Beeper - allow firmware/ACPI beeps */ - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3) | 0x1a}, - /* Analog mixer - docking mic; mute as default */ - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, - /* enable EAPD bit */ - {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x02}, - { } /* end */ -}; - -/* - * Dell Precision T3400 - */ -static const struct snd_kcontrol_new ad1984_dell_desktop_mixers[] = { - HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Speaker Playback Switch", 0x12, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT), - HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT), - HDA_CODEC_VOLUME("Line-In Playback Volume", 0x20, 0x01, HDA_INPUT), - HDA_CODEC_MUTE("Line-In Playback Switch", 0x20, 0x01, HDA_INPUT), - HDA_CODEC_VOLUME("Line-In Boost Volume", 0x15, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x14, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - /* The multiple "Capture Source" controls confuse alsamixer - * So call somewhat different.. - */ - /* .name = "Capture Source", */ - .name = "Input Source", - .count = 2, - .info = ad198x_mux_enum_info, - .get = ad198x_mux_enum_get, - .put = ad198x_mux_enum_put, - }, - { } /* end */ -}; - -/* Digial MIC ADC NID 0x05 + 0x06 */ -static int ad1984_pcm_dmic_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - snd_hda_codec_setup_stream(codec, 0x05 + substream->number, - stream_tag, 0, format); - return 0; -} - -static int ad1984_pcm_dmic_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - snd_hda_codec_cleanup_stream(codec, 0x05 + substream->number); - return 0; -} - -static const struct hda_pcm_stream ad1984_pcm_dmic_capture = { - .substreams = 2, - .channels_min = 2, - .channels_max = 2, - .nid = 0x05, - .ops = { - .prepare = ad1984_pcm_dmic_prepare, - .cleanup = ad1984_pcm_dmic_cleanup - }, -}; - -static int ad1984_build_pcms(struct hda_codec *codec) -{ - struct ad198x_spec *spec = codec->spec; - struct hda_pcm *info; - int err; - - err = ad198x_build_pcms(codec); - if (err < 0) - return err; - - info = spec->pcm_rec + codec->num_pcms; - codec->num_pcms++; - info->name = "AD1984 Digital Mic"; - info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad1984_pcm_dmic_capture; - return 0; -} - -/* models */ -enum { - AD1984_AUTO, - AD1984_BASIC, - AD1984_THINKPAD, - AD1984_DELL_DESKTOP, - AD1984_MODELS -}; - -static const char * const ad1984_models[AD1984_MODELS] = { - [AD1984_AUTO] = "auto", - [AD1984_BASIC] = "basic", - [AD1984_THINKPAD] = "thinkpad", - [AD1984_DELL_DESKTOP] = "dell_desktop", -}; - -static const struct snd_pci_quirk ad1984_cfg_tbl[] = { - /* Lenovo Thinkpad T61/X61 */ - SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo Thinkpad", AD1984_THINKPAD), - SND_PCI_QUIRK(0x1028, 0x0214, "Dell T3400", AD1984_DELL_DESKTOP), - SND_PCI_QUIRK(0x1028, 0x0233, "Dell Latitude E6400", AD1984_DELL_DESKTOP), - {} -}; - -static int patch_ad1984(struct hda_codec *codec) -{ - struct ad198x_spec *spec; - int board_config, err; - - board_config = snd_hda_check_board_config(codec, AD1984_MODELS, - ad1984_models, ad1984_cfg_tbl); - if (board_config < 0) { - printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", - codec->chip_name); - board_config = AD1984_AUTO; - } - - if (board_config == AD1984_AUTO) - return ad1884_parse_auto_config(codec); - - err = patch_ad1884_basic(codec); - if (err < 0) - return err; - spec = codec->spec; - - switch (board_config) { - case AD1984_BASIC: - /* additional digital mics */ - spec->mixers[spec->num_mixers++] = ad1984_dmic_mixers; - codec->patch_ops.build_pcms = ad1984_build_pcms; - break; - case AD1984_THINKPAD: - if (codec->subsystem_id == 0x17aa20fb) { - /* Thinpad X300 does not have the ability to do SPDIF, - or attach to docking station to use SPDIF */ - spec->multiout.dig_out_nid = 0; - } else - spec->multiout.dig_out_nid = AD1884_SPDIF_OUT; - spec->input_mux = &ad1984_thinkpad_capture_source; - spec->mixers[0] = ad1984_thinkpad_mixers; - spec->init_verbs[spec->num_init_verbs++] = ad1984_thinkpad_init_verbs; - spec->analog_beep = 1; - break; - case AD1984_DELL_DESKTOP: - spec->multiout.dig_out_nid = 0; - spec->input_mux = &ad1984_dell_desktop_capture_source; - spec->mixers[0] = ad1984_dell_desktop_mixers; - break; - } - return 0; -} -#else /* ENABLE_AD_STATIC_QUIRKS */ -#define patch_ad1984 ad1884_parse_auto_config -#endif /* ENABLE_AD_STATIC_QUIRKS */ - - -/* - * AD1883 / AD1884A / AD1984A / AD1984B - * - * port-B (0x14) - front mic-in - * port-E (0x1c) - rear mic-in - * port-F (0x16) - CD / ext out - * port-C (0x15) - rear line-in - * port-D (0x12) - rear line-out - * port-A (0x11) - front hp-out - * - * AD1984A = AD1884A + digital-mic - * AD1883 = equivalent with AD1984A - * AD1984B = AD1984A + extra SPDIF-out - * - * FIXME: - * We share the single DAC for both HP and line-outs (see AD1884/1984). - */ - -#ifdef ENABLE_AD_STATIC_QUIRKS -static const hda_nid_t ad1884a_dac_nids[1] = { - 0x03, -}; - -#define ad1884a_adc_nids ad1884_adc_nids -#define ad1884a_capsrc_nids ad1884_capsrc_nids - -#define AD1884A_SPDIF_OUT 0x02 - -static const struct hda_input_mux ad1884a_capture_source = { - .num_items = 5, - .items = { - { "Front Mic", 0x0 }, - { "Mic", 0x4 }, - { "Line", 0x1 }, - { "CD", 0x2 }, - { "Mix", 0x3 }, - }, -}; - -static const struct snd_kcontrol_new ad1884a_base_mixers[] = { - HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT), - HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT), - HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT), - HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x01, HDA_INPUT), - HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x01, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x04, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x04, HDA_INPUT), - HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x02, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x02, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x14, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Line Boost Volume", 0x15, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x25, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - /* The multiple "Capture Source" controls confuse alsamixer - * So call somewhat different.. - */ - /* .name = "Capture Source", */ - .name = "Input Source", - .count = 2, - .info = ad198x_mux_enum_info, - .get = ad198x_mux_enum_get, - .put = ad198x_mux_enum_put, - }, - /* SPDIF controls */ - HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source", - /* identical with ad1983 */ - .info = ad1983_spdif_route_info, - .get = ad1983_spdif_route_get, - .put = ad1983_spdif_route_put, - }, - { } /* end */ -}; - -/* - * initialization verbs - */ -static const struct hda_verb ad1884a_init_verbs[] = { - /* DACs; unmute as default */ - {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */ - {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */ - /* Port-A (HP) mixer - route only from analog mixer */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - /* Port-A pin */ - {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* Port-D (Line-out) mixer - route only from analog mixer */ - {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - /* Port-D pin */ - {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* Mono-out mixer - route only from analog mixer */ - {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - /* Mono-out pin */ - {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* Port-B (front mic) pin */ - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - /* Port-C (rear line-in) pin */ - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - /* Port-E (rear mic) pin */ - {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* no boost */ - /* Port-F (CD) pin */ - {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* Analog mixer; mute as default */ - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, /* aux */ - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, - /* Analog Mix output amp */ - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* capture sources */ - {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0}, - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0}, - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* SPDIF output amp */ - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */ - { } /* end */ -}; - -#ifdef CONFIG_PM -static const struct hda_amp_list ad1884a_loopbacks[] = { - { 0x20, HDA_INPUT, 0 }, /* Front Mic */ - { 0x20, HDA_INPUT, 1 }, /* Mic */ - { 0x20, HDA_INPUT, 2 }, /* CD */ - { 0x20, HDA_INPUT, 4 }, /* Docking */ - { } /* end */ -}; -#endif - -/* - * Laptop model - * - * Port A: Headphone jack - * Port B: MIC jack - * Port C: Internal MIC - * Port D: Dock Line Out (if enabled) - * Port E: Dock Line In (if enabled) - * Port F: Internal speakers - */ - -static int ad1884a_mobile_master_sw_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - int ret = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); - int mute = (!ucontrol->value.integer.value[0] && - !ucontrol->value.integer.value[1]); - /* toggle GPIO1 according to the mute state */ - snd_hda_codec_write_cache(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, - mute ? 0x02 : 0x0); - return ret; -} - -static const struct snd_kcontrol_new ad1884a_laptop_mixers[] = { - HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Master Playback Switch", - .subdevice = HDA_SUBDEV_AMP_FLAG, - .info = snd_hda_mixer_amp_switch_info, - .get = snd_hda_mixer_amp_switch_get, - .put = ad1884a_mobile_master_sw_put, - .private_value = HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT), - }, - HDA_CODEC_MUTE("Dock Playback Switch", 0x12, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT), - HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT), - HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x20, 0x01, HDA_INPUT), - HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x20, 0x01, HDA_INPUT), - HDA_CODEC_VOLUME("Dock Mic Playback Volume", 0x20, 0x04, HDA_INPUT), - HDA_CODEC_MUTE("Dock Mic Playback Switch", 0x20, 0x04, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x14, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x15, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Dock Mic Boost Volume", 0x25, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new ad1884a_mobile_mixers[] = { - HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT), - /*HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),*/ - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Master Playback Switch", - .subdevice = HDA_SUBDEV_AMP_FLAG, - .info = snd_hda_mixer_amp_switch_info, - .get = snd_hda_mixer_amp_switch_get, - .put = ad1884a_mobile_master_sw_put, - .private_value = HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT), - }, - HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT), - HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Capture Volume", 0x14, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Internal Mic Capture Volume", 0x15, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT), - { } /* end */ -}; - -/* mute internal speaker if HP is plugged */ -static void ad1884a_hp_automute(struct hda_codec *codec) -{ - unsigned int present; - - present = snd_hda_jack_detect(codec, 0x11); - snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0, - HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); - snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_EAPD_BTLENABLE, - present ? 0x00 : 0x02); -} - -/* switch to external mic if plugged */ -static void ad1884a_hp_automic(struct hda_codec *codec) -{ - unsigned int present; - - present = snd_hda_jack_detect(codec, 0x14); - snd_hda_codec_write(codec, 0x0c, 0, AC_VERB_SET_CONNECT_SEL, - present ? 0 : 1); -} - -#define AD1884A_HP_EVENT 0x37 -#define AD1884A_MIC_EVENT 0x36 - -/* unsolicited event for HP jack sensing */ -static void ad1884a_hp_unsol_event(struct hda_codec *codec, unsigned int res) -{ - switch (res >> 26) { - case AD1884A_HP_EVENT: - ad1884a_hp_automute(codec); - break; - case AD1884A_MIC_EVENT: - ad1884a_hp_automic(codec); - break; - } -} - -/* initialize jack-sensing, too */ -static int ad1884a_hp_init(struct hda_codec *codec) -{ - ad198x_init(codec); - ad1884a_hp_automute(codec); - ad1884a_hp_automic(codec); - return 0; -} - -/* mute internal speaker if HP or docking HP is plugged */ -static void ad1884a_laptop_automute(struct hda_codec *codec) -{ - unsigned int present; - - present = snd_hda_jack_detect(codec, 0x11); - if (!present) - present = snd_hda_jack_detect(codec, 0x12); - snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0, - HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); - snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_EAPD_BTLENABLE, - present ? 0x00 : 0x02); -} - -/* switch to external mic if plugged */ -static void ad1884a_laptop_automic(struct hda_codec *codec) -{ - unsigned int idx; - - if (snd_hda_jack_detect(codec, 0x14)) - idx = 0; - else if (snd_hda_jack_detect(codec, 0x1c)) - idx = 4; - else - idx = 1; - snd_hda_codec_write(codec, 0x0c, 0, AC_VERB_SET_CONNECT_SEL, idx); -} - -/* unsolicited event for HP jack sensing */ -static void ad1884a_laptop_unsol_event(struct hda_codec *codec, - unsigned int res) -{ - switch (res >> 26) { - case AD1884A_HP_EVENT: - ad1884a_laptop_automute(codec); - break; - case AD1884A_MIC_EVENT: - ad1884a_laptop_automic(codec); - break; - } -} - -/* initialize jack-sensing, too */ -static int ad1884a_laptop_init(struct hda_codec *codec) -{ - ad198x_init(codec); - ad1884a_laptop_automute(codec); - ad1884a_laptop_automic(codec); - return 0; -} - -/* additional verbs for laptop model */ -static const struct hda_verb ad1884a_laptop_verbs[] = { - /* Port-A (HP) pin - always unmuted */ - {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* Port-F (int speaker) mixer - route only from analog mixer */ - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - /* Port-F (int speaker) pin */ - {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* required for compaq 6530s/6531s speaker output */ - {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - /* Port-C pin - internal mic-in */ - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */ - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */ - /* Port-D (docking line-out) pin - default unmuted */ - {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* analog mix */ - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, - /* unsolicited event for pin-sense */ - {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT}, - {0x12, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT}, - {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT}, - {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT}, - /* allow to touch GPIO1 (for mute control) */ - {0x01, AC_VERB_SET_GPIO_MASK, 0x02}, - {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02}, - {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, /* first muted */ - { } /* end */ -}; - -static const struct hda_verb ad1884a_mobile_verbs[] = { - /* DACs; unmute as default */ - {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */ - {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */ - /* Port-A (HP) mixer - route only from analog mixer */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - /* Port-A pin */ - {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - /* Port-A (HP) pin - always unmuted */ - {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* Port-B (mic jack) pin */ - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */ - /* Port-C (int mic) pin */ - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */ - /* Port-F (int speaker) mixer - route only from analog mixer */ - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - /* Port-F pin */ - {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* Analog mixer; mute as default */ - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, - /* Analog Mix output amp */ - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* capture sources */ - /* {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0}, */ /* set via unsol */ - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0}, - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* unsolicited event for pin-sense */ - {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT}, - {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT}, - /* allow to touch GPIO1 (for mute control) */ - {0x01, AC_VERB_SET_GPIO_MASK, 0x02}, - {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02}, - {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, /* first muted */ - { } /* end */ -}; - -/* - * Thinkpad X300 - * 0x11 - HP - * 0x12 - speaker - * 0x14 - mic-in - * 0x17 - built-in mic - */ - -static const struct hda_verb ad1984a_thinkpad_verbs[] = { - /* HP unmute */ - {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* analog mix */ - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, - /* turn on EAPD */ - {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x02}, - /* unsolicited event for pin-sense */ - {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT}, - /* internal mic - dmic */ - {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - /* set magic COEFs for dmic */ - {0x01, AC_VERB_SET_COEF_INDEX, 0x13f7}, - {0x01, AC_VERB_SET_PROC_COEF, 0x08}, - { } /* end */ -}; - -static const struct snd_kcontrol_new ad1984a_thinkpad_mixers[] = { - HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT), - HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x14, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x17, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Capture Source", - .info = ad198x_mux_enum_info, - .get = ad198x_mux_enum_get, - .put = ad198x_mux_enum_put, - }, - { } /* end */ -}; - -static const struct hda_input_mux ad1984a_thinkpad_capture_source = { - .num_items = 3, - .items = { - { "Mic", 0x0 }, - { "Internal Mic", 0x5 }, - { "Mix", 0x3 }, - }, -}; - -/* mute internal speaker if HP is plugged */ -static void ad1984a_thinkpad_automute(struct hda_codec *codec) -{ - unsigned int present; - - present = snd_hda_jack_detect(codec, 0x11); - snd_hda_codec_amp_stereo(codec, 0x12, HDA_OUTPUT, 0, - HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); -} - -/* unsolicited event for HP jack sensing */ -static void ad1984a_thinkpad_unsol_event(struct hda_codec *codec, - unsigned int res) -{ - if ((res >> 26) != AD1884A_HP_EVENT) - return; - ad1984a_thinkpad_automute(codec); -} - -/* initialize jack-sensing, too */ -static int ad1984a_thinkpad_init(struct hda_codec *codec) -{ - ad198x_init(codec); - ad1984a_thinkpad_automute(codec); - return 0; -} - -/* - * Precision R5500 - * 0x12 - HP/line-out - * 0x13 - speaker (mono) - * 0x15 - mic-in - */ - -static const struct hda_verb ad1984a_precision_verbs[] = { - /* Unmute main output path */ - {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */ - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE + 0x1f}, /* 0dB */ - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(5) + 0x17}, /* 0dB */ - /* Analog mixer; mute as default */ - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, - /* Select mic as input */ - {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1}, - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE + 0x27}, /* 0dB */ - /* Configure as mic */ - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */ - /* HP unmute */ - {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* turn on EAPD */ - {0x13, AC_VERB_SET_EAPD_BTLENABLE, 0x02}, - /* unsolicited event for pin-sense */ - {0x12, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT}, - { } /* end */ -}; - -static const struct snd_kcontrol_new ad1984a_precision_mixers[] = { - HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT), - HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x15, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Speaker Playback Volume", 0x13, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT), - { } /* end */ -}; - - -/* mute internal speaker if HP is plugged */ -static void ad1984a_precision_automute(struct hda_codec *codec) -{ - unsigned int present; - - present = snd_hda_jack_detect(codec, 0x12); - snd_hda_codec_amp_stereo(codec, 0x13, HDA_OUTPUT, 0, - HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); -} - - -/* unsolicited event for HP jack sensing */ -static void ad1984a_precision_unsol_event(struct hda_codec *codec, - unsigned int res) -{ - if ((res >> 26) != AD1884A_HP_EVENT) - return; - ad1984a_precision_automute(codec); -} - -/* initialize jack-sensing, too */ -static int ad1984a_precision_init(struct hda_codec *codec) -{ - ad198x_init(codec); - ad1984a_precision_automute(codec); - return 0; -} - - -/* - * HP Touchsmart - * port-A (0x11) - front hp-out - * port-B (0x14) - unused - * port-C (0x15) - unused - * port-D (0x12) - rear line out - * port-E (0x1c) - front mic-in - * port-F (0x16) - Internal speakers - * digital-mic (0x17) - Internal mic - */ - -static const struct hda_verb ad1984a_touchsmart_verbs[] = { - /* DACs; unmute as default */ - {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */ - {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */ - /* Port-A (HP) mixer - route only from analog mixer */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - /* Port-A pin */ - {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - /* Port-A (HP) pin - always unmuted */ - {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* Port-E (int speaker) mixer - route only from analog mixer */ - {0x25, AC_VERB_SET_AMP_GAIN_MUTE, 0x03}, - /* Port-E pin */ - {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - /* Port-F (int speaker) mixer - route only from analog mixer */ - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - /* Port-F pin */ - {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* Analog mixer; mute as default */ - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, - /* Analog Mix output amp */ - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* capture sources */ - /* {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0}, */ /* set via unsol */ - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0}, - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - /* unsolicited event for pin-sense */ - {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT}, - {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT}, - /* allow to touch GPIO1 (for mute control) */ - {0x01, AC_VERB_SET_GPIO_MASK, 0x02}, - {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02}, - {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, /* first muted */ - /* internal mic - dmic */ - {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - /* set magic COEFs for dmic */ - {0x01, AC_VERB_SET_COEF_INDEX, 0x13f7}, - {0x01, AC_VERB_SET_PROC_COEF, 0x08}, - { } /* end */ -}; - -static const struct snd_kcontrol_new ad1984a_touchsmart_mixers[] = { - HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT), -/* HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),*/ - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .subdevice = HDA_SUBDEV_AMP_FLAG, - .name = "Master Playback Switch", - .info = snd_hda_mixer_amp_switch_info, - .get = snd_hda_mixer_amp_switch_get, - .put = ad1884a_mobile_master_sw_put, - .private_value = HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT), - }, - HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT), - HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT), - HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x25, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x17, 0x0, HDA_INPUT), - { } /* end */ -}; - -/* switch to external mic if plugged */ -static void ad1984a_touchsmart_automic(struct hda_codec *codec) -{ - if (snd_hda_jack_detect(codec, 0x1c)) - snd_hda_codec_write(codec, 0x0c, 0, - AC_VERB_SET_CONNECT_SEL, 0x4); - else - snd_hda_codec_write(codec, 0x0c, 0, - AC_VERB_SET_CONNECT_SEL, 0x5); -} - - -/* unsolicited event for HP jack sensing */ -static void ad1984a_touchsmart_unsol_event(struct hda_codec *codec, - unsigned int res) -{ - switch (res >> 26) { - case AD1884A_HP_EVENT: - ad1884a_hp_automute(codec); - break; - case AD1884A_MIC_EVENT: - ad1984a_touchsmart_automic(codec); - break; - } -} - -/* initialize jack-sensing, too */ -static int ad1984a_touchsmart_init(struct hda_codec *codec) -{ - ad198x_init(codec); - ad1884a_hp_automute(codec); - ad1984a_touchsmart_automic(codec); - return 0; -} - - -/* - */ - -enum { - AD1884A_AUTO, - AD1884A_DESKTOP, - AD1884A_LAPTOP, - AD1884A_MOBILE, - AD1884A_THINKPAD, - AD1984A_TOUCHSMART, - AD1984A_PRECISION, - AD1884A_MODELS -}; - -static const char * const ad1884a_models[AD1884A_MODELS] = { - [AD1884A_AUTO] = "auto", - [AD1884A_DESKTOP] = "desktop", - [AD1884A_LAPTOP] = "laptop", - [AD1884A_MOBILE] = "mobile", - [AD1884A_THINKPAD] = "thinkpad", - [AD1984A_TOUCHSMART] = "touchsmart", - [AD1984A_PRECISION] = "precision", -}; - -static const struct snd_pci_quirk ad1884a_cfg_tbl[] = { - SND_PCI_QUIRK(0x1028, 0x04ac, "Precision R5500", AD1984A_PRECISION), - SND_PCI_QUIRK(0x103c, 0x3030, "HP", AD1884A_MOBILE), - SND_PCI_QUIRK(0x103c, 0x3037, "HP 2230s", AD1884A_LAPTOP), - SND_PCI_QUIRK(0x103c, 0x3056, "HP", AD1884A_MOBILE), - SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x3070, "HP", AD1884A_MOBILE), - SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x30d0, "HP laptop", AD1884A_LAPTOP), - SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x30e0, "HP laptop", AD1884A_LAPTOP), - SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x3600, "HP laptop", AD1884A_LAPTOP), - SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x7010, "HP laptop", AD1884A_MOBILE), - SND_PCI_QUIRK(0x17aa, 0x20ac, "Thinkpad X300", AD1884A_THINKPAD), - SND_PCI_QUIRK(0x103c, 0x2a82, "Touchsmart", AD1984A_TOUCHSMART), - {} -}; - -static int patch_ad1884a(struct hda_codec *codec) -{ - struct ad198x_spec *spec; - int err, board_config; - - board_config = snd_hda_check_board_config(codec, AD1884A_MODELS, - ad1884a_models, - ad1884a_cfg_tbl); - if (board_config < 0) { - printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", - codec->chip_name); - board_config = AD1884A_AUTO; - } - - if (board_config == AD1884A_AUTO) - return ad1884_parse_auto_config(codec); - - err = alloc_ad_spec(codec); - if (err < 0) - return err; - spec = codec->spec; - - err = snd_hda_attach_beep_device(codec, 0x10); - if (err < 0) { - ad198x_free(codec); - return err; - } - set_beep_amp(spec, 0x10, 0, HDA_OUTPUT); - - spec->multiout.max_channels = 2; - spec->multiout.num_dacs = ARRAY_SIZE(ad1884a_dac_nids); - spec->multiout.dac_nids = ad1884a_dac_nids; - spec->multiout.dig_out_nid = AD1884A_SPDIF_OUT; - spec->num_adc_nids = ARRAY_SIZE(ad1884a_adc_nids); - spec->adc_nids = ad1884a_adc_nids; - spec->capsrc_nids = ad1884a_capsrc_nids; - spec->input_mux = &ad1884a_capture_source; - spec->num_mixers = 1; - spec->mixers[0] = ad1884a_base_mixers; - spec->num_init_verbs = 1; - spec->init_verbs[0] = ad1884a_init_verbs; - spec->spdif_route = 0; -#ifdef CONFIG_PM - spec->loopback.amplist = ad1884a_loopbacks; -#endif - codec->patch_ops = ad198x_patch_ops; - - /* override some parameters */ - switch (board_config) { - case AD1884A_LAPTOP: - spec->mixers[0] = ad1884a_laptop_mixers; - spec->init_verbs[spec->num_init_verbs++] = ad1884a_laptop_verbs; - spec->multiout.dig_out_nid = 0; - codec->patch_ops.unsol_event = ad1884a_laptop_unsol_event; - codec->patch_ops.init = ad1884a_laptop_init; - /* set the upper-limit for mixer amp to 0dB for avoiding the - * possible damage by overloading - */ - snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT, - (0x17 << AC_AMPCAP_OFFSET_SHIFT) | - (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) | - (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) | - (1 << AC_AMPCAP_MUTE_SHIFT)); - break; - case AD1884A_MOBILE: - spec->mixers[0] = ad1884a_mobile_mixers; - spec->init_verbs[0] = ad1884a_mobile_verbs; - spec->multiout.dig_out_nid = 0; - codec->patch_ops.unsol_event = ad1884a_hp_unsol_event; - codec->patch_ops.init = ad1884a_hp_init; - /* set the upper-limit for mixer amp to 0dB for avoiding the - * possible damage by overloading - */ - snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT, - (0x17 << AC_AMPCAP_OFFSET_SHIFT) | - (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) | - (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) | - (1 << AC_AMPCAP_MUTE_SHIFT)); - break; - case AD1884A_THINKPAD: - spec->mixers[0] = ad1984a_thinkpad_mixers; - spec->init_verbs[spec->num_init_verbs++] = - ad1984a_thinkpad_verbs; - spec->multiout.dig_out_nid = 0; - spec->input_mux = &ad1984a_thinkpad_capture_source; - codec->patch_ops.unsol_event = ad1984a_thinkpad_unsol_event; - codec->patch_ops.init = ad1984a_thinkpad_init; - break; - case AD1984A_PRECISION: - spec->mixers[0] = ad1984a_precision_mixers; - spec->init_verbs[spec->num_init_verbs++] = - ad1984a_precision_verbs; - spec->multiout.dig_out_nid = 0; - codec->patch_ops.unsol_event = ad1984a_precision_unsol_event; - codec->patch_ops.init = ad1984a_precision_init; - break; - case AD1984A_TOUCHSMART: - spec->mixers[0] = ad1984a_touchsmart_mixers; - spec->init_verbs[0] = ad1984a_touchsmart_verbs; - spec->multiout.dig_out_nid = 0; - codec->patch_ops.unsol_event = ad1984a_touchsmart_unsol_event; - codec->patch_ops.init = ad1984a_touchsmart_init; - /* set the upper-limit for mixer amp to 0dB for avoiding the - * possible damage by overloading - */ - snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT, - (0x17 << AC_AMPCAP_OFFSET_SHIFT) | - (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) | - (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) | - (1 << AC_AMPCAP_MUTE_SHIFT)); - break; - } - - codec->no_trigger_sense = 1; - codec->no_sticky_stream = 1; - - return 0; -} -#else /* ENABLE_AD_STATIC_QUIRKS */ -#define patch_ad1884a ad1884_parse_auto_config -#endif /* ENABLE_AD_STATIC_QUIRKS */ - - /* * AD1882 / AD1882A * @@ -4923,15 +3611,15 @@ static int patch_ad1882(struct hda_codec *codec) * patch entries */ static const struct hda_codec_preset snd_hda_preset_analog[] = { - { .id = 0x11d4184a, .name = "AD1884A", .patch = patch_ad1884a }, + { .id = 0x11d4184a, .name = "AD1884A", .patch = patch_ad1884 }, { .id = 0x11d41882, .name = "AD1882", .patch = patch_ad1882 }, - { .id = 0x11d41883, .name = "AD1883", .patch = patch_ad1884a }, + { .id = 0x11d41883, .name = "AD1883", .patch = patch_ad1884 }, { .id = 0x11d41884, .name = "AD1884", .patch = patch_ad1884 }, - { .id = 0x11d4194a, .name = "AD1984A", .patch = patch_ad1884a }, - { .id = 0x11d4194b, .name = "AD1984B", .patch = patch_ad1884a }, + { .id = 0x11d4194a, .name = "AD1984A", .patch = patch_ad1884 }, + { .id = 0x11d4194b, .name = "AD1984B", .patch = patch_ad1884 }, { .id = 0x11d41981, .name = "AD1981", .patch = patch_ad1981 }, { .id = 0x11d41983, .name = "AD1983", .patch = patch_ad1983 }, - { .id = 0x11d41984, .name = "AD1984", .patch = patch_ad1984 }, + { .id = 0x11d41984, .name = "AD1984", .patch = patch_ad1884 }, { .id = 0x11d41986, .name = "AD1986A", .patch = patch_ad1986a }, { .id = 0x11d41988, .name = "AD1988", .patch = patch_ad1988 }, { .id = 0x11d4198b, .name = "AD1988B", .patch = patch_ad1988 }, -- cgit v0.10.2 From bd450dcc357646cc277c560ab24b35f940efa585 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 4 Jul 2013 15:48:04 +0200 Subject: ALSA: hda - Remove static quirks for AD1981 and AD1983 codecs These are relatively easy ones, as we already converted all static quirks to the generic parser. Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index bfa8f53..4fedd9d 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -1427,161 +1427,6 @@ static int patch_ad1986a(struct hda_codec *codec) * AD1983 specific */ -#ifdef ENABLE_AD_STATIC_QUIRKS -#define AD1983_SPDIF_OUT 0x02 -#define AD1983_DAC 0x03 -#define AD1983_ADC 0x04 - -static const hda_nid_t ad1983_dac_nids[1] = { AD1983_DAC }; -static const hda_nid_t ad1983_adc_nids[1] = { AD1983_ADC }; -static const hda_nid_t ad1983_capsrc_nids[1] = { 0x15 }; - -static const struct hda_input_mux ad1983_capture_source = { - .num_items = 4, - .items = { - { "Mic", 0x0 }, - { "Line", 0x1 }, - { "Mix", 0x2 }, - { "Mix Mono", 0x3 }, - }, -}; - -/* - * SPDIF playback route - */ -static int ad1983_spdif_route_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) -{ - static const char * const texts[] = { "PCM", "ADC" }; - - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 2; - if (uinfo->value.enumerated.item > 1) - uinfo->value.enumerated.item = 1; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; -} - -static int ad1983_spdif_route_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct ad198x_spec *spec = codec->spec; - - ucontrol->value.enumerated.item[0] = spec->spdif_route; - return 0; -} - -static int ad1983_spdif_route_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct ad198x_spec *spec = codec->spec; - - if (ucontrol->value.enumerated.item[0] > 1) - return -EINVAL; - if (spec->spdif_route != ucontrol->value.enumerated.item[0]) { - spec->spdif_route = ucontrol->value.enumerated.item[0]; - snd_hda_codec_write_cache(codec, spec->multiout.dig_out_nid, 0, - AC_VERB_SET_CONNECT_SEL, - spec->spdif_route); - return 1; - } - return 0; -} - -static const struct snd_kcontrol_new ad1983_mixers[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x05, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Front Playback Switch", 0x05, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Headphone Playback Volume", 0x06, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x06, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x07, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x07, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Line Playback Volume", 0x13, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Line Playback Switch", 0x13, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Capture Source", - .info = ad198x_mux_enum_info, - .get = ad198x_mux_enum_get, - .put = ad198x_mux_enum_put, - }, - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source", - .info = ad1983_spdif_route_info, - .get = ad1983_spdif_route_get, - .put = ad1983_spdif_route_put, - }, - { } /* end */ -}; - -static const struct hda_verb ad1983_init_verbs[] = { - /* Front, HP, Mono; mute as default */ - {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - {0x06, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - /* Beep, PCM, Mic, Line-In: mute */ - {0x10, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - {0x11, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - {0x12, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - /* Front, HP selectors; from Mix */ - {0x05, AC_VERB_SET_CONNECT_SEL, 0x01}, - {0x06, AC_VERB_SET_CONNECT_SEL, 0x01}, - /* Mono selector; from Mix */ - {0x0b, AC_VERB_SET_CONNECT_SEL, 0x03}, - /* Mic selector; Mic */ - {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0}, - /* Line-in selector: Line-in */ - {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0}, - /* Mic boost: 0dB */ - {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, - /* Record selector: mic */ - {0x15, AC_VERB_SET_CONNECT_SEL, 0x0}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - /* SPDIF route: PCM */ - {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, - /* Front Pin */ - {0x05, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, - /* HP Pin */ - {0x06, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 }, - /* Mono Pin */ - {0x07, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, - /* Mic Pin */ - {0x08, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, - /* Line Pin */ - {0x09, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, - { } /* end */ -}; - -#ifdef CONFIG_PM -static const struct hda_amp_list ad1983_loopbacks[] = { - { 0x12, HDA_OUTPUT, 0 }, /* Mic */ - { 0x13, HDA_OUTPUT, 0 }, /* Line */ - { } /* end */ -}; -#endif - -/* models */ -enum { - AD1983_AUTO, - AD1983_BASIC, - AD1983_MODELS -}; - -static const char * const ad1983_models[AD1983_MODELS] = { - [AD1983_AUTO] = "auto", - [AD1983_BASIC] = "basic", -}; -#endif /* ENABLE_AD_STATIC_QUIRKS */ - - /* * SPDIF mux control for AD1983 auto-parser */ @@ -1656,7 +1501,7 @@ static int ad1983_add_spdif_mux_ctl(struct hda_codec *codec) return 0; } -static int ad1983_parse_auto_config(struct hda_codec *codec) +static int patch_ad1983(struct hda_codec *codec) { struct ad198x_spec *spec; int err; @@ -1681,432 +1526,11 @@ static int ad1983_parse_auto_config(struct hda_codec *codec) return err; } -#ifdef ENABLE_AD_STATIC_QUIRKS -static int patch_ad1983(struct hda_codec *codec) -{ - struct ad198x_spec *spec; - int board_config; - int err; - - board_config = snd_hda_check_board_config(codec, AD1983_MODELS, - ad1983_models, NULL); - if (board_config < 0) { - printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", - codec->chip_name); - board_config = AD1983_AUTO; - } - - if (board_config == AD1983_AUTO) - return ad1983_parse_auto_config(codec); - - err = alloc_ad_spec(codec); - if (err < 0) - return err; - spec = codec->spec; - - err = snd_hda_attach_beep_device(codec, 0x10); - if (err < 0) { - ad198x_free(codec); - return err; - } - set_beep_amp(spec, 0x10, 0, HDA_OUTPUT); - - spec->multiout.max_channels = 2; - spec->multiout.num_dacs = ARRAY_SIZE(ad1983_dac_nids); - spec->multiout.dac_nids = ad1983_dac_nids; - spec->multiout.dig_out_nid = AD1983_SPDIF_OUT; - spec->num_adc_nids = 1; - spec->adc_nids = ad1983_adc_nids; - spec->capsrc_nids = ad1983_capsrc_nids; - spec->input_mux = &ad1983_capture_source; - spec->num_mixers = 1; - spec->mixers[0] = ad1983_mixers; - spec->num_init_verbs = 1; - spec->init_verbs[0] = ad1983_init_verbs; - spec->spdif_route = 0; -#ifdef CONFIG_PM - spec->loopback.amplist = ad1983_loopbacks; -#endif - spec->vmaster_nid = 0x05; - - codec->patch_ops = ad198x_patch_ops; - - codec->no_trigger_sense = 1; - codec->no_sticky_stream = 1; - - return 0; -} -#else /* ENABLE_AD_STATIC_QUIRKS */ -#define patch_ad1983 ad1983_parse_auto_config -#endif /* ENABLE_AD_STATIC_QUIRKS */ - /* * AD1981 HD specific */ -#ifdef ENABLE_AD_STATIC_QUIRKS -#define AD1981_SPDIF_OUT 0x02 -#define AD1981_DAC 0x03 -#define AD1981_ADC 0x04 - -static const hda_nid_t ad1981_dac_nids[1] = { AD1981_DAC }; -static const hda_nid_t ad1981_adc_nids[1] = { AD1981_ADC }; -static const hda_nid_t ad1981_capsrc_nids[1] = { 0x15 }; - -/* 0x0c, 0x09, 0x0e, 0x0f, 0x19, 0x05, 0x18, 0x17 */ -static const struct hda_input_mux ad1981_capture_source = { - .num_items = 7, - .items = { - { "Front Mic", 0x0 }, - { "Line", 0x1 }, - { "Mix", 0x2 }, - { "Mix Mono", 0x3 }, - { "CD", 0x4 }, - { "Mic", 0x6 }, - { "Aux", 0x7 }, - }, -}; - -static const struct snd_kcontrol_new ad1981_mixers[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x05, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Front Playback Switch", 0x05, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Headphone Playback Volume", 0x06, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x06, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x07, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x07, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Front Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Line Playback Volume", 0x13, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Line Playback Switch", 0x13, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Aux Playback Volume", 0x1b, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Aux Playback Switch", 0x1b, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x1c, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x1c, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x08, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Capture Source", - .info = ad198x_mux_enum_info, - .get = ad198x_mux_enum_get, - .put = ad198x_mux_enum_put, - }, - /* identical with AD1983 */ - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source", - .info = ad1983_spdif_route_info, - .get = ad1983_spdif_route_get, - .put = ad1983_spdif_route_put, - }, - { } /* end */ -}; - -static const struct hda_verb ad1981_init_verbs[] = { - /* Front, HP, Mono; mute as default */ - {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - {0x06, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - /* Beep, PCM, Front Mic, Line, Rear Mic, Aux, CD-In: mute */ - {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - {0x11, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - {0x12, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - /* Front, HP selectors; from Mix */ - {0x05, AC_VERB_SET_CONNECT_SEL, 0x01}, - {0x06, AC_VERB_SET_CONNECT_SEL, 0x01}, - /* Mono selector; from Mix */ - {0x0b, AC_VERB_SET_CONNECT_SEL, 0x03}, - /* Mic Mixer; select Front Mic */ - {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, - {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - /* Mic boost: 0dB */ - {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - /* Record selector: Front mic */ - {0x15, AC_VERB_SET_CONNECT_SEL, 0x0}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - /* SPDIF route: PCM */ - {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, - /* Front Pin */ - {0x05, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, - /* HP Pin */ - {0x06, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 }, - /* Mono Pin */ - {0x07, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, - /* Front & Rear Mic Pins */ - {0x08, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, - {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, - /* Line Pin */ - {0x09, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, - /* Digital Beep */ - {0x0d, AC_VERB_SET_CONNECT_SEL, 0x00}, - /* Line-Out as Input: disabled */ - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - { } /* end */ -}; - -#ifdef CONFIG_PM -static const struct hda_amp_list ad1981_loopbacks[] = { - { 0x12, HDA_OUTPUT, 0 }, /* Front Mic */ - { 0x13, HDA_OUTPUT, 0 }, /* Line */ - { 0x1b, HDA_OUTPUT, 0 }, /* Aux */ - { 0x1c, HDA_OUTPUT, 0 }, /* Mic */ - { 0x1d, HDA_OUTPUT, 0 }, /* CD */ - { } /* end */ -}; -#endif - -/* - * Patch for HP nx6320 - * - * nx6320 uses EAPD in the reverse way - EAPD-on means the internal - * speaker output enabled _and_ mute-LED off. - */ - -#define AD1981_HP_EVENT 0x37 -#define AD1981_MIC_EVENT 0x38 - -static const struct hda_verb ad1981_hp_init_verbs[] = { - {0x05, AC_VERB_SET_EAPD_BTLENABLE, 0x00 }, /* default off */ - /* pin sensing on HP and Mic jacks */ - {0x06, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_HP_EVENT}, - {0x08, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_MIC_EVENT}, - {} -}; - -/* turn on/off EAPD (+ mute HP) as a master switch */ -static int ad1981_hp_master_sw_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct ad198x_spec *spec = codec->spec; - - if (! ad198x_eapd_put(kcontrol, ucontrol)) - return 0; - /* change speaker pin appropriately */ - snd_hda_set_pin_ctl(codec, 0x05, spec->cur_eapd ? PIN_OUT : 0); - /* toggle HP mute appropriately */ - snd_hda_codec_amp_stereo(codec, 0x06, HDA_OUTPUT, 0, - HDA_AMP_MUTE, - spec->cur_eapd ? 0 : HDA_AMP_MUTE); - return 1; -} - -/* bind volumes of both NID 0x05 and 0x06 */ -static const struct hda_bind_ctls ad1981_hp_bind_master_vol = { - .ops = &snd_hda_bind_vol, - .values = { - HDA_COMPOSE_AMP_VAL(0x05, 3, 0, HDA_OUTPUT), - HDA_COMPOSE_AMP_VAL(0x06, 3, 0, HDA_OUTPUT), - 0 - }, -}; - -/* mute internal speaker if HP is plugged */ -static void ad1981_hp_automute(struct hda_codec *codec) -{ - unsigned int present; - - present = snd_hda_jack_detect(codec, 0x06); - snd_hda_codec_amp_stereo(codec, 0x05, HDA_OUTPUT, 0, - HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); -} - -/* toggle input of built-in and mic jack appropriately */ -static void ad1981_hp_automic(struct hda_codec *codec) -{ - static const struct hda_verb mic_jack_on[] = { - {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, - {} - }; - static const struct hda_verb mic_jack_off[] = { - {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, - {} - }; - unsigned int present; - - present = snd_hda_jack_detect(codec, 0x08); - if (present) - snd_hda_sequence_write(codec, mic_jack_on); - else - snd_hda_sequence_write(codec, mic_jack_off); -} - -/* unsolicited event for HP jack sensing */ -static void ad1981_hp_unsol_event(struct hda_codec *codec, - unsigned int res) -{ - res >>= 26; - switch (res) { - case AD1981_HP_EVENT: - ad1981_hp_automute(codec); - break; - case AD1981_MIC_EVENT: - ad1981_hp_automic(codec); - break; - } -} - -static const struct hda_input_mux ad1981_hp_capture_source = { - .num_items = 3, - .items = { - { "Mic", 0x0 }, - { "Dock Mic", 0x1 }, - { "Mix", 0x2 }, - }, -}; - -static const struct snd_kcontrol_new ad1981_hp_mixers[] = { - HDA_BIND_VOL("Master Playback Volume", &ad1981_hp_bind_master_vol), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .subdevice = HDA_SUBDEV_NID_FLAG | 0x05, - .name = "Master Playback Switch", - .info = ad198x_eapd_info, - .get = ad198x_eapd_get, - .put = ad1981_hp_master_sw_put, - .private_value = 0x05, - }, - HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT), -#if 0 - /* FIXME: analog mic/line loopback doesn't work with my tests... - * (although recording is OK) - */ - HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Dock Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Dock Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x1c, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x1c, 0x0, HDA_OUTPUT), - /* FIXME: does this laptop have analog CD connection? */ - HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT), -#endif - HDA_CODEC_VOLUME("Mic Boost Volume", 0x08, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x18, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Capture Source", - .info = ad198x_mux_enum_info, - .get = ad198x_mux_enum_get, - .put = ad198x_mux_enum_put, - }, - { } /* end */ -}; - -/* initialize jack-sensing, too */ -static int ad1981_hp_init(struct hda_codec *codec) -{ - ad198x_init(codec); - ad1981_hp_automute(codec); - ad1981_hp_automic(codec); - return 0; -} - -/* configuration for Toshiba Laptops */ -static const struct hda_verb ad1981_toshiba_init_verbs[] = { - {0x05, AC_VERB_SET_EAPD_BTLENABLE, 0x01 }, /* default on */ - /* pin sensing on HP and Mic jacks */ - {0x06, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_HP_EVENT}, - {0x08, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_MIC_EVENT}, - {} -}; - -static const struct snd_kcontrol_new ad1981_toshiba_mixers[] = { - HDA_CODEC_VOLUME("Amp Volume", 0x1a, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Amp Switch", 0x1a, 0x0, HDA_OUTPUT), - { } -}; - -/* configuration for Lenovo Thinkpad T60 */ -static const struct snd_kcontrol_new ad1981_thinkpad_mixers[] = { - HDA_CODEC_VOLUME("Master Playback Volume", 0x05, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Master Playback Switch", 0x05, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x08, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Capture Source", - .info = ad198x_mux_enum_info, - .get = ad198x_mux_enum_get, - .put = ad198x_mux_enum_put, - }, - /* identical with AD1983 */ - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source", - .info = ad1983_spdif_route_info, - .get = ad1983_spdif_route_get, - .put = ad1983_spdif_route_put, - }, - { } /* end */ -}; - -static const struct hda_input_mux ad1981_thinkpad_capture_source = { - .num_items = 3, - .items = { - { "Mic", 0x0 }, - { "Mix", 0x2 }, - { "CD", 0x4 }, - }, -}; - -/* models */ -enum { - AD1981_AUTO, - AD1981_BASIC, - AD1981_HP, - AD1981_THINKPAD, - AD1981_TOSHIBA, - AD1981_MODELS -}; - -static const char * const ad1981_models[AD1981_MODELS] = { - [AD1981_AUTO] = "auto", - [AD1981_HP] = "hp", - [AD1981_THINKPAD] = "thinkpad", - [AD1981_BASIC] = "basic", - [AD1981_TOSHIBA] = "toshiba" -}; - -static const struct snd_pci_quirk ad1981_cfg_tbl[] = { - SND_PCI_QUIRK(0x1014, 0x0597, "Lenovo Z60", AD1981_THINKPAD), - SND_PCI_QUIRK(0x1014, 0x05b7, "Lenovo Z60m", AD1981_THINKPAD), - /* All HP models */ - SND_PCI_QUIRK_VENDOR(0x103c, "HP nx", AD1981_HP), - SND_PCI_QUIRK(0x1179, 0x0001, "Toshiba U205", AD1981_TOSHIBA), - /* Lenovo Thinkpad T60/X60/Z6xx */ - SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo Thinkpad", AD1981_THINKPAD), - /* HP nx6320 (reversed SSID, H/W bug) */ - SND_PCI_QUIRK(0x30b0, 0x103c, "HP nx6320", AD1981_HP), - {} -}; -#endif /* ENABLE_AD_STATIC_QUIRKS */ - - /* follow EAPD via vmaster hook */ static void ad_vmaster_eapd_hook(void *private_data, int enabled) { @@ -2172,7 +1596,7 @@ static const struct snd_pci_quirk ad1981_fixup_tbl[] = { {} }; -static int ad1981_parse_auto_config(struct hda_codec *codec) +static int patch_ad1981(struct hda_codec *codec) { struct ad198x_spec *spec; int err; @@ -2205,110 +1629,6 @@ static int ad1981_parse_auto_config(struct hda_codec *codec) return err; } -#ifdef ENABLE_AD_STATIC_QUIRKS -static int patch_ad1981(struct hda_codec *codec) -{ - struct ad198x_spec *spec; - int err, board_config; - - board_config = snd_hda_check_board_config(codec, AD1981_MODELS, - ad1981_models, - ad1981_cfg_tbl); - if (board_config < 0) { - printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", - codec->chip_name); - board_config = AD1981_AUTO; - } - - if (board_config == AD1981_AUTO) - return ad1981_parse_auto_config(codec); - - err = alloc_ad_spec(codec); - if (err < 0) - return -ENOMEM; - spec = codec->spec; - - err = snd_hda_attach_beep_device(codec, 0x10); - if (err < 0) { - ad198x_free(codec); - return err; - } - set_beep_amp(spec, 0x0d, 0, HDA_OUTPUT); - - spec->multiout.max_channels = 2; - spec->multiout.num_dacs = ARRAY_SIZE(ad1981_dac_nids); - spec->multiout.dac_nids = ad1981_dac_nids; - spec->multiout.dig_out_nid = AD1981_SPDIF_OUT; - spec->num_adc_nids = 1; - spec->adc_nids = ad1981_adc_nids; - spec->capsrc_nids = ad1981_capsrc_nids; - spec->input_mux = &ad1981_capture_source; - spec->num_mixers = 1; - spec->mixers[0] = ad1981_mixers; - spec->num_init_verbs = 1; - spec->init_verbs[0] = ad1981_init_verbs; - spec->spdif_route = 0; -#ifdef CONFIG_PM - spec->loopback.amplist = ad1981_loopbacks; -#endif - spec->vmaster_nid = 0x05; - - codec->patch_ops = ad198x_patch_ops; - - /* override some parameters */ - switch (board_config) { - case AD1981_HP: - spec->mixers[0] = ad1981_hp_mixers; - spec->num_init_verbs = 2; - spec->init_verbs[1] = ad1981_hp_init_verbs; - if (!is_jack_available(codec, 0x0a)) - spec->multiout.dig_out_nid = 0; - spec->input_mux = &ad1981_hp_capture_source; - - codec->patch_ops.init = ad1981_hp_init; - codec->patch_ops.unsol_event = ad1981_hp_unsol_event; - /* set the upper-limit for mixer amp to 0dB for avoiding the - * possible damage by overloading - */ - snd_hda_override_amp_caps(codec, 0x11, HDA_INPUT, - (0x17 << AC_AMPCAP_OFFSET_SHIFT) | - (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) | - (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) | - (1 << AC_AMPCAP_MUTE_SHIFT)); - break; - case AD1981_THINKPAD: - spec->mixers[0] = ad1981_thinkpad_mixers; - spec->input_mux = &ad1981_thinkpad_capture_source; - /* set the upper-limit for mixer amp to 0dB for avoiding the - * possible damage by overloading - */ - snd_hda_override_amp_caps(codec, 0x11, HDA_INPUT, - (0x17 << AC_AMPCAP_OFFSET_SHIFT) | - (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) | - (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) | - (1 << AC_AMPCAP_MUTE_SHIFT)); - break; - case AD1981_TOSHIBA: - spec->mixers[0] = ad1981_hp_mixers; - spec->mixers[1] = ad1981_toshiba_mixers; - spec->num_init_verbs = 2; - spec->init_verbs[1] = ad1981_toshiba_init_verbs; - spec->multiout.dig_out_nid = 0; - spec->input_mux = &ad1981_hp_capture_source; - codec->patch_ops.init = ad1981_hp_init; - codec->patch_ops.unsol_event = ad1981_hp_unsol_event; - break; - } - - codec->no_trigger_sense = 1; - codec->no_sticky_stream = 1; - - return 0; -} -#else /* ENABLE_AD_STATIC_QUIRKS */ -#define patch_ad1981 ad1981_parse_auto_config -#endif /* ENABLE_AD_STATIC_QUIRKS */ - /* * AD1988 -- cgit v0.10.2 From 36ad45309be840d652394cfb032b592b6a20a3dd Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 4 Jul 2013 16:34:20 +0200 Subject: ALSA: hda - Remove static quirks for AD1988 codecs For removing static quirks for AD1988 variants, a new fixup defining the 6stack pinconfig has been added for the buggy BIOS. Other than that, we can cut off straightforwardly. Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index 4fedd9d..7777a3a 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -1715,90 +1715,7 @@ static int patch_ad1981(struct hda_codec *codec) * E/F quad mic array */ - #ifdef ENABLE_AD_STATIC_QUIRKS -/* models */ -enum { - AD1988_AUTO, - AD1988_6STACK, - AD1988_6STACK_DIG, - AD1988_3STACK, - AD1988_3STACK_DIG, - AD1988_LAPTOP, - AD1988_LAPTOP_DIG, - AD1988_MODEL_LAST, -}; - -/* reivision id to check workarounds */ -#define AD1988A_REV2 0x100200 - -#define is_rev2(codec) \ - ((codec)->vendor_id == 0x11d41988 && \ - (codec)->revision_id == AD1988A_REV2) - -/* - * mixers - */ - -static const hda_nid_t ad1988_6stack_dac_nids[4] = { - 0x04, 0x06, 0x05, 0x0a -}; - -static const hda_nid_t ad1988_3stack_dac_nids[3] = { - 0x04, 0x05, 0x0a -}; - -/* for AD1988A revision-2, DAC2-4 are swapped */ -static const hda_nid_t ad1988_6stack_dac_nids_rev2[4] = { - 0x04, 0x05, 0x0a, 0x06 -}; - -static const hda_nid_t ad1988_alt_dac_nid[1] = { - 0x03 -}; - -static const hda_nid_t ad1988_3stack_dac_nids_rev2[3] = { - 0x04, 0x0a, 0x06 -}; - -static const hda_nid_t ad1988_adc_nids[3] = { - 0x08, 0x09, 0x0f -}; - -static const hda_nid_t ad1988_capsrc_nids[3] = { - 0x0c, 0x0d, 0x0e -}; - -#define AD1988_SPDIF_OUT 0x02 -#define AD1988_SPDIF_OUT_HDMI 0x0b -#define AD1988_SPDIF_IN 0x07 - -static const hda_nid_t ad1989b_slave_dig_outs[] = { - AD1988_SPDIF_OUT, AD1988_SPDIF_OUT_HDMI, 0 -}; - -static const struct hda_input_mux ad1988_6stack_capture_source = { - .num_items = 5, - .items = { - { "Front Mic", 0x1 }, /* port-B */ - { "Line", 0x2 }, /* port-C */ - { "Mic", 0x4 }, /* port-E */ - { "CD", 0x5 }, - { "Mix", 0x9 }, - }, -}; - -static const struct hda_input_mux ad1988_laptop_capture_source = { - .num_items = 3, - .items = { - { "Mic/Line", 0x1 }, /* port-B */ - { "CD", 0x5 }, - { "Mix", 0x9 }, - }, -}; - -/* - */ static int ad198x_ch_mode_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { @@ -1829,569 +1746,6 @@ static int ad198x_ch_mode_put(struct snd_kcontrol *kcontrol, spec->multiout.num_dacs = spec->multiout.max_channels / 2; return err; } - -/* 6-stack mode */ -static const struct snd_kcontrol_new ad1988_6stack_mixers1[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Surround Playback Volume", 0x06, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Side Playback Volume", 0x0a, 0x0, HDA_OUTPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new ad1988_6stack_mixers1_rev2[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Surround Playback Volume", 0x05, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0a, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0a, 2, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Side Playback Volume", 0x06, 0x0, HDA_OUTPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new ad1988_6stack_mixers2[] = { - HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Front Playback Switch", 0x29, 2, HDA_INPUT), - HDA_BIND_MUTE("Surround Playback Switch", 0x2a, 2, HDA_INPUT), - HDA_BIND_MUTE_MONO("Center Playback Switch", 0x27, 1, 2, HDA_INPUT), - HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x27, 2, 2, HDA_INPUT), - HDA_BIND_MUTE("Side Playback Switch", 0x28, 2, HDA_INPUT), - HDA_BIND_MUTE("Headphone Playback Switch", 0x22, 2, HDA_INPUT), - HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT), - - HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT), - HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x4, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x4, HDA_INPUT), - - HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT), - - HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x39, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x3c, 0x0, HDA_OUTPUT), - { } /* end */ -}; - -/* 3-stack mode */ -static const struct snd_kcontrol_new ad1988_3stack_mixers1[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Surround Playback Volume", 0x0a, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new ad1988_3stack_mixers1_rev2[] = { - HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Surround Playback Volume", 0x0a, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x06, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x06, 2, 0x0, HDA_OUTPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new ad1988_3stack_mixers2[] = { - HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Front Playback Switch", 0x29, 2, HDA_INPUT), - HDA_BIND_MUTE("Surround Playback Switch", 0x2c, 2, HDA_INPUT), - HDA_BIND_MUTE_MONO("Center Playback Switch", 0x26, 1, 2, HDA_INPUT), - HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x26, 2, 2, HDA_INPUT), - HDA_BIND_MUTE("Headphone Playback Switch", 0x22, 2, HDA_INPUT), - HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT), - - HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT), - HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x4, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x4, HDA_INPUT), - - HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT), - - HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x39, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x3c, 0x0, HDA_OUTPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Channel Mode", - .info = ad198x_ch_mode_info, - .get = ad198x_ch_mode_get, - .put = ad198x_ch_mode_put, - }, - - { } /* end */ -}; - -/* laptop mode */ -static const struct snd_kcontrol_new ad1988_laptop_mixers[] = { - HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("PCM Playback Switch", 0x29, 0x0, HDA_INPUT), - HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT), - - HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT), - HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT), - - HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT), - - HDA_CODEC_VOLUME("Mic Boost Volume", 0x39, 0x0, HDA_OUTPUT), - - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "External Amplifier", - .subdevice = HDA_SUBDEV_NID_FLAG | 0x12, - .info = ad198x_eapd_info, - .get = ad198x_eapd_get, - .put = ad198x_eapd_put, - .private_value = 0x12, /* port-D */ - }, - - { } /* end */ -}; - -/* capture */ -static const struct snd_kcontrol_new ad1988_capture_mixers[] = { - HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x0e, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x0e, 0x0, HDA_OUTPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - /* The multiple "Capture Source" controls confuse alsamixer - * So call somewhat different.. - */ - /* .name = "Capture Source", */ - .name = "Input Source", - .count = 3, - .info = ad198x_mux_enum_info, - .get = ad198x_mux_enum_get, - .put = ad198x_mux_enum_put, - }, - { } /* end */ -}; - -static int ad1988_spdif_playback_source_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - static const char * const texts[] = { - "PCM", "ADC1", "ADC2", "ADC3" - }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 4; - if (uinfo->value.enumerated.item >= 4) - uinfo->value.enumerated.item = 3; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; -} - -static int ad1988_spdif_playback_source_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - unsigned int sel; - - sel = snd_hda_codec_read(codec, 0x1d, 0, AC_VERB_GET_AMP_GAIN_MUTE, - AC_AMP_GET_INPUT); - if (!(sel & 0x80)) - ucontrol->value.enumerated.item[0] = 0; - else { - sel = snd_hda_codec_read(codec, 0x0b, 0, - AC_VERB_GET_CONNECT_SEL, 0); - if (sel < 3) - sel++; - else - sel = 0; - ucontrol->value.enumerated.item[0] = sel; - } - return 0; -} - -static int ad1988_spdif_playback_source_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - unsigned int val, sel; - int change; - - val = ucontrol->value.enumerated.item[0]; - if (val > 3) - return -EINVAL; - if (!val) { - sel = snd_hda_codec_read(codec, 0x1d, 0, - AC_VERB_GET_AMP_GAIN_MUTE, - AC_AMP_GET_INPUT); - change = sel & 0x80; - if (change) { - snd_hda_codec_write_cache(codec, 0x1d, 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AMP_IN_UNMUTE(0)); - snd_hda_codec_write_cache(codec, 0x1d, 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AMP_IN_MUTE(1)); - } - } else { - sel = snd_hda_codec_read(codec, 0x1d, 0, - AC_VERB_GET_AMP_GAIN_MUTE, - AC_AMP_GET_INPUT | 0x01); - change = sel & 0x80; - if (change) { - snd_hda_codec_write_cache(codec, 0x1d, 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AMP_IN_MUTE(0)); - snd_hda_codec_write_cache(codec, 0x1d, 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AMP_IN_UNMUTE(1)); - } - sel = snd_hda_codec_read(codec, 0x0b, 0, - AC_VERB_GET_CONNECT_SEL, 0) + 1; - change |= sel != val; - if (change) - snd_hda_codec_write_cache(codec, 0x0b, 0, - AC_VERB_SET_CONNECT_SEL, - val - 1); - } - return change; -} - -static const struct snd_kcontrol_new ad1988_spdif_out_mixers[] = { - HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "IEC958 Playback Source", - .subdevice = HDA_SUBDEV_NID_FLAG | 0x1b, - .info = ad1988_spdif_playback_source_info, - .get = ad1988_spdif_playback_source_get, - .put = ad1988_spdif_playback_source_put, - }, - { } /* end */ -}; - -static const struct snd_kcontrol_new ad1988_spdif_in_mixers[] = { - HDA_CODEC_VOLUME("IEC958 Capture Volume", 0x1c, 0x0, HDA_INPUT), - { } /* end */ -}; - -static const struct snd_kcontrol_new ad1989_spdif_out_mixers[] = { - HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("HDMI Playback Volume", 0x1d, 0x0, HDA_OUTPUT), - { } /* end */ -}; - -/* - * initialization verbs - */ - -/* - * for 6-stack (+dig) - */ -static const struct hda_verb ad1988_6stack_init_verbs[] = { - /* Front, Surround, CLFE, side DAC; unmute as default */ - {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* Port-A front headphon path */ - {0x37, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC0:03h */ - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - /* Port-D line-out path */ - {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - /* Port-F surround path */ - {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - /* Port-G CLFE path */ - {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - /* Port-H side path */ - {0x28, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x28, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x25, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - /* Mono out path */ - {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */ - {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */ - /* Port-B front mic-in path */ - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - /* Port-C line-in path */ - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x33, AC_VERB_SET_CONNECT_SEL, 0x0}, - /* Port-E mic-in path */ - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x34, AC_VERB_SET_CONNECT_SEL, 0x0}, - /* Analog CD Input */ - {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - /* Analog Mix output amp */ - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */ - - { } -}; - -static const struct hda_verb ad1988_6stack_fp_init_verbs[] = { - /* Headphone; unmute as default */ - {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* Port-A front headphon path */ - {0x37, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC0:03h */ - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - - { } -}; - -static const struct hda_verb ad1988_capture_init_verbs[] = { - /* mute analog mix */ - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)}, - /* select ADCs - front-mic */ - {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1}, - {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1}, - {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1}, - - { } -}; - -static const struct hda_verb ad1988_spdif_init_verbs[] = { - /* SPDIF out sel */ - {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */ - {0x0b, AC_VERB_SET_CONNECT_SEL, 0x0}, /* ADC1 */ - {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - /* SPDIF out pin */ - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */ - - { } -}; - -static const struct hda_verb ad1988_spdif_in_init_verbs[] = { - /* unmute SPDIF input pin */ - {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - { } -}; - -/* AD1989 has no ADC -> SPDIF route */ -static const struct hda_verb ad1989_spdif_init_verbs[] = { - /* SPDIF-1 out pin */ - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */ - /* SPDIF-2/HDMI out pin */ - {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */ - { } -}; - -/* - * verbs for 3stack (+dig) - */ -static const struct hda_verb ad1988_3stack_ch2_init[] = { - /* set port-C to line-in */ - { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, - { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, - /* set port-E to mic-in */ - { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, - { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, - { } /* end */ -}; - -static const struct hda_verb ad1988_3stack_ch6_init[] = { - /* set port-C to surround out */ - { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, - /* set port-E to CLFE out */ - { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, - { } /* end */ -}; - -static const struct hda_channel_mode ad1988_3stack_modes[2] = { - { 2, ad1988_3stack_ch2_init }, - { 6, ad1988_3stack_ch6_init }, -}; - -static const struct hda_verb ad1988_3stack_init_verbs[] = { - /* Front, Surround, CLFE, side DAC; unmute as default */ - {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* Port-A front headphon path */ - {0x37, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC0:03h */ - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - /* Port-D line-out path */ - {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - /* Mono out path */ - {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */ - {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */ - /* Port-B front mic-in path */ - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - /* Port-C line-in/surround path - 6ch mode as default */ - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x31, AC_VERB_SET_CONNECT_SEL, 0x0}, /* output sel: DAC 0x05 */ - {0x33, AC_VERB_SET_CONNECT_SEL, 0x0}, - /* Port-E mic-in/CLFE path - 6ch mode as default */ - {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x32, AC_VERB_SET_CONNECT_SEL, 0x1}, /* output sel: DAC 0x0a */ - {0x34, AC_VERB_SET_CONNECT_SEL, 0x0}, - /* mute analog mix */ - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)}, - /* select ADCs - front-mic */ - {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1}, - {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1}, - {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1}, - /* Analog Mix output amp */ - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */ - { } -}; - -/* - * verbs for laptop mode (+dig) - */ -static const struct hda_verb ad1988_laptop_hp_on[] = { - /* unmute port-A and mute port-D */ - { 0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, - { 0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, - { } /* end */ -}; -static const struct hda_verb ad1988_laptop_hp_off[] = { - /* mute port-A and unmute port-D */ - { 0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, - { 0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, - { } /* end */ -}; - -#define AD1988_HP_EVENT 0x01 - -static const struct hda_verb ad1988_laptop_init_verbs[] = { - /* Front, Surround, CLFE, side DAC; unmute as default */ - {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* Port-A front headphon path */ - {0x37, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC0:03h */ - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - /* unsolicited event for pin-sense */ - {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1988_HP_EVENT }, - /* Port-D line-out path + EAPD */ - {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x00}, /* EAPD-off */ - /* Mono out path */ - {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */ - {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */ - /* Port-B mic-in path */ - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - /* Port-C docking station - try to output */ - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x33, AC_VERB_SET_CONNECT_SEL, 0x0}, - /* mute analog mix */ - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)}, - {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)}, - /* select ADCs - mic */ - {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1}, - {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1}, - {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1}, - /* Analog Mix output amp */ - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */ - { } -}; - -static void ad1988_laptop_unsol_event(struct hda_codec *codec, unsigned int res) -{ - if ((res >> 26) != AD1988_HP_EVENT) - return; - if (snd_hda_jack_detect(codec, 0x11)) - snd_hda_sequence_write(codec, ad1988_laptop_hp_on); - else - snd_hda_sequence_write(codec, ad1988_laptop_hp_off); -} - -#ifdef CONFIG_PM -static const struct hda_amp_list ad1988_loopbacks[] = { - { 0x20, HDA_INPUT, 0 }, /* Front Mic */ - { 0x20, HDA_INPUT, 1 }, /* Line */ - { 0x20, HDA_INPUT, 4 }, /* Mic */ - { 0x20, HDA_INPUT, 6 }, /* CD */ - { } /* end */ -}; -#endif #endif /* ENABLE_AD_STATIC_QUIRKS */ static int ad1988_auto_smux_enum_info(struct snd_kcontrol *kcontrol, @@ -2540,7 +1894,34 @@ static int ad1988_add_spdif_mux_ctl(struct hda_codec *codec) /* */ -static int ad1988_parse_auto_config(struct hda_codec *codec) +enum { + AD1988_FIXUP_6STACK_DIG, +}; + +static const struct hda_fixup ad1988_fixups[] = { + [AD1988_FIXUP_6STACK_DIG] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x11, 0x02214130 }, /* front-hp */ + { 0x12, 0x01014010 }, /* line-out */ + { 0x14, 0x02a19122 }, /* front-mic */ + { 0x15, 0x01813021 }, /* line-in */ + { 0x16, 0x01011012 }, /* line-out */ + { 0x17, 0x01a19020 }, /* mic */ + { 0x1b, 0x0145f1f0 }, /* SPDIF */ + { 0x24, 0x01016011 }, /* line-out */ + { 0x25, 0x01012013 }, /* line-out */ + { } + } + }, +}; + +static const struct hda_model_fixup ad1988_fixup_models[] = { + { .id = AD1988_FIXUP_6STACK_DIG, .name = "6stack-dig" }, + {} +}; + +static int patch_ad1988(struct hda_codec *codec) { struct ad198x_spec *spec; int err; @@ -2554,12 +1935,19 @@ static int ad1988_parse_auto_config(struct hda_codec *codec) spec->gen.mixer_merge_nid = 0x21; spec->gen.beep_nid = 0x10; set_beep_amp(spec, 0x10, 0, HDA_OUTPUT); + + snd_hda_pick_fixup(codec, ad1988_fixup_models, NULL, ad1988_fixups); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); + err = ad198x_parse_auto_config(codec); if (err < 0) goto error; err = ad1988_add_spdif_mux_ctl(codec); if (err < 0) goto error; + + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); + return 0; error: @@ -2567,169 +1955,6 @@ static int ad1988_parse_auto_config(struct hda_codec *codec) return err; } -/* - */ - -#ifdef ENABLE_AD_STATIC_QUIRKS -static const char * const ad1988_models[AD1988_MODEL_LAST] = { - [AD1988_6STACK] = "6stack", - [AD1988_6STACK_DIG] = "6stack-dig", - [AD1988_3STACK] = "3stack", - [AD1988_3STACK_DIG] = "3stack-dig", - [AD1988_LAPTOP] = "laptop", - [AD1988_LAPTOP_DIG] = "laptop-dig", - [AD1988_AUTO] = "auto", -}; - -static const struct snd_pci_quirk ad1988_cfg_tbl[] = { - SND_PCI_QUIRK(0x1043, 0x81ec, "Asus P5B-DLX", AD1988_6STACK_DIG), - SND_PCI_QUIRK(0x1043, 0x81f6, "Asus M2N-SLI", AD1988_6STACK_DIG), - SND_PCI_QUIRK(0x1043, 0x8277, "Asus P5K-E/WIFI-AP", AD1988_6STACK_DIG), - SND_PCI_QUIRK(0x1043, 0x82c0, "Asus M3N-HT Deluxe", AD1988_6STACK_DIG), - SND_PCI_QUIRK(0x1043, 0x8311, "Asus P5Q-Premium/Pro", AD1988_6STACK_DIG), - {} -}; - -static int patch_ad1988(struct hda_codec *codec) -{ - struct ad198x_spec *spec; - int err, board_config; - - board_config = snd_hda_check_board_config(codec, AD1988_MODEL_LAST, - ad1988_models, ad1988_cfg_tbl); - if (board_config < 0) { - printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", - codec->chip_name); - board_config = AD1988_AUTO; - } - - if (board_config == AD1988_AUTO) - return ad1988_parse_auto_config(codec); - - err = alloc_ad_spec(codec); - if (err < 0) - return err; - spec = codec->spec; - - if (is_rev2(codec)) - snd_printk(KERN_INFO "patch_analog: AD1988A rev.2 is detected, enable workarounds\n"); - - err = snd_hda_attach_beep_device(codec, 0x10); - if (err < 0) { - ad198x_free(codec); - return err; - } - set_beep_amp(spec, 0x10, 0, HDA_OUTPUT); - - if (!spec->multiout.hp_nid) - spec->multiout.hp_nid = ad1988_alt_dac_nid[0]; - switch (board_config) { - case AD1988_6STACK: - case AD1988_6STACK_DIG: - spec->multiout.max_channels = 8; - spec->multiout.num_dacs = 4; - if (is_rev2(codec)) - spec->multiout.dac_nids = ad1988_6stack_dac_nids_rev2; - else - spec->multiout.dac_nids = ad1988_6stack_dac_nids; - spec->input_mux = &ad1988_6stack_capture_source; - spec->num_mixers = 2; - if (is_rev2(codec)) - spec->mixers[0] = ad1988_6stack_mixers1_rev2; - else - spec->mixers[0] = ad1988_6stack_mixers1; - spec->mixers[1] = ad1988_6stack_mixers2; - spec->num_init_verbs = 1; - spec->init_verbs[0] = ad1988_6stack_init_verbs; - if (board_config == AD1988_6STACK_DIG) { - spec->multiout.dig_out_nid = AD1988_SPDIF_OUT; - spec->dig_in_nid = AD1988_SPDIF_IN; - } - break; - case AD1988_3STACK: - case AD1988_3STACK_DIG: - spec->multiout.max_channels = 6; - spec->multiout.num_dacs = 3; - if (is_rev2(codec)) - spec->multiout.dac_nids = ad1988_3stack_dac_nids_rev2; - else - spec->multiout.dac_nids = ad1988_3stack_dac_nids; - spec->input_mux = &ad1988_6stack_capture_source; - spec->channel_mode = ad1988_3stack_modes; - spec->num_channel_mode = ARRAY_SIZE(ad1988_3stack_modes); - spec->num_mixers = 2; - if (is_rev2(codec)) - spec->mixers[0] = ad1988_3stack_mixers1_rev2; - else - spec->mixers[0] = ad1988_3stack_mixers1; - spec->mixers[1] = ad1988_3stack_mixers2; - spec->num_init_verbs = 1; - spec->init_verbs[0] = ad1988_3stack_init_verbs; - if (board_config == AD1988_3STACK_DIG) - spec->multiout.dig_out_nid = AD1988_SPDIF_OUT; - break; - case AD1988_LAPTOP: - case AD1988_LAPTOP_DIG: - spec->multiout.max_channels = 2; - spec->multiout.num_dacs = 1; - spec->multiout.dac_nids = ad1988_3stack_dac_nids; - spec->input_mux = &ad1988_laptop_capture_source; - spec->num_mixers = 1; - spec->mixers[0] = ad1988_laptop_mixers; - codec->inv_eapd = 1; /* inverted EAPD */ - spec->num_init_verbs = 1; - spec->init_verbs[0] = ad1988_laptop_init_verbs; - if (board_config == AD1988_LAPTOP_DIG) - spec->multiout.dig_out_nid = AD1988_SPDIF_OUT; - break; - } - - spec->num_adc_nids = ARRAY_SIZE(ad1988_adc_nids); - spec->adc_nids = ad1988_adc_nids; - spec->capsrc_nids = ad1988_capsrc_nids; - spec->mixers[spec->num_mixers++] = ad1988_capture_mixers; - spec->init_verbs[spec->num_init_verbs++] = ad1988_capture_init_verbs; - if (spec->multiout.dig_out_nid) { - if (codec->vendor_id >= 0x11d4989a) { - spec->mixers[spec->num_mixers++] = - ad1989_spdif_out_mixers; - spec->init_verbs[spec->num_init_verbs++] = - ad1989_spdif_init_verbs; - codec->slave_dig_outs = ad1989b_slave_dig_outs; - } else { - spec->mixers[spec->num_mixers++] = - ad1988_spdif_out_mixers; - spec->init_verbs[spec->num_init_verbs++] = - ad1988_spdif_init_verbs; - } - } - if (spec->dig_in_nid && codec->vendor_id < 0x11d4989a) { - spec->mixers[spec->num_mixers++] = ad1988_spdif_in_mixers; - spec->init_verbs[spec->num_init_verbs++] = - ad1988_spdif_in_init_verbs; - } - - codec->patch_ops = ad198x_patch_ops; - switch (board_config) { - case AD1988_LAPTOP: - case AD1988_LAPTOP_DIG: - codec->patch_ops.unsol_event = ad1988_laptop_unsol_event; - break; - } -#ifdef CONFIG_PM - spec->loopback.amplist = ad1988_loopbacks; -#endif - spec->vmaster_nid = 0x04; - - codec->no_trigger_sense = 1; - codec->no_sticky_stream = 1; - - return 0; -} -#else /* ENABLE_AD_STATIC_QUIRKS */ -#define patch_ad1988 ad1988_parse_auto_config -#endif /* ENABLE_AD_STATIC_QUIRKS */ - /* * AD1884 / AD1984 -- cgit v0.10.2 From e0b27167c2d6464ff7ae7e35725024349e44596b Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 4 Jul 2013 16:50:46 +0200 Subject: ALSA: hda - Convert the static quirk for Samsung Q1 Ultra ... to a fixup entry. Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index 7777a3a..056810c 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -1063,17 +1063,6 @@ static const struct hda_verb ad1986a_automic_verbs[] = { {} }; -/* Ultra initialization */ -static const struct hda_verb ad1986a_ultra_init[] = { - /* eapd initialization */ - { 0x1b, AC_VERB_SET_EAPD_BTLENABLE, 0x00 }, - /* CLFE -> Mic in */ - { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x2 }, - { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, - { 0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080 }, - { } /* end */ -}; - /* pin sensing on HP jack */ static const struct hda_verb ad1986a_hp_init_verbs[] = { {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1986A_HP_EVENT}, @@ -1110,7 +1099,6 @@ enum { AD1986A_LAPTOP, AD1986A_LAPTOP_EAPD, AD1986A_LAPTOP_AUTOMUTE, - AD1986A_ULTRA, AD1986A_SAMSUNG, AD1986A_SAMSUNG_P50, AD1986A_MODELS @@ -1123,7 +1111,6 @@ static const char * const ad1986a_models[AD1986A_MODELS] = { [AD1986A_LAPTOP] = "laptop", [AD1986A_LAPTOP_EAPD] = "laptop-eapd", [AD1986A_LAPTOP_AUTOMUTE] = "laptop-automute", - [AD1986A_ULTRA] = "ultra", [AD1986A_SAMSUNG] = "samsung", [AD1986A_SAMSUNG_P50] = "samsung-p50", }; @@ -1149,7 +1136,6 @@ static const struct snd_pci_quirk ad1986a_cfg_tbl[] = { SND_PCI_QUIRK(0x144d, 0xb03c, "Samsung R55", AD1986A_3STACK), SND_PCI_QUIRK(0x144d, 0xc01e, "FSC V2060", AD1986A_LAPTOP), SND_PCI_QUIRK(0x144d, 0xc024, "Samsung P50", AD1986A_SAMSUNG_P50), - SND_PCI_QUIRK(0x144d, 0xc027, "Samsung Q1", AD1986A_ULTRA), SND_PCI_QUIRK_MASK(0x144d, 0xff00, 0xc000, "Samsung", AD1986A_SAMSUNG), SND_PCI_QUIRK(0x144d, 0xc504, "Samsung Q35", AD1986A_3STACK), SND_PCI_QUIRK(0x17aa, 0x1011, "Lenovo M55", AD1986A_LAPTOP), @@ -1203,6 +1189,7 @@ static void ad_fixup_inv_jack_detect(struct hda_codec *codec, enum { AD1986A_FIXUP_INV_JACK_DETECT, + AD1986A_FIXUP_ULTRA, }; static const struct hda_fixup ad1986a_fixups[] = { @@ -1210,9 +1197,18 @@ static const struct hda_fixup ad1986a_fixups[] = { .type = HDA_FIXUP_FUNC, .v.func = ad_fixup_inv_jack_detect, }, + [AD1986A_FIXUP_ULTRA] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x1b, 0x90170110 }, /* speaker */ + { 0x1d, 0x90a7013e }, /* int mic */ + {} + }, + }, }; static const struct snd_pci_quirk ad1986a_fixup_tbl[] = { + SND_PCI_QUIRK(0x144d, 0xc027, "Samsung Q1", AD1986A_FIXUP_ULTRA), SND_PCI_QUIRK(0x17aa, 0x2066, "Lenovo N100", AD1986A_FIXUP_INV_JACK_DETECT), {} }; @@ -1395,15 +1391,6 @@ static int patch_ad1986a(struct hda_codec *codec) */ spec->inv_jack_detect = 1; break; - case AD1986A_ULTRA: - spec->mixers[0] = ad1986a_laptop_eapd_mixers; - spec->num_init_verbs = 2; - spec->init_verbs[1] = ad1986a_ultra_init; - spec->multiout.max_channels = 2; - spec->multiout.num_dacs = 1; - spec->multiout.dac_nids = ad1986a_laptop_dac_nids; - spec->multiout.dig_out_nid = 0; - break; } /* AD1986A has a hardware problem that it can't share a stream -- cgit v0.10.2 From f8c0ab1798b601493f29cb4836ccdaa3811ba390 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 4 Jul 2013 17:06:04 +0200 Subject: ALSA: hda - Convert static quirks for AD1986A Samsung laptops Just need to override some pin-configurations. Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index 056810c..1e4dc98 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -857,33 +857,6 @@ static const struct snd_kcontrol_new ad1986a_laptop_intmic_mixers[] = { { } /* end */ }; -/* re-connect the mic boost input according to the jack sensing */ -static void ad1986a_automic(struct hda_codec *codec) -{ - unsigned int present; - present = snd_hda_jack_detect(codec, 0x1f); - /* 0 = 0x1f, 2 = 0x1d, 4 = mixed */ - snd_hda_codec_write(codec, 0x0f, 0, AC_VERB_SET_CONNECT_SEL, - present ? 0 : 2); -} - -#define AD1986A_MIC_EVENT 0x36 - -static void ad1986a_automic_unsol_event(struct hda_codec *codec, - unsigned int res) -{ - if ((res >> 26) != AD1986A_MIC_EVENT) - return; - ad1986a_automic(codec); -} - -static int ad1986a_automic_init(struct hda_codec *codec) -{ - ad198x_init(codec); - ad1986a_automic(codec); - return 0; -} - /* laptop-automute - 2ch only */ static void ad1986a_update_hp(struct hda_codec *codec) @@ -1054,42 +1027,12 @@ static const struct hda_verb ad1986a_eapd_init_verbs[] = { {} }; -static const struct hda_verb ad1986a_automic_verbs[] = { - {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - /*{0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},*/ - {0x0f, AC_VERB_SET_CONNECT_SEL, 0x0}, - {0x1f, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1986A_MIC_EVENT}, - {} -}; - /* pin sensing on HP jack */ static const struct hda_verb ad1986a_hp_init_verbs[] = { {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1986A_HP_EVENT}, {} }; -static void ad1986a_samsung_p50_unsol_event(struct hda_codec *codec, - unsigned int res) -{ - switch (res >> 26) { - case AD1986A_HP_EVENT: - ad1986a_hp_automute(codec); - break; - case AD1986A_MIC_EVENT: - ad1986a_automic(codec); - break; - } -} - -static int ad1986a_samsung_p50_init(struct hda_codec *codec) -{ - ad198x_init(codec); - ad1986a_hp_automute(codec); - ad1986a_automic(codec); - return 0; -} - /* models */ enum { @@ -1099,8 +1042,6 @@ enum { AD1986A_LAPTOP, AD1986A_LAPTOP_EAPD, AD1986A_LAPTOP_AUTOMUTE, - AD1986A_SAMSUNG, - AD1986A_SAMSUNG_P50, AD1986A_MODELS }; @@ -1111,8 +1052,6 @@ static const char * const ad1986a_models[AD1986A_MODELS] = { [AD1986A_LAPTOP] = "laptop", [AD1986A_LAPTOP_EAPD] = "laptop-eapd", [AD1986A_LAPTOP_AUTOMUTE] = "laptop-automute", - [AD1986A_SAMSUNG] = "samsung", - [AD1986A_SAMSUNG_P50] = "samsung-p50", }; static const struct snd_pci_quirk ad1986a_cfg_tbl[] = { @@ -1135,8 +1074,6 @@ static const struct snd_pci_quirk ad1986a_cfg_tbl[] = { SND_PCI_QUIRK(0x1179, 0xff40, "Toshiba Satellite L40-10Q", AD1986A_3STACK), SND_PCI_QUIRK(0x144d, 0xb03c, "Samsung R55", AD1986A_3STACK), SND_PCI_QUIRK(0x144d, 0xc01e, "FSC V2060", AD1986A_LAPTOP), - SND_PCI_QUIRK(0x144d, 0xc024, "Samsung P50", AD1986A_SAMSUNG_P50), - SND_PCI_QUIRK_MASK(0x144d, 0xff00, 0xc000, "Samsung", AD1986A_SAMSUNG), SND_PCI_QUIRK(0x144d, 0xc504, "Samsung Q35", AD1986A_3STACK), SND_PCI_QUIRK(0x17aa, 0x1011, "Lenovo M55", AD1986A_LAPTOP), SND_PCI_QUIRK(0x17aa, 0x1017, "Lenovo A60", AD1986A_3STACK), @@ -1190,6 +1127,7 @@ static void ad_fixup_inv_jack_detect(struct hda_codec *codec, enum { AD1986A_FIXUP_INV_JACK_DETECT, AD1986A_FIXUP_ULTRA, + AD1986A_FIXUP_SAMSUNG, }; static const struct hda_fixup ad1986a_fixups[] = { @@ -1205,9 +1143,20 @@ static const struct hda_fixup ad1986a_fixups[] = { {} }, }, + [AD1986A_FIXUP_SAMSUNG] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x1b, 0x90170110 }, /* speaker */ + { 0x1d, 0x90a7013e }, /* int mic */ + { 0x20, 0x411111f0 }, /* N/A */ + { 0x24, 0x411111f0 }, /* N/A */ + {} + }, + }, }; static const struct snd_pci_quirk ad1986a_fixup_tbl[] = { + SND_PCI_QUIRK_MASK(0x144d, 0xff00, 0xc000, "Samsung", AD1986A_FIXUP_SAMSUNG), SND_PCI_QUIRK(0x144d, 0xc027, "Samsung Q1", AD1986A_FIXUP_ULTRA), SND_PCI_QUIRK(0x17aa, 0x2066, "Lenovo N100", AD1986A_FIXUP_INV_JACK_DETECT), {} @@ -1337,39 +1286,6 @@ static int patch_ad1986a(struct hda_codec *codec) spec->multiout.dig_out_nid = 0; spec->input_mux = &ad1986a_laptop_eapd_capture_source; break; - case AD1986A_SAMSUNG: - spec->num_mixers = 2; - spec->mixers[0] = ad1986a_laptop_master_mixers; - spec->mixers[1] = ad1986a_laptop_eapd_mixers; - spec->num_init_verbs = 3; - spec->init_verbs[1] = ad1986a_eapd_init_verbs; - spec->init_verbs[2] = ad1986a_automic_verbs; - spec->multiout.max_channels = 2; - spec->multiout.num_dacs = 1; - spec->multiout.dac_nids = ad1986a_laptop_dac_nids; - if (!is_jack_available(codec, 0x25)) - spec->multiout.dig_out_nid = 0; - spec->input_mux = &ad1986a_automic_capture_source; - codec->patch_ops.unsol_event = ad1986a_automic_unsol_event; - codec->patch_ops.init = ad1986a_automic_init; - break; - case AD1986A_SAMSUNG_P50: - spec->num_mixers = 2; - spec->mixers[0] = ad1986a_automute_master_mixers; - spec->mixers[1] = ad1986a_laptop_eapd_mixers; - spec->num_init_verbs = 4; - spec->init_verbs[1] = ad1986a_eapd_init_verbs; - spec->init_verbs[2] = ad1986a_automic_verbs; - spec->init_verbs[3] = ad1986a_hp_init_verbs; - spec->multiout.max_channels = 2; - spec->multiout.num_dacs = 1; - spec->multiout.dac_nids = ad1986a_laptop_dac_nids; - if (!is_jack_available(codec, 0x25)) - spec->multiout.dig_out_nid = 0; - spec->input_mux = &ad1986a_automic_capture_source; - codec->patch_ops.unsol_event = ad1986a_samsung_p50_unsol_event; - codec->patch_ops.init = ad1986a_samsung_p50_init; - break; case AD1986A_LAPTOP_AUTOMUTE: spec->num_mixers = 3; spec->mixers[0] = ad1986a_automute_master_mixers; -- cgit v0.10.2 From 7fc116ec27cf51831d2d4e555c89d899be410340 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 4 Jul 2013 17:18:48 +0200 Subject: ALSA: hda - Drop static quirks for other AD1986A Samsung machines BIOS on Samsung R55, M55 and M50 provide the proper pin-configs, so we can remove the corresponding static quirk entries gracefully. Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index 1e4dc98..3f2434a 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -1072,13 +1072,10 @@ static const struct snd_pci_quirk ad1986a_cfg_tbl[] = { SND_PCI_QUIRK(0x1043, 0x8234, "ASUS M2N", AD1986A_3STACK), SND_PCI_QUIRK(0x10de, 0xcb84, "ASUS A8N-VM", AD1986A_3STACK), SND_PCI_QUIRK(0x1179, 0xff40, "Toshiba Satellite L40-10Q", AD1986A_3STACK), - SND_PCI_QUIRK(0x144d, 0xb03c, "Samsung R55", AD1986A_3STACK), SND_PCI_QUIRK(0x144d, 0xc01e, "FSC V2060", AD1986A_LAPTOP), - SND_PCI_QUIRK(0x144d, 0xc504, "Samsung Q35", AD1986A_3STACK), SND_PCI_QUIRK(0x17aa, 0x1011, "Lenovo M55", AD1986A_LAPTOP), SND_PCI_QUIRK(0x17aa, 0x1017, "Lenovo A60", AD1986A_3STACK), SND_PCI_QUIRK(0x17aa, 0x2066, "Lenovo N100", AD1986A_LAPTOP_AUTOMUTE), - SND_PCI_QUIRK(0x17c0, 0x2017, "Samsung M50", AD1986A_LAPTOP), {} }; -- cgit v0.10.2 From fc39a7ea9235104b06ee43385d4265f2d078e62b Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 4 Jul 2013 17:25:03 +0200 Subject: ALSA: hda - Drop static quirk for Toshiba Satellite L40-10Q The BIOS provides good pin-configurations, so we can drop the static quirk now. Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index 3f2434a..a41e121 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -1071,7 +1071,6 @@ static const struct snd_pci_quirk ad1986a_cfg_tbl[] = { SND_PCI_QUIRK(0x1043, 0x81cb, "ASUS M2N", AD1986A_3STACK), SND_PCI_QUIRK(0x1043, 0x8234, "ASUS M2N", AD1986A_3STACK), SND_PCI_QUIRK(0x10de, 0xcb84, "ASUS A8N-VM", AD1986A_3STACK), - SND_PCI_QUIRK(0x1179, 0xff40, "Toshiba Satellite L40-10Q", AD1986A_3STACK), SND_PCI_QUIRK(0x144d, 0xc01e, "FSC V2060", AD1986A_LAPTOP), SND_PCI_QUIRK(0x17aa, 0x1011, "Lenovo M55", AD1986A_LAPTOP), SND_PCI_QUIRK(0x17aa, 0x1017, "Lenovo A60", AD1986A_3STACK), -- cgit v0.10.2 From 0f7dbda0ec3bc4d778d7acf741b220fbf4318a20 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 4 Jul 2013 18:03:56 +0200 Subject: ALSA: hda - Drop a few other static quirks for AD1986A Most of ASUS laptops and Lenovo N100 provide proper BIOS pin-configs. Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index a41e121..3b23280 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -1056,15 +1056,6 @@ static const char * const ad1986a_models[AD1986A_MODELS] = { static const struct snd_pci_quirk ad1986a_cfg_tbl[] = { SND_PCI_QUIRK(0x103c, 0x30af, "HP B2800", AD1986A_LAPTOP_EAPD), - SND_PCI_QUIRK(0x1043, 0x1153, "ASUS M9", AD1986A_LAPTOP_EAPD), - SND_PCI_QUIRK(0x1043, 0x11f7, "ASUS U5A", AD1986A_LAPTOP_EAPD), - SND_PCI_QUIRK(0x1043, 0x1213, "ASUS A6J", AD1986A_LAPTOP_EAPD), - SND_PCI_QUIRK(0x1043, 0x1263, "ASUS U5F", AD1986A_LAPTOP_EAPD), - SND_PCI_QUIRK(0x1043, 0x1297, "ASUS Z62F", AD1986A_LAPTOP_EAPD), - SND_PCI_QUIRK(0x1043, 0x12b3, "ASUS V1j", AD1986A_LAPTOP_EAPD), - SND_PCI_QUIRK(0x1043, 0x1302, "ASUS W3j", AD1986A_LAPTOP_EAPD), - SND_PCI_QUIRK(0x1043, 0x1443, "ASUS VX1", AD1986A_LAPTOP), - SND_PCI_QUIRK(0x1043, 0x1447, "ASUS A8J", AD1986A_3STACK), SND_PCI_QUIRK(0x1043, 0x817f, "ASUS P5", AD1986A_3STACK), SND_PCI_QUIRK(0x1043, 0x818f, "ASUS P5", AD1986A_LAPTOP), SND_PCI_QUIRK(0x1043, 0x81b3, "ASUS P5", AD1986A_3STACK), @@ -1074,7 +1065,6 @@ static const struct snd_pci_quirk ad1986a_cfg_tbl[] = { SND_PCI_QUIRK(0x144d, 0xc01e, "FSC V2060", AD1986A_LAPTOP), SND_PCI_QUIRK(0x17aa, 0x1011, "Lenovo M55", AD1986A_LAPTOP), SND_PCI_QUIRK(0x17aa, 0x1017, "Lenovo A60", AD1986A_3STACK), - SND_PCI_QUIRK(0x17aa, 0x2066, "Lenovo N100", AD1986A_LAPTOP_AUTOMUTE), {} }; -- cgit v0.10.2 From 632408adfe70be6706cb89522b0d5b3dce188d84 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 5 Jul 2013 14:14:14 +0200 Subject: ALSA: hda - Remove static quirks for AD1986A codec Finally all the static quirks in patch_analog.c are reduced by this patch. As machines with AD1986A codec are all old and often their BIOS are buggy, we need to keep at least a few static pin conifgs. Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index 3b23280..0cbdd87 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -32,7 +32,6 @@ #include "hda_jack.h" #include "hda_generic.h" -#define ENABLE_AD_STATIC_QUIRKS struct ad198x_spec { struct hda_gen_spec gen; @@ -43,114 +42,8 @@ struct ad198x_spec { hda_nid_t eapd_nid; unsigned int beep_amp; /* beep amp value, set via set_beep_amp() */ - -#ifdef ENABLE_AD_STATIC_QUIRKS - const struct snd_kcontrol_new *mixers[6]; - int num_mixers; - const struct hda_verb *init_verbs[6]; /* initialization verbs - * don't forget NULL termination! - */ - unsigned int num_init_verbs; - - /* playback */ - struct hda_multi_out multiout; /* playback set-up - * max_channels, dacs must be set - * dig_out_nid and hp_nid are optional - */ - unsigned int cur_eapd; - unsigned int need_dac_fix; - - /* capture */ - unsigned int num_adc_nids; - const hda_nid_t *adc_nids; - hda_nid_t dig_in_nid; /* digital-in NID; optional */ - - /* capture source */ - const struct hda_input_mux *input_mux; - const hda_nid_t *capsrc_nids; - unsigned int cur_mux[3]; - - /* channel model */ - const struct hda_channel_mode *channel_mode; - int num_channel_mode; - - /* PCM information */ - struct hda_pcm pcm_rec[3]; /* used in alc_build_pcms() */ - - unsigned int spdif_route; - - unsigned int jack_present: 1; - unsigned int inv_jack_detect: 1;/* inverted jack-detection */ - unsigned int analog_beep: 1; /* analog beep input present */ - unsigned int avoid_init_slave_vol:1; - -#ifdef CONFIG_PM - struct hda_loopback_check loopback; -#endif - /* for virtual master */ - hda_nid_t vmaster_nid; - const char * const *slave_vols; - const char * const *slave_sws; -#endif /* ENABLE_AD_STATIC_QUIRKS */ }; -#ifdef ENABLE_AD_STATIC_QUIRKS -/* - * input MUX handling (common part) - */ -static int ad198x_mux_enum_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct ad198x_spec *spec = codec->spec; - - return snd_hda_input_mux_info(spec->input_mux, uinfo); -} - -static int ad198x_mux_enum_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct ad198x_spec *spec = codec->spec; - unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); - - ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx]; - return 0; -} - -static int ad198x_mux_enum_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct ad198x_spec *spec = codec->spec; - unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); - - return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol, - spec->capsrc_nids[adc_idx], - &spec->cur_mux[adc_idx]); -} - -/* - * initialization (common callbacks) - */ -static int ad198x_init(struct hda_codec *codec) -{ - struct ad198x_spec *spec = codec->spec; - int i; - - for (i = 0; i < spec->num_init_verbs; i++) - snd_hda_sequence_write(codec, spec->init_verbs[i]); - return 0; -} - -static const char * const ad_slave_pfxs[] = { - "Front", "Surround", "Center", "LFE", "Side", - "Headphone", "Mono", "Speaker", "IEC958", - NULL -}; - -static const char * const ad1988_6stack_fp_slave_pfxs[] = { - "Front", "Surround", "Center", "LFE", "Side", "IEC958", - NULL -}; -#endif /* ENABLE_AD_STATIC_QUIRKS */ #ifdef CONFIG_SND_HDA_INPUT_BEEP /* additional beep mixers; the actual parameters are overwritten at build */ @@ -160,12 +53,6 @@ static const struct snd_kcontrol_new ad_beep_mixer[] = { { } /* end */ }; -static const struct snd_kcontrol_new ad_beep2_mixer[] = { - HDA_CODEC_VOLUME("Digital Beep Playback Volume", 0, 0, HDA_OUTPUT), - HDA_CODEC_MUTE_BEEP("Digital Beep Playback Switch", 0, 0, HDA_OUTPUT), - { } /* end */ -}; - #define set_beep_amp(spec, nid, idx, dir) \ ((spec)->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir)) /* mono */ #else @@ -181,8 +68,7 @@ static int create_beep_ctls(struct hda_codec *codec) if (!spec->beep_amp) return 0; - knew = spec->analog_beep ? ad_beep2_mixer : ad_beep_mixer; - for ( ; knew->name; knew++) { + for (knew = ad_beep_mixer ; knew->name; knew++) { int err; struct snd_kcontrol *kctl; kctl = snd_ctl_new1(knew, codec); @@ -199,268 +85,6 @@ static int create_beep_ctls(struct hda_codec *codec) #define create_beep_ctls(codec) 0 #endif -#ifdef ENABLE_AD_STATIC_QUIRKS -static int ad198x_build_controls(struct hda_codec *codec) -{ - struct ad198x_spec *spec = codec->spec; - struct snd_kcontrol *kctl; - unsigned int i; - int err; - - for (i = 0; i < spec->num_mixers; i++) { - err = snd_hda_add_new_ctls(codec, spec->mixers[i]); - if (err < 0) - return err; - } - if (spec->multiout.dig_out_nid) { - err = snd_hda_create_spdif_out_ctls(codec, - spec->multiout.dig_out_nid, - spec->multiout.dig_out_nid); - if (err < 0) - return err; - err = snd_hda_create_spdif_share_sw(codec, - &spec->multiout); - if (err < 0) - return err; - spec->multiout.share_spdif = 1; - } - if (spec->dig_in_nid) { - err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid); - if (err < 0) - return err; - } - - /* create beep controls if needed */ - err = create_beep_ctls(codec); - if (err < 0) - return err; - - /* if we have no master control, let's create it */ - if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) { - unsigned int vmaster_tlv[4]; - snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid, - HDA_OUTPUT, vmaster_tlv); - err = __snd_hda_add_vmaster(codec, "Master Playback Volume", - vmaster_tlv, - (spec->slave_vols ? - spec->slave_vols : ad_slave_pfxs), - "Playback Volume", - !spec->avoid_init_slave_vol, NULL); - if (err < 0) - return err; - } - if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) { - err = snd_hda_add_vmaster(codec, "Master Playback Switch", - NULL, - (spec->slave_sws ? - spec->slave_sws : ad_slave_pfxs), - "Playback Switch"); - if (err < 0) - return err; - } - - /* assign Capture Source enums to NID */ - kctl = snd_hda_find_mixer_ctl(codec, "Capture Source"); - if (!kctl) - kctl = snd_hda_find_mixer_ctl(codec, "Input Source"); - for (i = 0; kctl && i < kctl->count; i++) { - err = snd_hda_add_nid(codec, kctl, i, spec->capsrc_nids[i]); - if (err < 0) - return err; - } - - /* assign IEC958 enums to NID */ - kctl = snd_hda_find_mixer_ctl(codec, - SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source"); - if (kctl) { - err = snd_hda_add_nid(codec, kctl, 0, - spec->multiout.dig_out_nid); - if (err < 0) - return err; - } - - return 0; -} - -#ifdef CONFIG_PM -static int ad198x_check_power_status(struct hda_codec *codec, hda_nid_t nid) -{ - struct ad198x_spec *spec = codec->spec; - return snd_hda_check_amp_list_power(codec, &spec->loopback, nid); -} -#endif - -/* - * Analog playback callbacks - */ -static int ad198x_playback_pcm_open(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct ad198x_spec *spec = codec->spec; - return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, - hinfo); -} - -static int ad198x_playback_pcm_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - struct ad198x_spec *spec = codec->spec; - return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag, - format, substream); -} - -static int ad198x_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct ad198x_spec *spec = codec->spec; - return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout); -} - -/* - * Digital out - */ -static int ad198x_dig_playback_pcm_open(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct ad198x_spec *spec = codec->spec; - return snd_hda_multi_out_dig_open(codec, &spec->multiout); -} - -static int ad198x_dig_playback_pcm_close(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct ad198x_spec *spec = codec->spec; - return snd_hda_multi_out_dig_close(codec, &spec->multiout); -} - -static int ad198x_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - struct ad198x_spec *spec = codec->spec; - return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag, - format, substream); -} - -static int ad198x_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct ad198x_spec *spec = codec->spec; - return snd_hda_multi_out_dig_cleanup(codec, &spec->multiout); -} - -/* - * Analog capture - */ -static int ad198x_capture_pcm_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - struct ad198x_spec *spec = codec->spec; - snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], - stream_tag, 0, format); - return 0; -} - -static int ad198x_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct ad198x_spec *spec = codec->spec; - snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]); - return 0; -} - -/* - */ -static const struct hda_pcm_stream ad198x_pcm_analog_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 6, /* changed later */ - .nid = 0, /* fill later */ - .ops = { - .open = ad198x_playback_pcm_open, - .prepare = ad198x_playback_pcm_prepare, - .cleanup = ad198x_playback_pcm_cleanup, - }, -}; - -static const struct hda_pcm_stream ad198x_pcm_analog_capture = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - .nid = 0, /* fill later */ - .ops = { - .prepare = ad198x_capture_pcm_prepare, - .cleanup = ad198x_capture_pcm_cleanup - }, -}; - -static const struct hda_pcm_stream ad198x_pcm_digital_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - .nid = 0, /* fill later */ - .ops = { - .open = ad198x_dig_playback_pcm_open, - .close = ad198x_dig_playback_pcm_close, - .prepare = ad198x_dig_playback_pcm_prepare, - .cleanup = ad198x_dig_playback_pcm_cleanup - }, -}; - -static const struct hda_pcm_stream ad198x_pcm_digital_capture = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - /* NID is set in alc_build_pcms */ -}; - -static int ad198x_build_pcms(struct hda_codec *codec) -{ - struct ad198x_spec *spec = codec->spec; - struct hda_pcm *info = spec->pcm_rec; - - codec->num_pcms = 1; - codec->pcm_info = info; - - info->name = "AD198x Analog"; - info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ad198x_pcm_analog_playback; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = spec->multiout.max_channels; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dac_nids[0]; - info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad198x_pcm_analog_capture; - info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = spec->num_adc_nids; - info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0]; - - if (spec->multiout.dig_out_nid) { - info++; - codec->num_pcms++; - codec->spdif_status_reset = 1; - info->name = "AD198x Digital"; - info->pcm_type = HDA_PCM_TYPE_SPDIF; - info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ad198x_pcm_digital_playback; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid; - if (spec->dig_in_nid) { - info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad198x_pcm_digital_capture; - info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid; - } - } - - return 0; -} -#endif /* ENABLE_AD_STATIC_QUIRKS */ static void ad198x_power_eapd_write(struct hda_codec *codec, hda_nid_t front, hda_nid_t hp) @@ -507,18 +131,6 @@ static void ad198x_shutup(struct hda_codec *codec) ad198x_power_eapd(codec); } -static void ad198x_free(struct hda_codec *codec) -{ - struct ad198x_spec *spec = codec->spec; - - if (!spec) - return; - - snd_hda_gen_spec_free(&spec->gen); - kfree(spec); - snd_hda_detach_beep_device(codec); -} - #ifdef CONFIG_PM static int ad198x_suspend(struct hda_codec *codec) { @@ -527,65 +139,6 @@ static int ad198x_suspend(struct hda_codec *codec) } #endif -#ifdef ENABLE_AD_STATIC_QUIRKS -static const struct hda_codec_ops ad198x_patch_ops = { - .build_controls = ad198x_build_controls, - .build_pcms = ad198x_build_pcms, - .init = ad198x_init, - .free = ad198x_free, -#ifdef CONFIG_PM - .check_power_status = ad198x_check_power_status, - .suspend = ad198x_suspend, -#endif - .reboot_notify = ad198x_shutup, -}; - - -/* - * EAPD control - * the private value = nid - */ -#define ad198x_eapd_info snd_ctl_boolean_mono_info - -static int ad198x_eapd_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct ad198x_spec *spec = codec->spec; - if (codec->inv_eapd) - ucontrol->value.integer.value[0] = ! spec->cur_eapd; - else - ucontrol->value.integer.value[0] = spec->cur_eapd; - return 0; -} - -static int ad198x_eapd_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct ad198x_spec *spec = codec->spec; - hda_nid_t nid = kcontrol->private_value & 0xff; - unsigned int eapd; - eapd = !!ucontrol->value.integer.value[0]; - if (codec->inv_eapd) - eapd = !eapd; - if (eapd == spec->cur_eapd) - return 0; - spec->cur_eapd = eapd; - snd_hda_codec_write_cache(codec, nid, - 0, AC_VERB_SET_EAPD_BTLENABLE, - eapd ? 0x02 : 0x00); - return 1; -} - -static int ad198x_ch_mode_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo); -static int ad198x_ch_mode_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol); -static int ad198x_ch_mode_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol); -#endif /* ENABLE_AD_STATIC_QUIRKS */ - /* * Automatic parse of I/O pins from the BIOS configuration @@ -646,446 +199,6 @@ static int ad198x_parse_auto_config(struct hda_codec *codec) * AD1986A specific */ -#ifdef ENABLE_AD_STATIC_QUIRKS -#define AD1986A_SPDIF_OUT 0x02 -#define AD1986A_FRONT_DAC 0x03 -#define AD1986A_SURR_DAC 0x04 -#define AD1986A_CLFE_DAC 0x05 -#define AD1986A_ADC 0x06 - -static const hda_nid_t ad1986a_dac_nids[3] = { - AD1986A_FRONT_DAC, AD1986A_SURR_DAC, AD1986A_CLFE_DAC -}; -static const hda_nid_t ad1986a_adc_nids[1] = { AD1986A_ADC }; -static const hda_nid_t ad1986a_capsrc_nids[1] = { 0x12 }; - -static const struct hda_input_mux ad1986a_capture_source = { - .num_items = 7, - .items = { - { "Mic", 0x0 }, - { "CD", 0x1 }, - { "Aux", 0x3 }, - { "Line", 0x4 }, - { "Mix", 0x5 }, - { "Mono", 0x6 }, - { "Phone", 0x7 }, - }, -}; - - -static const struct hda_bind_ctls ad1986a_bind_pcm_vol = { - .ops = &snd_hda_bind_vol, - .values = { - HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT), - HDA_COMPOSE_AMP_VAL(AD1986A_SURR_DAC, 3, 0, HDA_OUTPUT), - HDA_COMPOSE_AMP_VAL(AD1986A_CLFE_DAC, 3, 0, HDA_OUTPUT), - 0 - }, -}; - -static const struct hda_bind_ctls ad1986a_bind_pcm_sw = { - .ops = &snd_hda_bind_sw, - .values = { - HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT), - HDA_COMPOSE_AMP_VAL(AD1986A_SURR_DAC, 3, 0, HDA_OUTPUT), - HDA_COMPOSE_AMP_VAL(AD1986A_CLFE_DAC, 3, 0, HDA_OUTPUT), - 0 - }, -}; - -/* - * mixers - */ -static const struct snd_kcontrol_new ad1986a_mixers[] = { - /* - * bind volumes/mutes of 3 DACs as a single PCM control for simplicity - */ - HDA_BIND_VOL("PCM Playback Volume", &ad1986a_bind_pcm_vol), - HDA_BIND_SW("PCM Playback Switch", &ad1986a_bind_pcm_sw), - HDA_CODEC_VOLUME("Front Playback Volume", 0x1b, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Front Playback Switch", 0x1b, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Surround Playback Volume", 0x1c, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Surround Playback Switch", 0x1c, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x1d, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x1d, 2, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x1d, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x1d, 2, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Headphone Playback Volume", 0x1a, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Line Playback Volume", 0x17, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Line Playback Switch", 0x17, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Aux Playback Volume", 0x16, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Aux Playback Switch", 0x16, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x0f, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Mono Playback Volume", 0x1e, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Mono Playback Switch", 0x1e, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Capture Source", - .info = ad198x_mux_enum_info, - .get = ad198x_mux_enum_get, - .put = ad198x_mux_enum_put, - }, - HDA_CODEC_MUTE("Stereo Downmix Switch", 0x09, 0x0, HDA_OUTPUT), - { } /* end */ -}; - -/* additional mixers for 3stack mode */ -static const struct snd_kcontrol_new ad1986a_3st_mixers[] = { - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Channel Mode", - .info = ad198x_ch_mode_info, - .get = ad198x_ch_mode_get, - .put = ad198x_ch_mode_put, - }, - { } /* end */ -}; - -/* laptop model - 2ch only */ -static const hda_nid_t ad1986a_laptop_dac_nids[1] = { AD1986A_FRONT_DAC }; - -/* master controls both pins 0x1a and 0x1b */ -static const struct hda_bind_ctls ad1986a_laptop_master_vol = { - .ops = &snd_hda_bind_vol, - .values = { - HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT), - HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT), - 0, - }, -}; - -static const struct hda_bind_ctls ad1986a_laptop_master_sw = { - .ops = &snd_hda_bind_sw, - .values = { - HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT), - HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT), - 0, - }, -}; - -static const struct snd_kcontrol_new ad1986a_laptop_mixers[] = { - HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT), - HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol), - HDA_BIND_SW("Master Playback Switch", &ad1986a_laptop_master_sw), - HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Line Playback Volume", 0x17, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Line Playback Switch", 0x17, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Aux Playback Volume", 0x16, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Aux Playback Switch", 0x16, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x0f, 0x0, HDA_OUTPUT), - /* - HDA_CODEC_VOLUME("Mono Playback Volume", 0x1e, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Mono Playback Switch", 0x1e, 0x0, HDA_OUTPUT), */ - HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Capture Source", - .info = ad198x_mux_enum_info, - .get = ad198x_mux_enum_get, - .put = ad198x_mux_enum_put, - }, - { } /* end */ -}; - -/* laptop-eapd model - 2ch only */ - -static const struct hda_input_mux ad1986a_laptop_eapd_capture_source = { - .num_items = 3, - .items = { - { "Mic", 0x0 }, - { "Internal Mic", 0x4 }, - { "Mix", 0x5 }, - }, -}; - -static const struct hda_input_mux ad1986a_automic_capture_source = { - .num_items = 2, - .items = { - { "Mic", 0x0 }, - { "Mix", 0x5 }, - }, -}; - -static const struct snd_kcontrol_new ad1986a_laptop_master_mixers[] = { - HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol), - HDA_BIND_SW("Master Playback Switch", &ad1986a_laptop_master_sw), - { } /* end */ -}; - -static const struct snd_kcontrol_new ad1986a_laptop_eapd_mixers[] = { - HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x0f, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Capture Source", - .info = ad198x_mux_enum_info, - .get = ad198x_mux_enum_get, - .put = ad198x_mux_enum_put, - }, - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "External Amplifier", - .subdevice = HDA_SUBDEV_NID_FLAG | 0x1b, - .info = ad198x_eapd_info, - .get = ad198x_eapd_get, - .put = ad198x_eapd_put, - .private_value = 0x1b, /* port-D */ - }, - { } /* end */ -}; - -static const struct snd_kcontrol_new ad1986a_laptop_intmic_mixers[] = { - HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x17, 0, HDA_OUTPUT), - HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x17, 0, HDA_OUTPUT), - { } /* end */ -}; - -/* laptop-automute - 2ch only */ - -static void ad1986a_update_hp(struct hda_codec *codec) -{ - struct ad198x_spec *spec = codec->spec; - unsigned int mute; - - if (spec->jack_present) - mute = HDA_AMP_MUTE; /* mute internal speaker */ - else - /* unmute internal speaker if necessary */ - mute = snd_hda_codec_amp_read(codec, 0x1a, 0, HDA_OUTPUT, 0); - snd_hda_codec_amp_stereo(codec, 0x1b, HDA_OUTPUT, 0, - HDA_AMP_MUTE, mute); -} - -static void ad1986a_hp_automute(struct hda_codec *codec) -{ - struct ad198x_spec *spec = codec->spec; - - spec->jack_present = snd_hda_jack_detect(codec, 0x1a); - if (spec->inv_jack_detect) - spec->jack_present = !spec->jack_present; - ad1986a_update_hp(codec); -} - -#define AD1986A_HP_EVENT 0x37 - -static void ad1986a_hp_unsol_event(struct hda_codec *codec, unsigned int res) -{ - if ((res >> 26) != AD1986A_HP_EVENT) - return; - ad1986a_hp_automute(codec); -} - -static int ad1986a_hp_init(struct hda_codec *codec) -{ - ad198x_init(codec); - ad1986a_hp_automute(codec); - return 0; -} - -/* bind hp and internal speaker mute (with plug check) */ -static int ad1986a_hp_master_sw_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); - if (change) - ad1986a_update_hp(codec); - return change; -} - -static const struct snd_kcontrol_new ad1986a_automute_master_mixers[] = { - HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Master Playback Switch", - .subdevice = HDA_SUBDEV_AMP_FLAG, - .info = snd_hda_mixer_amp_switch_info, - .get = snd_hda_mixer_amp_switch_get, - .put = ad1986a_hp_master_sw_put, - .private_value = HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT), - }, - { } /* end */ -}; - - -/* - * initialization verbs - */ -static const struct hda_verb ad1986a_init_verbs[] = { - /* Front, Surround, CLFE DAC; mute as default */ - {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - /* Downmix - off */ - {0x09, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - /* HP, Line-Out, Surround, CLFE selectors */ - {0x0a, AC_VERB_SET_CONNECT_SEL, 0x0}, - {0x0b, AC_VERB_SET_CONNECT_SEL, 0x0}, - {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0}, - {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0}, - /* Mono selector */ - {0x0e, AC_VERB_SET_CONNECT_SEL, 0x0}, - /* Mic selector: Mic 1/2 pin */ - {0x0f, AC_VERB_SET_CONNECT_SEL, 0x0}, - /* Line-in selector: Line-in */ - {0x10, AC_VERB_SET_CONNECT_SEL, 0x0}, - /* Mic 1/2 swap */ - {0x11, AC_VERB_SET_CONNECT_SEL, 0x0}, - /* Record selector: mic */ - {0x12, AC_VERB_SET_CONNECT_SEL, 0x0}, - /* Mic, Phone, CD, Aux, Line-In amp; mute as default */ - {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - /* PC beep */ - {0x18, AC_VERB_SET_CONNECT_SEL, 0x0}, - /* HP, Line-Out, Surround, CLFE, Mono pins; mute as default */ - {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - /* HP Pin */ - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 }, - /* Front, Surround, CLFE Pins */ - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, - {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, - {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, - /* Mono Pin */ - {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, - /* Mic Pin */ - {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, - /* Line, Aux, CD, Beep-In Pin */ - {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, - {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, - {0x22, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, - {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, - {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 }, - { } /* end */ -}; - -static const struct hda_verb ad1986a_ch2_init[] = { - /* Surround out -> Line In */ - { 0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, - /* Line-in selectors */ - { 0x10, AC_VERB_SET_CONNECT_SEL, 0x1 }, - /* CLFE -> Mic in */ - { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, - /* Mic selector, mix C/LFE (backmic) and Mic (frontmic) */ - { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x4 }, - { } /* end */ -}; - -static const struct hda_verb ad1986a_ch4_init[] = { - /* Surround out -> Surround */ - { 0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x10, AC_VERB_SET_CONNECT_SEL, 0x0 }, - /* CLFE -> Mic in */ - { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, - { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x4 }, - { } /* end */ -}; - -static const struct hda_verb ad1986a_ch6_init[] = { - /* Surround out -> Surround out */ - { 0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x10, AC_VERB_SET_CONNECT_SEL, 0x0 }, - /* CLFE -> CLFE */ - { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, - { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x0 }, - { } /* end */ -}; - -static const struct hda_channel_mode ad1986a_modes[3] = { - { 2, ad1986a_ch2_init }, - { 4, ad1986a_ch4_init }, - { 6, ad1986a_ch6_init }, -}; - -/* eapd initialization */ -static const struct hda_verb ad1986a_eapd_init_verbs[] = { - {0x1b, AC_VERB_SET_EAPD_BTLENABLE, 0x00 }, - {} -}; - -/* pin sensing on HP jack */ -static const struct hda_verb ad1986a_hp_init_verbs[] = { - {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1986A_HP_EVENT}, - {} -}; - - -/* models */ -enum { - AD1986A_AUTO, - AD1986A_6STACK, - AD1986A_3STACK, - AD1986A_LAPTOP, - AD1986A_LAPTOP_EAPD, - AD1986A_LAPTOP_AUTOMUTE, - AD1986A_MODELS -}; - -static const char * const ad1986a_models[AD1986A_MODELS] = { - [AD1986A_AUTO] = "auto", - [AD1986A_6STACK] = "6stack", - [AD1986A_3STACK] = "3stack", - [AD1986A_LAPTOP] = "laptop", - [AD1986A_LAPTOP_EAPD] = "laptop-eapd", - [AD1986A_LAPTOP_AUTOMUTE] = "laptop-automute", -}; - -static const struct snd_pci_quirk ad1986a_cfg_tbl[] = { - SND_PCI_QUIRK(0x103c, 0x30af, "HP B2800", AD1986A_LAPTOP_EAPD), - SND_PCI_QUIRK(0x1043, 0x817f, "ASUS P5", AD1986A_3STACK), - SND_PCI_QUIRK(0x1043, 0x818f, "ASUS P5", AD1986A_LAPTOP), - SND_PCI_QUIRK(0x1043, 0x81b3, "ASUS P5", AD1986A_3STACK), - SND_PCI_QUIRK(0x1043, 0x81cb, "ASUS M2N", AD1986A_3STACK), - SND_PCI_QUIRK(0x1043, 0x8234, "ASUS M2N", AD1986A_3STACK), - SND_PCI_QUIRK(0x10de, 0xcb84, "ASUS A8N-VM", AD1986A_3STACK), - SND_PCI_QUIRK(0x144d, 0xc01e, "FSC V2060", AD1986A_LAPTOP), - SND_PCI_QUIRK(0x17aa, 0x1011, "Lenovo M55", AD1986A_LAPTOP), - SND_PCI_QUIRK(0x17aa, 0x1017, "Lenovo A60", AD1986A_3STACK), - {} -}; - -#ifdef CONFIG_PM -static const struct hda_amp_list ad1986a_loopbacks[] = { - { 0x13, HDA_OUTPUT, 0 }, /* Mic */ - { 0x14, HDA_OUTPUT, 0 }, /* Phone */ - { 0x15, HDA_OUTPUT, 0 }, /* CD */ - { 0x16, HDA_OUTPUT, 0 }, /* Aux */ - { 0x17, HDA_OUTPUT, 0 }, /* Line */ - { } /* end */ -}; -#endif - -static int is_jack_available(struct hda_codec *codec, hda_nid_t nid) -{ - unsigned int conf = snd_hda_codec_get_pincfg(codec, nid); - return get_defcfg_connect(conf) != AC_JACK_PORT_NONE; -} -#endif /* ENABLE_AD_STATIC_QUIRKS */ - static int alloc_ad_spec(struct hda_codec *codec) { struct ad198x_spec *spec; @@ -1114,6 +227,9 @@ enum { AD1986A_FIXUP_INV_JACK_DETECT, AD1986A_FIXUP_ULTRA, AD1986A_FIXUP_SAMSUNG, + AD1986A_FIXUP_3STACK, + AD1986A_FIXUP_LAPTOP, + AD1986A_FIXUP_LAPTOP_IMIC, }; static const struct hda_fixup ad1986a_fixups[] = { @@ -1139,18 +255,68 @@ static const struct hda_fixup ad1986a_fixups[] = { {} }, }, + [AD1986A_FIXUP_3STACK] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x1a, 0x02214021 }, /* headphone */ + { 0x1b, 0x01014011 }, /* front */ + { 0x1c, 0x01013012 }, /* surround */ + { 0x1d, 0x01019015 }, /* clfe */ + { 0x1e, 0x411111f0 }, /* N/A */ + { 0x1f, 0x02a190f0 }, /* mic */ + { 0x20, 0x018130f0 }, /* line-in */ + {} + }, + }, + [AD1986A_FIXUP_LAPTOP] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x1a, 0x02214021 }, /* headphone */ + { 0x1b, 0x90170110 }, /* speaker */ + { 0x1c, 0x411111f0 }, /* N/A */ + { 0x1d, 0x411111f0 }, /* N/A */ + { 0x1e, 0x411111f0 }, /* N/A */ + { 0x1f, 0x02a191f0 }, /* mic */ + { 0x20, 0x411111f0 }, /* N/A */ + {} + }, + }, + [AD1986A_FIXUP_LAPTOP_IMIC] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x1d, 0x90a7013e }, /* int mic */ + {} + }, + .chained_before = 1, + .chain_id = AD1986A_FIXUP_LAPTOP, + }, }; static const struct snd_pci_quirk ad1986a_fixup_tbl[] = { + SND_PCI_QUIRK(0x103c, 0x30af, "HP B2800", AD1986A_FIXUP_LAPTOP_IMIC), + SND_PCI_QUIRK_MASK(0x1043, 0xff00, 0x8100, "ASUS P5", AD1986A_FIXUP_3STACK), + SND_PCI_QUIRK_MASK(0x1043, 0xff00, 0x8200, "ASUS M2", AD1986A_FIXUP_3STACK), + SND_PCI_QUIRK(0x10de, 0xcb84, "ASUS A8N-VM", AD1986A_FIXUP_3STACK), + SND_PCI_QUIRK(0x144d, 0xc01e, "FSC V2060", AD1986A_FIXUP_LAPTOP), SND_PCI_QUIRK_MASK(0x144d, 0xff00, 0xc000, "Samsung", AD1986A_FIXUP_SAMSUNG), SND_PCI_QUIRK(0x144d, 0xc027, "Samsung Q1", AD1986A_FIXUP_ULTRA), SND_PCI_QUIRK(0x17aa, 0x2066, "Lenovo N100", AD1986A_FIXUP_INV_JACK_DETECT), + SND_PCI_QUIRK(0x17aa, 0x1011, "Lenovo M55", AD1986A_FIXUP_3STACK), + SND_PCI_QUIRK(0x17aa, 0x1017, "Lenovo A60", AD1986A_FIXUP_3STACK), + {} +}; + +static const struct hda_model_fixup ad1986a_fixup_models[] = { + { .id = AD1986A_FIXUP_3STACK, .name = "3stack" }, + { .id = AD1986A_FIXUP_LAPTOP, .name = "laptop" }, + { .id = AD1986A_FIXUP_LAPTOP_IMIC, .name = "laptop-imic" }, + { .id = AD1986A_FIXUP_LAPTOP_IMIC, .name = "laptop-eapd" }, /* alias */ {} }; /* */ -static int ad1986a_parse_auto_config(struct hda_codec *codec) +static int patch_ad1986a(struct hda_codec *codec) { int err; struct ad198x_spec *spec; @@ -1175,7 +341,8 @@ static int ad1986a_parse_auto_config(struct hda_codec *codec) */ spec->gen.multiout.no_share_stream = 1; - snd_hda_pick_fixup(codec, NULL, ad1986a_fixup_tbl, ad1986a_fixups); + snd_hda_pick_fixup(codec, ad1986a_fixup_models, ad1986a_fixup_tbl, + ad1986a_fixups); snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); err = ad198x_parse_auto_config(codec); @@ -1189,128 +356,6 @@ static int ad1986a_parse_auto_config(struct hda_codec *codec) return 0; } -#ifdef ENABLE_AD_STATIC_QUIRKS -static int patch_ad1986a(struct hda_codec *codec) -{ - struct ad198x_spec *spec; - int err, board_config; - - board_config = snd_hda_check_board_config(codec, AD1986A_MODELS, - ad1986a_models, - ad1986a_cfg_tbl); - if (board_config < 0) { - printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", - codec->chip_name); - board_config = AD1986A_AUTO; - } - - if (board_config == AD1986A_AUTO) - return ad1986a_parse_auto_config(codec); - - err = alloc_ad_spec(codec); - if (err < 0) - return err; - spec = codec->spec; - - err = snd_hda_attach_beep_device(codec, 0x19); - if (err < 0) { - ad198x_free(codec); - return err; - } - set_beep_amp(spec, 0x18, 0, HDA_OUTPUT); - - spec->multiout.max_channels = 6; - spec->multiout.num_dacs = ARRAY_SIZE(ad1986a_dac_nids); - spec->multiout.dac_nids = ad1986a_dac_nids; - spec->multiout.dig_out_nid = AD1986A_SPDIF_OUT; - spec->num_adc_nids = 1; - spec->adc_nids = ad1986a_adc_nids; - spec->capsrc_nids = ad1986a_capsrc_nids; - spec->input_mux = &ad1986a_capture_source; - spec->num_mixers = 1; - spec->mixers[0] = ad1986a_mixers; - spec->num_init_verbs = 1; - spec->init_verbs[0] = ad1986a_init_verbs; -#ifdef CONFIG_PM - spec->loopback.amplist = ad1986a_loopbacks; -#endif - spec->vmaster_nid = 0x1b; - codec->inv_eapd = 1; /* AD1986A has the inverted EAPD implementation */ - - codec->patch_ops = ad198x_patch_ops; - - /* override some parameters */ - switch (board_config) { - case AD1986A_3STACK: - spec->num_mixers = 2; - spec->mixers[1] = ad1986a_3st_mixers; - spec->num_init_verbs = 2; - spec->init_verbs[1] = ad1986a_ch2_init; - spec->channel_mode = ad1986a_modes; - spec->num_channel_mode = ARRAY_SIZE(ad1986a_modes); - spec->need_dac_fix = 1; - spec->multiout.max_channels = 2; - spec->multiout.num_dacs = 1; - break; - case AD1986A_LAPTOP: - spec->mixers[0] = ad1986a_laptop_mixers; - spec->multiout.max_channels = 2; - spec->multiout.num_dacs = 1; - spec->multiout.dac_nids = ad1986a_laptop_dac_nids; - break; - case AD1986A_LAPTOP_EAPD: - spec->num_mixers = 3; - spec->mixers[0] = ad1986a_laptop_master_mixers; - spec->mixers[1] = ad1986a_laptop_eapd_mixers; - spec->mixers[2] = ad1986a_laptop_intmic_mixers; - spec->num_init_verbs = 2; - spec->init_verbs[1] = ad1986a_eapd_init_verbs; - spec->multiout.max_channels = 2; - spec->multiout.num_dacs = 1; - spec->multiout.dac_nids = ad1986a_laptop_dac_nids; - if (!is_jack_available(codec, 0x25)) - spec->multiout.dig_out_nid = 0; - spec->input_mux = &ad1986a_laptop_eapd_capture_source; - break; - case AD1986A_LAPTOP_AUTOMUTE: - spec->num_mixers = 3; - spec->mixers[0] = ad1986a_automute_master_mixers; - spec->mixers[1] = ad1986a_laptop_eapd_mixers; - spec->mixers[2] = ad1986a_laptop_intmic_mixers; - spec->num_init_verbs = 3; - spec->init_verbs[1] = ad1986a_eapd_init_verbs; - spec->init_verbs[2] = ad1986a_hp_init_verbs; - spec->multiout.max_channels = 2; - spec->multiout.num_dacs = 1; - spec->multiout.dac_nids = ad1986a_laptop_dac_nids; - if (!is_jack_available(codec, 0x25)) - spec->multiout.dig_out_nid = 0; - spec->input_mux = &ad1986a_laptop_eapd_capture_source; - codec->patch_ops.unsol_event = ad1986a_hp_unsol_event; - codec->patch_ops.init = ad1986a_hp_init; - /* Lenovo N100 seems to report the reversed bit - * for HP jack-sensing - */ - spec->inv_jack_detect = 1; - break; - } - - /* AD1986A has a hardware problem that it can't share a stream - * with multiple output pins. The copy of front to surrounds - * causes noisy or silent outputs at a certain timing, e.g. - * changing the volume. - * So, let's disable the shared stream. - */ - spec->multiout.no_share_stream = 1; - - codec->no_trigger_sense = 1; - codec->no_sticky_stream = 1; - - return 0; -} -#else /* ENABLE_AD_STATIC_QUIRKS */ -#define patch_ad1986a ad1986a_parse_auto_config -#endif /* ENABLE_AD_STATIC_QUIRKS */ /* * AD1983 specific -- cgit v0.10.2 From 384f778fd924cc843acf93c23f52cb168cb3f02a Mon Sep 17 00:00:00 2001 From: Adrian Knoth Date: Fri, 5 Jul 2013 11:27:53 +0200 Subject: ALSA: hdspm - Add missing defines for RME AIO and RayDAT The driver did not support all possible configurations. These defines will be used by later commits to add the missing functionality. Signed-off-by: Adrian Knoth Signed-off-by: Takashi Iwai diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c index bd50193..a0fc961 100644 --- a/sound/pci/rme9652/hdspm.c +++ b/sound/pci/rme9652/hdspm.c @@ -258,6 +258,25 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}"); #define HDSPM_wclk_sel (1<<30) +/* additional control register bits for AIO*/ +#define HDSPM_c0_Wck48 0x20 /* also RayDAT */ +#define HDSPM_c0_Input0 0x1000 +#define HDSPM_c0_Input1 0x2000 +#define HDSPM_c0_Spdif_Opt 0x4000 +#define HDSPM_c0_Pro 0x8000 +#define HDSPM_c0_clr_tms 0x10000 +#define HDSPM_c0_AEB1 0x20000 +#define HDSPM_c0_AEB2 0x40000 +#define HDSPM_c0_LineOut 0x80000 +#define HDSPM_c0_AD_GAIN0 0x100000 +#define HDSPM_c0_AD_GAIN1 0x200000 +#define HDSPM_c0_DA_GAIN0 0x400000 +#define HDSPM_c0_DA_GAIN1 0x800000 +#define HDSPM_c0_PH_GAIN0 0x1000000 +#define HDSPM_c0_PH_GAIN1 0x2000000 +#define HDSPM_c0_Sym6db 0x4000000 + + /* --- bit helper defines */ #define HDSPM_LatencyMask (HDSPM_Latency0|HDSPM_Latency1|HDSPM_Latency2) #define HDSPM_FrequencyMask (HDSPM_Frequency0|HDSPM_Frequency1|\ -- cgit v0.10.2 From b2ed6326874b1bf5410871d83df4086a395ab13b Mon Sep 17 00:00:00 2001 From: Adrian Knoth Date: Fri, 5 Jul 2013 11:27:54 +0200 Subject: ALSA: hdspm - Introduce hdspm_is_raydat_or_aio() RME RayDAT and AIO cards are new designs with different register settings. Since we need to distinguish them from older cards multiple times in the driver, refactor the code into a separate helper function. No functional change intended. Signed-off-by: Adrian Knoth Signed-off-by: Takashi Iwai diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c index a0fc961..32a87dc 100644 --- a/sound/pci/rme9652/hdspm.c +++ b/sound/pci/rme9652/hdspm.c @@ -1011,6 +1011,12 @@ static inline int HDSPM_bit2freq(int n) return bit2freq_tab[n]; } +static bool hdspm_is_raydat_or_aio(struct hdspm *hdspm) +{ + return ((AIO == hdspm->io_type) || (RayDAT == hdspm->io_type)); +} + + /* Write/read to/from HDSPM with Adresses in Bytes not words but only 32Bit writes are allowed */ @@ -5142,9 +5148,8 @@ static int snd_hdspm_set_defaults(struct hdspm * hdspm) all_in_all_mixer(hdspm, 0 * UNITY_GAIN); - if (hdspm->io_type == AIO || hdspm->io_type == RayDAT) { + if (hdspm_is_raydat_or_aio(hdspm)) hdspm_write(hdspm, HDSPM_WR_SETTINGS, hdspm->settings_register); - } /* set a default rate so that the channel map is set up. */ hdspm_set_rate(hdspm, 48000, 1); -- cgit v0.10.2 From ce13f3f33a32895da9304a9f9cb865f337dd0933 Mon Sep 17 00:00:00 2001 From: Adrian Knoth Date: Fri, 5 Jul 2013 11:27:55 +0200 Subject: ALSA: hdspm - Augment HDSPM_TOGGLE_SETTING for AIO/RayDAT The HDSPM_TOGGLE_SETTING functions alter the control_register on older cards. On newer cards (AIO/RayDAT), they have to operate on the settings_register instead. This patch augments the existing functions to work with AIO/RayDAT, too. Signed-off-by: Adrian Knoth Signed-off-by: Takashi Iwai diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c index 32a87dc..118d727 100644 --- a/sound/pci/rme9652/hdspm.c +++ b/sound/pci/rme9652/hdspm.c @@ -3092,16 +3092,35 @@ static int snd_hdspm_get_tco_ltc_frames(struct snd_kcontrol *kcontrol, static int hdspm_toggle_setting(struct hdspm *hdspm, u32 regmask) { - return (hdspm->control_register & regmask) ? 1 : 0; + u32 reg; + + if (hdspm_is_raydat_or_aio(hdspm)) + reg = hdspm->settings_register; + else + reg = hdspm->control_register; + + return (reg & regmask) ? 1 : 0; } static int hdspm_set_toggle_setting(struct hdspm *hdspm, u32 regmask, int out) { + u32 *reg; + u32 target_reg; + + if (hdspm_is_raydat_or_aio(hdspm)) { + reg = &(hdspm->settings_register); + target_reg = HDSPM_WR_SETTINGS; + } else { + reg = &(hdspm->control_register); + target_reg = HDSPM_controlRegister; + } + if (out) - hdspm->control_register |= regmask; + *reg |= regmask; else - hdspm->control_register &= ~regmask; - hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register); + *reg &= ~regmask; + + hdspm_write(hdspm, target_reg, *reg); return 0; } -- cgit v0.10.2 From 34be7ebbb4488818a2c413290b7b5835173fe44d Mon Sep 17 00:00:00 2001 From: Adrian Knoth Date: Fri, 5 Jul 2013 11:27:56 +0200 Subject: ALSA: hdspm - Drop duplicate code in hdspm_set_system_clock_mode() hdspm_set_system_clock_mode() is almost a one-by-one copy of hdspm_set_toggle_setting(). To improve code quality, remove the duplication. Signed-off-by: Adrian Knoth Signed-off-by: Takashi Iwai diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c index 118d727..631c546 100644 --- a/sound/pci/rme9652/hdspm.c +++ b/sound/pci/rme9652/hdspm.c @@ -995,6 +995,7 @@ static inline void snd_hdspm_initialize_midi_flush(struct hdspm *hdspm); static inline int hdspm_get_pll_freq(struct hdspm *hdspm); static int hdspm_update_simple_mixer_controls(struct hdspm *hdspm); static int hdspm_autosync_ref(struct hdspm *hdspm); +static int hdspm_set_toggle_setting(struct hdspm *hdspm, u32 regmask, int out); static int snd_hdspm_set_defaults(struct hdspm *hdspm); static int hdspm_system_clock_mode(struct hdspm *hdspm); static void hdspm_set_sgbuf(struct hdspm *hdspm, @@ -2384,26 +2385,10 @@ static int hdspm_system_clock_mode(struct hdspm *hdspm) **/ static void hdspm_set_system_clock_mode(struct hdspm *hdspm, int mode) { - switch (hdspm->io_type) { - case AIO: - case RayDAT: - if (0 == mode) - hdspm->settings_register |= HDSPM_c0Master; - else - hdspm->settings_register &= ~HDSPM_c0Master; - - hdspm_write(hdspm, HDSPM_WR_SETTINGS, hdspm->settings_register); - break; - - default: - if (0 == mode) - hdspm->control_register |= HDSPM_ClockModeMaster; - else - hdspm->control_register &= ~HDSPM_ClockModeMaster; - - hdspm_write(hdspm, HDSPM_controlRegister, - hdspm->control_register); - } + hdspm_set_toggle_setting(hdspm, + (hdspm_is_raydat_or_aio(hdspm)) ? + HDSPM_c0Master : HDSPM_ClockModeMaster, + (0 == mode)); } -- cgit v0.10.2 From 11a5cd3c950ee27b165b5c170e588dff22cadeca Mon Sep 17 00:00:00 2001 From: Adrian Knoth Date: Fri, 5 Jul 2013 11:27:57 +0200 Subject: ALSA: hdspm - Add S/PDIF and WCK48 controls for RME RayDAT This commit adds new ALSA controls to send single-speed WordClock and S/PDIF-Professional on RME RayDAT cards. Signed-off-by: Adrian Knoth Signed-off-by: Takashi Iwai diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c index 631c546..4a3a822 100644 --- a/sound/pci/rme9652/hdspm.c +++ b/sound/pci/rme9652/hdspm.c @@ -4364,7 +4364,9 @@ static struct snd_kcontrol_new snd_hdspm_controls_raydat[] = { HDSPM_AUTOSYNC_SAMPLE_RATE("ADAT3 Frequency", 5), HDSPM_AUTOSYNC_SAMPLE_RATE("ADAT4 Frequency", 6), HDSPM_AUTOSYNC_SAMPLE_RATE("TCO Frequency", 7), - HDSPM_AUTOSYNC_SAMPLE_RATE("SYNC IN Frequency", 8) + HDSPM_AUTOSYNC_SAMPLE_RATE("SYNC IN Frequency", 8), + HDSPM_TOGGLE_SETTING("S/PDIF Out Professional", HDSPM_c0_Pro), + HDSPM_TOGGLE_SETTING("Single Speed WordClock Out", HDSPM_c0_Wck48) }; static struct snd_kcontrol_new snd_hdspm_controls_aes32[] = { -- cgit v0.10.2 From fb0f121e0f346bec45810a9439e936ae62fd2441 Mon Sep 17 00:00:00 2001 From: Adrian Knoth Date: Fri, 5 Jul 2013 11:27:58 +0200 Subject: ALSA: hdspm - Add S/PDIF, XLR, WCK48 and ADAT-in controls for RME AIO cards This commit adds the following ALSA controls: - S/PDIF Out Optical to switch S/PDIF Out from coaxial to optical - S/PDIF Out Professional to send the Pro bit in the output stream - ADAT-Internal to enable ADAT/TDIF Expansion Board (AEB/TEB) - XLR Breakout Cable if analogue I/O uses the XLR breakout cable - WCK48 to force WordClock to the 32-48kHz range (single speed) if the card is operating at higher frequencies Signed-off-by: Adrian Knoth Signed-off-by: Takashi Iwai diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c index 4a3a822..15f1e7b 100644 --- a/sound/pci/rme9652/hdspm.c +++ b/sound/pci/rme9652/hdspm.c @@ -4327,7 +4327,12 @@ static struct snd_kcontrol_new snd_hdspm_controls_aio[] = { HDSPM_AUTOSYNC_SAMPLE_RATE("SPDIF Frequency", 2), HDSPM_AUTOSYNC_SAMPLE_RATE("ADAT Frequency", 3), HDSPM_AUTOSYNC_SAMPLE_RATE("TCO Frequency", 4), - HDSPM_AUTOSYNC_SAMPLE_RATE("SYNC IN Frequency", 5) + HDSPM_AUTOSYNC_SAMPLE_RATE("SYNC IN Frequency", 5), + HDSPM_TOGGLE_SETTING("S/PDIF Out Optical", HDSPM_c0_Spdif_Opt), + HDSPM_TOGGLE_SETTING("S/PDIF Out Professional", HDSPM_c0_Pro), + HDSPM_TOGGLE_SETTING("ADAT internal (AEB/TEB)", HDSPM_c0_AEB1), + HDSPM_TOGGLE_SETTING("XLR Breakout Cable", HDSPM_c0_Sym6db), + HDSPM_TOGGLE_SETTING("Single Speed WordClock Out", HDSPM_c0_Wck48) /* HDSPM_INPUT_SELECT("Input Select", 0), -- cgit v0.10.2 From 8cea57104273909ab0825df48149840aad9d2b14 Mon Sep 17 00:00:00 2001 From: Adrian Knoth Date: Fri, 5 Jul 2013 11:27:59 +0200 Subject: ALSA: hdspm - Refactor ENUMERATED_CTL_INFO into function ENUMERATED_CTL_INFO is a macro, so the binary code is generated multiple times. To avoid code duplication, refactor the involved functionality into a function and make ENUMERATED_CTL_INFO a call to this function. Signed-off-by: Adrian Knoth Signed-off-by: Takashi Iwai diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c index 15f1e7b..b271853 100644 --- a/sound/pci/rme9652/hdspm.c +++ b/sound/pci/rme9652/hdspm.c @@ -2221,16 +2221,22 @@ static int hdspm_get_s1_sample_rate(struct hdspm *hdspm, unsigned int idx) return (status >> (idx*4)) & 0xF; } -#define ENUMERATED_CTL_INFO(info, texts) \ -{ \ - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; \ - uinfo->count = 1; \ - uinfo->value.enumerated.items = ARRAY_SIZE(texts); \ - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) \ - uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; \ - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); \ +static void snd_hdspm_set_infotext(struct snd_ctl_elem_info *uinfo, + char **texts, const int count) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = count; + if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) + uinfo->value.enumerated.item = + uinfo->value.enumerated.items - 1; + strcpy(uinfo->value.enumerated.name, + texts[uinfo->value.enumerated.item]); } +#define ENUMERATED_CTL_INFO(info, texts) \ + snd_hdspm_set_infotext(info, texts, ARRAY_SIZE(texts)) + #define HDSPM_AUTOSYNC_SAMPLE_RATE(xname, xindex) \ -- cgit v0.10.2 From acf14767e17ab7ee8b6213f9e56d07d9ffa033da Mon Sep 17 00:00:00 2001 From: Adrian Knoth Date: Fri, 5 Jul 2013 11:28:00 +0200 Subject: ALSA: hdspm - Introduce generic AIO tristate control AIO cards offer at least four individual settings options with three states each. Those settings are represented as two bits in the settings register with the following meaning: 0*some_base_bit --> Option value 0 1*some_base_bit --> Option value 1 2*some_base_bit --> Option value 2 3*some_base_bit --> mask to select the two involved bits This patch adds a generic ALSA control macro for such a value-to-bit pattern mapping. It will be used in a later commit to expose four new controls. Signed-off-by: Adrian Knoth Signed-off-by: Takashi Iwai diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c index b271853..d9532c4 100644 --- a/sound/pci/rme9652/hdspm.c +++ b/sound/pci/rme9652/hdspm.c @@ -3348,6 +3348,84 @@ static int snd_hdspm_put_qs_wire(struct snd_kcontrol *kcontrol, return change; } +#define HDSPM_CONTROL_TRISTATE(xname, xindex) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .private_value = xindex, \ + .info = snd_hdspm_info_tristate, \ + .get = snd_hdspm_get_tristate, \ + .put = snd_hdspm_put_tristate \ +} + +static int hdspm_tristate(struct hdspm *hdspm, u32 regmask) +{ + u32 reg = hdspm->settings_register & (regmask * 3); + return reg / regmask; +} + +static int hdspm_set_tristate(struct hdspm *hdspm, int mode, u32 regmask) +{ + hdspm->settings_register &= ~(regmask * 3); + hdspm->settings_register |= (regmask * mode); + hdspm_write(hdspm, HDSPM_WR_SETTINGS, hdspm->settings_register); + + return 0; +} + +static int snd_hdspm_info_tristate(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + u32 regmask = kcontrol->private_value; + + static char *texts_spdif[] = { "Optical", "Coaxial", "Internal" }; + static char *texts_levels[] = { "Hi Gain", "+4 dBu", "-10 dBV" }; + + switch (regmask) { + case HDSPM_c0_Input0: + ENUMERATED_CTL_INFO(uinfo, texts_spdif); + break; + default: + ENUMERATED_CTL_INFO(uinfo, texts_levels); + break; + } + return 0; +} + +static int snd_hdspm_get_tristate(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); + u32 regmask = kcontrol->private_value; + + spin_lock_irq(&hdspm->lock); + ucontrol->value.enumerated.item[0] = hdspm_tristate(hdspm, regmask); + spin_unlock_irq(&hdspm->lock); + return 0; +} + +static int snd_hdspm_put_tristate(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); + u32 regmask = kcontrol->private_value; + int change; + int val; + + if (!snd_hdspm_use_is_exclusive(hdspm)) + return -EBUSY; + val = ucontrol->value.integer.value[0]; + if (val < 0) + val = 0; + if (val > 2) + val = 2; + + spin_lock_irq(&hdspm->lock); + change = val != hdspm_tristate(hdspm, regmask); + hdspm_set_tristate(hdspm, val, regmask); + spin_unlock_irq(&hdspm->lock); + return change; +} + #define HDSPM_MADI_SPEEDMODE(xname, xindex) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = xname, \ -- cgit v0.10.2 From 42f4c12dcf46cbca8b7bb17610c0cb7ffbd7ab2e Mon Sep 17 00:00:00 2001 From: Adrian Knoth Date: Fri, 5 Jul 2013 11:28:01 +0200 Subject: ALSA: hdspm - Enable AD/DA/PH gains and S/PDIF-Input select on AIO This patch uses the newly introduced HDSPM_CONTROL_TRISTATE functions to create and expose the following ALSA controls: - Gain selection for Input, Output and Phones (HiGain, +4dBu, -10dbV) - S/PDIF Input select (Coaxial, Optical, Internal) Signed-off-by: Adrian Knoth Signed-off-by: Takashi Iwai diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c index d9532c4..778fc23 100644 --- a/sound/pci/rme9652/hdspm.c +++ b/sound/pci/rme9652/hdspm.c @@ -4412,11 +4412,15 @@ static struct snd_kcontrol_new snd_hdspm_controls_aio[] = { HDSPM_AUTOSYNC_SAMPLE_RATE("ADAT Frequency", 3), HDSPM_AUTOSYNC_SAMPLE_RATE("TCO Frequency", 4), HDSPM_AUTOSYNC_SAMPLE_RATE("SYNC IN Frequency", 5), + HDSPM_CONTROL_TRISTATE("S/PDIF Input", HDSPM_c0_Input0), HDSPM_TOGGLE_SETTING("S/PDIF Out Optical", HDSPM_c0_Spdif_Opt), HDSPM_TOGGLE_SETTING("S/PDIF Out Professional", HDSPM_c0_Pro), HDSPM_TOGGLE_SETTING("ADAT internal (AEB/TEB)", HDSPM_c0_AEB1), HDSPM_TOGGLE_SETTING("XLR Breakout Cable", HDSPM_c0_Sym6db), - HDSPM_TOGGLE_SETTING("Single Speed WordClock Out", HDSPM_c0_Wck48) + HDSPM_TOGGLE_SETTING("Single Speed WordClock Out", HDSPM_c0_Wck48), + HDSPM_CONTROL_TRISTATE("Input Level", HDSPM_c0_AD_GAIN0), + HDSPM_CONTROL_TRISTATE("Output Level", HDSPM_c0_DA_GAIN0), + HDSPM_CONTROL_TRISTATE("Phones Level", HDSPM_c0_PH_GAIN0) /* HDSPM_INPUT_SELECT("Input Select", 0), -- cgit v0.10.2 From 3de9db264cef4bc984f928e08cccf36304f30d0a Mon Sep 17 00:00:00 2001 From: Adrian Knoth Date: Fri, 5 Jul 2013 11:28:02 +0200 Subject: ALSA: hdspm - Add support for AEBs on RME AIO AIO cards allow to use AEB (Analogue Expansion Boards) to add four input and/or output channels. This patch adds the necessary code to detect and enable the additional I/O channels. Signed-off-by: Adrian Knoth Signed-off-by: Takashi Iwai diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c index 778fc23..ad41636 100644 --- a/sound/pci/rme9652/hdspm.c +++ b/sound/pci/rme9652/hdspm.c @@ -648,7 +648,8 @@ static char *texts_ports_aio_in_ss[] = { "AES.L", "AES.R", "SPDIF.L", "SPDIF.R", "ADAT.1", "ADAT.2", "ADAT.3", "ADAT.4", "ADAT.5", "ADAT.6", - "ADAT.7", "ADAT.8" + "ADAT.7", "ADAT.8", + "AEB.1", "AEB.2", "AEB.3", "AEB.4" }; static char *texts_ports_aio_out_ss[] = { @@ -657,14 +658,16 @@ static char *texts_ports_aio_out_ss[] = { "SPDIF.L", "SPDIF.R", "ADAT.1", "ADAT.2", "ADAT.3", "ADAT.4", "ADAT.5", "ADAT.6", "ADAT.7", "ADAT.8", - "Phone.L", "Phone.R" + "Phone.L", "Phone.R", + "AEB.1", "AEB.2", "AEB.3", "AEB.4" }; static char *texts_ports_aio_in_ds[] = { "Analogue.L", "Analogue.R", "AES.L", "AES.R", "SPDIF.L", "SPDIF.R", - "ADAT.1", "ADAT.2", "ADAT.3", "ADAT.4" + "ADAT.1", "ADAT.2", "ADAT.3", "ADAT.4", + "AEB.1", "AEB.2", "AEB.3", "AEB.4" }; static char *texts_ports_aio_out_ds[] = { @@ -672,14 +675,16 @@ static char *texts_ports_aio_out_ds[] = { "AES.L", "AES.R", "SPDIF.L", "SPDIF.R", "ADAT.1", "ADAT.2", "ADAT.3", "ADAT.4", - "Phone.L", "Phone.R" + "Phone.L", "Phone.R", + "AEB.1", "AEB.2", "AEB.3", "AEB.4" }; static char *texts_ports_aio_in_qs[] = { "Analogue.L", "Analogue.R", "AES.L", "AES.R", "SPDIF.L", "SPDIF.R", - "ADAT.1", "ADAT.2", "ADAT.3", "ADAT.4" + "ADAT.1", "ADAT.2", "ADAT.3", "ADAT.4", + "AEB.1", "AEB.2", "AEB.3", "AEB.4" }; static char *texts_ports_aio_out_qs[] = { @@ -687,7 +692,8 @@ static char *texts_ports_aio_out_qs[] = { "AES.L", "AES.R", "SPDIF.L", "SPDIF.R", "ADAT.1", "ADAT.2", "ADAT.3", "ADAT.4", - "Phone.L", "Phone.R" + "Phone.L", "Phone.R", + "AEB.1", "AEB.2", "AEB.3", "AEB.4" }; static char *texts_ports_aes32[] = { @@ -764,8 +770,8 @@ static char channel_map_aio_in_ss[HDSPM_MAX_CHANNELS] = { 8, 9, /* aes in, */ 10, 11, /* spdif in */ 12, 13, 14, 15, 16, 17, 18, 19, /* ADAT in */ - -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, + 2, 3, 4, 5, /* AEB */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, @@ -779,7 +785,8 @@ static char channel_map_aio_out_ss[HDSPM_MAX_CHANNELS] = { 10, 11, /* spdif out */ 12, 13, 14, 15, 16, 17, 18, 19, /* ADAT out */ 6, 7, /* phone out */ - -1, -1, -1, -1, -1, -1, -1, -1, + 2, 3, 4, 5, /* AEB */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, @@ -792,7 +799,8 @@ static char channel_map_aio_in_ds[HDSPM_MAX_CHANNELS] = { 8, 9, /* aes in */ 10, 11, /* spdif in */ 12, 14, 16, 18, /* adat in */ - -1, -1, -1, -1, -1, -1, + 2, 3, 4, 5, /* AEB */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, @@ -807,7 +815,7 @@ static char channel_map_aio_out_ds[HDSPM_MAX_CHANNELS] = { 10, 11, /* spdif out */ 12, 14, 16, 18, /* adat out */ 6, 7, /* phone out */ - -1, -1, -1, -1, + 2, 3, 4, 5, /* AEB */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, @@ -821,7 +829,8 @@ static char channel_map_aio_in_qs[HDSPM_MAX_CHANNELS] = { 8, 9, /* aes in */ 10, 11, /* spdif in */ 12, 16, /* adat in */ - -1, -1, -1, -1, -1, -1, -1, -1, + 2, 3, 4, 5, /* AEB */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, @@ -836,7 +845,8 @@ static char channel_map_aio_out_qs[HDSPM_MAX_CHANNELS] = { 10, 11, /* spdif out */ 12, 16, /* adat out */ 6, 7, /* phone out */ - -1, -1, -1, -1, -1, -1, + 2, 3, 4, 5, /* AEB */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, @@ -6602,10 +6612,6 @@ static int snd_hdspm_create(struct snd_card *card, break; case AIO: - if (0 == (hdspm_read(hdspm, HDSPM_statusRegister2) & HDSPM_s2_AEBI_D)) { - snd_printk(KERN_INFO "HDSPM: AEB input board found, but not supported\n"); - } - hdspm->ss_in_channels = AIO_IN_SS_CHANNELS; hdspm->ds_in_channels = AIO_IN_DS_CHANNELS; hdspm->qs_in_channels = AIO_IN_QS_CHANNELS; @@ -6613,6 +6619,20 @@ static int snd_hdspm_create(struct snd_card *card, hdspm->ds_out_channels = AIO_OUT_DS_CHANNELS; hdspm->qs_out_channels = AIO_OUT_QS_CHANNELS; + if (0 == (hdspm_read(hdspm, HDSPM_statusRegister2) & HDSPM_s2_AEBI_D)) { + snd_printk(KERN_INFO "HDSPM: AEB input board found\n"); + hdspm->ss_in_channels += 4; + hdspm->ds_in_channels += 4; + hdspm->qs_in_channels += 4; + } + + if (0 == (hdspm_read(hdspm, HDSPM_statusRegister2) & HDSPM_s2_AEBO_D)) { + snd_printk(KERN_INFO "HDSPM: AEB output board found\n"); + hdspm->ss_out_channels += 4; + hdspm->ds_out_channels += 4; + hdspm->qs_out_channels += 4; + } + hdspm->channel_map_out_ss = channel_map_aio_out_ss; hdspm->channel_map_out_ds = channel_map_aio_out_ds; hdspm->channel_map_out_qs = channel_map_aio_out_qs; -- cgit v0.10.2 From 1cb7dbf489f9985b7a117e34d00f20799adb138a Mon Sep 17 00:00:00 2001 From: Adrian Knoth Date: Fri, 5 Jul 2013 11:28:03 +0200 Subject: ALSA: hdspm - Fix S/PDIF Sync status and frequency on RME AIO This is a left-over mistake from old code, the correct register offset is provided in kcontrol->private_value, not in the index. Cf. RayDAT case, where it has already been corrected. Signed-off-by: Adrian Knoth Signed-off-by: Takashi Iwai diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c index ad41636..06e69de 100644 --- a/sound/pci/rme9652/hdspm.c +++ b/sound/pci/rme9652/hdspm.c @@ -2312,7 +2312,7 @@ static int snd_hdspm_get_autosync_sample_rate(struct snd_kcontrol *kcontrol, default: ucontrol->value.enumerated.item[0] = hdspm_get_s1_sample_rate(hdspm, - ucontrol->id.index-1); + kcontrol->private_value-1); } break; @@ -3930,7 +3930,8 @@ static int snd_hdspm_get_sync_check(struct snd_kcontrol *kcontrol, case 5: /* SYNC IN */ val = hdspm_sync_in_sync_check(hdspm); break; default: - val = hdspm_s1_sync_check(hdspm, ucontrol->id.index-1); + val = hdspm_s1_sync_check(hdspm, + kcontrol->private_value-1); } break; -- cgit v0.10.2 From 5760107c8263cf518968ece65453b7d9b8ca3d0a Mon Sep 17 00:00:00 2001 From: Adrian Knoth Date: Fri, 5 Jul 2013 11:28:04 +0200 Subject: ALSA: hdspm - Create TCO readout function This patch separates the TCO bits from snd_hdspm_proc_read_madi(), so the new function can later be shared between MADI and AES32 cards. It's essentially only moving code around, no new functionality. Signed-off-by: Adrian Knoth Signed-off-by: Takashi Iwai diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c index 06e69de..58b2104 100644 --- a/sound/pci/rme9652/hdspm.c +++ b/sound/pci/rme9652/hdspm.c @@ -4636,77 +4636,22 @@ static int snd_hdspm_create_controls(struct snd_card *card, ------------------------------------------------------------*/ static void -snd_hdspm_proc_read_madi(struct snd_info_entry * entry, - struct snd_info_buffer *buffer) +snd_hdspm_proc_read_tco(struct snd_info_entry *entry, + struct snd_info_buffer *buffer) { struct hdspm *hdspm = entry->private_data; - unsigned int status, status2, control, freq; - - char *pref_sync_ref; - char *autosync_ref; - char *system_clock_mode; - char *insel; - int x, x2; - - /* TCO stuff */ + unsigned int status, control; int a, ltc, frames, seconds, minutes, hours; unsigned int period; u64 freq_const = 0; u32 rate; + snd_iprintf(buffer, "--- TCO ---\n"); + status = hdspm_read(hdspm, HDSPM_statusRegister); - status2 = hdspm_read(hdspm, HDSPM_statusRegister2); control = hdspm->control_register; - freq = hdspm_read(hdspm, HDSPM_timecodeRegister); - - snd_iprintf(buffer, "%s (Card #%d) Rev.%x Status2first3bits: %x\n", - hdspm->card_name, hdspm->card->number + 1, - hdspm->firmware_rev, - (status2 & HDSPM_version0) | - (status2 & HDSPM_version1) | (status2 & - HDSPM_version2)); - - snd_iprintf(buffer, "HW Serial: 0x%06x%06x\n", - (hdspm_read(hdspm, HDSPM_midiStatusIn1)>>8) & 0xFFFFFF, - hdspm->serial); - - snd_iprintf(buffer, "IRQ: %d Registers bus: 0x%lx VM: 0x%lx\n", - hdspm->irq, hdspm->port, (unsigned long)hdspm->iobase); - - snd_iprintf(buffer, "--- System ---\n"); - snd_iprintf(buffer, - "IRQ Pending: Audio=%d, MIDI0=%d, MIDI1=%d, IRQcount=%d\n", - status & HDSPM_audioIRQPending, - (status & HDSPM_midi0IRQPending) ? 1 : 0, - (status & HDSPM_midi1IRQPending) ? 1 : 0, - hdspm->irq_count); - snd_iprintf(buffer, - "HW pointer: id = %d, rawptr = %d (%d->%d) " - "estimated= %ld (bytes)\n", - ((status & HDSPM_BufferID) ? 1 : 0), - (status & HDSPM_BufferPositionMask), - (status & HDSPM_BufferPositionMask) % - (2 * (int)hdspm->period_bytes), - ((status & HDSPM_BufferPositionMask) - 64) % - (2 * (int)hdspm->period_bytes), - (long) hdspm_hw_pointer(hdspm) * 4); - snd_iprintf(buffer, - "MIDI FIFO: Out1=0x%x, Out2=0x%x, In1=0x%x, In2=0x%x \n", - hdspm_read(hdspm, HDSPM_midiStatusOut0) & 0xFF, - hdspm_read(hdspm, HDSPM_midiStatusOut1) & 0xFF, - hdspm_read(hdspm, HDSPM_midiStatusIn0) & 0xFF, - hdspm_read(hdspm, HDSPM_midiStatusIn1) & 0xFF); - snd_iprintf(buffer, - "MIDIoverMADI FIFO: In=0x%x, Out=0x%x \n", - hdspm_read(hdspm, HDSPM_midiStatusIn2) & 0xFF, - hdspm_read(hdspm, HDSPM_midiStatusOut2) & 0xFF); - snd_iprintf(buffer, - "Register: ctrl1=0x%x, ctrl2=0x%x, status1=0x%x, " - "status2=0x%x\n", - hdspm->control_register, hdspm->control2_register, - status, status2); if (status & HDSPM_tco_detect) { snd_iprintf(buffer, "TCO module detected.\n"); a = hdspm_read(hdspm, HDSPM_RD_TCO+4); @@ -4800,6 +4745,75 @@ snd_hdspm_proc_read_madi(struct snd_info_entry * entry, } else { snd_iprintf(buffer, "No TCO module detected.\n"); } +} + +static void +snd_hdspm_proc_read_madi(struct snd_info_entry *entry, + struct snd_info_buffer *buffer) +{ + struct hdspm *hdspm = entry->private_data; + unsigned int status, status2, control, freq; + + char *pref_sync_ref; + char *autosync_ref; + char *system_clock_mode; + char *insel; + int x, x2; + + status = hdspm_read(hdspm, HDSPM_statusRegister); + status2 = hdspm_read(hdspm, HDSPM_statusRegister2); + control = hdspm->control_register; + freq = hdspm_read(hdspm, HDSPM_timecodeRegister); + + snd_iprintf(buffer, "%s (Card #%d) Rev.%x Status2first3bits: %x\n", + hdspm->card_name, hdspm->card->number + 1, + hdspm->firmware_rev, + (status2 & HDSPM_version0) | + (status2 & HDSPM_version1) | (status2 & + HDSPM_version2)); + + snd_iprintf(buffer, "HW Serial: 0x%06x%06x\n", + (hdspm_read(hdspm, HDSPM_midiStatusIn1)>>8) & 0xFFFFFF, + hdspm->serial); + + snd_iprintf(buffer, "IRQ: %d Registers bus: 0x%lx VM: 0x%lx\n", + hdspm->irq, hdspm->port, (unsigned long)hdspm->iobase); + + snd_iprintf(buffer, "--- System ---\n"); + + snd_iprintf(buffer, + "IRQ Pending: Audio=%d, MIDI0=%d, MIDI1=%d, IRQcount=%d\n", + status & HDSPM_audioIRQPending, + (status & HDSPM_midi0IRQPending) ? 1 : 0, + (status & HDSPM_midi1IRQPending) ? 1 : 0, + hdspm->irq_count); + snd_iprintf(buffer, + "HW pointer: id = %d, rawptr = %d (%d->%d) " + "estimated= %ld (bytes)\n", + ((status & HDSPM_BufferID) ? 1 : 0), + (status & HDSPM_BufferPositionMask), + (status & HDSPM_BufferPositionMask) % + (2 * (int)hdspm->period_bytes), + ((status & HDSPM_BufferPositionMask) - 64) % + (2 * (int)hdspm->period_bytes), + (long) hdspm_hw_pointer(hdspm) * 4); + + snd_iprintf(buffer, + "MIDI FIFO: Out1=0x%x, Out2=0x%x, In1=0x%x, In2=0x%x \n", + hdspm_read(hdspm, HDSPM_midiStatusOut0) & 0xFF, + hdspm_read(hdspm, HDSPM_midiStatusOut1) & 0xFF, + hdspm_read(hdspm, HDSPM_midiStatusIn0) & 0xFF, + hdspm_read(hdspm, HDSPM_midiStatusIn1) & 0xFF); + snd_iprintf(buffer, + "MIDIoverMADI FIFO: In=0x%x, Out=0x%x \n", + hdspm_read(hdspm, HDSPM_midiStatusIn2) & 0xFF, + hdspm_read(hdspm, HDSPM_midiStatusOut2) & 0xFF); + snd_iprintf(buffer, + "Register: ctrl1=0x%x, ctrl2=0x%x, status1=0x%x, " + "status2=0x%x\n", + hdspm->control_register, hdspm->control2_register, + status, status2); + snd_iprintf(buffer, "--- Settings ---\n"); @@ -4903,6 +4917,9 @@ snd_hdspm_proc_read_madi(struct snd_info_entry * entry, (status & HDSPM_RX_64ch) ? "64 channels" : "56 channels"); + /* call readout function for TCO specific status */ + snd_hdspm_proc_read_tco(entry, buffer); + snd_iprintf(buffer, "\n"); } -- cgit v0.10.2 From b0bf550476a5a6238baf1309ba913ca9f7a379ba Mon Sep 17 00:00:00 2001 From: Adrian Knoth Date: Fri, 5 Jul 2013 11:28:05 +0200 Subject: ALSA: hdspm - AES32: Fix TCO sync check reporting HDSPM_tco_lock and HDSPM_tcoLock were too close, so the previous code didn't honour the difference between the two. Let's be more verbose and use HDSPM_tcoLockMadi for MADI cards, HDSPM_tcoLockAes for AES(32) and fix the code that makes use of both. Signed-off-by: Adrian Knoth Signed-off-by: Takashi Iwai diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c index 58b2104..bdd8c77 100644 --- a/sound/pci/rme9652/hdspm.c +++ b/sound/pci/rme9652/hdspm.c @@ -360,11 +360,11 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}"); #define HDSPM_madiLock (1<<3) /* MADI Locked =1, no=0 */ #define HDSPM_madiSync (1<<18) /* MADI is in sync */ -#define HDSPM_tcoLock 0x00000020 /* Optional TCO locked status FOR HDSPe MADI! */ -#define HDSPM_tcoSync 0x10000000 /* Optional TCO sync status */ +#define HDSPM_tcoLockMadi 0x00000020 /* Optional TCO locked status for HDSPe MADI*/ +#define HDSPM_tcoSync 0x10000000 /* Optional TCO sync status for HDSPe MADI and AES32!*/ -#define HDSPM_syncInLock 0x00010000 /* Sync In lock status FOR HDSPe MADI! */ -#define HDSPM_syncInSync 0x00020000 /* Sync In sync status FOR HDSPe MADI! */ +#define HDSPM_syncInLock 0x00010000 /* Sync In lock status for HDSPe MADI! */ +#define HDSPM_syncInSync 0x00020000 /* Sync In sync status for HDSPe MADI! */ #define HDSPM_BufferPositionMask 0x000FFC0 /* Bit 6..15 : h/w buffer pointer */ /* since 64byte accurate, last 6 bits are not used */ @@ -382,7 +382,7 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}"); * Interrupt */ #define HDSPM_tco_detect 0x08000000 -#define HDSPM_tco_lock 0x20000000 +#define HDSPM_tcoLockAes 0x20000000 /* Optional TCO locked status for HDSPe AES */ #define HDSPM_s2_tco_detect 0x00000040 #define HDSPM_s2_AEBO_D 0x00000080 @@ -480,7 +480,9 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}"); #define HDSPM_AES32_AUTOSYNC_FROM_AES6 6 #define HDSPM_AES32_AUTOSYNC_FROM_AES7 7 #define HDSPM_AES32_AUTOSYNC_FROM_AES8 8 -#define HDSPM_AES32_AUTOSYNC_FROM_NONE 9 +#define HDSPM_AES32_AUTOSYNC_FROM_TCO 9 +#define HDSPM_AES32_AUTOSYNC_FROM_SYNC_IN 10 +#define HDSPM_AES32_AUTOSYNC_FROM_NONE 11 /* status2 */ /* HDSPM_LockAES_bit is given by HDSPM_LockAES >> (AES# - 1) */ @@ -3868,9 +3870,18 @@ static int hdspm_tco_sync_check(struct hdspm *hdspm) if (hdspm->tco) { switch (hdspm->io_type) { case MADI: + status = hdspm_read(hdspm, HDSPM_statusRegister); + if (status & HDSPM_tcoLockMadi) { + if (status & HDSPM_tcoSync) + return 2; + else + return 1; + } + return 0; + break; case AES32: status = hdspm_read(hdspm, HDSPM_statusRegister); - if (status & HDSPM_tcoLock) { + if (status & HDSPM_tcoLockAes) { if (status & HDSPM_tcoSync) return 2; else -- cgit v0.10.2 From e71b95ad71e3ee44ec634e242b186e3ff03bd459 Mon Sep 17 00:00:00 2001 From: Adrian Knoth Date: Fri, 5 Jul 2013 11:28:06 +0200 Subject: ALSA: hdspm - Cosmetics, no real change This patch does nothing, it's sole intent is to clean up the code. Signed-off-by: Adrian Knoth Signed-off-by: Takashi Iwai diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c index bdd8c77..d95100e 100644 --- a/sound/pci/rme9652/hdspm.c +++ b/sound/pci/rme9652/hdspm.c @@ -2926,7 +2926,7 @@ static int hdspm_autosync_ref(struct hdspm *hdspm) case HDSPM_SelSyncRef_NVALID: return HDSPM_AUTOSYNC_FROM_NONE; default: - return 0; + return HDSPM_AUTOSYNC_FROM_NONE; } } @@ -5260,7 +5260,7 @@ static int snd_hdspm_set_defaults(struct hdspm * hdspm) case AES32: hdspm->control_register = - HDSPM_ClockModeMaster | /* Master Cloack Mode on */ + HDSPM_ClockModeMaster | /* Master Clock Mode on */ hdspm_encode_latency(7) | /* latency max=8192samples */ HDSPM_SyncRef0 | /* AES1 is syncclock */ HDSPM_LineOut | /* Analog output in */ @@ -6737,7 +6737,7 @@ static int snd_hdspm_create(struct snd_card *card, if (NULL != hdspm->tco) { hdspm_tco_write(hdspm); } - snd_printk(KERN_INFO "HDSPM: MADI TCO module found\n"); + snd_printk(KERN_INFO "HDSPM: MADI/AES TCO module found\n"); } else { hdspm->tco = NULL; } @@ -6752,10 +6752,12 @@ static int snd_hdspm_create(struct snd_card *card, case AES32: if (hdspm->tco) { hdspm->texts_autosync = texts_autosync_aes_tco; - hdspm->texts_autosync_items = 10; + hdspm->texts_autosync_items = + ARRAY_SIZE(texts_autosync_aes_tco); } else { hdspm->texts_autosync = texts_autosync_aes; - hdspm->texts_autosync_items = 9; + hdspm->texts_autosync_items = + ARRAY_SIZE(texts_autosync_aes); } break; -- cgit v0.10.2 From 3c32de58ae9a3d534ba1a66274bf43631e36eb5c Mon Sep 17 00:00:00 2001 From: Adrian Knoth Date: Fri, 5 Jul 2013 11:28:07 +0200 Subject: ALSA: hdspm - AIO: Drop superfluous HDSPM_AUTOSYNC_REF The HDSPM_AUTOSYNC_REF macro is only implemented for MADI and AES32 cards, so it doesn't make sense to call it on AIO boards. Signed-off-by: Adrian Knoth Signed-off-by: Takashi Iwai diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c index d95100e..d1e0582 100644 --- a/sound/pci/rme9652/hdspm.c +++ b/sound/pci/rme9652/hdspm.c @@ -4419,7 +4419,6 @@ static struct snd_kcontrol_new snd_hdspm_controls_aio[] = { HDSPM_INTERNAL_CLOCK("Internal Clock", 0), HDSPM_SYSTEM_CLOCK_MODE("System Clock Mode", 0), HDSPM_PREF_SYNC_REF("Preferred Sync Reference", 0), - HDSPM_AUTOSYNC_REF("AutoSync Reference", 0), HDSPM_SYSTEM_SAMPLE_RATE("System Sample Rate", 0), HDSPM_AUTOSYNC_SAMPLE_RATE("External Rate", 0), HDSPM_SYNC_CHECK("WC SyncCheck", 0), -- cgit v0.10.2 From db2d1a913d838ecfab5b903508bcdd4e4ad42419 Mon Sep 17 00:00:00 2001 From: Adrian Knoth Date: Fri, 5 Jul 2013 11:28:08 +0200 Subject: ALSA: hdspm - AES32: Add TCO and Sync-In text entries Provide the text for the two new clock options "TCO" and "Sync In" on AES32 cards. Signed-off-by: Adrian Knoth Signed-off-by: Takashi Iwai diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c index d1e0582..8e6ce14 100644 --- a/sound/pci/rme9652/hdspm.c +++ b/sound/pci/rme9652/hdspm.c @@ -561,10 +561,13 @@ static char *hdspm_speed_names[] = { "single", "double", "quad" }; static char *texts_autosync_aes_tco[] = { "Word Clock", "AES1", "AES2", "AES3", "AES4", "AES5", "AES6", "AES7", "AES8", - "TCO" }; + "TCO", "Sync In" +}; static char *texts_autosync_aes[] = { "Word Clock", "AES1", "AES2", "AES3", "AES4", - "AES5", "AES6", "AES7", "AES8" }; + "AES5", "AES6", "AES7", "AES8", + "Sync In" +}; static char *texts_autosync_madi_tco[] = { "Word Clock", "MADI", "TCO", "Sync In" }; static char *texts_autosync_madi[] = { "Word Clock", @@ -2941,11 +2944,11 @@ static int snd_hdspm_info_autosync_ref(struct snd_kcontrol *kcontrol, if (AES32 == hdspm->io_type) { static char *texts[] = { "WordClock", "AES1", "AES2", "AES3", - "AES4", "AES5", "AES6", "AES7", "AES8", "None"}; + "AES4", "AES5", "AES6", "AES7", "AES8", "TCO", "Sync In", "None"}; uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; uinfo->count = 1; - uinfo->value.enumerated.items = 10; + uinfo->value.enumerated.items = ARRAY_SIZE(texts); if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) uinfo->value.enumerated.item = -- cgit v0.10.2 From d3c36ed8e578185b752dac4277819965fa5f6879 Mon Sep 17 00:00:00 2001 From: Adrian Knoth Date: Fri, 5 Jul 2013 11:28:09 +0200 Subject: ALSA: hdspm - Introduce hdspm_get_aes_sample_rate() Helper function to return the AES sample rate class. This class needs to be translated via HDSPM_bit2freq() to get the more common representation. Signed-off-by: Adrian Knoth Signed-off-by: Takashi Iwai diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c index 8e6ce14..b7702b2 100644 --- a/sound/pci/rme9652/hdspm.c +++ b/sound/pci/rme9652/hdspm.c @@ -2224,6 +2224,23 @@ static int hdspm_get_sync_in_sample_rate(struct hdspm *hdspm) return 0; } +/** + * Returns the AES sample rate class for the given card. + **/ +static int hdspm_get_aes_sample_rate(struct hdspm *hdspm, int index) +{ + int timecode; + + switch (hdspm->io_type) { + case AES32: + timecode = hdspm_read(hdspm, HDSPM_timecodeRegister); + return (timecode >> (4*index)) & 0xF; + break; + default: + break; + } + return 0; +} /** * Returns the sample rate class for input source for -- cgit v0.10.2 From 5b266354b91087d8f1b1d1b6853a2c012f3e1518 Mon Sep 17 00:00:00 2001 From: Adrian Knoth Date: Fri, 5 Jul 2013 11:28:10 +0200 Subject: ALSA: hdspm - Add prototype declarations This patch only introduces prototype declarations, no real change. The functions themselves are already present. Signed-off-by: Adrian Knoth Signed-off-by: Takashi Iwai diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c index b7702b2..367dd41 100644 --- a/sound/pci/rme9652/hdspm.c +++ b/sound/pci/rme9652/hdspm.c @@ -1017,6 +1017,17 @@ static void hdspm_set_sgbuf(struct hdspm *hdspm, struct snd_pcm_substream *substream, unsigned int reg, int channels); +static int hdspm_aes_sync_check(struct hdspm *hdspm, int idx); +static int hdspm_wc_sync_check(struct hdspm *hdspm); +static int hdspm_tco_sync_check(struct hdspm *hdspm); +static int hdspm_sync_in_sync_check(struct hdspm *hdspm); + +static int hdspm_get_aes_sample_rate(struct hdspm *hdspm, int index); +static int hdspm_get_tco_sample_rate(struct hdspm *hdspm); +static int hdspm_get_wc_sample_rate(struct hdspm *hdspm); + + + static inline int HDSPM_bit2freq(int n) { static const int bit2freq_tab[] = { @@ -1152,10 +1163,7 @@ static int hdspm_rate_multiplier(struct hdspm *hdspm, int rate) return rate; } -static int hdspm_tco_sync_check(struct hdspm *hdspm); -static int hdspm_sync_in_sync_check(struct hdspm *hdspm); - -/* check for external sample rate */ +/* check for external sample rate, returns the sample rate in Hz*/ static int hdspm_external_sample_rate(struct hdspm *hdspm) { unsigned int status, status2, timecode; -- cgit v0.10.2 From a57fea8ed44a2d32f8cbdd5455262aca88e72aa6 Mon Sep 17 00:00:00 2001 From: Adrian Knoth Date: Fri, 5 Jul 2013 11:28:11 +0200 Subject: ALSA: hdspm - Enable AES32 in hdspm_get_wc_sample_rate This patch adds AES32 specific code to hdspm_get_wc_sample_rate() to query the wordclock frequency. Signed-off-by: Adrian Knoth Signed-off-by: Takashi Iwai diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c index 367dd41..a69957c 100644 --- a/sound/pci/rme9652/hdspm.c +++ b/sound/pci/rme9652/hdspm.c @@ -2178,6 +2178,9 @@ static int hdspm_get_wc_sample_rate(struct hdspm *hdspm) status = hdspm_read(hdspm, HDSPM_RD_STATUS_1); return (status >> 16) & 0xF; break; + case AES32: + status = hdspm_read(hdspm, HDSPM_statusRegister); + return (status >> HDSPM_AES32_wcFreq_bit) & 0xF; default: break; } -- cgit v0.10.2 From 051c44fec7e250a93d8f3b6704a3ce880a11bb0f Mon Sep 17 00:00:00 2001 From: Adrian Knoth Date: Fri, 5 Jul 2013 11:28:12 +0200 Subject: ALSA: hdspm - Enable AES32 in hdspm_get_tco_sample_rate This patch adds AES32 specific code to hdspm_get_tco_sample_rate to query the TCO sample rate. Signed-off-by: Adrian Knoth Signed-off-by: Takashi Iwai diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c index a69957c..c0143cf 100644 --- a/sound/pci/rme9652/hdspm.c +++ b/sound/pci/rme9652/hdspm.c @@ -2204,6 +2204,9 @@ static int hdspm_get_tco_sample_rate(struct hdspm *hdspm) status = hdspm_read(hdspm, HDSPM_RD_STATUS_1); return (status >> 20) & 0xF; break; + case AES32: + status = hdspm_read(hdspm, HDSPM_statusRegister); + return (status >> 1) & 0xF; default: break; } -- cgit v0.10.2 From 3ac9b0acc34fbe56e2d31b8f2f7e59d45c53cb3b Mon Sep 17 00:00:00 2001 From: Adrian Knoth Date: Fri, 5 Jul 2013 11:28:13 +0200 Subject: ALSA: hdspm - AES32: Ignore float/int format bit As mentioned in the comment, the AES32 cards must not set the format bit, since it is used to indicate the preferred sync setting instead. We hence simply skip the corresponding part in the hw_params function. Signed-off-by: Adrian Knoth Signed-off-by: Takashi Iwai diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c index c0143cf..a9f4c7c 100644 --- a/sound/pci/rme9652/hdspm.c +++ b/sound/pci/rme9652/hdspm.c @@ -5566,6 +5566,16 @@ static int snd_hdspm_hw_params(struct snd_pcm_substream *substream, */ + /* For AES cards, the float format bit is the same as the + * preferred sync reference. Since we don't want to break + * sync settings, we have to skip the remaining part of this + * function. + */ + if (hdspm->io_type == AES32) { + return 0; + } + + /* Switch to native float format if requested */ if (SNDRV_PCM_FORMAT_FLOAT_LE == params_format(params)) { if (!(hdspm->control_register & HDSPe_FLOAT_FORMAT)) -- cgit v0.10.2 From dbae4a0c8d8794df1a6bd7e644ed94b915f46f7e Mon Sep 17 00:00:00 2001 From: Adrian Knoth Date: Fri, 5 Jul 2013 11:28:14 +0200 Subject: ALSA: hdspm - AES32: Enable TCO input in hdspm_external_sample_rate() This patch adds support to read the TCO sample rate in hdspm_external_sample_rate() on RME AES(32) cards. Signed-off-by: Adrian Knoth Signed-off-by: Takashi Iwai diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c index a9f4c7c..80b2247 100644 --- a/sound/pci/rme9652/hdspm.c +++ b/sound/pci/rme9652/hdspm.c @@ -1176,17 +1176,36 @@ static int hdspm_external_sample_rate(struct hdspm *hdspm) timecode = hdspm_read(hdspm, HDSPM_timecodeRegister); syncref = hdspm_autosync_ref(hdspm); + switch (syncref) { + case HDSPM_AES32_AUTOSYNC_FROM_WORD: + /* Check WC sync and get sample rate */ + if (hdspm_wc_sync_check(hdspm)) + return HDSPM_bit2freq(hdspm_get_wc_sample_rate(hdspm)); + break; + + case HDSPM_AES32_AUTOSYNC_FROM_AES1: + case HDSPM_AES32_AUTOSYNC_FROM_AES2: + case HDSPM_AES32_AUTOSYNC_FROM_AES3: + case HDSPM_AES32_AUTOSYNC_FROM_AES4: + case HDSPM_AES32_AUTOSYNC_FROM_AES5: + case HDSPM_AES32_AUTOSYNC_FROM_AES6: + case HDSPM_AES32_AUTOSYNC_FROM_AES7: + case HDSPM_AES32_AUTOSYNC_FROM_AES8: + /* Check AES sync and get sample rate */ + if (hdspm_aes_sync_check(hdspm, syncref - HDSPM_AES32_AUTOSYNC_FROM_AES1)) + return HDSPM_bit2freq(hdspm_get_aes_sample_rate(hdspm, + syncref - HDSPM_AES32_AUTOSYNC_FROM_AES1)); + break; - if (syncref == HDSPM_AES32_AUTOSYNC_FROM_WORD && - status & HDSPM_AES32_wcLock) - return HDSPM_bit2freq((status >> HDSPM_AES32_wcFreq_bit) & 0xF); - if (syncref >= HDSPM_AES32_AUTOSYNC_FROM_AES1 && - syncref <= HDSPM_AES32_AUTOSYNC_FROM_AES8 && - status2 & (HDSPM_LockAES >> - (syncref - HDSPM_AES32_AUTOSYNC_FROM_AES1))) - return HDSPM_bit2freq((timecode >> (4*(syncref-HDSPM_AES32_AUTOSYNC_FROM_AES1))) & 0xF); - return 0; + case HDSPM_AES32_AUTOSYNC_FROM_TCO: + /* Check TCO sync and get sample rate */ + if (hdspm_tco_sync_check(hdspm)) + return HDSPM_bit2freq(hdspm_get_tco_sample_rate(hdspm)); + break; + default: + return 0; + } /* end switch(syncref) */ break; case MADIface: -- cgit v0.10.2 From 2d60fc7f7d3d79e5646646bb34811961f19d111a Mon Sep 17 00:00:00 2001 From: Adrian Knoth Date: Fri, 5 Jul 2013 11:28:15 +0200 Subject: ALSA: hdspm - AES32: Enable TCO/Sync-In in snd_hdspm_put_sync_ref() This patch enables the user to select "TCO" and "Sync In" as a preferred sync reference on RME AES(32) cards. Signed-off-by: Adrian Knoth Signed-off-by: Takashi Iwai diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c index 80b2247..73d9626 100644 --- a/sound/pci/rme9652/hdspm.c +++ b/sound/pci/rme9652/hdspm.c @@ -2954,19 +2954,20 @@ static int snd_hdspm_put_pref_sync_ref(struct snd_kcontrol *kcontrol, static int hdspm_autosync_ref(struct hdspm *hdspm) { + /* This looks at the autosync selected sync reference */ if (AES32 == hdspm->io_type) { + unsigned int status = hdspm_read(hdspm, HDSPM_statusRegister); - unsigned int syncref = - (status >> HDSPM_AES32_syncref_bit) & 0xF; - if (syncref == 0) - return HDSPM_AES32_AUTOSYNC_FROM_WORD; - if (syncref <= 8) + unsigned int syncref = (status >> HDSPM_AES32_syncref_bit) & 0xF; + if ((syncref >= HDSPM_AES32_AUTOSYNC_FROM_WORD) && + (syncref <= HDSPM_AES32_AUTOSYNC_FROM_SYNC_IN)) { return syncref; + } return HDSPM_AES32_AUTOSYNC_FROM_NONE; + } else if (MADI == hdspm->io_type) { - /* This looks at the autosync selected sync reference */ - unsigned int status2 = hdspm_read(hdspm, HDSPM_statusRegister2); + unsigned int status2 = hdspm_read(hdspm, HDSPM_statusRegister2); switch (status2 & HDSPM_SelSyncRefMask) { case HDSPM_SelSyncRef_WORD: return HDSPM_AUTOSYNC_FROM_WORD; -- cgit v0.10.2 From 194062daba00688dfd47caaf01f3131700cd726f Mon Sep 17 00:00:00 2001 From: Adrian Knoth Date: Fri, 5 Jul 2013 11:28:16 +0200 Subject: ALSA: hdspm - AES32: Include TCO and Sync-In in proc output Also report TCO status and Sync-In via /proc/ on AES(32) cards. Signed-off-by: Adrian Knoth Signed-off-by: Takashi Iwai diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c index 73d9626..f6e922c 100644 --- a/sound/pci/rme9652/hdspm.c +++ b/sound/pci/rme9652/hdspm.c @@ -5125,11 +5125,18 @@ snd_hdspm_proc_read_aes32(struct snd_info_entry * entry, autosync_ref = "AES7"; break; case HDSPM_AES32_AUTOSYNC_FROM_AES8: autosync_ref = "AES8"; break; + case HDSPM_AES32_AUTOSYNC_FROM_TCO: + autosync_ref = "TCO"; break; + case HDSPM_AES32_AUTOSYNC_FROM_SYNC_IN: + autosync_ref = "Sync In"; break; default: autosync_ref = "---"; break; } snd_iprintf(buffer, "AutoSync ref = %s\n", autosync_ref); + /* call readout function for TCO specific status */ + snd_hdspm_proc_read_tco(entry, buffer); + snd_iprintf(buffer, "\n"); } -- cgit v0.10.2 From 2336142fc0470db2ac831225936b8e37b3ecb2bd Mon Sep 17 00:00:00 2001 From: Adrian Knoth Date: Fri, 5 Jul 2013 11:28:17 +0200 Subject: ALSA: hdspm - Introduce hdspm_external_rate_to_enum() helper function This patch refactors the code to query the external sample rate and its translation into the corresponding enum into a helper function to prevent future code duplication. A later commit will make use of this new helper function. Signed-off-by: Adrian Knoth Signed-off-by: Takashi Iwai diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c index f6e922c..26f10fd 100644 --- a/sound/pci/rme9652/hdspm.c +++ b/sound/pci/rme9652/hdspm.c @@ -2303,6 +2303,21 @@ static void snd_hdspm_set_infotext(struct snd_ctl_elem_info *uinfo, snd_hdspm_set_infotext(info, texts, ARRAY_SIZE(texts)) +/* Helper function to query the external sample rate and return the + * corresponding enum to be returned to userspace. + */ +static int hdspm_external_rate_to_enum(struct hdspm *hdspm) +{ + int rate = hdspm_external_sample_rate(hdspm); + int i, selected_rate = 0; + for (i = 1; i < 10; i++) + if (HDSPM_bit2freq(i) == rate) { + selected_rate = i; + break; + } + return selected_rate; +} + #define HDSPM_AUTOSYNC_SAMPLE_RATE(xname, xindex) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ @@ -2396,18 +2411,9 @@ static int snd_hdspm_get_autosync_sample_rate(struct snd_kcontrol *kcontrol, case MADI: case MADIface: - { - int rate = hdspm_external_sample_rate(hdspm); - int i, selected_rate = 0; - for (i = 1; i < 10; i++) - if (HDSPM_bit2freq(i) == rate) { - selected_rate = i; - break; - } - ucontrol->value.enumerated.item[0] = selected_rate; - } + ucontrol->value.enumerated.item[0] = + hdspm_external_rate_to_enum(hdspm); break; - default: break; } -- cgit v0.10.2 From 2d63ec38f5bb1f598baa003a964805c852a80b33 Mon Sep 17 00:00:00 2001 From: Adrian Knoth Date: Fri, 5 Jul 2013 11:28:18 +0200 Subject: ALSA: hdspm - AES32: Report external sample rate to userspace This patch adds a new ALSA control to read the external sample rate from userspace on RME AES(32) cards. Signed-off-by: Adrian Knoth Signed-off-by: Takashi Iwai diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c index 26f10fd..2f58e07 100644 --- a/sound/pci/rme9652/hdspm.c +++ b/sound/pci/rme9652/hdspm.c @@ -2401,10 +2401,15 @@ static int snd_hdspm_get_autosync_sample_rate(struct snd_kcontrol *kcontrol, ucontrol->value.enumerated.item[0] = hdspm_get_sync_in_sample_rate(hdspm); break; + case 11: /* External Rate */ + ucontrol->value.enumerated.item[0] = + hdspm_external_rate_to_enum(hdspm); + break; default: /* AES1 to AES8 */ ucontrol->value.enumerated.item[0] = - hdspm_get_s1_sample_rate(hdspm, - kcontrol->private_value-1); + hdspm_get_aes_sample_rate(hdspm, + kcontrol->private_value - + HDSPM_AES32_AUTOSYNC_FROM_AES1); break; } break; @@ -4550,7 +4555,7 @@ static struct snd_kcontrol_new snd_hdspm_controls_aes32[] = { HDSPM_PREF_SYNC_REF("Preferred Sync Reference", 0), HDSPM_AUTOSYNC_REF("AutoSync Reference", 0), HDSPM_SYSTEM_SAMPLE_RATE("System Sample Rate", 0), - HDSPM_AUTOSYNC_SAMPLE_RATE("External Rate", 0), + HDSPM_AUTOSYNC_SAMPLE_RATE("External Rate", 11), HDSPM_SYNC_CHECK("WC Sync Check", 0), HDSPM_SYNC_CHECK("AES1 Sync Check", 1), HDSPM_SYNC_CHECK("AES2 Sync Check", 2), -- cgit v0.10.2 From 0dc831b9bca98a0d8dafb00fa7f20b3aef6cab67 Mon Sep 17 00:00:00 2001 From: Adrian Knoth Date: Fri, 5 Jul 2013 11:28:19 +0200 Subject: ALSA: hdspm - AES32: Enable TCO support This patch finally enables TCO support on RME AES(32) cards. Signed-off-by: Adrian Knoth Signed-off-by: Takashi Iwai diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c index 2f58e07..630316c 100644 --- a/sound/pci/rme9652/hdspm.c +++ b/sound/pci/rme9652/hdspm.c @@ -6811,6 +6811,7 @@ static int snd_hdspm_create(struct snd_card *card, break; case MADI: + case AES32: if (hdspm_read(hdspm, HDSPM_statusRegister) & HDSPM_tco_detect) { hdspm->midiPorts++; hdspm->tco = kzalloc(sizeof(struct hdspm_tco), -- cgit v0.10.2 From 38816545a2cc6f436e5f9b26ebcb4cc2813eeb5c Mon Sep 17 00:00:00 2001 From: Adrian Knoth Date: Fri, 5 Jul 2013 11:28:20 +0200 Subject: ALSA: hdspm - Use snd_ctl_enum_info for most text arrays Use snd_ctl_enum_info() to fill most of the enumerated controls. More non-trivial occurrences will follow in separate commits. Signed-off-by: Adrian Knoth Signed-off-by: Takashi Iwai diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c index 630316c..5a2eb64 100644 --- a/sound/pci/rme9652/hdspm.c +++ b/sound/pci/rme9652/hdspm.c @@ -590,7 +590,7 @@ static char *texts_autosync_aio_tco[] = { static char *texts_autosync_aio[] = { "Word Clock", "ADAT", "AES", "SPDIF", "Sync In" }; -static char *texts_freq[] = { +static const char *const texts_freq[] = { "No Lock", "32 kHz", "44.1 kHz", @@ -2286,21 +2286,8 @@ static int hdspm_get_s1_sample_rate(struct hdspm *hdspm, unsigned int idx) return (status >> (idx*4)) & 0xF; } -static void snd_hdspm_set_infotext(struct snd_ctl_elem_info *uinfo, - char **texts, const int count) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = count; - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = - uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, - texts[uinfo->value.enumerated.item]); -} - #define ENUMERATED_CTL_INFO(info, texts) \ - snd_hdspm_set_infotext(info, texts, ARRAY_SIZE(texts)) + snd_ctl_enum_info(info, 1, ARRAY_SIZE(texts), texts) /* Helper function to query the external sample rate and return the @@ -2477,7 +2464,7 @@ static void hdspm_set_system_clock_mode(struct hdspm *hdspm, int mode) static int snd_hdspm_info_system_clock_mode(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[] = { "Master", "AutoSync" }; + static const char *const texts[] = { "Master", "AutoSync" }; ENUMERATED_CTL_INFO(uinfo, texts); return 0; } @@ -3057,7 +3044,7 @@ static int snd_hdspm_get_autosync_ref(struct snd_kcontrol *kcontrol, static int snd_hdspm_info_tco_video_input_format(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[] = {"No video", "NTSC", "PAL"}; + static const char *const texts[] = {"No video", "NTSC", "PAL"}; ENUMERATED_CTL_INFO(uinfo, texts); return 0; } @@ -3103,7 +3090,7 @@ static int snd_hdspm_get_tco_video_input_format(struct snd_kcontrol *kcontrol, static int snd_hdspm_info_tco_ltc_frames(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[] = {"No lock", "24 fps", "25 fps", "29.97 fps", + static const char *const texts[] = {"No lock", "24 fps", "25 fps", "29.97 fps", "30 fps"}; ENUMERATED_CTL_INFO(uinfo, texts); return 0; @@ -3253,7 +3240,7 @@ static int hdspm_set_input_select(struct hdspm * hdspm, int out) static int snd_hdspm_info_input_select(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[] = { "optical", "coaxial" }; + static const char *const texts[] = { "optical", "coaxial" }; ENUMERATED_CTL_INFO(uinfo, texts); return 0; } @@ -3315,7 +3302,7 @@ static int hdspm_set_ds_wire(struct hdspm * hdspm, int ds) static int snd_hdspm_info_ds_wire(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[] = { "Single", "Double" }; + static const char *const texts[] = { "Single", "Double" }; ENUMERATED_CTL_INFO(uinfo, texts); return 0; } @@ -3388,7 +3375,7 @@ static int hdspm_set_qs_wire(struct hdspm * hdspm, int mode) static int snd_hdspm_info_qs_wire(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[] = { "Single", "Double", "Quad" }; + static const char *const texts[] = { "Single", "Double", "Quad" }; ENUMERATED_CTL_INFO(uinfo, texts); return 0; } @@ -3454,8 +3441,8 @@ static int snd_hdspm_info_tristate(struct snd_kcontrol *kcontrol, { u32 regmask = kcontrol->private_value; - static char *texts_spdif[] = { "Optical", "Coaxial", "Internal" }; - static char *texts_levels[] = { "Hi Gain", "+4 dBu", "-10 dBV" }; + static const char *const texts_spdif[] = { "Optical", "Coaxial", "Internal" }; + static const char *const texts_levels[] = { "Hi Gain", "+4 dBu", "-10 dBV" }; switch (regmask) { case HDSPM_c0_Input0: @@ -3542,7 +3529,7 @@ static int hdspm_set_madi_speedmode(struct hdspm *hdspm, int mode) static int snd_hdspm_info_madi_speedmode(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[] = { "Single", "Double", "Quad" }; + static const char *const texts[] = { "Single", "Double", "Quad" }; ENUMERATED_CTL_INFO(uinfo, texts); return 0; } @@ -3777,7 +3764,7 @@ static int snd_hdspm_put_playback_mixer(struct snd_kcontrol *kcontrol, static int snd_hdspm_info_sync_check(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[] = { "No Lock", "Lock", "Sync", "N/A" }; + static const char *const texts[] = { "No Lock", "Lock", "Sync", "N/A" }; ENUMERATED_CTL_INFO(uinfo, texts); return 0; } @@ -3785,7 +3772,7 @@ static int snd_hdspm_info_sync_check(struct snd_kcontrol *kcontrol, static int snd_hdspm_tco_info_lock_check(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[] = { "No Lock", "Lock" }; + static const char *const texts[] = { "No Lock", "Lock" }; ENUMERATED_CTL_INFO(uinfo, texts); return 0; } @@ -4175,7 +4162,7 @@ static void hdspm_tco_write(struct hdspm *hdspm) static int snd_hdspm_info_tco_sample_rate(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[] = { "44.1 kHz", "48 kHz" }; + static const char *const texts[] = { "44.1 kHz", "48 kHz" }; ENUMERATED_CTL_INFO(uinfo, texts); return 0; } @@ -4221,7 +4208,8 @@ static int snd_hdspm_put_tco_sample_rate(struct snd_kcontrol *kcontrol, static int snd_hdspm_info_tco_pull(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[] = { "0", "+ 0.1 %", "- 0.1 %", "+ 4 %", "- 4 %" }; + static const char *const texts[] = { "0", "+ 0.1 %", "- 0.1 %", + "+ 4 %", "- 4 %" }; ENUMERATED_CTL_INFO(uinfo, texts); return 0; } @@ -4266,7 +4254,7 @@ static int snd_hdspm_put_tco_pull(struct snd_kcontrol *kcontrol, static int snd_hdspm_info_tco_wck_conversion(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[] = { "1:1", "44.1 -> 48", "48 -> 44.1" }; + static const char *const texts[] = { "1:1", "44.1 -> 48", "48 -> 44.1" }; ENUMERATED_CTL_INFO(uinfo, texts); return 0; } @@ -4312,7 +4300,7 @@ static int snd_hdspm_put_tco_wck_conversion(struct snd_kcontrol *kcontrol, static int snd_hdspm_info_tco_frame_rate(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[] = { "24 fps", "25 fps", "29.97fps", + static const char *const texts[] = { "24 fps", "25 fps", "29.97fps", "29.97 dfps", "30 fps", "30 dfps" }; ENUMERATED_CTL_INFO(uinfo, texts); return 0; @@ -4359,7 +4347,7 @@ static int snd_hdspm_put_tco_frame_rate(struct snd_kcontrol *kcontrol, static int snd_hdspm_info_tco_sync_source(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[] = { "LTC", "Video", "WCK" }; + static const char *const texts[] = { "LTC", "Video", "WCK" }; ENUMERATED_CTL_INFO(uinfo, texts); return 0; } -- cgit v0.10.2 From eb0d4dbf3d7f503f435022da46ef1495ca570d85 Mon Sep 17 00:00:00 2001 From: Adrian Knoth Date: Fri, 5 Jul 2013 11:28:21 +0200 Subject: ALSA: hdspm - Use snd_ctl_enum_info() for texts_autosync Also use snd_ctl_enum_info() to fill the autosync enumerated controls. Signed-off-by: Adrian Knoth Signed-off-by: Takashi Iwai diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c index 5a2eb64..ffd5d7c 100644 --- a/sound/pci/rme9652/hdspm.c +++ b/sound/pci/rme9652/hdspm.c @@ -558,36 +558,36 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}"); /* names for speed modes */ static char *hdspm_speed_names[] = { "single", "double", "quad" }; -static char *texts_autosync_aes_tco[] = { "Word Clock", +static const char *const texts_autosync_aes_tco[] = { "Word Clock", "AES1", "AES2", "AES3", "AES4", "AES5", "AES6", "AES7", "AES8", "TCO", "Sync In" }; -static char *texts_autosync_aes[] = { "Word Clock", +static const char *const texts_autosync_aes[] = { "Word Clock", "AES1", "AES2", "AES3", "AES4", "AES5", "AES6", "AES7", "AES8", "Sync In" }; -static char *texts_autosync_madi_tco[] = { "Word Clock", +static const char *const texts_autosync_madi_tco[] = { "Word Clock", "MADI", "TCO", "Sync In" }; -static char *texts_autosync_madi[] = { "Word Clock", +static const char *const texts_autosync_madi[] = { "Word Clock", "MADI", "Sync In" }; -static char *texts_autosync_raydat_tco[] = { +static const char *const texts_autosync_raydat_tco[] = { "Word Clock", "ADAT 1", "ADAT 2", "ADAT 3", "ADAT 4", "AES", "SPDIF", "TCO", "Sync In" }; -static char *texts_autosync_raydat[] = { +static const char *const texts_autosync_raydat[] = { "Word Clock", "ADAT 1", "ADAT 2", "ADAT 3", "ADAT 4", "AES", "SPDIF", "Sync In" }; -static char *texts_autosync_aio_tco[] = { +static const char *const texts_autosync_aio_tco[] = { "Word Clock", "ADAT", "AES", "SPDIF", "TCO", "Sync In" }; -static char *texts_autosync_aio[] = { "Word Clock", +static const char *const texts_autosync_aio[] = { "Word Clock", "ADAT", "AES", "SPDIF", "Sync In" }; static const char *const texts_freq[] = { @@ -975,7 +975,7 @@ struct hdspm { struct hdspm_tco *tco; /* NULL if no TCO detected */ - char **texts_autosync; + const char *const *texts_autosync; int texts_autosync_items; cycles_t last_interrupt; @@ -2888,16 +2888,7 @@ static int snd_hdspm_info_pref_sync_ref(struct snd_kcontrol *kcontrol, { struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = hdspm->texts_autosync_items; - - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = - uinfo->value.enumerated.items - 1; - - strcpy(uinfo->value.enumerated.name, - hdspm->texts_autosync[uinfo->value.enumerated.item]); + snd_ctl_enum_info(uinfo, 1, hdspm->texts_autosync_items, hdspm->texts_autosync); return 0; } -- cgit v0.10.2 From 04659f9e9e6f2493d0e2dc52c72c4f20c22d9c61 Mon Sep 17 00:00:00 2001 From: Adrian Knoth Date: Fri, 5 Jul 2013 11:28:22 +0200 Subject: ALSA: hdspm - Use snd_ctl_enum_info() in snd_hdspm_info_autosync_ref Also use snd_ctl_enum_info() to fill the autosync text fields on AES32 and MADI cards (only users of snd_hdspm_info_autosync_ref). Signed-off-by: Adrian Knoth Signed-off-by: Takashi Iwai diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c index ffd5d7c..7a09b2d 100644 --- a/sound/pci/rme9652/hdspm.c +++ b/sound/pci/rme9652/hdspm.c @@ -2983,31 +2983,15 @@ static int snd_hdspm_info_autosync_ref(struct snd_kcontrol *kcontrol, struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); if (AES32 == hdspm->io_type) { - static char *texts[] = { "WordClock", "AES1", "AES2", "AES3", + static const char *const texts[] = { "WordClock", "AES1", "AES2", "AES3", "AES4", "AES5", "AES6", "AES7", "AES8", "TCO", "Sync In", "None"}; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = ARRAY_SIZE(texts); - if (uinfo->value.enumerated.item >= - uinfo->value.enumerated.items) - uinfo->value.enumerated.item = - uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, - texts[uinfo->value.enumerated.item]); + ENUMERATED_CTL_INFO(uinfo, texts); } else if (MADI == hdspm->io_type) { - static char *texts[] = {"Word Clock", "MADI", "TCO", + static const char *const texts[] = {"Word Clock", "MADI", "TCO", "Sync In", "None" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 5; - if (uinfo->value.enumerated.item >= - uinfo->value.enumerated.items) - uinfo->value.enumerated.item = - uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, - texts[uinfo->value.enumerated.item]); + ENUMERATED_CTL_INFO(uinfo, texts); } return 0; } -- cgit v0.10.2 From 69358fca4203eda93e008f234fabf603d9dba15e Mon Sep 17 00:00:00 2001 From: Martin Dausel Date: Fri, 5 Jul 2013 11:28:23 +0200 Subject: ALSA: hdspm - Added some comments and control register documentation Signed-off-by: Martin Dausel Signed-off-by: Adrian Knoth Signed-off-by: Takashi Iwai diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c index 7a09b2d..a3a71ac 100644 --- a/sound/pci/rme9652/hdspm.c +++ b/sound/pci/rme9652/hdspm.c @@ -38,6 +38,97 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ + +/* ************* Register Documentation ******************************************************* + * + * Work in progress! Documentation is based on the code in this file. + * + * --------- HDSPM_controlRegister --------- + * :7654.3210:7654.3210:7654.3210:7654.3210: bit number per byte + * :||||.||||:||||.||||:||||.||||:||||.||||: + * :3322.2222:2222.1111:1111.1100:0000.0000: bit number + * :1098.7654:3210.9876:5432.1098:7654.3210: 0..31 + * :||||.||||:||||.||||:||||.||||:||||.||||: + * :8421.8421:8421.8421:8421.8421:8421.8421: hex digit + * : . : . : . : x . : HDSPM_AudioInterruptEnable \_ setting both bits + * : . : . : . : . x: HDSPM_Start / enables audio IO + * : . : . : . : x. : HDSPM_ClockModeMaster - 1: Master, 0: Slave + * : . : . : . : .210 : HDSPM_LatencyMask - 3 Bit value for latency + * : . : . : . : . : 0:64, 1:128, 2:256, 3:512, + * : . : . : . : . : 4:1024, 5:2048, 6:4096, 7:8192 + * :x . : . : . x:xx . : HDSPM_FrequencyMask + * : . : . : . :10 . : HDSPM_Frequency1|HDSPM_Frequency0: 1=32K,2=44.1K,3=48K,0=?? + * : . : . : . x: . : HDSPM_DoubleSpeed + * :x . : . : . : . : HDSPM_QuadSpeed + * : . 3 : . 10: 2 . : . : HDSPM_SyncRefMask : + * : . : . x: . : . : HDSPM_SyncRef0 + * : . : . x : . : . : HDSPM_SyncRef1 + * : . : . : x . : . : HDSPM_SyncRef2 + * : . x : . : . : . : HDSPM_SyncRef3 + * : . : . 10: . : . : sync ref: 0:WC, 1:Madi, 2:TCO, 3:SyncIn + * : . 3 : . 10: 2 . : . : 0:WC, 1:AES1 ... 8:AES8, 9: TCO, 10:SyncIn? + * : . x : . : . : . : HDSPe_FLOAT_FORMAT + * : . : . : x . : . : HDSPM_InputSelect0 : 0=optical,1=coax + * : . : . :x . : . : HDSPM_InputSelect1 + * : . : .x : . : . : HDSPM_clr_tms + * : . : . : . x : . : HDSPM_TX_64ch + * : . : . : . x : . : HDSPM_Emphasis + * : . : . : .x : . : HDSPM_AutoInp + * : . : . x : . : . : HDSPM_SMUX + * : . : .x : . : . : HDSPM_clr_tms + * : . : x. : . : . : HDSPM_taxi_reset + * : . x: . : . : . : HDSPM_LineOut + * : . x: . : . : . : ?????????????????? + * : . : x. : . : . : HDSPM_WCK48 + * : . : . : .x : . : HDSPM_Dolby + * : . : x . : . : . : HDSPM_Midi0InterruptEnable + * : . :x . : . : . : HDSPM_Midi1InterruptEnable + * : . : x . : . : . : HDSPM_Midi2InterruptEnable + * : . x : . : . : . : HDSPM_Midi3InterruptEnable + * : . x : . : . : . : HDSPM_DS_DoubleWire + * : .x : . : . : . : HDSPM_QS_DoubleWire + * : x. : . : . : . : HDSPM_QS_QuadWire + * : . : . : . x : . : HDSPM_Professional + * : x . : . : . : . : HDSPM_wclk_sel + * : . : . : . : . : + * :7654.3210:7654.3210:7654.3210:7654.3210: bit number per byte + * :||||.||||:||||.||||:||||.||||:||||.||||: + * :3322.2222:2222.1111:1111.1100:0000.0000: bit number + * :1098.7654:3210.9876:5432.1098:7654.3210: 0..31 + * :||||.||||:||||.||||:||||.||||:||||.||||: + * :8421.8421:8421.8421:8421.8421:8421.8421:hex digit + * + * + * + * AIO / RayDAT only + * + * ------------ HDSPM_WR_SETTINGS ---------- + * :3322.2222:2222.1111:1111.1100:0000.0000: bit number per byte + * :1098.7654:3210.9876:5432.1098:7654.3210: + * :||||.||||:||||.||||:||||.||||:||||.||||: bit number + * :7654.3210:7654.3210:7654.3210:7654.3210: 0..31 + * :||||.||||:||||.||||:||||.||||:||||.||||: + * :8421.8421:8421.8421:8421.8421:8421.8421: hex digit + * : . : . : . : . x: HDSPM_c0Master 1: Master, 0: Slave + * : . : . : . : . x : HDSPM_c0_SyncRef0 + * : . : . : . : . x : HDSPM_c0_SyncRef1 + * : . : . : . : .x : HDSPM_c0_SyncRef2 + * : . : . : . : x. : HDSPM_c0_SyncRef3 + * : . : . : . : 3.210 : HDSPM_c0_SyncRefMask: + * : . : . : . : . : RayDat: 0:WC, 1:AES, 2:SPDIF, 3..6: ADAT1..4, + * : . : . : . : . : 9:TCO, 10:SyncIn + * : . : . : . : . : AIO: 0:WC, 1:AES, 2: SPDIF, 3: ATAT, + * : . : . : . : . : 9:TCO, 10:SyncIn + * : . : . : . : . : + * : . : . : . : . : + * :3322.2222:2222.1111:1111.1100:0000.0000: bit number per byte + * :1098.7654:3210.9876:5432.1098:7654.3210: + * :||||.||||:||||.||||:||||.||||:||||.||||: bit number + * :7654.3210:7654.3210:7654.3210:7654.3210: 0..31 + * :||||.||||:||||.||||:||||.||||:||||.||||: + * :8421.8421:8421.8421:8421.8421:8421.8421: hex digit + * + */ #include #include #include @@ -95,7 +186,7 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}"); #define HDSPM_controlRegister 64 #define HDSPM_interruptConfirmation 96 #define HDSPM_control2Reg 256 /* not in specs ???????? */ -#define HDSPM_freqReg 256 /* for AES32 */ +#define HDSPM_freqReg 256 /* for setting arbitrary clock values (DDS feature) */ #define HDSPM_midiDataOut0 352 /* just believe in old code */ #define HDSPM_midiDataOut1 356 #define HDSPM_eeprom_wr 384 /* for AES32 */ @@ -890,11 +981,11 @@ struct hdspm_midi { }; struct hdspm_tco { - int input; - int framerate; - int wordclock; - int samplerate; - int pull; + int input; /* 0: LTC, 1:Video, 2: WC*/ + int framerate; /* 0=24, 1=25, 2=29.97, 3=29.97d, 4=30, 5=30d */ + int wordclock; /* 0=1:1, 1=44.1->48, 2=48->44.1 */ + int samplerate; /* 0=44.1, 1=48, 2= freq from app */ + int pull; /* 0=0, 1=+0.1%, 2=-0.1%, 3=+4%, 4=-4%*/ int term; /* 0 = off, 1 = on */ }; @@ -913,7 +1004,7 @@ struct hdspm { u32 control_register; /* cached value */ u32 control2_register; /* cached value */ - u32 settings_register; + u32 settings_register; /* cached value for AIO / RayDat (sync reference, master/slave) */ struct hdspm_midi midi[4]; struct tasklet_struct midi_tasklet; @@ -4137,6 +4228,7 @@ static void hdspm_tco_write(struct hdspm *hdspm) static int snd_hdspm_info_tco_sample_rate(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { + /* TODO freq from app could be supported here, see tco->samplerate */ static const char *const texts[] = { "44.1 kHz", "48 kHz" }; ENUMERATED_CTL_INFO(uinfo, texts); return 0; -- cgit v0.10.2 From b6c44f41823e50a5e109e929e07d787eabf4b0d3 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Wed, 10 Jul 2013 00:22:46 +0900 Subject: ALSA: firewire-speakers: remove not-reused member from structure "pcm" member in struct fwspk is used to set pcm operations but is not used again. This commit remove this member and set pcm operations with snd_pcm_set_ops(). Signed-off-by: Takashi Sakamoto Acked-by: Clemens Ladisch Signed-off-by: Takashi Iwai diff --git a/sound/firewire/speakers.c b/sound/firewire/speakers.c index d684655..0f1e5d8 100644 --- a/sound/firewire/speakers.c +++ b/sound/firewire/speakers.c @@ -49,7 +49,6 @@ struct fwspk { struct snd_card *card; struct fw_unit *unit; const struct device_info *device_info; - struct snd_pcm_substream *pcm; struct mutex mutex; struct cmp_connection connection; struct amdtp_out_stream stream; @@ -363,8 +362,7 @@ static int fwspk_create_pcm(struct fwspk *fwspk) return err; pcm->private_data = fwspk; strcpy(pcm->name, fwspk->device_info->short_name); - fwspk->pcm = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; - fwspk->pcm->ops = &ops; + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &ops); return 0; } -- cgit v0.10.2 From e394fe55f7cf5a4f6c20fbd02ab37b1d5c3dd364 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Mon, 1 Jul 2013 16:49:04 +0800 Subject: ASoC: adav80x: Add module device table for adav801 This driver can be built as module, thus add module device table for adav801 to support module auto loading. To make the naming consistent, also rename adav80x_id to adav80x_i2c_id. Signed-off-by: Axel Lin Acked-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/adav80x.c b/sound/soc/codecs/adav80x.c index 3c839cc..15b012d0 100644 --- a/sound/soc/codecs/adav80x.c +++ b/sound/soc/codecs/adav80x.c @@ -868,6 +868,12 @@ static int adav80x_bus_remove(struct device *dev) } #if defined(CONFIG_SPI_MASTER) +static const struct spi_device_id adav80x_spi_id[] = { + { "adav801", 0 }, + { } +}; +MODULE_DEVICE_TABLE(spi, adav80x_spi_id); + static int adav80x_spi_probe(struct spi_device *spi) { return adav80x_bus_probe(&spi->dev, SND_SOC_SPI); @@ -885,15 +891,16 @@ static struct spi_driver adav80x_spi_driver = { }, .probe = adav80x_spi_probe, .remove = adav80x_spi_remove, + .id_table = adav80x_spi_id, }; #endif #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) -static const struct i2c_device_id adav80x_id[] = { +static const struct i2c_device_id adav80x_i2c_id[] = { { "adav803", 0 }, { } }; -MODULE_DEVICE_TABLE(i2c, adav80x_id); +MODULE_DEVICE_TABLE(i2c, adav80x_i2c_id); static int adav80x_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) @@ -913,7 +920,7 @@ static struct i2c_driver adav80x_i2c_driver = { }, .probe = adav80x_i2c_probe, .remove = adav80x_i2c_remove, - .id_table = adav80x_id, + .id_table = adav80x_i2c_id, }; #endif -- cgit v0.10.2 From a2911cdb1fd09de7c0da3938ffab1bc5cedda4f9 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Wed, 3 Jul 2013 21:15:13 -0700 Subject: ASoC: add ak4554 driver ak4554 is very simple DA/AD converter which has no setting register. Note that it has hard coded asymmetric data format playback : SND_SOC_DAIFMT_RIGHT_J capture : SND_SOC_DAIFMT_LEFT_J This driver has single DAI and doesn't have set_fmt. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index badb6fb..ffb9adb 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -198,6 +198,9 @@ config SND_SOC_AK4104 config SND_SOC_AK4535 tristate +config SND_SOC_AK4554 + tristate + config SND_SOC_AK4641 tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 70fd806..fab4086 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -11,6 +11,7 @@ snd-soc-adav80x-objs := adav80x.o snd-soc-ads117x-objs := ads117x.o snd-soc-ak4104-objs := ak4104.o snd-soc-ak4535-objs := ak4535.o +snd-soc-ak4554-objs := ak4554.o snd-soc-ak4641-objs := ak4641.o snd-soc-ak4642-objs := ak4642.o snd-soc-ak4671-objs := ak4671.o @@ -138,6 +139,7 @@ obj-$(CONFIG_SND_SOC_ADAV80X) += snd-soc-adav80x.o obj-$(CONFIG_SND_SOC_ADS117X) += snd-soc-ads117x.o obj-$(CONFIG_SND_SOC_AK4104) += snd-soc-ak4104.o obj-$(CONFIG_SND_SOC_AK4535) += snd-soc-ak4535.o +obj-$(CONFIG_SND_SOC_AK4554) += snd-soc-ak4554.o obj-$(CONFIG_SND_SOC_AK4641) += snd-soc-ak4641.o obj-$(CONFIG_SND_SOC_AK4642) += snd-soc-ak4642.o obj-$(CONFIG_SND_SOC_AK4671) += snd-soc-ak4671.o diff --git a/sound/soc/codecs/ak4554.c b/sound/soc/codecs/ak4554.c new file mode 100644 index 0000000..c1a1733 --- /dev/null +++ b/sound/soc/codecs/ak4554.c @@ -0,0 +1,79 @@ +/* + * ak4554.c + * + * Copyright (C) 2013 Renesas Solutions Corp. + * Kuninori Morimoto + * + * 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. + */ + +#include +#include + +/* + * ak4554 is very simple DA/AD converter which has no setting register. + * + * CAUTION + * + * ak4554 playback format is SND_SOC_DAIFMT_RIGHT_J, + * and, capture format is SND_SOC_DAIFMT_LEFT_J + * on same bit clock, LR clock. + * But, this driver doesn't have snd_soc_dai_ops :: set_fmt + * + * CPU/Codec DAI image + * + * CPU-DAI1 (plaback only fmt = RIGHT_J) --+-- ak4554 + * | + * CPU-DAI2 (capture only fmt = LEFT_J) ---+ + */ + +static struct snd_soc_dai_driver ak4554_dai = { + .name = "ak4554-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .symmetric_rates = 1, +}; + +static struct snd_soc_codec_driver soc_codec_dev_ak4554 = { +}; + +static int ak4554_soc_probe(struct platform_device *pdev) +{ + return snd_soc_register_codec(&pdev->dev, + &soc_codec_dev_ak4554, + &ak4554_dai, 1); +} + +static int ak4554_soc_remove(struct platform_device *pdev) +{ + snd_soc_unregister_codec(&pdev->dev); + return 0; +} + +static struct platform_driver ak4554_driver = { + .driver = { + .name = "ak4554-adc-dac", + .owner = THIS_MODULE, + }, + .probe = ak4554_soc_probe, + .remove = ak4554_soc_remove, +}; +module_platform_driver(ak4554_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("SoC AK4554 driver"); +MODULE_AUTHOR("Kuninori Morimoto "); -- cgit v0.10.2 From b25f77815021ec6e7400a82c4984b9c80699ce80 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 4 Jul 2013 19:42:49 -0700 Subject: ASoC: ak4554: add DT support Support for loading the ak4554 codec module via devicetree. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/sound/ak4554.c b/Documentation/devicetree/bindings/sound/ak4554.c new file mode 100644 index 0000000..934fa02 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/ak4554.c @@ -0,0 +1,11 @@ +AK4554 ADC/DAC + +Required properties: + + - compatible : "asahi-kasei,ak4554" + +Example: + +ak4554-adc-dac { + compatible = "asahi-kasei,ak4554"; +}; diff --git a/sound/soc/codecs/ak4554.c b/sound/soc/codecs/ak4554.c index c1a1733..6aed9c4 100644 --- a/sound/soc/codecs/ak4554.c +++ b/sound/soc/codecs/ak4554.c @@ -64,10 +64,17 @@ static int ak4554_soc_remove(struct platform_device *pdev) return 0; } +static struct of_device_id ak4554_of_match[] = { + { .compatible = "asahi-kasei,ak4554" }, + {}, +}; +MODULE_DEVICE_TABLE(of, ak4554_of_match); + static struct platform_driver ak4554_driver = { .driver = { .name = "ak4554-adc-dac", .owner = THIS_MODULE, + .of_match_table = ak4554_of_match, }, .probe = ak4554_soc_probe, .remove = ak4554_soc_remove, -- cgit v0.10.2 From 326c29b6bedb7440b4232631686027af84a62cf5 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Thu, 4 Jul 2013 08:56:27 +0100 Subject: mfd: arizona: Add GPIO control register bit definitions Signed-off-by: Charles Keepax Signed-off-by: Mark Brown diff --git a/include/linux/mfd/arizona/gpio.h b/include/linux/mfd/arizona/gpio.h new file mode 100644 index 0000000..d2146bb --- /dev/null +++ b/include/linux/mfd/arizona/gpio.h @@ -0,0 +1,96 @@ +/* + * GPIO configuration for Arizona devices + * + * Copyright 2013 Wolfson Microelectronics. PLC. + * + * Author: Charles Keepax + * + * 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. + */ + +#ifndef _ARIZONA_GPIO_H +#define _ARIZONA_GPIO_H + +#define ARIZONA_GP_FN_TXLRCLK 0x00 +#define ARIZONA_GP_FN_GPIO 0x01 +#define ARIZONA_GP_FN_IRQ1 0x02 +#define ARIZONA_GP_FN_IRQ2 0x03 +#define ARIZONA_GP_FN_OPCLK 0x04 +#define ARIZONA_GP_FN_FLL1_OUT 0x05 +#define ARIZONA_GP_FN_FLL2_OUT 0x06 +#define ARIZONA_GP_FN_PWM1 0x08 +#define ARIZONA_GP_FN_PWM2 0x09 +#define ARIZONA_GP_FN_SYSCLK_UNDERCLOCKED 0x0A +#define ARIZONA_GP_FN_ASYNCCLK_UNDERCLOCKED 0x0B +#define ARIZONA_GP_FN_FLL1_LOCK 0x0C +#define ARIZONA_GP_FN_FLL2_LOCK 0x0D +#define ARIZONA_GP_FN_FLL1_CLOCK_OK 0x0F +#define ARIZONA_GP_FN_FLL2_CLOCK_OK 0x10 +#define ARIZONA_GP_FN_HEADPHONE_DET 0x12 +#define ARIZONA_GP_FN_MIC_DET 0x13 +#define ARIZONA_GP_FN_WSEQ_STATUS 0x15 +#define ARIZONA_GP_FN_CIF_ADDRESS_ERROR 0x16 +#define ARIZONA_GP_FN_ASRC1_LOCK 0x1A +#define ARIZONA_GP_FN_ASRC2_LOCK 0x1B +#define ARIZONA_GP_FN_ASRC_CONFIG_ERROR 0x1C +#define ARIZONA_GP_FN_DRC1_SIGNAL_DETECT 0x1D +#define ARIZONA_GP_FN_DRC1_ANTICLIP 0x1E +#define ARIZONA_GP_FN_DRC1_DECAY 0x1F +#define ARIZONA_GP_FN_DRC1_NOISE 0x20 +#define ARIZONA_GP_FN_DRC1_QUICK_RELEASE 0x21 +#define ARIZONA_GP_FN_DRC2_SIGNAL_DETECT 0x22 +#define ARIZONA_GP_FN_DRC2_ANTICLIP 0x23 +#define ARIZONA_GP_FN_DRC2_DECAY 0x24 +#define ARIZONA_GP_FN_DRC2_NOISE 0x25 +#define ARIZONA_GP_FN_DRC2_QUICK_RELEASE 0x26 +#define ARIZONA_GP_FN_MIXER_DROPPED_SAMPLE 0x27 +#define ARIZONA_GP_FN_AIF1_CONFIG_ERROR 0x28 +#define ARIZONA_GP_FN_AIF2_CONFIG_ERROR 0x29 +#define ARIZONA_GP_FN_AIF3_CONFIG_ERROR 0x2A +#define ARIZONA_GP_FN_SPK_TEMP_SHUTDOWN 0x2B +#define ARIZONA_GP_FN_SPK_TEMP_WARNING 0x2C +#define ARIZONA_GP_FN_UNDERCLOCKED 0x2D +#define ARIZONA_GP_FN_OVERCLOCKED 0x2E +#define ARIZONA_GP_FN_DSP_IRQ1 0x35 +#define ARIZONA_GP_FN_DSP_IRQ2 0x36 +#define ARIZONA_GP_FN_ASYNC_OPCLK 0x3D +#define ARIZONA_GP_FN_BOOT_DONE 0x44 +#define ARIZONA_GP_FN_DSP1_RAM_READY 0x45 +#define ARIZONA_GP_FN_SYSCLK_ENA_STATUS 0x4B +#define ARIZONA_GP_FN_ASYNCCLK_ENA_STATUS 0x4C + +#define ARIZONA_GPN_DIR 0x8000 /* GPN_DIR */ +#define ARIZONA_GPN_DIR_MASK 0x8000 /* GPN_DIR */ +#define ARIZONA_GPN_DIR_SHIFT 15 /* GPN_DIR */ +#define ARIZONA_GPN_DIR_WIDTH 1 /* GPN_DIR */ +#define ARIZONA_GPN_PU 0x4000 /* GPN_PU */ +#define ARIZONA_GPN_PU_MASK 0x4000 /* GPN_PU */ +#define ARIZONA_GPN_PU_SHIFT 14 /* GPN_PU */ +#define ARIZONA_GPN_PU_WIDTH 1 /* GPN_PU */ +#define ARIZONA_GPN_PD 0x2000 /* GPN_PD */ +#define ARIZONA_GPN_PD_MASK 0x2000 /* GPN_PD */ +#define ARIZONA_GPN_PD_SHIFT 13 /* GPN_PD */ +#define ARIZONA_GPN_PD_WIDTH 1 /* GPN_PD */ +#define ARIZONA_GPN_LVL 0x0800 /* GPN_LVL */ +#define ARIZONA_GPN_LVL_MASK 0x0800 /* GPN_LVL */ +#define ARIZONA_GPN_LVL_SHIFT 11 /* GPN_LVL */ +#define ARIZONA_GPN_LVL_WIDTH 1 /* GPN_LVL */ +#define ARIZONA_GPN_POL 0x0400 /* GPN_POL */ +#define ARIZONA_GPN_POL_MASK 0x0400 /* GPN_POL */ +#define ARIZONA_GPN_POL_SHIFT 10 /* GPN_POL */ +#define ARIZONA_GPN_POL_WIDTH 1 /* GPN_POL */ +#define ARIZONA_GPN_OP_CFG 0x0200 /* GPN_OP_CFG */ +#define ARIZONA_GPN_OP_CFG_MASK 0x0200 /* GPN_OP_CFG */ +#define ARIZONA_GPN_OP_CFG_SHIFT 9 /* GPN_OP_CFG */ +#define ARIZONA_GPN_OP_CFG_WIDTH 1 /* GPN_OP_CFG */ +#define ARIZONA_GPN_DB 0x0100 /* GPN_DB */ +#define ARIZONA_GPN_DB_MASK 0x0100 /* GPN_DB */ +#define ARIZONA_GPN_DB_SHIFT 8 /* GPN_DB */ +#define ARIZONA_GPN_DB_WIDTH 1 /* GPN_DB */ +#define ARIZONA_GPN_FN_MASK 0x007F /* GPN_DB */ +#define ARIZONA_GPN_FN_SHIFT 0 /* GPN_DB */ +#define ARIZONA_GPN_FN_WIDTH 7 /* GPN_DB */ + +#endif -- cgit v0.10.2 From b63144e6c6097486e7678f9ecc2769b68d2ec83e Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Thu, 4 Jul 2013 08:56:28 +0100 Subject: ASoC: arizona: Add signal activity output for DRC When doing signal activity detection, the only output from the DRC will often be a GPIO pin. This patch adds a signal activity output that is activated when a GPIO is configured to output the DRC signal activity detection. Signed-off-by: Charles Keepax Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c index de62581..c9116ac 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c @@ -19,6 +19,7 @@ #include #include +#include #include #include "arizona.h" @@ -223,6 +224,38 @@ int arizona_init_spk(struct snd_soc_codec *codec) } EXPORT_SYMBOL_GPL(arizona_init_spk); +int arizona_init_gpio(struct snd_soc_codec *codec) +{ + struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec); + struct arizona *arizona = priv->arizona; + int i; + + switch (arizona->type) { + case WM5110: + snd_soc_dapm_disable_pin(&codec->dapm, "DRC2 Signal Activity"); + } + + snd_soc_dapm_disable_pin(&codec->dapm, "DRC1 Signal Activity"); + + for (i = 0; i < ARRAY_SIZE(arizona->pdata.gpio_defaults); i++) { + switch (arizona->pdata.gpio_defaults[i] & ARIZONA_GPN_FN_MASK) { + case ARIZONA_GP_FN_DRC1_SIGNAL_DETECT: + snd_soc_dapm_enable_pin(&codec->dapm, + "DRC1 Signal Activity"); + break; + case ARIZONA_GP_FN_DRC2_SIGNAL_DETECT: + snd_soc_dapm_enable_pin(&codec->dapm, + "DRC2 Signal Activity"); + break; + default: + break; + } + } + + return 0; +} +EXPORT_SYMBOL_GPL(arizona_init_gpio); + const char *arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS] = { "None", "Tone Generator 1", diff --git a/sound/soc/codecs/arizona.h b/sound/soc/codecs/arizona.h index b60b08c..fe1b794 100644 --- a/sound/soc/codecs/arizona.h +++ b/sound/soc/codecs/arizona.h @@ -242,6 +242,7 @@ extern int arizona_set_fll(struct arizona_fll *fll, int source, unsigned int Fref, unsigned int Fout); extern int arizona_init_spk(struct snd_soc_codec *codec); +extern int arizona_init_gpio(struct snd_soc_codec *codec); extern int arizona_init_dai(struct arizona_priv *priv, int dai); diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c index 282fd23..a6cbdb4 100644 --- a/sound/soc/codecs/wm5102.c +++ b/sound/soc/codecs/wm5102.c @@ -998,6 +998,8 @@ SND_SOC_DAPM_INPUT("IN2R"), SND_SOC_DAPM_INPUT("IN3L"), SND_SOC_DAPM_INPUT("IN3R"), +SND_SOC_DAPM_OUTPUT("DRC1 Signal Activity"), + SND_SOC_DAPM_PGA_E("IN1L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN1L_ENA_SHIFT, 0, NULL, 0, arizona_in_ev, SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | @@ -1614,6 +1616,9 @@ static const struct snd_soc_dapm_route wm5102_dapm_routes[] = { { "SPKDAT1R", NULL, "OUT5R" }, { "MICSUPP", NULL, "SYSCLK" }, + + { "DRC1 Signal Activity", NULL, "DRC1L" }, + { "DRC1 Signal Activity", NULL, "DRC1R" }, }; static int wm5102_set_fll(struct snd_soc_codec *codec, int fll_id, int source, @@ -1781,6 +1786,7 @@ static int wm5102_codec_probe(struct snd_soc_codec *codec) return ret; arizona_init_spk(codec); + arizona_init_gpio(codec); snd_soc_dapm_disable_pin(&codec->dapm, "HAPTICS"); diff --git a/sound/soc/codecs/wm5110.c b/sound/soc/codecs/wm5110.c index 2e7cb4b..fc41037 100644 --- a/sound/soc/codecs/wm5110.c +++ b/sound/soc/codecs/wm5110.c @@ -432,6 +432,9 @@ SND_SOC_DAPM_INPUT("IN3R"), SND_SOC_DAPM_INPUT("IN4L"), SND_SOC_DAPM_INPUT("IN4R"), +SND_SOC_DAPM_OUTPUT("DRC1 Signal Activity"), +SND_SOC_DAPM_OUTPUT("DRC2 Signal Activity"), + SND_SOC_DAPM_PGA_E("IN1L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN1L_ENA_SHIFT, 0, NULL, 0, arizona_in_ev, SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | @@ -1006,6 +1009,11 @@ static const struct snd_soc_dapm_route wm5110_dapm_routes[] = { { "SPKDAT2R", NULL, "OUT6R" }, { "MICSUPP", NULL, "SYSCLK" }, + + { "DRC1 Signal Activity", NULL, "DRC1L" }, + { "DRC1 Signal Activity", NULL, "DRC1R" }, + { "DRC2 Signal Activity", NULL, "DRC2L" }, + { "DRC2 Signal Activity", NULL, "DRC2R" }, }; static int wm5110_set_fll(struct snd_soc_codec *codec, int fll_id, int source, @@ -1170,6 +1178,7 @@ static int wm5110_codec_probe(struct snd_soc_codec *codec) return ret; arizona_init_spk(codec); + arizona_init_gpio(codec); snd_soc_dapm_disable_pin(&codec->dapm, "HAPTICS"); -- cgit v0.10.2 From b79fae606c921522577f3000b6b9a807cd733d2e Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Thu, 4 Jul 2013 16:53:03 +0100 Subject: ASoC: arizona: Add default case to silence build warning Reported-by: Fengguang Wu Signed-off-by: Charles Keepax Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c index c9116ac..8dc6881 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c @@ -233,6 +233,9 @@ int arizona_init_gpio(struct snd_soc_codec *codec) switch (arizona->type) { case WM5110: snd_soc_dapm_disable_pin(&codec->dapm, "DRC2 Signal Activity"); + break; + default: + break; } snd_soc_dapm_disable_pin(&codec->dapm, "DRC1 Signal Activity"); -- cgit v0.10.2 From 01f00d55a7f21b966417fece78214154f01590ed Mon Sep 17 00:00:00 2001 From: Bo Shen Date: Wed, 3 Jul 2013 16:37:56 +0800 Subject: ASoC: atmel_ssc_dai: move set dma data to startup callback move set dma data to startup callback function, if the set dma data exist in hw_params callback, so the dma data only usable when call hw_params, if want use it before hw_params callback, it will cause NULL pointer access oops Signed-off-by: Bo Shen Reviewed-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c index f3fdfa0..6cf9cf1 100644 --- a/sound/soc/atmel/atmel_ssc_dai.c +++ b/sound/soc/atmel/atmel_ssc_dai.c @@ -196,15 +196,27 @@ static int atmel_ssc_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct atmel_ssc_info *ssc_p = &ssc_info[dai->id]; - int dir_mask; + struct atmel_pcm_dma_params *dma_params; + int dir, dir_mask; pr_debug("atmel_ssc_startup: SSC_SR=0x%u\n", ssc_readl(ssc_p->ssc->regs, SR)); - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + dir = 0; dir_mask = SSC_DIR_MASK_PLAYBACK; - else + } else { + dir = 1; dir_mask = SSC_DIR_MASK_CAPTURE; + } + + dma_params = &ssc_dma_params[dai->id][dir]; + dma_params->ssc = ssc_p->ssc; + dma_params->substream = substream; + + ssc_p->dma_params[dir] = dma_params; + + snd_soc_dai_set_dma_data(dai, substream, dma_params); spin_lock_irq(&ssc_p->lock); if (ssc_p->dir_mask & dir_mask) { @@ -325,7 +337,6 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { - struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); int id = dai->id; struct atmel_ssc_info *ssc_p = &ssc_info[id]; struct atmel_pcm_dma_params *dma_params; @@ -344,19 +355,7 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream, else dir = 1; - dma_params = &ssc_dma_params[id][dir]; - dma_params->ssc = ssc_p->ssc; - dma_params->substream = substream; - - ssc_p->dma_params[dir] = dma_params; - - /* - * The snd_soc_pcm_stream->dma_data field is only used to communicate - * the appropriate DMA parameters to the pcm driver hw_params() - * function. It should not be used for other purposes - * as it is common to all substreams. - */ - snd_soc_dai_set_dma_data(rtd->cpu_dai, substream, dma_params); + dma_params = ssc_p->dma_params[dir]; channels = params_channels(params); -- cgit v0.10.2 From f1b0dd8b9377590b387fd21ba67081ed0e7111e3 Mon Sep 17 00:00:00 2001 From: Bo Shen Date: Wed, 3 Jul 2013 16:37:57 +0800 Subject: ASoC: atmel_ssc_dai: add error mask define add error mask define, which will be used when execute DMA transfer Signed-off-by: Bo Shen Reviewed-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c index 6cf9cf1..1ab4763 100644 --- a/sound/soc/atmel/atmel_ssc_dai.c +++ b/sound/soc/atmel/atmel_ssc_dai.c @@ -73,6 +73,7 @@ static struct atmel_ssc_mask ssc_tx_mask = { .ssc_disable = SSC_BIT(CR_TXDIS), .ssc_endx = SSC_BIT(SR_ENDTX), .ssc_endbuf = SSC_BIT(SR_TXBUFE), + .ssc_error = SSC_BIT(SR_OVRUN), .pdc_enable = ATMEL_PDC_TXTEN, .pdc_disable = ATMEL_PDC_TXTDIS, }; @@ -82,6 +83,7 @@ static struct atmel_ssc_mask ssc_rx_mask = { .ssc_disable = SSC_BIT(CR_RXDIS), .ssc_endx = SSC_BIT(SR_ENDRX), .ssc_endbuf = SSC_BIT(SR_RXBUFF), + .ssc_error = SSC_BIT(SR_OVRUN), .pdc_enable = ATMEL_PDC_RXTEN, .pdc_disable = ATMEL_PDC_RXTDIS, }; -- cgit v0.10.2 From cede8d7aaa60bd7c03b9ec5eb43b09714710b8ba Mon Sep 17 00:00:00 2001 From: Bo Shen Date: Wed, 3 Jul 2013 16:37:58 +0800 Subject: ASoC: atmel-pcm-dma: move prepare for dma to dai prepare as prepare callback for dma is acctually access ssc register which better done in dai driver, so move it to dai prepare callback function Signed-off-by: Bo Shen Reviewed-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/atmel/atmel-pcm-dma.c b/sound/soc/atmel/atmel-pcm-dma.c index 1d38fd0..5a57803 100644 --- a/sound/soc/atmel/atmel-pcm-dma.c +++ b/sound/soc/atmel/atmel-pcm-dma.c @@ -175,19 +175,6 @@ err: return ret; } -static int atmel_pcm_dma_prepare(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct atmel_pcm_dma_params *prtd; - - prtd = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); - - ssc_writex(prtd->ssc->regs, SSC_IER, prtd->mask->ssc_error); - ssc_writex(prtd->ssc->regs, SSC_CR, prtd->mask->ssc_enable); - - return 0; -} - static int atmel_pcm_open(struct snd_pcm_substream *substream) { snd_soc_set_runtime_hwparams(substream, &atmel_pcm_dma_hardware); @@ -200,7 +187,6 @@ static struct snd_pcm_ops atmel_pcm_ops = { .close = snd_dmaengine_pcm_close_release_chan, .ioctl = snd_pcm_lib_ioctl, .hw_params = atmel_pcm_hw_params, - .prepare = atmel_pcm_dma_prepare, .trigger = snd_dmaengine_pcm_trigger, .pointer = snd_dmaengine_pcm_pointer_no_residue, .mmap = atmel_pcm_mmap, diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c index 1ab4763..0ecf356 100644 --- a/sound/soc/atmel/atmel_ssc_dai.c +++ b/sound/soc/atmel/atmel_ssc_dai.c @@ -649,6 +649,7 @@ static int atmel_ssc_prepare(struct snd_pcm_substream *substream, dma_params = ssc_p->dma_params[dir]; ssc_writel(ssc_p->ssc->regs, CR, dma_params->mask->ssc_enable); + ssc_writel(ssc_p->ssc->regs, IER, dma_params->mask->ssc_error); pr_debug("%s enabled SSC_SR=0x%08x\n", dir ? "receive" : "transmit", -- cgit v0.10.2 From 10175b3b2fdd287515db4c103d96b323bb4cd690 Mon Sep 17 00:00:00 2001 From: Bo Shen Date: Wed, 3 Jul 2013 16:37:59 +0800 Subject: ARM: atmel-ssc: change phybase type to dma_addr_t as the phybase paramter only used for DMA operation, change it's type from resource_size_t to dma_addr_t Signed-off-by: Bo Shen Reviewed-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/include/linux/atmel-ssc.h b/include/linux/atmel-ssc.h index deb0ae5..66a0e53 100644 --- a/include/linux/atmel-ssc.h +++ b/include/linux/atmel-ssc.h @@ -11,7 +11,7 @@ struct atmel_ssc_platform_data { struct ssc_device { struct list_head list; - resource_size_t phybase; + dma_addr_t phybase; void __iomem *regs; struct platform_device *pdev; struct atmel_ssc_platform_data *pdata; -- cgit v0.10.2 From 95e0e07e710e24a240f2c9645ecaad3559d6040d Mon Sep 17 00:00:00 2001 From: Bo Shen Date: Wed, 3 Jul 2013 16:38:00 +0800 Subject: ASoC: atmel-pcm: use generic dmaengine framework Align atmel pcm to use ASoC generic dmaengine framework DMA is fully device tree based Signed-off-by: Bo Shen Reviewed-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/atmel/Kconfig b/sound/soc/atmel/Kconfig index 3fdd87f..1c0b185 100644 --- a/sound/soc/atmel/Kconfig +++ b/sound/soc/atmel/Kconfig @@ -13,6 +13,7 @@ config SND_ATMEL_SOC_PDC config SND_ATMEL_SOC_DMA tristate depends on SND_ATMEL_SOC + select SND_SOC_GENERIC_DMAENGINE_PCM config SND_ATMEL_SOC_SSC tristate diff --git a/sound/soc/atmel/atmel-pcm-dma.c b/sound/soc/atmel/atmel-pcm-dma.c index 5a57803..3ff5601 100644 --- a/sound/soc/atmel/atmel-pcm-dma.c +++ b/sound/soc/atmel/atmel-pcm-dma.c @@ -89,124 +89,52 @@ static void atmel_pcm_dma_irq(u32 ssc_sr, } } -/*--------------------------------------------------------------------------*\ - * DMAENGINE operations -\*--------------------------------------------------------------------------*/ -static bool filter(struct dma_chan *chan, void *slave) -{ - struct at_dma_slave *sl = slave; - - if (sl->dma_dev == chan->device->dev) { - chan->private = sl; - return true; - } else { - return false; - } -} - static int atmel_pcm_configure_dma(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, struct atmel_pcm_dma_params *prtd) + struct snd_pcm_hw_params *params, struct dma_slave_config *slave_config) { + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct atmel_pcm_dma_params *prtd; struct ssc_device *ssc; - struct dma_chan *dma_chan; - struct dma_slave_config slave_config; int ret; + prtd = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); ssc = prtd->ssc; - ret = snd_hwparams_to_dma_slave_config(substream, params, - &slave_config); + ret = snd_hwparams_to_dma_slave_config(substream, params, slave_config); if (ret) { pr_err("atmel-pcm: hwparams to dma slave configure failed\n"); return ret; } if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - slave_config.dst_addr = (dma_addr_t)ssc->phybase + SSC_THR; - slave_config.dst_maxburst = 1; + slave_config->dst_addr = ssc->phybase + SSC_THR; + slave_config->dst_maxburst = 1; } else { - slave_config.src_addr = (dma_addr_t)ssc->phybase + SSC_RHR; - slave_config.src_maxburst = 1; - } - - dma_chan = snd_dmaengine_pcm_get_chan(substream); - if (dmaengine_slave_config(dma_chan, &slave_config)) { - pr_err("atmel-pcm: failed to configure dma channel\n"); - ret = -EBUSY; - return ret; - } - - return 0; -} - -static int atmel_pcm_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct atmel_pcm_dma_params *prtd; - struct ssc_device *ssc; - struct at_dma_slave *sdata = NULL; - int ret; - - snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); - - prtd = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); - ssc = prtd->ssc; - if (ssc->pdev) - sdata = ssc->pdev->dev.platform_data; - - ret = snd_dmaengine_pcm_open_request_chan(substream, filter, sdata); - if (ret) { - pr_err("atmel-pcm: dmaengine pcm open failed\n"); - return -EINVAL; - } - - ret = atmel_pcm_configure_dma(substream, params, prtd); - if (ret) { - pr_err("atmel-pcm: failed to configure dmai\n"); - goto err; + slave_config->src_addr = ssc->phybase + SSC_RHR; + slave_config->src_maxburst = 1; } prtd->dma_intr_handler = atmel_pcm_dma_irq; return 0; -err: - snd_dmaengine_pcm_close_release_chan(substream); - return ret; -} - -static int atmel_pcm_open(struct snd_pcm_substream *substream) -{ - snd_soc_set_runtime_hwparams(substream, &atmel_pcm_dma_hardware); - - return 0; } -static struct snd_pcm_ops atmel_pcm_ops = { - .open = atmel_pcm_open, - .close = snd_dmaengine_pcm_close_release_chan, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = atmel_pcm_hw_params, - .trigger = snd_dmaengine_pcm_trigger, - .pointer = snd_dmaengine_pcm_pointer_no_residue, - .mmap = atmel_pcm_mmap, -}; - -static struct snd_soc_platform_driver atmel_soc_platform = { - .ops = &atmel_pcm_ops, - .pcm_new = atmel_pcm_new, - .pcm_free = atmel_pcm_free, +static const struct snd_dmaengine_pcm_config atmel_dmaengine_pcm_config = { + .prepare_slave_config = atmel_pcm_configure_dma, + .pcm_hardware = &atmel_pcm_dma_hardware, + .prealloc_buffer_size = ATMEL_SSC_DMABUF_SIZE, }; int atmel_pcm_dma_platform_register(struct device *dev) { - return snd_soc_register_platform(dev, &atmel_soc_platform); + return snd_dmaengine_pcm_register(dev, &atmel_dmaengine_pcm_config, + SND_DMAENGINE_PCM_FLAG_NO_RESIDUE); } EXPORT_SYMBOL(atmel_pcm_dma_platform_register); void atmel_pcm_dma_platform_unregister(struct device *dev) { - snd_soc_unregister_platform(dev); + snd_dmaengine_pcm_unregister(dev); } EXPORT_SYMBOL(atmel_pcm_dma_platform_unregister); -- cgit v0.10.2 From 35261edca31b0084bb615e6b50dfa5757b26db76 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Tue, 2 Jul 2013 17:19:25 +0800 Subject: ASoC: db1200: add .owner to struct snd_soc_card Add missing .owner of struct snd_soc_card. This prevents the module from being removed from underneath its users. Signed-off-by: Wei Yongjun Signed-off-by: Mark Brown diff --git a/sound/soc/au1x/db1200.c b/sound/soc/au1x/db1200.c index a497a0c..decba87 100644 --- a/sound/soc/au1x/db1200.c +++ b/sound/soc/au1x/db1200.c @@ -73,12 +73,14 @@ static struct snd_soc_dai_link db1300_ac97_dai = { static struct snd_soc_card db1300_ac97_machine = { .name = "DB1300_AC97", + .owner = THIS_MODULE, .dai_link = &db1300_ac97_dai, .num_links = 1, }; static struct snd_soc_card db1550_ac97_machine = { .name = "DB1550_AC97", + .owner = THIS_MODULE, .dai_link = &db1200_ac97_dai, .num_links = 1, }; @@ -145,6 +147,7 @@ static struct snd_soc_dai_link db1300_i2s_dai = { static struct snd_soc_card db1300_i2s_machine = { .name = "DB1300_I2S", + .owner = THIS_MODULE, .dai_link = &db1300_i2s_dai, .num_links = 1, }; @@ -161,6 +164,7 @@ static struct snd_soc_dai_link db1550_i2s_dai = { static struct snd_soc_card db1550_i2s_machine = { .name = "DB1550_I2S", + .owner = THIS_MODULE, .dai_link = &db1550_i2s_dai, .num_links = 1, }; -- cgit v0.10.2 From 64b0c282e87948bc0c5a1b94ca3d4dd9f6415c6f Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 25 Jun 2013 11:00:06 +0100 Subject: ASoC: codecs: Make ALL_CODECS depend on COMPILE_TEST The main function of the option is to enable compile testing. There is still an option since COMPILE_TEST is intended to enable selection of extra drivers rather than forcing them on. Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index badb6fb..01d112b 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -10,6 +10,7 @@ config SND_SOC_I2C_AND_SPI config SND_SOC_ALL_CODECS tristate "Build all ASoC CODEC drivers" + depends on COMPILE_TEST select SND_SOC_88PM860X if MFD_88PM860X select SND_SOC_L3 select SND_SOC_AB8500_CODEC if ABX500_CORE -- cgit v0.10.2 From 7464dcd061045e664723acbf6be49e3ff53d97d8 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Tue, 2 Jul 2013 17:26:00 +0800 Subject: ASoC: imx_mc13783: add .owner to struct snd_soc_card Add missing .owner of struct snd_soc_card. This prevents the module from being removed from underneath its users. Signed-off-by: Wei Yongjun Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/imx-mc13783.c b/sound/soc/fsl/imx-mc13783.c index 9df173c..a3d60d4 100644 --- a/sound/soc/fsl/imx-mc13783.c +++ b/sound/soc/fsl/imx-mc13783.c @@ -90,6 +90,7 @@ static const struct snd_soc_dapm_route imx_mc13783_routes[] = { static struct snd_soc_card imx_mc13783 = { .name = "imx_mc13783", + .owner = THIS_MODULE, .dai_link = imx_mc13783_dai_mc13783, .num_links = ARRAY_SIZE(imx_mc13783_dai_mc13783), .dapm_widgets = imx_mc13783_widget, -- cgit v0.10.2 From c364796a473db467b9201ea31a096bc0cf23547a Mon Sep 17 00:00:00 2001 From: Markus Pargmann Date: Thu, 20 Jun 2013 15:20:20 +0200 Subject: ASoC: imx-pcm-dma: DT support This patch removes the NO_DT flag. The pdev pointer may have a proper of_node with the dmas property, so we can use it to request DMA channels. Signed-off-by: Markus Pargmann Tested-by: Shawn Guo Acked-by: Timur Tabi Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/imx-pcm-dma.c b/sound/soc/fsl/imx-pcm-dma.c index fde4d2e..f323ce0 100644 --- a/sound/soc/fsl/imx-pcm-dma.c +++ b/sound/soc/fsl/imx-pcm-dma.c @@ -64,7 +64,6 @@ int imx_pcm_dma_init(struct platform_device *pdev) { return snd_dmaengine_pcm_register(&pdev->dev, &imx_dmaengine_pcm_config, SND_DMAENGINE_PCM_FLAG_NO_RESIDUE | - SND_DMAENGINE_PCM_FLAG_NO_DT | SND_DMAENGINE_PCM_FLAG_COMPAT); } EXPORT_SYMBOL_GPL(imx_pcm_dma_init); -- cgit v0.10.2 From 9051cba110000985c1a50374fea16f1493955b6e Mon Sep 17 00:00:00 2001 From: Markus Pargmann Date: Thu, 20 Jun 2013 15:20:21 +0200 Subject: ASoC: imx-pcm-fiq: Introduce pcm-fiq-params Cleaner parameter passing for imx-pcm-fiq. Create a seperated fiq-params struct to pass all arguments. Signed-off-by: Markus Pargmann Tested-by: Shawn Guo Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/imx-pcm-fiq.c b/sound/soc/fsl/imx-pcm-fiq.c index 310d902..3b2ba99 100644 --- a/sound/soc/fsl/imx-pcm-fiq.c +++ b/sound/soc/fsl/imx-pcm-fiq.c @@ -22,6 +22,7 @@ #include #include +#include #include #include #include @@ -32,6 +33,7 @@ #include #include "imx-ssi.h" +#include "imx-pcm.h" struct imx_pcm_runtime_data { unsigned int period; @@ -366,9 +368,9 @@ static struct snd_soc_platform_driver imx_soc_platform_fiq = { .pcm_free = imx_pcm_fiq_free, }; -int imx_pcm_fiq_init(struct platform_device *pdev) +int imx_pcm_fiq_init(struct platform_device *pdev, + struct imx_pcm_fiq_params *params) { - struct imx_ssi *ssi = platform_get_drvdata(pdev); int ret; ret = claim_fiq(&fh); @@ -377,15 +379,15 @@ int imx_pcm_fiq_init(struct platform_device *pdev) return ret; } - mxc_set_irq_fiq(ssi->irq, 1); - ssi_irq = ssi->irq; + mxc_set_irq_fiq(params->irq, 1); + ssi_irq = params->irq; - imx_pcm_fiq = ssi->irq; + imx_pcm_fiq = params->irq; - imx_ssi_fiq_base = (unsigned long)ssi->base; + imx_ssi_fiq_base = (unsigned long)params->base; - ssi->dma_params_tx.maxburst = 4; - ssi->dma_params_rx.maxburst = 6; + params->dma_params_tx->maxburst = 4; + params->dma_params_rx->maxburst = 6; ret = snd_soc_register_platform(&pdev->dev, &imx_soc_platform_fiq); if (ret) diff --git a/sound/soc/fsl/imx-pcm.h b/sound/soc/fsl/imx-pcm.h index 67f656c..fd56cad 100644 --- a/sound/soc/fsl/imx-pcm.h +++ b/sound/soc/fsl/imx-pcm.h @@ -32,6 +32,15 @@ imx_pcm_dma_params_init_data(struct imx_dma_data *dma_data, dma_data->peripheral_type = IMX_DMATYPE_SSI; } +struct imx_pcm_fiq_params { + int irq; + void __iomem *base; + + /* Pointer to original ssi driver to setup tx rx sizes */ + struct snd_dmaengine_dai_dma_data *dma_params_rx; + struct snd_dmaengine_dai_dma_data *dma_params_tx; +}; + #ifdef CONFIG_SND_SOC_IMX_PCM_DMA int imx_pcm_dma_init(struct platform_device *pdev); void imx_pcm_dma_exit(struct platform_device *pdev); @@ -47,10 +56,12 @@ static inline void imx_pcm_dma_exit(struct platform_device *pdev) #endif #ifdef CONFIG_SND_SOC_IMX_PCM_FIQ -int imx_pcm_fiq_init(struct platform_device *pdev); +int imx_pcm_fiq_init(struct platform_device *pdev, + struct imx_pcm_fiq_params *params); void imx_pcm_fiq_exit(struct platform_device *pdev); #else -static inline int imx_pcm_fiq_init(struct platform_device *pdev) +static inline int imx_pcm_fiq_init(struct platform_device *pdev, + struct imx_pcm_fiq_params *params) { return -ENODEV; } diff --git a/sound/soc/fsl/imx-ssi.c b/sound/soc/fsl/imx-ssi.c index 51be377..f029e27 100644 --- a/sound/soc/fsl/imx-ssi.c +++ b/sound/soc/fsl/imx-ssi.c @@ -595,7 +595,12 @@ static int imx_ssi_probe(struct platform_device *pdev) goto failed_register; } - ret = imx_pcm_fiq_init(pdev); + ssi->fiq_params.irq = ssi->irq; + ssi->fiq_params.base = ssi->base; + ssi->fiq_params.dma_params_rx = &ssi->dma_params_rx; + ssi->fiq_params.dma_params_tx = &ssi->dma_params_tx; + + ret = imx_pcm_fiq_init(pdev, &ssi->fiq_params); if (ret) goto failed_pcm_fiq; diff --git a/sound/soc/fsl/imx-ssi.h b/sound/soc/fsl/imx-ssi.h index d5003ce..fb1616b 100644 --- a/sound/soc/fsl/imx-ssi.h +++ b/sound/soc/fsl/imx-ssi.h @@ -209,6 +209,7 @@ struct imx_ssi { struct snd_dmaengine_dai_dma_data dma_params_tx; struct imx_dma_data filter_data_tx; struct imx_dma_data filter_data_rx; + struct imx_pcm_fiq_params fiq_params; int enabled; }; -- cgit v0.10.2 From 624dcbdea24b599484895ed5b6a18e869e08d2a5 Mon Sep 17 00:00:00 2001 From: Markus Pargmann Date: Thu, 20 Jun 2013 15:20:28 +0200 Subject: ASoC: fsl: Move fsl-ssi binding doc to sound/ fsl-ssi was located in powerpc/fsl/ssi.txt. This is no powerpc specific device, so it should be moved to sound/ as it connects to differen audio codecs. Signed-off-by: Markus Pargmann Tested-by: Shawn Guo Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/powerpc/fsl/ssi.txt b/Documentation/devicetree/bindings/powerpc/fsl/ssi.txt deleted file mode 100644 index 5ff76c9..0000000 --- a/Documentation/devicetree/bindings/powerpc/fsl/ssi.txt +++ /dev/null @@ -1,73 +0,0 @@ -Freescale Synchronous Serial Interface - -The SSI is a serial device that communicates with audio codecs. It can -be programmed in AC97, I2S, left-justified, or right-justified modes. - -Required properties: -- compatible: Compatible list, contains "fsl,ssi". -- cell-index: The SSI, <0> = SSI1, <1> = SSI2, and so on. -- reg: Offset and length of the register set for the device. -- interrupts: where a is the interrupt number and b is a - field that represents an encoding of the sense and - level information for the interrupt. This should be - encoded based on the information in section 2) - depending on the type of interrupt controller you - have. -- interrupt-parent: The phandle for the interrupt controller that - services interrupts for this device. -- fsl,mode: The operating mode for the SSI interface. - "i2s-slave" - I2S mode, SSI is clock slave - "i2s-master" - I2S mode, SSI is clock master - "lj-slave" - left-justified mode, SSI is clock slave - "lj-master" - l.j. mode, SSI is clock master - "rj-slave" - right-justified mode, SSI is clock slave - "rj-master" - r.j., SSI is clock master - "ac97-slave" - AC97 mode, SSI is clock slave - "ac97-master" - AC97 mode, SSI is clock master -- fsl,playback-dma: Phandle to a node for the DMA channel to use for - playback of audio. This is typically dictated by SOC - design. See the notes below. -- fsl,capture-dma: Phandle to a node for the DMA channel to use for - capture (recording) of audio. This is typically dictated - by SOC design. See the notes below. -- fsl,fifo-depth: The number of elements in the transmit and receive FIFOs. - This number is the maximum allowed value for SFCSR[TFWM0]. -- fsl,ssi-asynchronous: - If specified, the SSI is to be programmed in asynchronous - mode. In this mode, pins SRCK, STCK, SRFS, and STFS must - all be connected to valid signals. In synchronous mode, - SRCK and SRFS are ignored. Asynchronous mode allows - playback and capture to use different sample sizes and - sample rates. Some drivers may require that SRCK and STCK - be connected together, and SRFS and STFS be connected - together. This would still allow different sample sizes, - but not different sample rates. - -Optional properties: -- codec-handle: Phandle to a 'codec' node that defines an audio - codec connected to this SSI. This node is typically - a child of an I2C or other control node. - -Child 'codec' node required properties: -- compatible: Compatible list, contains the name of the codec - -Child 'codec' node optional properties: -- clock-frequency: The frequency of the input clock, which typically comes - from an on-board dedicated oscillator. - -Notes on fsl,playback-dma and fsl,capture-dma: - -On SOCs that have an SSI, specific DMA channels are hard-wired for playback -and capture. On the MPC8610, for example, SSI1 must use DMA channel 0 for -playback and DMA channel 1 for capture. SSI2 must use DMA channel 2 for -playback and DMA channel 3 for capture. The developer can choose which -DMA controller to use, but the channels themselves are hard-wired. The -purpose of these two properties is to represent this hardware design. - -The device tree nodes for the DMA channels that are referenced by -"fsl,playback-dma" and "fsl,capture-dma" must be marked as compatible with -"fsl,ssi-dma-channel". The SOC-specific compatible string (e.g. -"fsl,mpc8610-dma-channel") can remain. If these nodes are left as -"fsl,elo-dma-channel" or "fsl,eloplus-dma-channel", then the generic Elo DMA -drivers (fsldma) will attempt to use them, and it will conflict with the -sound drivers. diff --git a/Documentation/devicetree/bindings/sound/fsl,ssi.txt b/Documentation/devicetree/bindings/sound/fsl,ssi.txt new file mode 100644 index 0000000..5ff76c9 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/fsl,ssi.txt @@ -0,0 +1,73 @@ +Freescale Synchronous Serial Interface + +The SSI is a serial device that communicates with audio codecs. It can +be programmed in AC97, I2S, left-justified, or right-justified modes. + +Required properties: +- compatible: Compatible list, contains "fsl,ssi". +- cell-index: The SSI, <0> = SSI1, <1> = SSI2, and so on. +- reg: Offset and length of the register set for the device. +- interrupts: where a is the interrupt number and b is a + field that represents an encoding of the sense and + level information for the interrupt. This should be + encoded based on the information in section 2) + depending on the type of interrupt controller you + have. +- interrupt-parent: The phandle for the interrupt controller that + services interrupts for this device. +- fsl,mode: The operating mode for the SSI interface. + "i2s-slave" - I2S mode, SSI is clock slave + "i2s-master" - I2S mode, SSI is clock master + "lj-slave" - left-justified mode, SSI is clock slave + "lj-master" - l.j. mode, SSI is clock master + "rj-slave" - right-justified mode, SSI is clock slave + "rj-master" - r.j., SSI is clock master + "ac97-slave" - AC97 mode, SSI is clock slave + "ac97-master" - AC97 mode, SSI is clock master +- fsl,playback-dma: Phandle to a node for the DMA channel to use for + playback of audio. This is typically dictated by SOC + design. See the notes below. +- fsl,capture-dma: Phandle to a node for the DMA channel to use for + capture (recording) of audio. This is typically dictated + by SOC design. See the notes below. +- fsl,fifo-depth: The number of elements in the transmit and receive FIFOs. + This number is the maximum allowed value for SFCSR[TFWM0]. +- fsl,ssi-asynchronous: + If specified, the SSI is to be programmed in asynchronous + mode. In this mode, pins SRCK, STCK, SRFS, and STFS must + all be connected to valid signals. In synchronous mode, + SRCK and SRFS are ignored. Asynchronous mode allows + playback and capture to use different sample sizes and + sample rates. Some drivers may require that SRCK and STCK + be connected together, and SRFS and STFS be connected + together. This would still allow different sample sizes, + but not different sample rates. + +Optional properties: +- codec-handle: Phandle to a 'codec' node that defines an audio + codec connected to this SSI. This node is typically + a child of an I2C or other control node. + +Child 'codec' node required properties: +- compatible: Compatible list, contains the name of the codec + +Child 'codec' node optional properties: +- clock-frequency: The frequency of the input clock, which typically comes + from an on-board dedicated oscillator. + +Notes on fsl,playback-dma and fsl,capture-dma: + +On SOCs that have an SSI, specific DMA channels are hard-wired for playback +and capture. On the MPC8610, for example, SSI1 must use DMA channel 0 for +playback and DMA channel 1 for capture. SSI2 must use DMA channel 2 for +playback and DMA channel 3 for capture. The developer can choose which +DMA controller to use, but the channels themselves are hard-wired. The +purpose of these two properties is to represent this hardware design. + +The device tree nodes for the DMA channels that are referenced by +"fsl,playback-dma" and "fsl,capture-dma" must be marked as compatible with +"fsl,ssi-dma-channel". The SOC-specific compatible string (e.g. +"fsl,mpc8610-dma-channel") can remain. If these nodes are left as +"fsl,elo-dma-channel" or "fsl,eloplus-dma-channel", then the generic Elo DMA +drivers (fsldma) will attempt to use them, and it will conflict with the +sound drivers. -- cgit v0.10.2 From b2c119b0bba808608c48a8f7c9d727956d56561a Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Wed, 10 Jul 2013 18:43:54 +0800 Subject: ASoC: fsl: Disable SSI in trigger() if RE/TE are both cleared The code enabled SSIEN when triggered by SNDRV_PCM_TRIGGER_START, so move the disable code to SNDRV_PCM_TRIGGER_STOP for symmetric. This also allows us to use the SSI driver more flexible so that it can support some use cases like "aplay S16_LE.wav S24_LE.wav" which would call the driver in sequence like: startup()->hw_params(S16_LE)->trigger(START)->tirgger(STOP)-> hw_params(S24_LE)->trigger(START)->tirgger(STOP)->shutdown() If we disable SSIEN in shutdown(), the second hw_params() would bypass the sample bits setting while using symmetric_rate. Signed-off-by: Nicolin Chen Acked-by: Shawn Guo Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 2f2d837..b6ab341 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -510,6 +510,9 @@ static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd, write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_TE, 0); else write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_RE, 0); + + if ((read_ssi(&ssi->scr) & (CCSR_SSI_SCR_TE | CCSR_SSI_SCR_RE)) == 0) + write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_SSIEN, 0); break; default: @@ -534,15 +537,6 @@ static void fsl_ssi_shutdown(struct snd_pcm_substream *substream, ssi_private->first_stream = ssi_private->second_stream; ssi_private->second_stream = NULL; - - /* - * If this is the last active substream, disable the SSI. - */ - if (!ssi_private->first_stream) { - struct ccsr_ssi __iomem *ssi = ssi_private->ssi; - - write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_SSIEN, 0); - } } static int fsl_ssi_dai_probe(struct snd_soc_dai *dai) -- cgit v0.10.2 From b641edfbf253a67a87698df275e3a35a7deac5d4 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 10 Jul 2013 16:31:58 +0100 Subject: ASoC: pcm3008: Remove noisy version print The version number has never been updated and the printk isn't based on any interaction with the device. Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/pcm3008.c b/sound/soc/codecs/pcm3008.c index f2a6282..32e5a59 100644 --- a/sound/soc/codecs/pcm3008.c +++ b/sound/soc/codecs/pcm3008.c @@ -28,8 +28,6 @@ #include "pcm3008.h" -#define PCM3008_VERSION "0.2" - #define PCM3008_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ SNDRV_PCM_RATE_48000) @@ -64,8 +62,6 @@ static int pcm3008_soc_probe(struct snd_soc_codec *codec) struct pcm3008_setup_data *setup = codec->dev->platform_data; int ret = 0; - printk(KERN_INFO "PCM3008 SoC Audio Codec %s\n", PCM3008_VERSION); - /* DEM1 DEM0 DE-EMPHASIS_MODE * Low Low De-emphasis 44.1 kHz ON * Low High De-emphasis OFF -- cgit v0.10.2 From d7f184958e5292280c1048df951932bbfcd95f60 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 10 Jul 2013 16:48:24 +0100 Subject: ASoC: pcm3008: Move gpio allocation to probe This is better from a device model point of view since we don't try to do things like instantiate the card until the required resources appear. Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/pcm3008.c b/sound/soc/codecs/pcm3008.c index 32e5a59..8ab1c03 100644 --- a/sound/soc/codecs/pcm3008.c +++ b/sound/soc/codecs/pcm3008.c @@ -57,10 +57,40 @@ static void pcm3008_gpio_free(struct pcm3008_setup_data *setup) gpio_free(setup->pdda_pin); } -static int pcm3008_soc_probe(struct snd_soc_codec *codec) +#ifdef CONFIG_PM +static int pcm3008_soc_suspend(struct snd_soc_codec *codec) +{ + struct pcm3008_setup_data *setup = codec->dev->platform_data; + + gpio_set_value(setup->pdad_pin, 0); + gpio_set_value(setup->pdda_pin, 0); + + return 0; +} + +static int pcm3008_soc_resume(struct snd_soc_codec *codec) { struct pcm3008_setup_data *setup = codec->dev->platform_data; - int ret = 0; + + gpio_set_value(setup->pdad_pin, 1); + gpio_set_value(setup->pdda_pin, 1); + + return 0; +} +#else +#define pcm3008_soc_suspend NULL +#define pcm3008_soc_resume NULL +#endif + +static struct snd_soc_codec_driver soc_codec_dev_pcm3008 = { + .suspend = pcm3008_soc_suspend, + .resume = pcm3008_soc_resume, +}; + +static int pcm3008_codec_probe(struct platform_device *pdev) +{ + struct pcm3008_setup_data *setup = pdev->dev.platform_data; + int ret; /* DEM1 DEM0 DE-EMPHASIS_MODE * Low Low De-emphasis 44.1 kHz ON @@ -97,63 +127,22 @@ static int pcm3008_soc_probe(struct snd_soc_codec *codec) if (ret != 0) goto gpio_err; - return ret; + return snd_soc_register_codec(&pdev->dev, + &soc_codec_dev_pcm3008, &pcm3008_dai, 1); gpio_err: pcm3008_gpio_free(setup); - return ret; } -static int pcm3008_soc_remove(struct snd_soc_codec *codec) -{ - struct pcm3008_setup_data *setup = codec->dev->platform_data; - - pcm3008_gpio_free(setup); - return 0; -} - -#ifdef CONFIG_PM -static int pcm3008_soc_suspend(struct snd_soc_codec *codec) -{ - struct pcm3008_setup_data *setup = codec->dev->platform_data; - - gpio_set_value(setup->pdad_pin, 0); - gpio_set_value(setup->pdda_pin, 0); - - return 0; -} - -static int pcm3008_soc_resume(struct snd_soc_codec *codec) +static int pcm3008_codec_remove(struct platform_device *pdev) { - struct pcm3008_setup_data *setup = codec->dev->platform_data; + struct pcm3008_setup_data *setup = pdev->dev.platform_data; - gpio_set_value(setup->pdad_pin, 1); - gpio_set_value(setup->pdda_pin, 1); - - return 0; -} -#else -#define pcm3008_soc_suspend NULL -#define pcm3008_soc_resume NULL -#endif - -static struct snd_soc_codec_driver soc_codec_dev_pcm3008 = { - .probe = pcm3008_soc_probe, - .remove = pcm3008_soc_remove, - .suspend = pcm3008_soc_suspend, - .resume = pcm3008_soc_resume, -}; + snd_soc_unregister_codec(&pdev->dev); -static int pcm3008_codec_probe(struct platform_device *pdev) -{ - return snd_soc_register_codec(&pdev->dev, - &soc_codec_dev_pcm3008, &pcm3008_dai, 1); -} + pcm3008_gpio_free(setup); -static int pcm3008_codec_remove(struct platform_device *pdev) -{ - snd_soc_unregister_codec(&pdev->dev); return 0; } -- cgit v0.10.2 From 33319a2fcc4482a97a8924e777bc0036d1d96696 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 10 Jul 2013 16:49:16 +0100 Subject: ASoC: pcm3008: Check for platform data The driver will crash if none is provided. Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/pcm3008.c b/sound/soc/codecs/pcm3008.c index 8ab1c03..4fa4ded 100644 --- a/sound/soc/codecs/pcm3008.c +++ b/sound/soc/codecs/pcm3008.c @@ -92,6 +92,9 @@ static int pcm3008_codec_probe(struct platform_device *pdev) struct pcm3008_setup_data *setup = pdev->dev.platform_data; int ret; + if (!setup) + return -EINVAL; + /* DEM1 DEM0 DE-EMPHASIS_MODE * Low Low De-emphasis 44.1 kHz ON * Low High De-emphasis OFF -- cgit v0.10.2 From d57a79acc77160089e191b6c78e9d42bed517a62 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 10 Jul 2013 16:53:55 +0100 Subject: ASoC: pcm3008: Convert to devm_gpio_request_one() Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/pcm3008.c b/sound/soc/codecs/pcm3008.c index 4fa4ded..b883f99 100644 --- a/sound/soc/codecs/pcm3008.c +++ b/sound/soc/codecs/pcm3008.c @@ -49,14 +49,6 @@ static struct snd_soc_dai_driver pcm3008_dai = { }, }; -static void pcm3008_gpio_free(struct pcm3008_setup_data *setup) -{ - gpio_free(setup->dem0_pin); - gpio_free(setup->dem1_pin); - gpio_free(setup->pdad_pin); - gpio_free(setup->pdda_pin); -} - #ifdef CONFIG_PM static int pcm3008_soc_suspend(struct snd_soc_codec *codec) { @@ -103,49 +95,37 @@ static int pcm3008_codec_probe(struct platform_device *pdev) */ /* Configure DEM0 GPIO (turning OFF DAC De-emphasis). */ - ret = gpio_request(setup->dem0_pin, "codec_dem0"); - if (ret == 0) - ret = gpio_direction_output(setup->dem0_pin, 1); + ret = devm_gpio_request_one(&pdev->dev, setup->dem0_pin, + GPIOF_OUT_INIT_HIGH, "codec_dem0"); if (ret != 0) - goto gpio_err; + return ret; /* Configure DEM1 GPIO (turning OFF DAC De-emphasis). */ - ret = gpio_request(setup->dem1_pin, "codec_dem1"); - if (ret == 0) - ret = gpio_direction_output(setup->dem1_pin, 0); + ret = devm_gpio_request_one(&pdev->dev, setup->dem1_pin, + GPIOF_OUT_INIT_LOW, "codec_dem1"); if (ret != 0) - goto gpio_err; + return ret; /* Configure PDAD GPIO. */ - ret = gpio_request(setup->pdad_pin, "codec_pdad"); - if (ret == 0) - ret = gpio_direction_output(setup->pdad_pin, 1); + ret = devm_gpio_request_one(&pdev->dev, setup->pdad_pin, + GPIOF_OUT_INIT_HIGH, "codec_pdad"); if (ret != 0) - goto gpio_err; + return ret; /* Configure PDDA GPIO. */ - ret = gpio_request(setup->pdda_pin, "codec_pdda"); - if (ret == 0) - ret = gpio_direction_output(setup->pdda_pin, 1); + ret = devm_gpio_request_one(&pdev->dev, setup->pdda_pin, + GPIOF_OUT_INIT_HIGH, "codec_pdda"); if (ret != 0) - goto gpio_err; + return ret; return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_pcm3008, &pcm3008_dai, 1); - -gpio_err: - pcm3008_gpio_free(setup); - return ret; } static int pcm3008_codec_remove(struct platform_device *pdev) { - struct pcm3008_setup_data *setup = pdev->dev.platform_data; - snd_soc_unregister_codec(&pdev->dev); - pcm3008_gpio_free(setup); - return 0; } -- cgit v0.10.2 From 0d6178662cf3dde6829ace63408f25cab42e21c3 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Tue, 2 Jul 2013 17:26:21 +0800 Subject: ASoC: brownstone: add .owner to struct snd_soc_card Add missing .owner of struct snd_soc_card. This prevents the module from being removed from underneath its users. Signed-off-by: Wei Yongjun Signed-off-by: Mark Brown diff --git a/sound/soc/pxa/brownstone.c b/sound/soc/pxa/brownstone.c index 4ad7609..5b7d969 100644 --- a/sound/soc/pxa/brownstone.c +++ b/sound/soc/pxa/brownstone.c @@ -129,6 +129,7 @@ static struct snd_soc_dai_link brownstone_wm8994_dai[] = { /* audio machine driver */ static struct snd_soc_card brownstone = { .name = "brownstone", + .owner = THIS_MODULE, .dai_link = brownstone_wm8994_dai, .num_links = ARRAY_SIZE(brownstone_wm8994_dai), -- cgit v0.10.2 From 3986b9829f638c8a7864e81cdb066aa3d79f05a5 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Tue, 2 Jul 2013 17:26:40 +0800 Subject: ASoC: ttc_dkb: add .owner to struct snd_soc_card Add missing .owner of struct snd_soc_card. This prevents the module from being removed from underneath its users. Signed-off-by: Wei Yongjun Signed-off-by: Mark Brown diff --git a/sound/soc/pxa/ttc-dkb.c b/sound/soc/pxa/ttc-dkb.c index f4ea4f6..13c9ee0 100644 --- a/sound/soc/pxa/ttc-dkb.c +++ b/sound/soc/pxa/ttc-dkb.c @@ -122,6 +122,7 @@ static struct snd_soc_dai_link ttc_pm860x_hifi_dai[] = { /* ttc/td audio machine driver */ static struct snd_soc_card ttc_dkb_card = { .name = "ttc-dkb-hifi", + .owner = THIS_MODULE, .dai_link = ttc_pm860x_hifi_dai, .num_links = ARRAY_SIZE(ttc_pm860x_hifi_dai), -- cgit v0.10.2 From e7c8c589bb640a86685ee040e683b1ec93339f13 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Tue, 2 Jul 2013 17:25:37 +0800 Subject: ASoC: mop500: add .owner to struct snd_soc_card Add missing .owner of struct snd_soc_card. This prevents the module from being removed from underneath its users. Signed-off-by: Wei Yongjun Signed-off-by: Mark Brown diff --git a/sound/soc/ux500/mop500.c b/sound/soc/ux500/mop500.c index 8f5cd00..178d1ba 100644 --- a/sound/soc/ux500/mop500.c +++ b/sound/soc/ux500/mop500.c @@ -52,6 +52,7 @@ static struct snd_soc_dai_link mop500_dai_links[] = { static struct snd_soc_card mop500_card = { .name = "MOP500-card", + .owner = THIS_MODULE, .probe = NULL, .dai_link = mop500_dai_links, .num_links = ARRAY_SIZE(mop500_dai_links), -- cgit v0.10.2 From 57e265c8d71fb94c130bfb31f589cc9e97fb3928 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 1 Jul 2013 20:46:34 +0100 Subject: ASoC: wm8994: Move runtime PM init to platform device init As well as being better style this allows the device to idle when there is no audio card instantaited which is probably what we want. Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index 1d4b1ec..02c320f 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -4014,9 +4014,6 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec) wm8994->micdet_irq = control->pdata.micdet_irq; - pm_runtime_enable(codec->dev); - pm_runtime_idle(codec->dev); - /* By default use idle_bias_off, will override for WM8994 */ codec->dapm.idle_bias_off = 1; @@ -4389,8 +4386,6 @@ static int wm8994_codec_remove(struct snd_soc_codec *codec) wm8994_set_bias_level(codec, SND_SOC_BIAS_OFF); - pm_runtime_disable(codec->dev); - for (i = 0; i < ARRAY_SIZE(wm8994->fll_locked); i++) wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_FLL1_LOCK + i, &wm8994->fll_locked[i]); @@ -4449,6 +4444,9 @@ static int wm8994_probe(struct platform_device *pdev) wm8994->wm8994 = dev_get_drvdata(pdev->dev.parent); + pm_runtime_enable(&pdev->dev); + pm_runtime_idle(&pdev->dev); + return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm8994, wm8994_dai, ARRAY_SIZE(wm8994_dai)); } @@ -4456,6 +4454,8 @@ static int wm8994_probe(struct platform_device *pdev) static int wm8994_remove(struct platform_device *pdev) { snd_soc_unregister_codec(&pdev->dev); + pm_runtime_disable(&pdev->dev); + return 0; } -- cgit v0.10.2 From cb23e852aabb50f5083fb734c2067220d087d26e Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Thu, 4 Jul 2013 20:01:01 -0300 Subject: ASoC: sglt5000: Provide the reg_stride field sgtl5000 has 16-bit registers, and only even numbers are valid for its registers addresses. Let regmap knows about this feature by specifying the 'reg_stride' field, so that it can access only the valid registers. Signed-off-by: Fabio Estevam Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c index d441559..7c99f3c 100644 --- a/sound/soc/codecs/sgtl5000.c +++ b/sound/soc/codecs/sgtl5000.c @@ -1470,6 +1470,7 @@ static struct snd_soc_codec_driver sgtl5000_driver = { static const struct regmap_config sgtl5000_regmap = { .reg_bits = 16, .val_bits = 16, + .reg_stride = 2, .max_register = SGTL5000_MAX_REG_OFFSET, .volatile_reg = sgtl5000_volatile, -- cgit v0.10.2 From 68593c9340847662ac1d337b3c5621a1f4ca05db Mon Sep 17 00:00:00 2001 From: Fengguang Wu Date: Mon, 15 Jul 2013 21:41:32 +0800 Subject: ALSA: hdspm - remove unneeded semicolon sound/pci/rme9652/hdspm.c:1110:2-3: Unneeded semicolon Generated by: coccinelle/misc/semicolon.cocci Reported-by: Fengguang Wu Signed-off-by: Takashi Iwai diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c index a3a71ac..ec6335e 100644 --- a/sound/pci/rme9652/hdspm.c +++ b/sound/pci/rme9652/hdspm.c @@ -1250,7 +1250,7 @@ static int hdspm_rate_multiplier(struct hdspm *hdspm, int rate) else if (hdspm->control_register & HDSPM_DoubleSpeed) return rate * 2; - }; + } return rate; } -- cgit v0.10.2 From c50c2f7af1fe503b1c3b8cb0fa0e3c9b4bba9e92 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 15 Jul 2013 16:39:09 +0100 Subject: ASoC: kirkwood: Remove unused headers Signed-off-by: Mark Brown diff --git a/sound/soc/kirkwood/kirkwood-openrd.c b/sound/soc/kirkwood/kirkwood-openrd.c index b979c71..addbebc 100644 --- a/sound/soc/kirkwood/kirkwood-openrd.c +++ b/sound/soc/kirkwood/kirkwood-openrd.c @@ -16,9 +16,7 @@ #include #include #include -#include #include -#include #include "../codecs/cs42l51.h" static int openrd_client_hw_params(struct snd_pcm_substream *substream, diff --git a/sound/soc/kirkwood/kirkwood-t5325.c b/sound/soc/kirkwood/kirkwood-t5325.c index 1d0ed6f..4f4cb56 100644 --- a/sound/soc/kirkwood/kirkwood-t5325.c +++ b/sound/soc/kirkwood/kirkwood-t5325.c @@ -15,9 +15,7 @@ #include #include #include -#include #include -#include #include "../codecs/alc5623.h" static int t5325_hw_params(struct snd_pcm_substream *substream, -- cgit v0.10.2 From 30d3924852c7b07df4e015610aca1cafa5c15cab Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 15 Jul 2013 16:40:18 +0100 Subject: ASoC: kirkwood: Enable build on non-Kirkwood platforms Improve build coverage by enabling build on other platforms if COMPILE_TEST is enabled. Signed-off-by: Mark Brown diff --git a/sound/soc/kirkwood/Kconfig b/sound/soc/kirkwood/Kconfig index c62d715..59085ad 100644 --- a/sound/soc/kirkwood/Kconfig +++ b/sound/soc/kirkwood/Kconfig @@ -1,6 +1,6 @@ config SND_KIRKWOOD_SOC tristate "SoC Audio for the Marvell Kirkwood chip" - depends on ARCH_KIRKWOOD + depends on ARCH_KIRKWOOD || COMPILE_TEST help Say Y or M if you want to add support for codecs attached to the Kirkwood I2S interface. You will also need to select the @@ -11,7 +11,7 @@ config SND_KIRKWOOD_SOC_I2S config SND_KIRKWOOD_SOC_OPENRD tristate "SoC Audio support for Kirkwood Openrd Client" - depends on SND_KIRKWOOD_SOC && (MACH_OPENRD_CLIENT || MACH_OPENRD_ULTIMATE) + depends on SND_KIRKWOOD_SOC && (MACH_OPENRD_CLIENT || MACH_OPENRD_ULTIMATE || COMPILE_TEST) depends on I2C select SND_KIRKWOOD_SOC_I2S select SND_SOC_CS42L51 @@ -21,7 +21,7 @@ config SND_KIRKWOOD_SOC_OPENRD config SND_KIRKWOOD_SOC_T5325 tristate "SoC Audio support for HP t5325" - depends on SND_KIRKWOOD_SOC && MACH_T5325 && I2C + depends on SND_KIRKWOOD_SOC && (MACH_T5325 || COMPILE_TEST) && I2C select SND_KIRKWOOD_SOC_I2S select SND_SOC_ALC5623 help -- cgit v0.10.2 From 4734dc96ea50109ae08603af10804f989ff35437 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 15 Jul 2013 16:41:14 +0100 Subject: ASoC: kirkwood-i2s: Use devm_clk_get() for extclk Signed-off-by: Mark Brown diff --git a/sound/soc/kirkwood/kirkwood-i2s.c b/sound/soc/kirkwood/kirkwood-i2s.c index 4c9dad3..44412ea 100644 --- a/sound/soc/kirkwood/kirkwood-i2s.c +++ b/sound/soc/kirkwood/kirkwood-i2s.c @@ -498,10 +498,9 @@ static int kirkwood_i2s_dev_probe(struct platform_device *pdev) if (err < 0) return err; - priv->extclk = clk_get(&pdev->dev, "extclk"); + priv->extclk = devm_clk_get(&pdev->dev, "extclk"); if (!IS_ERR(priv->extclk)) { if (priv->extclk == priv->clk) { - clk_put(priv->extclk); priv->extclk = ERR_PTR(-EINVAL); } else { dev_info(&pdev->dev, "found external clock\n"); @@ -529,10 +528,8 @@ static int kirkwood_i2s_dev_probe(struct platform_device *pdev) return 0; dev_err(&pdev->dev, "snd_soc_register_component failed\n"); - if (!IS_ERR(priv->extclk)) { + if (!IS_ERR(priv->extclk)) clk_disable_unprepare(priv->extclk); - clk_put(priv->extclk); - } clk_disable_unprepare(priv->clk); return err; @@ -544,10 +541,8 @@ static int kirkwood_i2s_dev_remove(struct platform_device *pdev) snd_soc_unregister_component(&pdev->dev); - if (!IS_ERR(priv->extclk)) { + if (!IS_ERR(priv->extclk)) clk_disable_unprepare(priv->extclk); - clk_put(priv->extclk); - } clk_disable_unprepare(priv->clk); return 0; -- cgit v0.10.2 From 6ad74047f47e1c48223237a0032c5e000f01193f Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 16 Jul 2013 10:46:16 +0100 Subject: ASoC: kirkwood-i2s: Remove empty remove() Signed-off-by: Mark Brown diff --git a/sound/soc/kirkwood/kirkwood-i2s.c b/sound/soc/kirkwood/kirkwood-i2s.c index 44412ea..becc082 100644 --- a/sound/soc/kirkwood/kirkwood-i2s.c +++ b/sound/soc/kirkwood/kirkwood-i2s.c @@ -398,11 +398,6 @@ static int kirkwood_i2s_probe(struct snd_soc_dai *dai) } -static int kirkwood_i2s_remove(struct snd_soc_dai *dai) -{ - return 0; -} - static const struct snd_soc_dai_ops kirkwood_i2s_dai_ops = { .startup = kirkwood_i2s_startup, .trigger = kirkwood_i2s_trigger, @@ -413,7 +408,6 @@ static const struct snd_soc_dai_ops kirkwood_i2s_dai_ops = { static struct snd_soc_dai_driver kirkwood_i2s_dai = { .probe = kirkwood_i2s_probe, - .remove = kirkwood_i2s_remove, .playback = { .channels_min = 1, .channels_max = 2, @@ -431,7 +425,6 @@ static struct snd_soc_dai_driver kirkwood_i2s_dai = { static struct snd_soc_dai_driver kirkwood_i2s_dai_extclk = { .probe = kirkwood_i2s_probe, - .remove = kirkwood_i2s_remove, .playback = { .channels_min = 1, .channels_max = 2, -- cgit v0.10.2 From 9e12cbd93232c20544d16aa33c587786a6cb726d Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 16 Jul 2013 10:47:10 +0100 Subject: ASoC: kirkwood-i2s: Inline KIRKWOOD_I2S_RATES The addition of extclk support makes this misleading as it's only the rates used when there is no extclk so put it in the specific DAI it applies to. Signed-off-by: Mark Brown diff --git a/sound/soc/kirkwood/kirkwood-i2s.c b/sound/soc/kirkwood/kirkwood-i2s.c index becc082..e6027fd 100644 --- a/sound/soc/kirkwood/kirkwood-i2s.c +++ b/sound/soc/kirkwood/kirkwood-i2s.c @@ -26,9 +26,6 @@ #define DRV_NAME "kirkwood-i2s" -#define KIRKWOOD_I2S_RATES \ - (SNDRV_PCM_RATE_44100 | \ - SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000) #define KIRKWOOD_I2S_FORMATS \ (SNDRV_PCM_FMTBIT_S16_LE | \ SNDRV_PCM_FMTBIT_S24_LE | \ @@ -411,13 +408,15 @@ static struct snd_soc_dai_driver kirkwood_i2s_dai = { .playback = { .channels_min = 1, .channels_max = 2, - .rates = KIRKWOOD_I2S_RATES, + .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_96000, .formats = KIRKWOOD_I2S_FORMATS, }, .capture = { .channels_min = 1, .channels_max = 2, - .rates = KIRKWOOD_I2S_RATES, + .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_96000, .formats = KIRKWOOD_I2S_FORMATS, }, .ops = &kirkwood_i2s_dai_ops, -- cgit v0.10.2 From e4c3bce26de240457370d00ce396602cc98bb3cc Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Tue, 16 Jul 2013 11:48:10 +0200 Subject: ALSA: hda - Headphone mic support for an Asus/Conexant device This Conexant codec has a single jack that can be used as either headphone or mic (but not headset). The existing hp_mic functionality does not apply here, because the mic and the HP are on separate pins. Hence make a lighter version of what has been earlier done for Realtek codecs. BugLink: https://bugs.launchpad.net/bugs/1198030 Tested-by: Franz Hsieh Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index de00ce1..4edd2d0 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -66,6 +66,8 @@ struct conexant_spec { hda_nid_t eapds[4]; bool dynamic_eapd; + unsigned int parse_flags; /* flag for snd_hda_parse_pin_defcfg() */ + #ifdef ENABLE_CXT_STATIC_QUIRKS const struct snd_kcontrol_new *mixers[5]; int num_mixers; @@ -3200,6 +3202,9 @@ static int cx_auto_init(struct hda_codec *codec) snd_hda_gen_init(codec); if (!spec->dynamic_eapd) cx_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, true); + + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_INIT); + return 0; } @@ -3224,6 +3229,8 @@ enum { CXT_PINCFG_LEMOTE_A1205, CXT_FIXUP_STEREO_DMIC, CXT_FIXUP_INC_MIC_BOOST, + CXT_FIXUP_HEADPHONE_MIC_PIN, + CXT_FIXUP_HEADPHONE_MIC, }; static void cxt_fixup_stereo_dmic(struct hda_codec *codec, @@ -3246,6 +3253,59 @@ static void cxt5066_increase_mic_boost(struct hda_codec *codec, (0 << AC_AMPCAP_MUTE_SHIFT)); } +static void cxt_update_headset_mode(struct hda_codec *codec) +{ + /* The verbs used in this function were tested on a Conexant CX20751/2 codec. */ + int i; + bool mic_mode = false; + struct conexant_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->gen.autocfg; + + hda_nid_t mux_pin = spec->gen.imux_pins[spec->gen.cur_mux[0]]; + + for (i = 0; i < cfg->num_inputs; i++) + if (cfg->inputs[i].pin == mux_pin) { + mic_mode = !!cfg->inputs[i].is_headphone_mic; + break; + } + + if (mic_mode) { + snd_hda_codec_write_cache(codec, 0x1c, 0, 0x410, 0x7c); /* enable merged mode for analog int-mic */ + spec->gen.hp_jack_present = false; + } else { + snd_hda_codec_write_cache(codec, 0x1c, 0, 0x410, 0x54); /* disable merged mode for analog int-mic */ + spec->gen.hp_jack_present = snd_hda_jack_detect(codec, spec->gen.autocfg.hp_pins[0]); + } + + snd_hda_gen_update_outputs(codec); +} + +static void cxt_update_headset_mode_hook(struct hda_codec *codec, + struct snd_ctl_elem_value *ucontrol) +{ + cxt_update_headset_mode(codec); +} + +static void cxt_fixup_headphone_mic(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct conexant_spec *spec = codec->spec; + + switch (action) { + case HDA_FIXUP_ACT_PRE_PROBE: + spec->parse_flags |= HDA_PINCFG_HEADPHONE_MIC; + break; + case HDA_FIXUP_ACT_PROBE: + spec->gen.cap_sync_hook = cxt_update_headset_mode_hook; + spec->gen.automute_hook = cxt_update_headset_mode; + break; + case HDA_FIXUP_ACT_INIT: + cxt_update_headset_mode(codec); + break; + } +} + + /* ThinkPad X200 & co with cxt5051 */ static const struct hda_pintbl cxt_pincfg_lenovo_x200[] = { { 0x16, 0x042140ff }, /* HP (seq# overridden) */ @@ -3302,6 +3362,19 @@ static const struct hda_fixup cxt_fixups[] = { .type = HDA_FIXUP_FUNC, .v.func = cxt5066_increase_mic_boost, }, + [CXT_FIXUP_HEADPHONE_MIC_PIN] = { + .type = HDA_FIXUP_PINS, + .chained = true, + .chain_id = CXT_FIXUP_HEADPHONE_MIC, + .v.pins = (const struct hda_pintbl[]) { + { 0x18, 0x03a1913d }, /* use as headphone mic, without its own jack detect */ + { } + } + }, + [CXT_FIXUP_HEADPHONE_MIC] = { + .type = HDA_FIXUP_FUNC, + .v.func = cxt_fixup_headphone_mic, + }, }; static const struct snd_pci_quirk cxt5051_fixups[] = { @@ -3311,6 +3384,7 @@ static const struct snd_pci_quirk cxt5051_fixups[] = { static const struct snd_pci_quirk cxt5066_fixups[] = { SND_PCI_QUIRK(0x1025, 0x0543, "Acer Aspire One 522", CXT_FIXUP_STEREO_DMIC), + SND_PCI_QUIRK(0x1043, 0x138d, "Asus", CXT_FIXUP_HEADPHONE_MIC_PIN), SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo T400", CXT_PINCFG_LENOVO_TP410), SND_PCI_QUIRK(0x17aa, 0x215e, "Lenovo T410", CXT_PINCFG_LENOVO_TP410), SND_PCI_QUIRK(0x17aa, 0x215f, "Lenovo T510", CXT_PINCFG_LENOVO_TP410), @@ -3395,7 +3469,8 @@ static int patch_conexant_auto(struct hda_codec *codec) snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); - err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, 0); + err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, + spec->parse_flags); if (err < 0) goto error; @@ -3416,6 +3491,8 @@ static int patch_conexant_auto(struct hda_codec *codec) codec->bus->allow_bus_reset = 1; } + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); + return 0; error: -- cgit v0.10.2 From 8a537f85e9db8a43b323b0ffcf358c51448491de Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Tue, 16 Jul 2013 08:47:47 +0200 Subject: ASoC: kirkwood-i2s: fix a compilation warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In the function kirkwood_set_rate, when the rate cannot be satisfied by the internal nor by an external clock, the clock source in undefined: warning: ‘clks_ctrl’ may be used uninitialized in this function The ALSA subsystem should never gives such a rate because: - the rates with the internal clock are limited to 44.1, 48 and 96 kHz as specified by the kirkwood_i2s_dai structure, - the other rates are proposed in the structure kirkwood_i2s_dai_extclk only when the external clock is present. In case of programming error (bad rate for internal clock and no external clock), the function will simply cause a backtrace. Signed-off-by: Jean-Francois Moine Signed-off-by: Mark Brown diff --git a/sound/soc/kirkwood/kirkwood-i2s.c b/sound/soc/kirkwood/kirkwood-i2s.c index e6027fd..ba72039 100644 --- a/sound/soc/kirkwood/kirkwood-i2s.c +++ b/sound/soc/kirkwood/kirkwood-i2s.c @@ -102,14 +102,16 @@ static void kirkwood_set_rate(struct snd_soc_dai *dai, uint32_t clks_ctrl; if (rate == 44100 || rate == 48000 || rate == 96000) { - /* use internal dco for supported rates */ + /* use internal dco for the supported rates + * defined in kirkwood_i2s_dai */ dev_dbg(dai->dev, "%s: dco set rate = %lu\n", __func__, rate); kirkwood_set_dco(priv->io, rate); clks_ctrl = KIRKWOOD_MCLK_SOURCE_DCO; - } else if (!IS_ERR(priv->extclk)) { - /* use optional external clk for other rates */ + } else { + /* use the external clock for the other rates + * defined in kirkwood_i2s_dai_extclk */ dev_dbg(dai->dev, "%s: extclk set rate = %lu -> %lu\n", __func__, rate, 256 * rate); clk_set_rate(priv->extclk, 256 * rate); -- cgit v0.10.2 From f2c4fa655f7139a181a6d6db99a49cab96ed0337 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 16 Jul 2013 13:36:05 +0100 Subject: ASoC: tlv320aic3x: Add compatible strings for specific devices The driver supports a range of devices but currently doesn't allow those device names to be used for enumeration on DT. Add the currently listed I2C IDs as compatible strings. Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/sound/tlv320aic3x.txt b/Documentation/devicetree/bindings/sound/tlv320aic3x.txt index f47c3f5..26f65f9 100644 --- a/Documentation/devicetree/bindings/sound/tlv320aic3x.txt +++ b/Documentation/devicetree/bindings/sound/tlv320aic3x.txt @@ -3,7 +3,13 @@ Texas Instruments - tlv320aic3x Codec module The tlv320aic3x serial control bus communicates through I2C protocols Required properties: -- compatible - "string" - "ti,tlv320aic3x" + +- compatible - "string" - One of: + "ti,tlv320aic3x" - Generic TLV320AIC3x device + "ti,tlv320aic33" - TLV320AIC33 + "ti,tlv320aic3007" - TLV320AIC3007 + + - reg - - I2C slave address diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c index e5b9268..c9bb760 100644 --- a/sound/soc/codecs/tlv320aic3x.c +++ b/sound/soc/codecs/tlv320aic3x.c @@ -1582,6 +1582,8 @@ static int aic3x_i2c_remove(struct i2c_client *client) #if defined(CONFIG_OF) static const struct of_device_id tlv320aic3x_of_match[] = { { .compatible = "ti,tlv320aic3x", }, + { .compatible = "ti,tlv320aic33" }, + { .compatible = "ti,tlv320aic3007" }, {}, }; MODULE_DEVICE_TABLE(of, tlv320aic3x_of_match); -- cgit v0.10.2 From cbaa56896146cbb5ab54bd65f98d020af282e6c6 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 16 Jul 2013 13:39:52 +0100 Subject: ASoC: tlv320aic3x: List tlv320aic3106 as a supported device Currently there is no specific handling for it but the tlv320aic3106 is supported using this driver. Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/sound/tlv320aic3x.txt b/Documentation/devicetree/bindings/sound/tlv320aic3x.txt index 26f65f9..705a6b1 100644 --- a/Documentation/devicetree/bindings/sound/tlv320aic3x.txt +++ b/Documentation/devicetree/bindings/sound/tlv320aic3x.txt @@ -8,6 +8,7 @@ Required properties: "ti,tlv320aic3x" - Generic TLV320AIC3x device "ti,tlv320aic33" - TLV320AIC33 "ti,tlv320aic3007" - TLV320AIC3007 + "ti,tlv320aic3106" - TLV320AIC3106 - reg - - I2C slave address diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c index c9bb760..cad4fb1 100644 --- a/sound/soc/codecs/tlv320aic3x.c +++ b/sound/soc/codecs/tlv320aic3x.c @@ -1492,6 +1492,7 @@ static const struct i2c_device_id aic3x_i2c_id[] = { { "tlv320aic3x", AIC3X_MODEL_3X }, { "tlv320aic33", AIC3X_MODEL_33 }, { "tlv320aic3007", AIC3X_MODEL_3007 }, + { "tlv320aic3106", AIC3X_MODEL_3X }, { } }; MODULE_DEVICE_TABLE(i2c, aic3x_i2c_id); @@ -1584,6 +1585,7 @@ static const struct of_device_id tlv320aic3x_of_match[] = { { .compatible = "ti,tlv320aic3x", }, { .compatible = "ti,tlv320aic33" }, { .compatible = "ti,tlv320aic3007" }, + { .compatible = "ti,tlv320aic3106" }, {}, }; MODULE_DEVICE_TABLE(of, tlv320aic3x_of_match); -- cgit v0.10.2 From a1df5c2b310ff88cad66de6d55c06d4ad3b9684b Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 15 Jul 2013 17:03:18 +0100 Subject: ASoC: imx: Enable COMPILE_TEST builds Since DT based boards don't have any dependency on arch/arm enable them if COMPILE_TEST is enabled. Signed-off-by: Mark Brown Acked-by: Shawn Guo diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index aa43854..87e28bc 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig @@ -98,7 +98,7 @@ endif # SND_POWERPC_SOC menuconfig SND_IMX_SOC tristate "SoC Audio for Freescale i.MX CPUs" - depends on ARCH_MXC + depends on ARCH_MXC || COMPILE_TEST help Say Y or M if you want to add support for codecs attached to the i.MX CPUs. -- cgit v0.10.2 From 7497185f8c5a3e0dc92765bfc723900b492cd8a4 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 15 Jul 2013 18:13:35 +0100 Subject: ASoC: samsung-spdif: Convert to devm_clk_get() Signed-off-by: Mark Brown diff --git a/sound/soc/samsung/spdif.c b/sound/soc/samsung/spdif.c index 2e5ebb2..5ea70ab 100644 --- a/sound/soc/samsung/spdif.c +++ b/sound/soc/samsung/spdif.c @@ -395,7 +395,7 @@ static int spdif_probe(struct platform_device *pdev) spin_lock_init(&spdif->lock); - spdif->pclk = clk_get(&pdev->dev, "spdif"); + spdif->pclk = devm_clk_get(&pdev->dev, "spdif"); if (IS_ERR(spdif->pclk)) { dev_err(&pdev->dev, "failed to get peri-clock\n"); ret = -ENOENT; @@ -403,7 +403,7 @@ static int spdif_probe(struct platform_device *pdev) } clk_prepare_enable(spdif->pclk); - spdif->sclk = clk_get(&pdev->dev, "sclk_spdif"); + spdif->sclk = devm_clk_get(&pdev->dev, "sclk_spdif"); if (IS_ERR(spdif->sclk)) { dev_err(&pdev->dev, "failed to get internal source clock\n"); ret = -ENOENT; @@ -457,10 +457,8 @@ err3: release_mem_region(mem_res->start, resource_size(mem_res)); err2: clk_disable_unprepare(spdif->sclk); - clk_put(spdif->sclk); err1: clk_disable_unprepare(spdif->pclk); - clk_put(spdif->pclk); err0: return ret; } @@ -480,9 +478,7 @@ static int spdif_remove(struct platform_device *pdev) release_mem_region(mem_res->start, resource_size(mem_res)); clk_disable_unprepare(spdif->sclk); - clk_put(spdif->sclk); clk_disable_unprepare(spdif->pclk); - clk_put(spdif->pclk); return 0; } -- cgit v0.10.2 From f1269ae41f2e2846031cb361b59ebc36d5f9528a Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Tue, 16 Jul 2013 20:05:07 +0800 Subject: ASoC: imx-sgtl5000: fix error return code in imx_sgtl5000_probe() Fix to return a negative error code from the error handling case instead of 0, as done elsewhere in this function. Signed-off-by: Wei Yongjun Acked-by: Shawn Guo Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/imx-sgtl5000.c b/sound/soc/fsl/imx-sgtl5000.c index 3f726e4..389cbfa 100644 --- a/sound/soc/fsl/imx-sgtl5000.c +++ b/sound/soc/fsl/imx-sgtl5000.c @@ -129,8 +129,10 @@ static int imx_sgtl5000_probe(struct platform_device *pdev) } data->codec_clk = devm_clk_get(&codec_dev->dev, NULL); - if (IS_ERR(data->codec_clk)) + if (IS_ERR(data->codec_clk)) { + ret = PTR_ERR(data->codec_clk); goto fail; + } data->clk_frequency = clk_get_rate(data->codec_clk); -- cgit v0.10.2 From b0e0a4d6faaa0415ecc1fa6b9a08dd17df05edad Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 15 Jul 2013 16:56:32 +0100 Subject: ASoC: omap: Enable COMPILE_TEST build for DT platforms The DT platforms don't have any source dependency on any OMAP stuff so allow them to be built when COMPILE_TEST is enabled. Signed-off-by: Mark Brown Acked-by: Peter Ujfalusi diff --git a/sound/soc/omap/Kconfig b/sound/soc/omap/Kconfig index 9f5d55e..44b5acf 100644 --- a/sound/soc/omap/Kconfig +++ b/sound/soc/omap/Kconfig @@ -1,6 +1,6 @@ config SND_OMAP_SOC tristate "SoC Audio for the Texas Instruments OMAP chips" - depends on ARCH_OMAP && DMA_OMAP + depends on (ARCH_OMAP && DMA_OMAP) || (ARCH_ARM && COMPILE_TEST) select SND_SOC_DMAENGINE_PCM config SND_OMAP_SOC_DMIC @@ -26,7 +26,7 @@ config SND_OMAP_SOC_N810 config SND_OMAP_SOC_RX51 tristate "SoC Audio support for Nokia RX-51" - depends on SND_OMAP_SOC && MACH_NOKIA_RX51 + depends on SND_OMAP_SOC && ARCH_ARM && (MACH_NOKIA_RX51 || COMPILE_TEST) select SND_OMAP_SOC_MCBSP select SND_SOC_TLV320AIC3X select SND_SOC_TPA6130A2 @@ -87,7 +87,7 @@ config SND_OMAP_SOC_OMAP_TWL4030 config SND_OMAP_SOC_OMAP_ABE_TWL6040 tristate "SoC Audio support for OMAP boards using ABE and twl6040 codec" - depends on TWL6040_CORE && SND_OMAP_SOC && ARCH_OMAP4 + depends on TWL6040_CORE && SND_OMAP_SOC && (ARCH_OMAP4 || COMPILE_TEST) select SND_OMAP_SOC_DMIC select SND_OMAP_SOC_MCPDM select SND_SOC_TWL6040 -- cgit v0.10.2 From f9b4243fc2e0be109b957a0a5a25968facf7565d Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 16 Jul 2013 17:00:27 +0100 Subject: ASoC: tegra: Remove unneeded mach-type.h incldues Signed-off-by: Mark Brown Acked-by: Stephen Warren Tested-by: Stephen Warren diff --git a/sound/soc/tegra/tegra_alc5632.c b/sound/soc/tegra/tegra_alc5632.c index 48d05d9..c61ea3a 100644 --- a/sound/soc/tegra/tegra_alc5632.c +++ b/sound/soc/tegra/tegra_alc5632.c @@ -13,8 +13,6 @@ * published by the Free Software Foundation. */ -#include - #include #include #include diff --git a/sound/soc/tegra/tegra_wm8753.c b/sound/soc/tegra/tegra_wm8753.c index f87fc53..8e774d1 100644 --- a/sound/soc/tegra/tegra_wm8753.c +++ b/sound/soc/tegra/tegra_wm8753.c @@ -28,8 +28,6 @@ * */ -#include - #include #include #include diff --git a/sound/soc/tegra/trimslice.c b/sound/soc/tegra/trimslice.c index 05c68aa..734bfcd 100644 --- a/sound/soc/tegra/trimslice.c +++ b/sound/soc/tegra/trimslice.c @@ -24,8 +24,6 @@ * */ -#include - #include #include #include -- cgit v0.10.2 From 2fa1b9008c73525bbd7de93bf36e406b8a754bd1 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 16 Jul 2013 18:49:09 +0100 Subject: ASoC: tegra: Add GPIOLIB dependencies For build coverage. Signed-off-by: Mark Brown Acked-by: Stephen Warren diff --git a/sound/soc/tegra/Kconfig b/sound/soc/tegra/Kconfig index 995b120..b0c8ecf 100644 --- a/sound/soc/tegra/Kconfig +++ b/sound/soc/tegra/Kconfig @@ -61,7 +61,7 @@ config SND_SOC_TEGRA30_I2S config SND_SOC_TEGRA_RT5640 tristate "SoC Audio support for Tegra boards using an RT5640 codec" - depends on SND_SOC_TEGRA && I2C + depends on SND_SOC_TEGRA && I2C && GPIOLIB select SND_SOC_TEGRA20_I2S if ARCH_TEGRA_2x_SOC select SND_SOC_TEGRA30_I2S if ARCH_TEGRA_3x_SOC select SND_SOC_RT5640 @@ -71,7 +71,7 @@ config SND_SOC_TEGRA_RT5640 config SND_SOC_TEGRA_WM8753 tristate "SoC Audio support for Tegra boards using a WM8753 codec" - depends on SND_SOC_TEGRA && I2C + depends on SND_SOC_TEGRA && I2C && GPIOLIB select SND_SOC_TEGRA20_I2S if ARCH_TEGRA_2x_SOC select SND_SOC_TEGRA30_I2S if ARCH_TEGRA_3x_SOC select SND_SOC_WM8753 @@ -81,7 +81,7 @@ config SND_SOC_TEGRA_WM8753 config SND_SOC_TEGRA_WM8903 tristate "SoC Audio support for Tegra boards using a WM8903 codec" - depends on SND_SOC_TEGRA && I2C + depends on SND_SOC_TEGRA && I2C && GPIOLIB select SND_SOC_TEGRA20_I2S if ARCH_TEGRA_2x_SOC select SND_SOC_TEGRA30_I2S if ARCH_TEGRA_3x_SOC select SND_SOC_WM8903 @@ -92,7 +92,7 @@ config SND_SOC_TEGRA_WM8903 config SND_SOC_TEGRA_WM9712 tristate "SoC Audio support for Tegra boards using a WM9712 codec" - depends on SND_SOC_TEGRA && ARCH_TEGRA_2x_SOC + depends on SND_SOC_TEGRA && ARCH_TEGRA_2x_SOC && GPIOLIB select SND_SOC_TEGRA20_AC97 select SND_SOC_WM9712 help @@ -110,7 +110,7 @@ config SND_SOC_TEGRA_TRIMSLICE config SND_SOC_TEGRA_ALC5632 tristate "SoC Audio support for Tegra boards using an ALC5632 codec" - depends on SND_SOC_TEGRA && I2C + depends on SND_SOC_TEGRA && I2C && GPIOLIB select SND_SOC_TEGRA20_I2S if ARCH_TEGRA_2x_SOC select SND_SOC_ALC5632 help -- cgit v0.10.2 From c6c0925ea32d37696da7d71631a4a0c999f2094f Mon Sep 17 00:00:00 2001 From: Rongjun Ying Date: Wed, 17 Jul 2013 14:12:16 +0800 Subject: ASoC: hdmi-codec: let the driver support HDMI sink Devices like mobilephones, computers are typically used as HDMI sources, but devices like TV, navigators will be HDMI sinks. for auto scenerios, In-Vehicle Infotainment(IVI) can be HDMI sink to display movies from mobilephones. Signed-off-by: Rongjun Ying Signed-off-by: Barry Song Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/hdmi.c b/sound/soc/codecs/hdmi.c index 2bcae2b..f0986b9 100644 --- a/sound/soc/codecs/hdmi.c +++ b/sound/soc/codecs/hdmi.c @@ -37,6 +37,17 @@ static struct snd_soc_dai_driver hdmi_codec_dai = { .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, }, + .capture = { + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + }, + }; static int hdmi_codec_probe(struct platform_device *pdev) -- cgit v0.10.2 From b0a4747a5d6498d37ebb6e4ce53dd5d89c51ab51 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Wed, 17 Jul 2013 02:00:38 -0300 Subject: ASoC: fsl: fsl_ssi: Use devm_ functions Using devm_ functions can make the code cleaner and smaller. Signed-off-by: Fabio Estevam Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index b6ab341..d078b1b 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -674,7 +674,7 @@ static int fsl_ssi_probe(struct platform_device *pdev) /* The DAI name is the last part of the full name of the node. */ p = strrchr(np->full_name, '/') + 1; - ssi_private = kzalloc(sizeof(struct fsl_ssi_private) + strlen(p), + ssi_private = devm_kzalloc(&pdev->dev, sizeof(*ssi_private) + strlen(p), GFP_KERNEL); if (!ssi_private) { dev_err(&pdev->dev, "could not allocate DAI object\n"); @@ -692,26 +692,24 @@ static int fsl_ssi_probe(struct platform_device *pdev) ret = of_address_to_resource(np, 0, &res); if (ret) { dev_err(&pdev->dev, "could not determine device resources\n"); - goto error_kmalloc; + return ret; } ssi_private->ssi = of_iomap(np, 0); if (!ssi_private->ssi) { dev_err(&pdev->dev, "could not map device resources\n"); - ret = -ENOMEM; - goto error_kmalloc; + return -ENOMEM; } ssi_private->ssi_phys = res.start; ssi_private->irq = irq_of_parse_and_map(np, 0); if (ssi_private->irq == NO_IRQ) { dev_err(&pdev->dev, "no irq for node %s\n", np->full_name); - ret = -ENXIO; - goto error_iomap; + return -ENXIO; } /* The 'name' should not have any slashes in it. */ - ret = request_irq(ssi_private->irq, fsl_ssi_isr, 0, ssi_private->name, - ssi_private); + ret = devm_request_irq(&pdev->dev, ssi_private->irq, fsl_ssi_isr, 0, + ssi_private->name, ssi_private); if (ret < 0) { dev_err(&pdev->dev, "could not claim irq %u\n", ssi_private->irq); goto error_irqmap; @@ -733,11 +731,11 @@ static int fsl_ssi_probe(struct platform_device *pdev) u32 dma_events[2]; ssi_private->ssi_on_imx = true; - ssi_private->clk = clk_get(&pdev->dev, NULL); + ssi_private->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(ssi_private->clk)) { ret = PTR_ERR(ssi_private->clk); dev_err(&pdev->dev, "could not get clock: %d\n", ret); - goto error_irq; + goto error_irqmap; } clk_prepare_enable(ssi_private->clk); @@ -788,7 +786,7 @@ static int fsl_ssi_probe(struct platform_device *pdev) if (ret) { dev_err(&pdev->dev, "could not create sysfs %s file\n", ssi_private->dev_attr.attr.name); - goto error_irq; + goto error_clk; } /* Register with ASoC */ @@ -851,23 +849,12 @@ error_dev: device_remove_file(&pdev->dev, dev_attr); error_clk: - if (ssi_private->ssi_on_imx) { + if (ssi_private->ssi_on_imx) clk_disable_unprepare(ssi_private->clk); - clk_put(ssi_private->clk); - } - -error_irq: - free_irq(ssi_private->irq, ssi_private); error_irqmap: irq_dispose_mapping(ssi_private->irq); -error_iomap: - iounmap(ssi_private->ssi); - -error_kmalloc: - kfree(ssi_private); - return ret; } @@ -880,15 +867,10 @@ static int fsl_ssi_remove(struct platform_device *pdev) if (ssi_private->ssi_on_imx) { imx_pcm_dma_exit(pdev); clk_disable_unprepare(ssi_private->clk); - clk_put(ssi_private->clk); } snd_soc_unregister_component(&pdev->dev); device_remove_file(&pdev->dev, &ssi_private->dev_attr); - - free_irq(ssi_private->irq, ssi_private); irq_dispose_mapping(ssi_private->irq); - - kfree(ssi_private); dev_set_drvdata(&pdev->dev, NULL); return 0; -- cgit v0.10.2 From ede32d3a237e102884cd5b223aba9afe3e6fb679 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Wed, 17 Jul 2013 02:00:39 -0300 Subject: ASoC: fsl: fsl_ssi: Check the return value from clk_prepare_enable() clk_prepare_enable() may fail, so let's check its return value and propagate it in the case of error. Signed-off-by: Fabio Estevam Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index d078b1b..c9974a4 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -737,7 +737,12 @@ static int fsl_ssi_probe(struct platform_device *pdev) dev_err(&pdev->dev, "could not get clock: %d\n", ret); goto error_irqmap; } - clk_prepare_enable(ssi_private->clk); + ret = clk_prepare_enable(ssi_private->clk); + if (ret) { + dev_err(&pdev->dev, "clk_prepare_enable failed: %d\n", + ret); + goto error_irqmap; + } /* * We have burstsize be "fifo_depth - 2" to match the SSI -- cgit v0.10.2 From 2086d078359f0fa512543404f772fc0615da385a Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 17 Jul 2013 10:18:33 +0100 Subject: ASoC: tegra: Always use the generic dmaengine helper library The usage of the dmaengine helpers is unconditional, especially when doing compile testing. Reported-by: Fengguang Wu Signed-off-by: Mark Brown diff --git a/sound/soc/tegra/Kconfig b/sound/soc/tegra/Kconfig index b0c8ecf..66b7a06 100644 --- a/sound/soc/tegra/Kconfig +++ b/sound/soc/tegra/Kconfig @@ -2,7 +2,7 @@ config SND_SOC_TEGRA tristate "SoC Audio for the Tegra System-on-Chip" depends on ARCH_TEGRA && TEGRA20_APB_DMA select REGMAP_MMIO - select SND_SOC_GENERIC_DMAENGINE_PCM if TEGRA20_APB_DMA + select SND_SOC_GENERIC_DMAENGINE_PCM help Say Y or M here if you want support for SoC audio on Tegra. -- cgit v0.10.2 From 22abf843af0686a58b2b6b33d02388d4bbbbcd25 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 15 Jul 2013 17:09:40 +0100 Subject: ASoC: tegra: Enable COMPILE_TEST builds Since there is no architecture dependency in the code allow it to be built on any platform when COMPILE_TEST is enabled. Signed-off-by: Mark Brown Acked-by: Stephen Warren diff --git a/sound/soc/tegra/Kconfig b/sound/soc/tegra/Kconfig index 66b7a06..8fc653c 100644 --- a/sound/soc/tegra/Kconfig +++ b/sound/soc/tegra/Kconfig @@ -1,6 +1,6 @@ config SND_SOC_TEGRA tristate "SoC Audio for the Tegra System-on-Chip" - depends on ARCH_TEGRA && TEGRA20_APB_DMA + depends on (ARCH_TEGRA && TEGRA20_APB_DMA) || COMPILE_TEST select REGMAP_MMIO select SND_SOC_GENERIC_DMAENGINE_PCM help -- cgit v0.10.2 From d0c05ad7827df545760e7659471965ce4ccf655d Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Tue, 16 Jul 2013 11:00:44 -0600 Subject: ASoC: tegra: fix compile warning in AC'97 driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fixes the following by deleting dead code: sound/soc/tegra/tegra20_ac97.c: In function ‘tegra20_ac97_platform_probe’: sound/soc/tegra/tegra20_ac97.c:435:1: warning: label ‘err_unregister_pcm’ defined but not used [-Wunused-label] Signed-off-by: Stephen Warren Signed-off-by: Mark Brown diff --git a/sound/soc/tegra/tegra20_ac97.c b/sound/soc/tegra/tegra20_ac97.c index e58233f..87b845f 100644 --- a/sound/soc/tegra/tegra20_ac97.c +++ b/sound/soc/tegra/tegra20_ac97.c @@ -432,8 +432,6 @@ static int tegra20_ac97_platform_probe(struct platform_device *pdev) return 0; -err_unregister_pcm: - tegra_pcm_platform_unregister(&pdev->dev); err_unregister_component: snd_soc_unregister_component(&pdev->dev); err_asoc_utils_fini: -- cgit v0.10.2 From 02502da4579ffcd2b96334297ba8e6daefe618c4 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 18 Jul 2013 10:46:29 +0100 Subject: ASoC: imx-mc13783: Depend on ARCH_ARM The driver uses the machine type macros so depends on ARM. Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index 87e28bc..3a79d01 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig @@ -194,7 +194,7 @@ config SND_SOC_IMX_SGTL5000 config SND_SOC_IMX_MC13783 tristate "SoC Audio support for I.MX boards with mc13783" - depends on MFD_MC13783 + depends on MFD_MC13783 && ARCH_ARM select SND_SOC_IMX_SSI select SND_SOC_IMX_AUDMUX select SND_SOC_MC13783 -- cgit v0.10.2 From e94a093c1c7a1b06fa574c99f69ed594e0c52ff2 Mon Sep 17 00:00:00 2001 From: Bo Shen Date: Thu, 18 Jul 2013 14:38:20 +0800 Subject: ASoC: wm8904: fix the typo error for LINER Mux fix the typo error, from "LINEL Mux" to "LINER Mux" Signed-off-by: Bo Shen Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm8904.c b/sound/soc/codecs/wm8904.c index 4c9fb14..91dfbfe 100644 --- a/sound/soc/codecs/wm8904.c +++ b/sound/soc/codecs/wm8904.c @@ -1012,7 +1012,7 @@ static const struct soc_enum liner_enum = SOC_ENUM_SINGLE(WM8904_ANALOGUE_OUT12_ZC, 0, 2, out_mux_text); static const struct snd_kcontrol_new liner_mux = - SOC_DAPM_ENUM("LINEL Mux", liner_enum); + SOC_DAPM_ENUM("LINER Mux", liner_enum); static const char *sidetone_text[] = { "None", "Left", "Right" -- cgit v0.10.2 From d4e1a73acd4e894f8332f2093bceaef585cfab67 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 18 Jul 2013 11:52:17 +0100 Subject: ASoC: pcm: Use the power efficient workqueue for delayed powerdown There is no need to use a normal per-CPU workqueue for delayed power downs as they're not timing or performance critical and waking up a core for them would defeat some of the point. Signed-off-by: Mark Brown Reviewed-by: Viresh Kumar diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index b6c6403..f4f68cb 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -411,8 +411,9 @@ static int soc_pcm_close(struct snd_pcm_substream *substream) } else { /* start delayed pop wq here for playback streams */ rtd->pop_wait = 1; - schedule_delayed_work(&rtd->delayed_work, - msecs_to_jiffies(rtd->pmdown_time)); + queue_delayed_work(system_power_efficient_wq, + &rtd->delayed_work, + msecs_to_jiffies(rtd->pmdown_time)); } } else { /* capture streams can be powered down now */ -- cgit v0.10.2 From d8a14e302ffeecc312186b8b9b0efc8963cec83b Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Thu, 18 Jul 2013 15:07:48 -0300 Subject: ASoC: fsl: imx-wm8962: Fix error path If the 'failed to find codec platform device' error path is executed, it should jump to 'fail' label instead of returning an error immediately. 'fail' label will then free the ssi_np and codec_np previously acquired nodes. Signed-off-by: Fabio Estevam Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/imx-wm8962.c b/sound/soc/fsl/imx-wm8962.c index 52a36a9..1d70e27 100644 --- a/sound/soc/fsl/imx-wm8962.c +++ b/sound/soc/fsl/imx-wm8962.c @@ -217,7 +217,8 @@ static int imx_wm8962_probe(struct platform_device *pdev) codec_dev = of_find_i2c_device_by_node(codec_np); if (!codec_dev || !codec_dev->driver) { dev_err(&pdev->dev, "failed to find codec platform device\n"); - return -EINVAL; + ret = -EINVAL; + goto fail; } data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); -- cgit v0.10.2 From e6058aaadcd473e5827720dc143af56aabbeecc7 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 18 Jul 2013 22:47:10 +0100 Subject: ASoC: jack: Use power efficient workqueue The accessory detect debounce work is not performance sensitive so let the scheduler run it wherever is most efficient rather than in a per CPU workqueue by using the system power efficient workqueue. Signed-off-by: Mark Brown Acked-by: Viresh Kumar diff --git a/sound/soc/soc-jack.c b/sound/soc/soc-jack.c index 0bb5cccd..7aa26b5 100644 --- a/sound/soc/soc-jack.c +++ b/sound/soc/soc-jack.c @@ -263,7 +263,7 @@ static irqreturn_t gpio_handler(int irq, void *data) if (device_may_wakeup(dev)) pm_wakeup_event(dev, gpio->debounce_time + 50); - schedule_delayed_work(&gpio->work, + queue_delayed_work(system_power_efficient_wq, &gpio->work, msecs_to_jiffies(gpio->debounce_time)); return IRQ_HANDLED; -- cgit v0.10.2 From 8ccbc3ebe9a74f22c4f0acb363962f4e7c99d3cf Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 15 Jul 2013 17:04:47 +0100 Subject: ASoC: mxs: Enable COMPILE_TEST builds Since DT based boards don't have any dependency on arch/arm enable them if COMPILE_TEST is enabled. Signed-off-by: Mark Brown diff --git a/sound/soc/mxs/Kconfig b/sound/soc/mxs/Kconfig index 78d321c..7daf860 100644 --- a/sound/soc/mxs/Kconfig +++ b/sound/soc/mxs/Kconfig @@ -1,6 +1,6 @@ menuconfig SND_MXS_SOC tristate "SoC Audio for Freescale MXS CPUs" - depends on ARCH_MXS + depends on ARCH_MXS || COMPILE_TEST select SND_SOC_GENERIC_DMAENGINE_PCM help Say Y or M if you want to add support for codecs attached to -- cgit v0.10.2 From 2df7c6aad63f432befe51ac3144a96b37fa5b4ba Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 18 Jul 2013 22:43:00 +0100 Subject: ASoC: max98090: Use power efficient workqueue None of the delayed work the driver schedules has particularly short delays and it is not performance sensitive so let the scheduler run it wherever is most efficient rather than in a per CPU workqueue by using the system power efficient workqueue. Signed-off-by: Mark Brown Acked-by: Viresh Kumar diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c index ad5313f..0569a4c 100644 --- a/sound/soc/codecs/max98090.c +++ b/sound/soc/codecs/max98090.c @@ -2084,8 +2084,9 @@ static irqreturn_t max98090_interrupt(int irq, void *data) pm_wakeup_event(codec->dev, 100); - schedule_delayed_work(&max98090->jack_work, - msecs_to_jiffies(100)); + queue_delayed_work(system_power_efficient_wq, + &max98090->jack_work, + msecs_to_jiffies(100)); } if (active & M98090_DRCACT_MASK) @@ -2132,8 +2133,9 @@ int max98090_mic_detect(struct snd_soc_codec *codec, snd_soc_jack_report(max98090->jack, 0, SND_JACK_HEADSET | SND_JACK_BTN_0); - schedule_delayed_work(&max98090->jack_work, - msecs_to_jiffies(100)); + queue_delayed_work(system_power_efficient_wq, + &max98090->jack_work, + msecs_to_jiffies(100)); return 0; } -- cgit v0.10.2 From a14d982962c1c3caee99ddeea632d97fc851ea60 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 18 Jul 2013 22:43:19 +0100 Subject: ASoC: sta32x: Use power efficient workqueue None of the delayed work the driver schedules has particularly short delays and it is not performance sensitive so let the scheduler run it wherever is most efficient rather than in a per CPU workqueue by using the system power efficient workqueue. Signed-off-by: Mark Brown Acked-by: Viresh Kumar diff --git a/sound/soc/codecs/sta32x.c b/sound/soc/codecs/sta32x.c index cfb55fe..06edb39 100644 --- a/sound/soc/codecs/sta32x.c +++ b/sound/soc/codecs/sta32x.c @@ -363,16 +363,18 @@ static void sta32x_watchdog(struct work_struct *work) } if (!sta32x->shutdown) - schedule_delayed_work(&sta32x->watchdog_work, - round_jiffies_relative(HZ)); + queue_delayed_work(system_power_efficient_wq, + &sta32x->watchdog_work, + round_jiffies_relative(HZ)); } static void sta32x_watchdog_start(struct sta32x_priv *sta32x) { if (sta32x->pdata->needs_esd_watchdog) { sta32x->shutdown = 0; - schedule_delayed_work(&sta32x->watchdog_work, - round_jiffies_relative(HZ)); + queue_delayed_work(system_power_efficient_wq, + &sta32x->watchdog_work, + round_jiffies_relative(HZ)); } } -- cgit v0.10.2 From 76394509f579cd4292b076f708da49404be6af37 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 18 Jul 2013 22:44:03 +0100 Subject: ASoC: twl6040: Use power efficient workqueue The accessory detect debounce work is not performance sensitive so let the scheduler run it wherever is most efficient rather than in a per CPU workqueue by using the system power efficient workqueue. Signed-off-by: Mark Brown Acked-by: Viresh Kumar diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c index 44621dd..caf8784 100644 --- a/sound/soc/codecs/twl6040.c +++ b/sound/soc/codecs/twl6040.c @@ -429,7 +429,8 @@ static irqreturn_t twl6040_audio_handler(int irq, void *data) struct snd_soc_codec *codec = data; struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); - schedule_delayed_work(&priv->hs_jack.work, msecs_to_jiffies(200)); + queue_delayed_work(system_power_efficient_wq, + &priv->hs_jack.work, msecs_to_jiffies(200)); return IRQ_HANDLED; } -- cgit v0.10.2 From 2c5920a787e68e0a61886d24b05cdbde344c4b0c Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 18 Jul 2013 22:45:40 +0100 Subject: ASoC: wm8350: Use power efficient workqueue The accessory detect debounce work is not performance sensitive so let the scheduler run it wherever is most efficient rather than in a per CPU workqueue by using the system power efficient workqueue. Signed-off-by: Mark Brown Acked-by: Viresh Kumar diff --git a/sound/soc/codecs/wm8350.c b/sound/soc/codecs/wm8350.c index 0e8b3aa..af1318d 100644 --- a/sound/soc/codecs/wm8350.c +++ b/sound/soc/codecs/wm8350.c @@ -1301,7 +1301,8 @@ static irqreturn_t wm8350_hpl_jack_handler(int irq, void *data) if (device_may_wakeup(wm8350->dev)) pm_wakeup_event(wm8350->dev, 250); - schedule_delayed_work(&priv->hpl.work, msecs_to_jiffies(200)); + queue_delayed_work(system_power_efficient_wq, + &priv->hpl.work, msecs_to_jiffies(200)); return IRQ_HANDLED; } @@ -1318,7 +1319,8 @@ static irqreturn_t wm8350_hpr_jack_handler(int irq, void *data) if (device_may_wakeup(wm8350->dev)) pm_wakeup_event(wm8350->dev, 250); - schedule_delayed_work(&priv->hpr.work, msecs_to_jiffies(200)); + queue_delayed_work(system_power_efficient_wq, + &priv->hpr.work, msecs_to_jiffies(200)); return IRQ_HANDLED; } -- cgit v0.10.2 From ec1d648d6c6589986072913a7d45b1cef49eb4b0 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 18 Jul 2013 22:46:24 +0100 Subject: ASoC: wm8753: Use power efficient workqueue The work used to allow the capcitors to ramp is not performance sensitive so let the scheduler run it wherever is most efficient rather than in a per CPU workqueue by using the system power efficient workqueue. Signed-off-by: Mark Brown Acked-by: Viresh Kumar diff --git a/sound/soc/codecs/wm8753.c b/sound/soc/codecs/wm8753.c index 0a4ab4c..d96ebf5 100644 --- a/sound/soc/codecs/wm8753.c +++ b/sound/soc/codecs/wm8753.c @@ -1456,8 +1456,9 @@ static int wm8753_resume(struct snd_soc_codec *codec) if (codec->dapm.suspend_bias_level == SND_SOC_BIAS_ON) { wm8753_set_bias_level(codec, SND_SOC_BIAS_PREPARE); codec->dapm.bias_level = SND_SOC_BIAS_ON; - schedule_delayed_work(&codec->dapm.delayed_work, - msecs_to_jiffies(caps_charge)); + queue_delayed_work(system_power_efficient_wq, + &codec->dapm.delayed_work, + msecs_to_jiffies(caps_charge)); } return 0; -- cgit v0.10.2 From 68defe585f333223f0f3733340136d1b02003062 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 18 Jul 2013 22:47:02 +0100 Subject: ASoC: wm8994: Use power efficient workqueue The accessory detect debounce work is not performance sensitive so let the scheduler run it wherever is most efficient rather than in a per CPU workqueue by using the system power efficient workqueue. Signed-off-by: Mark Brown Acked-by: Viresh Kumar diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index 02c320f..24131a7 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -819,8 +819,9 @@ static int clk_sys_event(struct snd_soc_dapm_widget *w, * don't want false reports. */ if (wm8994->jackdet && !wm8994->clk_has_run) { - schedule_delayed_work(&wm8994->jackdet_bootstrap, - msecs_to_jiffies(1000)); + queue_delayed_work(system_power_efficient_wq, + &wm8994->jackdet_bootstrap, + msecs_to_jiffies(1000)); wm8994->clk_has_run = true; } break; @@ -3487,7 +3488,8 @@ static irqreturn_t wm8994_mic_irq(int irq, void *data) pm_wakeup_event(codec->dev, 300); - schedule_delayed_work(&priv->mic_work, msecs_to_jiffies(250)); + queue_delayed_work(system_power_efficient_wq, + &priv->mic_work, msecs_to_jiffies(250)); return IRQ_HANDLED; } @@ -3575,8 +3577,9 @@ static void wm8958_mic_id(void *data, u16 status) /* If nothing present then clear our statuses */ dev_dbg(codec->dev, "Detected open circuit\n"); - schedule_delayed_work(&wm8994->open_circuit_work, - msecs_to_jiffies(2500)); + queue_delayed_work(system_power_efficient_wq, + &wm8994->open_circuit_work, + msecs_to_jiffies(2500)); return; } @@ -3690,8 +3693,9 @@ static irqreturn_t wm1811_jackdet_irq(int irq, void *data) WM1811_JACKDET_DB, 0); delay = control->pdata.micdet_delay; - schedule_delayed_work(&wm8994->mic_work, - msecs_to_jiffies(delay)); + queue_delayed_work(system_power_efficient_wq, + &wm8994->mic_work, + msecs_to_jiffies(delay)); } else { dev_dbg(codec->dev, "Jack not detected\n"); @@ -3940,8 +3944,9 @@ static irqreturn_t wm8958_mic_irq(int irq, void *data) id_delay = wm8994->wm8994->pdata.mic_id_delay; if (wm8994->mic_detecting) - schedule_delayed_work(&wm8994->mic_complete_work, - msecs_to_jiffies(id_delay)); + queue_delayed_work(system_power_efficient_wq, + &wm8994->mic_complete_work, + msecs_to_jiffies(id_delay)); else wm8958_button_det(codec, reg); -- cgit v0.10.2 From 0a9eaa39db136aaf998d3aa0f7f25c331def336a Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 19 Jul 2013 11:40:13 +0100 Subject: ASoC: fsl_ssi: Provide register I/O functions by default Use the ARM version by default as that's the more generally portable one, it doesn't matter if they work well on random platforms when the goal is only build coverage. Signed-off-by: Mark Brown Acked-by: Timur Tabi diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index c9974a4..e12a997 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -36,7 +36,7 @@ #define read_ssi(addr) in_be32(addr) #define write_ssi(val, addr) out_be32(addr, val) #define write_ssi_mask(addr, clear, set) clrsetbits_be32(addr, clear, set) -#elif defined ARM +#else #define read_ssi(addr) readl(addr) #define write_ssi(val, addr) writel(val, addr) /* -- cgit v0.10.2 From 444fc4b369f9341d2cbcffe2d1ffde4cad5b4945 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Fri, 19 Jul 2013 10:22:21 -0300 Subject: ASoC: wm8962: Do not call configure_bclk() inside wm8962_set_dai_sysclk() Currently after playing any audio file, we get the following error message: $ aplay clarinet.wav Playing WAVE 'clarinet.wav' : Signed 16 bit Little Endian, Rate 44100 Hz, Stereo $ wm8962 0-001a: Unsupported sysclk ratio 544 This error message appears about 5 seconds after the audio playback has finished. Quoting Mark Brown [1]: "The issue here is triggered by the machine switching from the FLL to direct MCLK usage where the MCLK isn't generating a useful ratio. I suspect we should just kill the configure_bclk() in set_sysclk(), that one isn't safe as we can't reconfigure a live SYSCLK and it's probably the one that generates your warnings." Confirmed that the "Unsupported sysclk ratio" error message comes from wm8962_set_dai_sysclk(), so get rid of wm8962_configure_bclk() inside this function. [1] http://mailman.alsa-project.org/pipermail/alsa-devel/2013-July/064241.html Signed-off-by: Fabio Estevam Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index e2de9ec..8b8905d 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c @@ -2621,8 +2621,6 @@ static int wm8962_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, wm8962->sysclk_rate = freq; - wm8962_configure_bclk(codec); - return 0; } -- cgit v0.10.2 From 52f19b14ec18f3166e43cda6a16bb39ffb376053 Mon Sep 17 00:00:00 2001 From: Bo Shen Date: Fri, 19 Jul 2013 17:42:57 +0800 Subject: ASoC: atmel: add wm8904 based audio machine driver Add wm8904 based audio machine driver for Atmel EK board The following ek board based on it - at91sam9n12ek - sama5d3xek (d31, d33, d34, d35) Signed-off-by: Bo Shen Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/sound/atmel-wm8904.txt b/Documentation/devicetree/bindings/sound/atmel-wm8904.txt new file mode 100644 index 0000000..8bbe50c --- /dev/null +++ b/Documentation/devicetree/bindings/sound/atmel-wm8904.txt @@ -0,0 +1,55 @@ +Atmel ASoC driver with wm8904 audio codec complex + +Required properties: + - compatible: "atmel,asoc-wm8904" + - atmel,model: The user-visible name of this sound complex. + - atmel,audio-routing: A list of the connections between audio components. + Each entry is a pair of strings, the first being the connection's sink, + the second being the connection's source. Valid names for sources and + sinks are the WM8904's pins, and the jacks on the board: + + WM8904 pins: + + * IN1L + * IN1R + * IN2L + * IN2R + * IN3L + * IN3R + * HPOUTL + * HPOUTR + * LINEOUTL + * LINEOUTR + * MICBIAS + + Board connectors: + + * Headphone Jack + * Line In Jack + * Mic + + - atmel,ssc-controller: The phandle of the SSC controller + - atmel,audio-codec: The phandle of the WM8904 audio codec + +Optional properties: + - pinctrl-names, pinctrl-0: Please refer to pinctrl-bindings.txt + +Example: +sound { + compatible = "atmel,asoc-wm8904"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_pck0_as_mck>; + + atmel,model = "wm8904 @ AT91SAM9N12EK"; + + atmel,audio-routing = + "Headphone Jack", "HPOUTL", + "Headphone Jack", "HPOUTR", + "IN2L", "Line In Jack", + "IN2R", "Line In Jack", + "Mic", "MICBIAS", + "IN1L", "Mic"; + + atmel,ssc-controller = <&ssc0>; + atmel,audio-codec = <&wm8904>; +}; diff --git a/sound/soc/atmel/Kconfig b/sound/soc/atmel/Kconfig index 1c0b185..986323b 100644 --- a/sound/soc/atmel/Kconfig +++ b/sound/soc/atmel/Kconfig @@ -33,6 +33,16 @@ config SND_AT91_SOC_SAM9G20_WM8731 Say Y if you want to add support for SoC audio on WM8731-based AT91sam9g20 evaluation board. +config SND_ATMEL_SOC_WM8904 + tristate "Atmel ASoC driver for boards using WM8904 codec" + depends on ARCH_AT91 && ATMEL_SSC && SND_ATMEL_SOC + select SND_ATMEL_SOC_SSC + select SND_ATMEL_SOC_DMA + select SND_SOC_WM8904 + help + Say Y if you want to add support for Atmel ASoC driver for boards using + WM8904 codec. + config SND_AT91_SOC_AFEB9260 tristate "SoC Audio support for AFEB9260 board" depends on ARCH_AT91 && ATMEL_SSC && ARCH_AT91 && MACH_AFEB9260 && SND_ATMEL_SOC diff --git a/sound/soc/atmel/Makefile b/sound/soc/atmel/Makefile index 41967cc..922d4da 100644 --- a/sound/soc/atmel/Makefile +++ b/sound/soc/atmel/Makefile @@ -11,6 +11,8 @@ obj-$(CONFIG_SND_ATMEL_SOC_SSC) += snd-soc-atmel_ssc_dai.o # AT91 Machine Support snd-soc-sam9g20-wm8731-objs := sam9g20_wm8731.o +snd-atmel-soc-wm8904-objs := atmel_wm8904.o obj-$(CONFIG_SND_AT91_SOC_SAM9G20_WM8731) += snd-soc-sam9g20-wm8731.o +obj-$(CONFIG_SND_ATMEL_SOC_WM8904) += snd-atmel-soc-wm8904.o obj-$(CONFIG_SND_AT91_SOC_AFEB9260) += snd-soc-afeb9260.o diff --git a/sound/soc/atmel/atmel_wm8904.c b/sound/soc/atmel/atmel_wm8904.c new file mode 100644 index 0000000..7222380 --- /dev/null +++ b/sound/soc/atmel/atmel_wm8904.c @@ -0,0 +1,254 @@ +/* + * atmel_wm8904 - Atmel ASoC driver for boards with WM8904 codec. + * + * Copyright (C) 2012 Atmel + * + * Author: Bo Shen + * + * GPLv2 or later + */ + +#include +#include +#include +#include +#include + +#include + +#include "../codecs/wm8904.h" +#include "atmel_ssc_dai.h" + +#define MCLK_RATE 32768 + +static struct clk *mclk; + +static const struct snd_soc_dapm_widget atmel_asoc_wm8904_dapm_widgets[] = { + SND_SOC_DAPM_HP("Headphone Jack", NULL), + SND_SOC_DAPM_MIC("Mic", NULL), + SND_SOC_DAPM_LINE("Line In Jack", NULL), +}; + +static int atmel_asoc_wm8904_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; + int ret; + + ret = snd_soc_dai_set_pll(codec_dai, WM8904_FLL_MCLK, WM8904_FLL_MCLK, + 32768, params_rate(params) * 256); + if (ret < 0) { + pr_err("%s - failed to set wm8904 codec PLL.", __func__); + return ret; + } + + /* + * As here wm8904 use FLL output as its system clock + * so calling set_sysclk won't care freq parameter + * then we pass 0 + */ + ret = snd_soc_dai_set_sysclk(codec_dai, WM8904_CLK_FLL, + 0, SND_SOC_CLOCK_IN); + if (ret < 0) { + pr_err("%s -failed to set wm8904 SYSCLK\n", __func__); + return ret; + } + + return 0; +} + +static struct snd_soc_ops atmel_asoc_wm8904_ops = { + .hw_params = atmel_asoc_wm8904_hw_params, +}; + +static int atmel_set_bias_level(struct snd_soc_card *card, + struct snd_soc_dapm_context *dapm, + enum snd_soc_bias_level level) +{ + if (dapm->bias_level == SND_SOC_BIAS_STANDBY) { + switch (level) { + case SND_SOC_BIAS_PREPARE: + clk_prepare_enable(mclk); + break; + case SND_SOC_BIAS_OFF: + clk_disable_unprepare(mclk); + break; + default: + break; + } + } + + return 0; +}; + +static struct snd_soc_dai_link atmel_asoc_wm8904_dailink = { + .name = "WM8904", + .stream_name = "WM8904 PCM", + .codec_dai_name = "wm8904-hifi", + .dai_fmt = SND_SOC_DAIFMT_I2S + | SND_SOC_DAIFMT_NB_NF + | SND_SOC_DAIFMT_CBM_CFM, + .ops = &atmel_asoc_wm8904_ops, +}; + +static struct snd_soc_card atmel_asoc_wm8904_card = { + .name = "atmel_asoc_wm8904", + .owner = THIS_MODULE, + .set_bias_level = atmel_set_bias_level, + .dai_link = &atmel_asoc_wm8904_dailink, + .num_links = 1, + .dapm_widgets = atmel_asoc_wm8904_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(atmel_asoc_wm8904_dapm_widgets), + .fully_routed = true, +}; + +static int atmel_asoc_wm8904_dt_init(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct device_node *codec_np, *cpu_np; + struct snd_soc_card *card = &atmel_asoc_wm8904_card; + struct snd_soc_dai_link *dailink = &atmel_asoc_wm8904_dailink; + int ret; + + if (!np) { + dev_err(&pdev->dev, "only device tree supported\n"); + return -EINVAL; + } + + ret = snd_soc_of_parse_card_name(card, "atmel,model"); + if (ret) { + dev_err(&pdev->dev, "failed to parse card name\n"); + return ret; + } + + ret = snd_soc_of_parse_audio_routing(card, "atmel,audio-routing"); + if (ret) { + dev_err(&pdev->dev, "failed to parse audio routing\n"); + return ret; + } + + cpu_np = of_parse_phandle(np, "atmel,ssc-controller", 0); + if (!cpu_np) { + dev_err(&pdev->dev, "failed to get dai and pcm info\n"); + ret = -EINVAL; + return ret; + } + dailink->cpu_of_node = cpu_np; + dailink->platform_of_node = cpu_np; + of_node_put(cpu_np); + + codec_np = of_parse_phandle(np, "atmel,audio-codec", 0); + if (!codec_np) { + dev_err(&pdev->dev, "failed to get codec info\n"); + ret = -EINVAL; + return ret; + } + dailink->codec_of_node = codec_np; + of_node_put(codec_np); + + return 0; +} + +static int atmel_asoc_wm8904_probe(struct platform_device *pdev) +{ + struct snd_soc_card *card = &atmel_asoc_wm8904_card; + struct snd_soc_dai_link *dailink = &atmel_asoc_wm8904_dailink; + struct clk *clk_src; + struct pinctrl *pinctrl; + int id, ret; + + pinctrl = devm_pinctrl_get_select_default(&pdev->dev); + if (IS_ERR(pinctrl)) { + dev_err(&pdev->dev, "failed to request pinctrl\n"); + return PTR_ERR(pinctrl); + } + + card->dev = &pdev->dev; + ret = atmel_asoc_wm8904_dt_init(pdev); + if (ret) { + dev_err(&pdev->dev, "failed to init dt info\n"); + return ret; + } + + id = of_alias_get_id((struct device_node *)dailink->cpu_of_node, "ssc"); + ret = atmel_ssc_set_audio(id); + if (ret != 0) { + dev_err(&pdev->dev, "failed to set SSC %d for audio\n", id); + return ret; + } + + mclk = clk_get(NULL, "pck0"); + if (IS_ERR(mclk)) { + dev_err(&pdev->dev, "failed to get pck0\n"); + ret = PTR_ERR(mclk); + goto err_set_audio; + } + + clk_src = clk_get(NULL, "clk32k"); + if (IS_ERR(clk_src)) { + dev_err(&pdev->dev, "failed to get clk32k\n"); + ret = PTR_ERR(clk_src); + goto err_set_audio; + } + + ret = clk_set_parent(mclk, clk_src); + clk_put(clk_src); + if (ret != 0) { + dev_err(&pdev->dev, "failed to set MCLK parent\n"); + goto err_set_audio; + } + + dev_info(&pdev->dev, "setting pck0 to %dHz\n", MCLK_RATE); + clk_set_rate(mclk, MCLK_RATE); + + ret = snd_soc_register_card(card); + if (ret) { + dev_err(&pdev->dev, "snd_soc_register_card failed\n"); + goto err_set_audio; + } + + return 0; + +err_set_audio: + atmel_ssc_put_audio(id); + return ret; +} + +static int atmel_asoc_wm8904_remove(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + struct snd_soc_dai_link *dailink = &atmel_asoc_wm8904_dailink; + int id; + + id = of_alias_get_id((struct device_node *)dailink->cpu_of_node, "ssc"); + + snd_soc_unregister_card(card); + atmel_ssc_put_audio(id); + + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id atmel_asoc_wm8904_dt_ids[] = { + { .compatible = "atmel,asoc-wm8904", }, + { } +}; +#endif + +static struct platform_driver atmel_asoc_wm8904_driver = { + .driver = { + .name = "atmel-wm8904-audio", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(atmel_asoc_wm8904_dt_ids), + }, + .probe = atmel_asoc_wm8904_probe, + .remove = atmel_asoc_wm8904_remove, +}; + +module_platform_driver(atmel_asoc_wm8904_driver); + +/* Module information */ +MODULE_AUTHOR("Bo Shen "); +MODULE_DESCRIPTION("ALSA SoC machine driver for Atmel EK with WM8904 codec"); +MODULE_LICENSE("GPL"); -- cgit v0.10.2 From 60ea8ca21b4584cebb8163879b50ab3d941090bf Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 19 Jul 2013 16:59:46 +0200 Subject: ALSA: hda - Add snd_hda_jack_detect_state() helper function snd_hda_jack_detect() function returns a boolean value for a jack plugged in or not, but it also returns always true when the corresponding pin is phantom (i.e. fixed). This is OK in most cases, but it makes the generic parser misbehaving about the auto-mute or auto-mic switching, e.g. when one of headphone pins is a fixed. Namely, the driver decides whether to mute the speaker or not, just depending on the headphone plug state: if one of the headphone jacks is seen as active, then the speaker is muted. Thus this will result always in the muted speaker output. So, the problem is the function returns a boolean, after all, although we need to think of "phantom" jack. Now a new function, snd_hda_jack_detect_state() is introduced to return these tristates. The generic parser uses this function for checking the headphone or mic jack states. Meanwhile, the behavior of snd_hda_jack_detect() is kept as is, for keeping compatibility in other driver codes. Acked-by: David Henningsson Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 8e77cbb..f5c2d1f 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -3724,7 +3724,8 @@ static int mux_select(struct hda_codec *codec, unsigned int adc_idx, /* check each pin in the given array; returns true if any of them is plugged */ static bool detect_jacks(struct hda_codec *codec, int num_pins, hda_nid_t *pins) { - int i, present = 0; + int i; + bool present = false; for (i = 0; i < num_pins; i++) { hda_nid_t nid = pins[i]; @@ -3733,7 +3734,8 @@ static bool detect_jacks(struct hda_codec *codec, int num_pins, hda_nid_t *pins) /* don't detect pins retasked as inputs */ if (snd_hda_codec_get_pin_target(codec, nid) & AC_PINCTL_IN_EN) continue; - present |= snd_hda_jack_detect(codec, nid); + if (snd_hda_jack_detect_state(codec, nid) == HDA_JACK_PRESENT) + present = true; } return present; } @@ -3887,7 +3889,7 @@ void snd_hda_gen_mic_autoswitch(struct hda_codec *codec, struct hda_jack_tbl *ja /* don't detect pins retasked as outputs */ if (snd_hda_codec_get_pin_target(codec, pin) & AC_PINCTL_OUT_EN) continue; - if (snd_hda_jack_detect(codec, pin)) { + if (snd_hda_jack_detect_state(codec, pin) == HDA_JACK_PRESENT) { mux_select(codec, 0, spec->am_entry[i].idx); return; } diff --git a/sound/pci/hda/hda_jack.c b/sound/pci/hda/hda_jack.c index 3fd2973..dc93761 100644 --- a/sound/pci/hda/hda_jack.c +++ b/sound/pci/hda/hda_jack.c @@ -194,18 +194,24 @@ u32 snd_hda_pin_sense(struct hda_codec *codec, hda_nid_t nid) EXPORT_SYMBOL_HDA(snd_hda_pin_sense); /** - * snd_hda_jack_detect - query pin Presence Detect status + * snd_hda_jack_detect_state - query pin Presence Detect status * @codec: the CODEC to sense * @nid: the pin NID to sense * - * Query and return the pin's Presence Detect status. + * Query and return the pin's Presence Detect status, as either + * HDA_JACK_NOT_PRESENT, HDA_JACK_PRESENT or HDA_JACK_PHANTOM. */ -int snd_hda_jack_detect(struct hda_codec *codec, hda_nid_t nid) +int snd_hda_jack_detect_state(struct hda_codec *codec, hda_nid_t nid) { - u32 sense = snd_hda_pin_sense(codec, nid); - return get_jack_plug_state(sense); + struct hda_jack_tbl *jack = snd_hda_jack_tbl_get(codec, nid); + if (jack && jack->phantom_jack) + return HDA_JACK_PHANTOM; + else if (snd_hda_pin_sense(codec, nid) & AC_PINSENSE_PRESENCE) + return HDA_JACK_PRESENT; + else + return HDA_JACK_NOT_PRESENT; } -EXPORT_SYMBOL_HDA(snd_hda_jack_detect); +EXPORT_SYMBOL_HDA(snd_hda_jack_detect_state); /** * snd_hda_jack_detect_enable - enable the jack-detection diff --git a/sound/pci/hda/hda_jack.h b/sound/pci/hda/hda_jack.h index ec12abd..379420c 100644 --- a/sound/pci/hda/hda_jack.h +++ b/sound/pci/hda/hda_jack.h @@ -75,7 +75,18 @@ int snd_hda_jack_set_gating_jack(struct hda_codec *codec, hda_nid_t gated_nid, hda_nid_t gating_nid); u32 snd_hda_pin_sense(struct hda_codec *codec, hda_nid_t nid); -int snd_hda_jack_detect(struct hda_codec *codec, hda_nid_t nid); + +/* the jack state returned from snd_hda_jack_detect_state() */ +enum { + HDA_JACK_NOT_PRESENT, HDA_JACK_PRESENT, HDA_JACK_PHANTOM, +}; + +int snd_hda_jack_detect_state(struct hda_codec *codec, hda_nid_t nid); + +static inline bool snd_hda_jack_detect(struct hda_codec *codec, hda_nid_t nid) +{ + return snd_hda_jack_detect_state(codec, nid) != HDA_JACK_NOT_PRESENT; +} bool is_jack_detectable(struct hda_codec *codec, hda_nid_t nid); -- cgit v0.10.2 From b785a492c6eef578520594d5c4d6e9f2cb47cbeb Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Fri, 19 Jul 2013 16:24:59 +0900 Subject: ALSA: replace strict_strto*() with kstrto*() The usage of strict_strto*() is not preferred, because strict_strto*() is obsolete. Thus, kstrto*() should be used. Signed-off-by: Jingoo Han Signed-off-by: Takashi Iwai diff --git a/sound/drivers/dummy.c b/sound/drivers/dummy.c index 11048cc..915b4d7 100644 --- a/sound/drivers/dummy.c +++ b/sound/drivers/dummy.c @@ -1022,7 +1022,7 @@ static void dummy_proc_write(struct snd_info_entry *entry, if (i >= ARRAY_SIZE(fields)) continue; snd_info_get_str(item, ptr, sizeof(item)); - if (strict_strtoull(item, 0, &val)) + if (kstrtoull(item, 0, &val)) continue; if (fields[i].size == sizeof(int)) *get_dummy_int_ptr(dummy, fields[i].offset) = val; diff --git a/sound/pci/hda/hda_hwdep.c b/sound/pci/hda/hda_hwdep.c index ce67608..fe0bda1 100644 --- a/sound/pci/hda/hda_hwdep.c +++ b/sound/pci/hda/hda_hwdep.c @@ -295,7 +295,7 @@ static ssize_t type##_store(struct device *dev, \ struct snd_hwdep *hwdep = dev_get_drvdata(dev); \ struct hda_codec *codec = hwdep->private_data; \ unsigned long val; \ - int err = strict_strtoul(buf, 0, &val); \ + int err = kstrtoul(buf, 0, &val); \ if (err < 0) \ return err; \ codec->type = val; \ @@ -654,7 +654,7 @@ int snd_hda_get_int_hint(struct hda_codec *codec, const char *key, int *valp) p = snd_hda_get_hint(codec, key); if (!p) ret = -ENOENT; - else if (strict_strtoul(p, 0, &val)) + else if (kstrtoul(p, 0, &val)) ret = -EINVAL; else { *valp = val; @@ -751,7 +751,7 @@ static void parse_##name##_mode(char *buf, struct hda_bus *bus, \ struct hda_codec **codecp) \ { \ unsigned long val; \ - if (!strict_strtoul(buf, 0, &val)) \ + if (!kstrtoul(buf, 0, &val)) \ (*codecp)->name = val; \ } diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index e2de9ec..e37c06f 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c @@ -3175,7 +3175,7 @@ static ssize_t wm8962_beep_set(struct device *dev, long int time; int ret; - ret = strict_strtol(buf, 10, &time); + ret = kstrtol(buf, 10, &time); if (ret != 0) return ret; diff --git a/sound/soc/omap/mcbsp.c b/sound/soc/omap/mcbsp.c index eb68c7d..e4980c5 100644 --- a/sound/soc/omap/mcbsp.c +++ b/sound/soc/omap/mcbsp.c @@ -781,7 +781,7 @@ static ssize_t prop##_store(struct device *dev, \ unsigned long val; \ int status; \ \ - status = strict_strtoul(buf, 0, &val); \ + status = kstrtoul(buf, 0, &val); \ if (status) \ return status; \ \ diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 0ec070c..88daa64 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -192,7 +192,7 @@ static ssize_t pmdown_time_set(struct device *dev, struct snd_soc_pcm_runtime *rtd = dev_get_drvdata(dev); int ret; - ret = strict_strtol(buf, 10, &rtd->pmdown_time); + ret = kstrtol(buf, 10, &rtd->pmdown_time); if (ret) return ret; @@ -237,6 +237,7 @@ static ssize_t codec_reg_write_file(struct file *file, char *start = buf; unsigned long reg, value; struct snd_soc_codec *codec = file->private_data; + int ret; buf_size = min(count, (sizeof(buf)-1)); if (copy_from_user(buf, user_buf, buf_size)) @@ -248,8 +249,9 @@ static ssize_t codec_reg_write_file(struct file *file, reg = simple_strtoul(start, &start, 16); while (*start == ' ') start++; - if (strict_strtoul(start, 16, &value)) - return -EINVAL; + ret = kstrtoul(start, 16, &value); + if (ret) + return ret; /* Userspace has been fiddling around behind the kernel's back */ add_taint(TAINT_USER, LOCKDEP_NOW_UNRELIABLE); -- cgit v0.10.2 From f3142807fdb965a7ae1c3a8a6fd91ff92a8efa7a Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Sat, 20 Jul 2013 16:16:01 -0300 Subject: ASoC: fsl: fsl_ssi: Add MODULE_ALIAS Add MODULE_ALIAS, so that auto module loading can work. Signed-off-by: Fabio Estevam Acked-by: Timur Tavi Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index e12a997..11469fe 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -900,6 +900,7 @@ static struct platform_driver fsl_ssi_driver = { module_platform_driver(fsl_ssi_driver); +MODULE_ALIAS("platform:fsl-ssi-dai"); MODULE_AUTHOR("Timur Tabi "); MODULE_DESCRIPTION("Freescale Synchronous Serial Interface (SSI) ASoC Driver"); MODULE_LICENSE("GPL v2"); -- cgit v0.10.2 From 6e20b0d760759fd8c90a7b6ccfd6662e3edb94ed Mon Sep 17 00:00:00 2001 From: Michael Trimarchi Date: Sun, 21 Jul 2013 18:24:01 +0200 Subject: ASoC: omap-mcbsp: Support SND_SOC_DAIFMT_CBM_CFS for omap3/4 Add SND_SOC_DAIFMT_CBM_CFS support for omap3/omap4. The patch was tested on a pandaboard-es board connected to the pcm1792a codec. mcbspx_fsx must configured as output and mcbspx_clkx must be configured as input. Signed-off-by: Michael Trimarchi Acked-by: Peter Ujfalusi Signed-off-by: Mark Brown diff --git a/sound/soc/omap/omap-mcbsp.c b/sound/soc/omap/omap-mcbsp.c index 7483efb..6c19bba 100644 --- a/sound/soc/omap/omap-mcbsp.c +++ b/sound/soc/omap/omap-mcbsp.c @@ -433,6 +433,11 @@ static int omap_mcbsp_dai_set_dai_fmt(struct snd_soc_dai *cpu_dai, /* Sample rate generator drives the FS */ regs->srgr2 |= FSGM; break; + case SND_SOC_DAIFMT_CBM_CFS: + /* McBSP slave. FS clock as output */ + regs->srgr2 |= FSGM; + regs->pcr0 |= FSXM; + break; case SND_SOC_DAIFMT_CBM_CFM: /* McBSP slave */ break; -- cgit v0.10.2 From 72192366f4e1385fe6e44600aa5b75d0136e3d52 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Sun, 21 Jul 2013 13:39:08 -0300 Subject: ASoC: fsl: imx-audmux: Check the return value from clk_prepare_enable() clk_prepare_enable() may fail, so let's check its return value and propagate it in the case of error. Signed-off-by: Fabio Estevam Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/imx-audmux.c b/sound/soc/fsl/imx-audmux.c index e260f1f..1a5da1e 100644 --- a/sound/soc/fsl/imx-audmux.c +++ b/sound/soc/fsl/imx-audmux.c @@ -73,8 +73,11 @@ static ssize_t audmux_read_file(struct file *file, char __user *user_buf, if (!buf) return -ENOMEM; - if (audmux_clk) - clk_prepare_enable(audmux_clk); + if (audmux_clk) { + ret = clk_prepare_enable(audmux_clk); + if (ret) + return ret; + } ptcr = readl(audmux_base + IMX_AUDMUX_V2_PTCR(port)); pdcr = readl(audmux_base + IMX_AUDMUX_V2_PDCR(port)); @@ -224,14 +227,19 @@ EXPORT_SYMBOL_GPL(imx_audmux_v1_configure_port); int imx_audmux_v2_configure_port(unsigned int port, unsigned int ptcr, unsigned int pdcr) { + int ret; + if (audmux_type != IMX31_AUDMUX) return -EINVAL; if (!audmux_base) return -ENOSYS; - if (audmux_clk) - clk_prepare_enable(audmux_clk); + if (audmux_clk) { + ret = clk_prepare_enable(audmux_clk); + if (ret) + return ret; + } writel(ptcr, audmux_base + IMX_AUDMUX_V2_PTCR(port)); writel(pdcr, audmux_base + IMX_AUDMUX_V2_PDCR(port)); -- cgit v0.10.2 From a06e427d088d8a9b81defd42e6bae5f1cd69fc3f Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 18 Jul 2013 22:44:03 +0100 Subject: ASoC: twl6040: Use power efficient workqueue The accessory detect debounce work is not performance sensitive so let the scheduler run it wherever is most efficient rather than in a per CPU workqueue by using the system power efficient workqueue. Signed-off-by: Mark Brown Acked-by: Viresh Kumar Acked-by: Peter Ujfalusi diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c index 44621dd..caf8784 100644 --- a/sound/soc/codecs/twl6040.c +++ b/sound/soc/codecs/twl6040.c @@ -429,7 +429,8 @@ static irqreturn_t twl6040_audio_handler(int irq, void *data) struct snd_soc_codec *codec = data; struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); - schedule_delayed_work(&priv->hs_jack.work, msecs_to_jiffies(200)); + queue_delayed_work(system_power_efficient_wq, + &priv->hs_jack.work, msecs_to_jiffies(200)); return IRQ_HANDLED; } -- cgit v0.10.2 From 204f029155e7da98b59e6969cf29e210bbe84de5 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 19 Jul 2013 12:10:18 +0100 Subject: ASoC: mxs: Remove unneeded mach-types.h inclusions Signed-off-by: Mark Brown Acked-by: Shawn Guo diff --git a/sound/soc/mxs/mxs-saif.c b/sound/soc/mxs/mxs-saif.c index 54511c5..b56b8a0 100644 --- a/sound/soc/mxs/mxs-saif.c +++ b/sound/soc/mxs/mxs-saif.c @@ -31,7 +31,6 @@ #include #include #include -#include #include "mxs-saif.h" diff --git a/sound/soc/mxs/mxs-sgtl5000.c b/sound/soc/mxs/mxs-sgtl5000.c index 1b134d7..b2e372d 100644 --- a/sound/soc/mxs/mxs-sgtl5000.c +++ b/sound/soc/mxs/mxs-sgtl5000.c @@ -25,7 +25,6 @@ #include #include #include -#include #include "../codecs/sgtl5000.h" #include "mxs-saif.h" -- cgit v0.10.2 From da72c9619f4df033d431a0a4cee715cf14c78433 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 18 Jul 2013 22:46:46 +0100 Subject: ASoC: wm8962: Use power efficient workqueue The accessory detect debounce work is not performance sensitive so let the scheduler run it wherever is most efficient rather than in a per CPU workqueue by using the system power efficient workqueue. Signed-off-by: Mark Brown Acked-by: Viresh Kumar diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index 8b8905d..36782f0 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c @@ -3044,8 +3044,9 @@ static irqreturn_t wm8962_irq(int irq, void *data) pm_wakeup_event(dev, 300); - schedule_delayed_work(&wm8962->mic_work, - msecs_to_jiffies(250)); + queue_delayed_work(system_power_efficient_wq, + &wm8962->mic_work, + msecs_to_jiffies(250)); } return IRQ_HANDLED; -- cgit v0.10.2 From 315d9c649f15413abebea2212bc5bc8915fc7d2a Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 22 Jul 2013 12:17:31 +0100 Subject: ASoC: mxs: Depends on COMMON_CLK The SAIF driver is a clock provider so specifically needs the common clock implementedation. Reported-by: Fengguang Wu Signed-off-by: Mark Brown Acked-by: Shawn Guo diff --git a/sound/soc/mxs/Kconfig b/sound/soc/mxs/Kconfig index 7daf860..219235c 100644 --- a/sound/soc/mxs/Kconfig +++ b/sound/soc/mxs/Kconfig @@ -1,6 +1,7 @@ menuconfig SND_MXS_SOC tristate "SoC Audio for Freescale MXS CPUs" depends on ARCH_MXS || COMPILE_TEST + depends on COMMON_CLK select SND_SOC_GENERIC_DMAENGINE_PCM help Say Y or M if you want to add support for codecs attached to -- cgit v0.10.2 From 5f6e7d52c4959019d12a7deebbde548884a917d1 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 23 Jul 2013 11:12:25 +0200 Subject: ASoC: Remove unused dapm_get_snd_card() and dapm_get_soc_card() These two functions were added two years ago in commit 4805608 ("ASoC: dapm - Add methods to retrieve snd_card and soc_card from dapm context.") but have remained unused so far. Considering that the dapm context actually has a direct pointer to the card the functions also seem to be unnecessary. E.g. the expressions 'dapm_get_soc_card(dapm)' and 'dapm->card' yield the same result. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index b941908..93ea5d9 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -174,36 +174,6 @@ static inline struct snd_soc_dapm_widget *dapm_cnew_widget( return kmemdup(_widget, sizeof(*_widget), GFP_KERNEL); } -/* get snd_card from DAPM context */ -static inline struct snd_card *dapm_get_snd_card( - struct snd_soc_dapm_context *dapm) -{ - if (dapm->codec) - return dapm->codec->card->snd_card; - else if (dapm->platform) - return dapm->platform->card->snd_card; - else - BUG(); - - /* unreachable */ - return NULL; -} - -/* get soc_card from DAPM context */ -static inline struct snd_soc_card *dapm_get_soc_card( - struct snd_soc_dapm_context *dapm) -{ - if (dapm->codec) - return dapm->codec->card; - else if (dapm->platform) - return dapm->platform->card; - else - BUG(); - - /* unreachable */ - return NULL; -} - static void dapm_reset(struct snd_soc_card *card) { struct snd_soc_dapm_widget *w; -- cgit v0.10.2 From af2d8b5d95f12c36bfabe90c0879923efedefd2c Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 22 Jul 2013 18:49:52 +0200 Subject: ASoC: Build adau1701 when SND_SOC_ALL_CODECS is selected The adau1701 driver was removed from SND_SOC_ALL_CODECS in commit commit ee8c7e9 ("ASoC: Remove adau1701 from SND_SOC_ALL_CODECS due to Sigma dependency") due to the dependency on the SigmaDSP firmware loader which was only available on a limited set of platforms. This was fixed quite some time ago in commit 40216ce7 ("ASoC: Move SigmaDSP firmware loader to ASoC") though, so we can add the driver back again to SND_SOC_ALL_CODECS. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index badb6fb..adddb39 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -20,6 +20,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_AD73311 select SND_SOC_ADAU1373 if I2C select SND_SOC_ADAV80X if SND_SOC_I2C_AND_SPI + select SND_SOC_ADAU1701 if I2C select SND_SOC_ADS117X select SND_SOC_AK4104 if SPI_MASTER select SND_SOC_AK4535 if I2C -- cgit v0.10.2 From 96b9bc6174a030691a4a60b0117ba7718d2bb27a Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 22 Jul 2013 18:49:53 +0200 Subject: ASoC: adau1701: Add adau1702 and adau1401(a) device ids Both the adau1702 and the adau1401(a) are register compatible to the adau1701, so add them to adau1701 driver's device id table. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/adau1701.c b/sound/soc/codecs/adau1701.c index d1124a5..44d8a95 100644 --- a/sound/soc/codecs/adau1701.c +++ b/sound/soc/codecs/adau1701.c @@ -734,7 +734,10 @@ static int adau1701_i2c_remove(struct i2c_client *client) } static const struct i2c_device_id adau1701_i2c_id[] = { + { "adau1401", 0 }, + { "adau1401a", 0 }, { "adau1701", 0 }, + { "adau1702", 0 }, { } }; MODULE_DEVICE_TABLE(i2c, adau1701_i2c_id); -- cgit v0.10.2 From 9bfb2844a2f9e6eab52aed1eca0d03f4398c755f Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 24 Jul 2013 14:31:50 +0200 Subject: ALSA: hda/realtek - Selectively call snd_hda_shutup_pins() Instead of calling snd_hda_shutup_pins() unconditionally, allow it be called in spec->shutup callback. In this way, we can avoid calling this function if it causes a problem like we see in the next patch following this. Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 8bd2261..dbd59df 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -282,6 +282,7 @@ static void alc_eapd_shutup(struct hda_codec *codec) { alc_auto_setup_eapd(codec, false); msleep(200); + snd_hda_shutup_pins(codec); } /* generic EAPD initialization */ @@ -826,7 +827,8 @@ static inline void alc_shutup(struct hda_codec *codec) if (spec && spec->shutup) spec->shutup(codec); - snd_hda_shutup_pins(codec); + else + snd_hda_shutup_pins(codec); } #define alc_free snd_hda_gen_free @@ -2573,15 +2575,13 @@ static void alc269_shutup(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - if (spec->codec_variant != ALC269_TYPE_ALC269VB) - return; - if (spec->codec_variant == ALC269_TYPE_ALC269VB) alc269vb_toggle_power_output(codec, 0); if (spec->codec_variant == ALC269_TYPE_ALC269VB && (alc_get_coef0(codec) & 0x00ff) == 0x018) { msleep(150); } + snd_hda_shutup_pins(codec); } static void alc5505_coef_set(struct hda_codec *codec, unsigned int index_reg, -- cgit v0.10.2 From c5177c861e2bae584996f60667dc7b291ba6600a Mon Sep 17 00:00:00 2001 From: Kailang Yang Date: Wed, 24 Jul 2013 14:39:49 +0200 Subject: ALSA: hda - Fix the noise after suspend on ALC283 codec When the power state of ALC283 codec goes to D3, it gives a noise via headphone output. This is because the driver tries to clear all pins via snd_hda_shutup_pins(). Setting the mic pin to zero triggers such a noise. Define a new shutup call specific to this codec and control the pins there more precisely. Also, add the power-save enable/disable sequences in the resume and the new shutup calls. Signed-off-by: Kailang Yang Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index dbd59df..04a69e3 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -2712,6 +2712,13 @@ static int alc269_resume(struct hda_codec *codec) hda_call_check_power_status(codec, 0x01); if (spec->has_alc5505_dsp) alc5505_dsp_resume(codec); + + /* clear the power-save mode for ALC283 */ + if (codec->vendor_id == 0x10ec0283) { + alc_write_coef_idx(codec, 0x4, 0xaf01); + alc_write_coef_idx(codec, 0x6, 0x2104); + } + return 0; } #endif /* CONFIG_PM */ @@ -3775,6 +3782,30 @@ static void alc269_fill_coef(struct hda_codec *codec) alc_write_coef_idx(codec, 0x4, val | (1<<11)); } +/* don't clear mic pin; otherwise it results in noise in D3 */ +static void alc283_headset_shutup(struct hda_codec *codec) +{ + int i; + + if (codec->bus->shutdown) + return; + + for (i = 0; i < codec->init_pins.used; i++) { + struct hda_pincfg *pin = snd_array_elem(&codec->init_pins, i); + /* use read here for syncing after issuing each verb */ + if (pin->nid != 0x19) + snd_hda_codec_read(codec, pin->nid, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, 0); + } + + alc_write_coef_idx(codec, 0x4, 0x0f01); /* power save */ + alc_write_coef_idx(codec, 0x6, 0x2100); /* power save */ + snd_hda_codec_write(codec, 0x19, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, + PIN_VREFHIZ); + codec->pins_shutup = 1; +} + /* */ static int patch_alc269(struct hda_codec *codec) @@ -3789,6 +3820,9 @@ static int patch_alc269(struct hda_codec *codec) spec = codec->spec; spec->gen.shared_mic_vref_pin = 0x18; + if (codec->vendor_id == 0x10ec0283) + spec->shutup = alc283_headset_shutup; + snd_hda_pick_fixup(codec, alc269_fixup_models, alc269_fixup_tbl, alc269_fixups); snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); @@ -3862,7 +3896,8 @@ static int patch_alc269(struct hda_codec *codec) codec->patch_ops.suspend = alc269_suspend; codec->patch_ops.resume = alc269_resume; #endif - spec->shutup = alc269_shutup; + if (!spec->shutup) + spec->shutup = alc269_shutup; snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); -- cgit v0.10.2 From 63c69a6e4134a2085d40e40c02a395dd1bd8c023 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 18 Jul 2013 22:03:01 +0100 Subject: ASoC: dapm: Use generic power check for everything except DAIs As noticed by Lars-Peter Clausen since the move to using widgets to hook into the DAIs we no longer directly manage the power of AIF or DAC/ADC widgets from the stream integration so they can just use the generic power checks instead of the custom stream integration ones they currently do. Signed-off-by: Mark Brown diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index bd16010..3786558 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -3126,16 +3126,16 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm, case snd_soc_dapm_value_mux: w->power_check = dapm_generic_check_power; break; - case snd_soc_dapm_adc: - case snd_soc_dapm_aif_out: case snd_soc_dapm_dai_out: w->power_check = dapm_adc_check_power; break; - case snd_soc_dapm_dac: - case snd_soc_dapm_aif_in: case snd_soc_dapm_dai_in: w->power_check = dapm_dac_check_power; break; + case snd_soc_dapm_adc: + case snd_soc_dapm_aif_out: + case snd_soc_dapm_dac: + case snd_soc_dapm_aif_in: case snd_soc_dapm_pga: case snd_soc_dapm_out_drv: case snd_soc_dapm_input: -- cgit v0.10.2 From c3f48ae6fd5a1ebdcaff5efe35f88f31daaee225 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 24 Jul 2013 15:27:36 +0200 Subject: ASoC: dapm: Pass snd_soc_card directly to soc_dpcm_runtime_update() soc_dpcm_runtime_update() operates on a ASoC card as a whole. Currently it takes a snd_soc_dapm_widget as its only parameter though. The widget is then used to look up the card and is otherwise unused. This patch changes the function to take a pointer to the card directly. This makes it possible to to call soc_dpcm_runtime_update() for updates which are not related to one specific widget. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/include/sound/soc-dpcm.h b/include/sound/soc-dpcm.h index 04598f1..047d657 100644 --- a/include/sound/soc-dpcm.h +++ b/include/sound/soc-dpcm.h @@ -133,6 +133,6 @@ void snd_soc_dpcm_be_set_state(struct snd_soc_pcm_runtime *be, int stream, /* internal use only */ int soc_dpcm_be_digital_mute(struct snd_soc_pcm_runtime *fe, int mute); int soc_dpcm_debugfs_add(struct snd_soc_pcm_runtime *rtd); -int soc_dpcm_runtime_update(struct snd_soc_dapm_widget *); +int soc_dpcm_runtime_update(struct snd_soc_card *); #endif diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 3786558..8d9c09b 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -1986,7 +1986,7 @@ int snd_soc_dapm_mux_update_power(struct snd_soc_dapm_widget *widget, ret = soc_dapm_mux_update_power(widget, kcontrol, mux, e); mutex_unlock(&card->dapm_mutex); if (ret > 0) - soc_dpcm_runtime_update(widget); + soc_dpcm_runtime_update(card); return ret; } EXPORT_SYMBOL_GPL(snd_soc_dapm_mux_update_power); @@ -2032,7 +2032,7 @@ int snd_soc_dapm_mixer_update_power(struct snd_soc_dapm_widget *widget, ret = soc_dapm_mixer_update_power(widget, kcontrol, connect); mutex_unlock(&card->dapm_mutex); if (ret > 0) - soc_dpcm_runtime_update(widget); + soc_dpcm_runtime_update(card); return ret; } EXPORT_SYMBOL_GPL(snd_soc_dapm_mixer_update_power); diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index b6c6403..5c2c662 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -1832,18 +1832,10 @@ static int dpcm_run_old_update(struct snd_soc_pcm_runtime *fe, int stream) /* Called by DAPM mixer/mux changes to update audio routing between PCMs and * any DAI links. */ -int soc_dpcm_runtime_update(struct snd_soc_dapm_widget *widget) +int soc_dpcm_runtime_update(struct snd_soc_card *card) { - struct snd_soc_card *card; int i, old, new, paths; - if (widget->codec) - card = widget->codec->card; - else if (widget->platform) - card = widget->platform->card; - else - return -EINVAL; - mutex_lock_nested(&card->mutex, SND_SOC_CARD_CLASS_RUNTIME); for (i = 0; i < card->num_rtd; i++) { struct snd_soc_dapm_widget_list *list; -- cgit v0.10.2 From ce6cfaf1de136cd3e6ed7c0ed984be8d003a58c1 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 24 Jul 2013 15:27:37 +0200 Subject: ASoC: dapm: Run widget updates for shared controls at the same time Currently when updating a control that is shared between multiple widgets the whole power-up/power-down sequence is being run once for each widget. The control register is updated during the first run, which means the CODEC internal routing is also updated for all widgets during this first run. The input and output paths for each widgets are only updated though during the respective run for that widget. This leads to a slight inconsistency between the CODEC's internal state and ASoC's state, which causes non optimal behavior in regard to click and pop avoidance. E.g. consider the following setup where two MUXs share the same control. +------+ A1 ------| | | MUX1 |----- C1 B1 ------| | +------+ | control ---+ | +------+ A2 ------| | | MUX2 |----- C2 B2 ------| | +------+ If the control is updated to switch the MUXs from input A to input B with the current code the power-up/power-down sequence will look like this: Run soc_dapm_mux_update_power for MUX1 Power-down A1 Update MUXing Power-up B1 Run soc_dapm_mux_update_power for MUX2 Power-down A2 (Update MUXing) Power-up B2 Note that the second 'Update Muxing' is a no-op, since the register was already updated. While the preferred order for avoiding pops and clicks should be: Run soc_dapm_mux_update_power for control Power-down A1 Power-down A2 Update MUXing Power-up B1 Power-up B2 This patch changes the behavior to the later by running the updates for all widgets that the control is attached to at the same time. The new code is also a bit simpler since callers of soc_dapm_{mux,muxer}_update_power don't have to loop over each widget anymore and neither do we need to keep track for which of the kcontrol's widgets the current update is. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 3e479f4..3717ad0 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -391,10 +391,10 @@ void snd_soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd, int stream, void snd_soc_dapm_shutdown(struct snd_soc_card *card); /* external DAPM widget events */ -int snd_soc_dapm_mixer_update_power(struct snd_soc_dapm_widget *widget, +int snd_soc_dapm_mixer_update_power(struct snd_soc_dapm_context *dapm, struct snd_kcontrol *kcontrol, int connect); -int snd_soc_dapm_mux_update_power(struct snd_soc_dapm_widget *widget, - struct snd_kcontrol *kcontrol, int mux, struct soc_enum *e); +int snd_soc_dapm_mux_update_power(struct snd_soc_dapm_context *dapm, + struct snd_kcontrol *kcontrol, int mux, struct soc_enum *e); /* dapm sys fs - used by the core */ int snd_soc_dapm_sys_add(struct device *dev); @@ -559,7 +559,6 @@ struct snd_soc_dapm_widget { }; struct snd_soc_dapm_update { - struct snd_soc_dapm_widget *widget; struct snd_kcontrol *kcontrol; int reg; int mask; diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 8d9c09b..6f8a01b 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -1456,34 +1456,45 @@ static void dapm_seq_run(struct snd_soc_dapm_context *dapm, static void dapm_widget_update(struct snd_soc_dapm_context *dapm) { struct snd_soc_dapm_update *update = dapm->update; - struct snd_soc_dapm_widget *w; + struct snd_soc_dapm_widget_list *wlist; + struct snd_soc_dapm_widget *w = NULL; + unsigned int wi; int ret; if (!update) return; - w = update->widget; + wlist = snd_kcontrol_chip(update->kcontrol); - if (w->event && - (w->event_flags & SND_SOC_DAPM_PRE_REG)) { - ret = w->event(w, update->kcontrol, SND_SOC_DAPM_PRE_REG); - if (ret != 0) - dev_err(dapm->dev, "ASoC: %s DAPM pre-event failed: %d\n", - w->name, ret); + for (wi = 0; wi < wlist->num_widgets; wi++) { + w = wlist->widgets[wi]; + + if (w->event && (w->event_flags & SND_SOC_DAPM_PRE_REG)) { + ret = w->event(w, update->kcontrol, SND_SOC_DAPM_PRE_REG); + if (ret != 0) + dev_err(dapm->dev, "ASoC: %s DAPM pre-event failed: %d\n", + w->name, ret); + } } + if (!w) + return; + ret = soc_widget_update_bits_locked(w, update->reg, update->mask, update->val); if (ret < 0) dev_err(dapm->dev, "ASoC: %s DAPM update failed: %d\n", w->name, ret); - if (w->event && - (w->event_flags & SND_SOC_DAPM_POST_REG)) { - ret = w->event(w, update->kcontrol, SND_SOC_DAPM_POST_REG); - if (ret != 0) - dev_err(dapm->dev, "ASoC: %s DAPM post-event failed: %d\n", - w->name, ret); + for (wi = 0; wi < wlist->num_widgets; wi++) { + w = wlist->widgets[wi]; + + if (w->event && (w->event_flags & SND_SOC_DAPM_POST_REG)) { + ret = w->event(w, update->kcontrol, SND_SOC_DAPM_POST_REG); + if (ret != 0) + dev_err(dapm->dev, "ASoC: %s DAPM post-event failed: %d\n", + w->name, ret); + } } } @@ -1936,19 +1947,14 @@ static inline void dapm_debugfs_cleanup(struct snd_soc_dapm_context *dapm) #endif /* test and update the power status of a mux widget */ -static int soc_dapm_mux_update_power(struct snd_soc_dapm_widget *widget, +static int soc_dapm_mux_update_power(struct snd_soc_dapm_context *dapm, struct snd_kcontrol *kcontrol, int mux, struct soc_enum *e) { struct snd_soc_dapm_path *path; int found = 0; - if (widget->id != snd_soc_dapm_mux && - widget->id != snd_soc_dapm_virt_mux && - widget->id != snd_soc_dapm_value_mux) - return -ENODEV; - /* find dapm widget path assoc with kcontrol */ - list_for_each_entry(path, &widget->dapm->card->paths, list) { + list_for_each_entry(path, &dapm->card->paths, list) { if (path->kcontrol != kcontrol) continue; @@ -1966,24 +1972,23 @@ static int soc_dapm_mux_update_power(struct snd_soc_dapm_widget *widget, "mux disconnection"); path->connect = 0; /* old connection must be powered down */ } + dapm_mark_dirty(path->sink, "mux change"); } - if (found) { - dapm_mark_dirty(widget, "mux change"); - dapm_power_widgets(widget->dapm, SND_SOC_DAPM_STREAM_NOP); - } + if (found) + dapm_power_widgets(dapm, SND_SOC_DAPM_STREAM_NOP); return found; } -int snd_soc_dapm_mux_update_power(struct snd_soc_dapm_widget *widget, - struct snd_kcontrol *kcontrol, int mux, struct soc_enum *e) +int snd_soc_dapm_mux_update_power(struct snd_soc_dapm_context *dapm, + struct snd_kcontrol *kcontrol, int mux, struct soc_enum *e) { - struct snd_soc_card *card = widget->dapm->card; + struct snd_soc_card *card = dapm->card; int ret; mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); - ret = soc_dapm_mux_update_power(widget, kcontrol, mux, e); + ret = soc_dapm_mux_update_power(dapm, kcontrol, mux, e); mutex_unlock(&card->dapm_mutex); if (ret > 0) soc_dpcm_runtime_update(card); @@ -1992,19 +1997,14 @@ int snd_soc_dapm_mux_update_power(struct snd_soc_dapm_widget *widget, EXPORT_SYMBOL_GPL(snd_soc_dapm_mux_update_power); /* test and update the power status of a mixer or switch widget */ -static int soc_dapm_mixer_update_power(struct snd_soc_dapm_widget *widget, +static int soc_dapm_mixer_update_power(struct snd_soc_dapm_context *dapm, struct snd_kcontrol *kcontrol, int connect) { struct snd_soc_dapm_path *path; int found = 0; - if (widget->id != snd_soc_dapm_mixer && - widget->id != snd_soc_dapm_mixer_named_ctl && - widget->id != snd_soc_dapm_switch) - return -ENODEV; - /* find dapm widget path assoc with kcontrol */ - list_for_each_entry(path, &widget->dapm->card->paths, list) { + list_for_each_entry(path, &dapm->card->paths, list) { if (path->kcontrol != kcontrol) continue; @@ -2012,24 +2012,23 @@ static int soc_dapm_mixer_update_power(struct snd_soc_dapm_widget *widget, found = 1; path->connect = connect; dapm_mark_dirty(path->source, "mixer connection"); + dapm_mark_dirty(path->sink, "mixer update"); } - if (found) { - dapm_mark_dirty(widget, "mixer update"); - dapm_power_widgets(widget->dapm, SND_SOC_DAPM_STREAM_NOP); - } + if (found) + dapm_power_widgets(dapm, SND_SOC_DAPM_STREAM_NOP); return found; } -int snd_soc_dapm_mixer_update_power(struct snd_soc_dapm_widget *widget, - struct snd_kcontrol *kcontrol, int connect) +int snd_soc_dapm_mixer_update_power(struct snd_soc_dapm_context *dapm, + struct snd_kcontrol *kcontrol, int connect) { - struct snd_soc_card *card = widget->dapm->card; + struct snd_soc_card *card = dapm->card; int ret; mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); - ret = soc_dapm_mixer_update_power(widget, kcontrol, connect); + ret = soc_dapm_mixer_update_power(dapm, kcontrol, connect); mutex_unlock(&card->dapm_mutex); if (ret > 0) soc_dpcm_runtime_update(card); @@ -2695,7 +2694,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; if (snd_soc_volsw_is_stereo(mc)) dev_warn(widget->dapm->dev, @@ -2714,22 +2712,16 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, change = snd_soc_test_bits(widget->codec, reg, mask, val); if (change) { - for (wi = 0; wi < wlist->num_widgets; wi++) { - widget = wlist->widgets[wi]; - - widget->value = val; + update.kcontrol = kcontrol; + update.reg = reg; + update.mask = mask; + update.val = val; - update.kcontrol = kcontrol; - update.widget = widget; - update.reg = reg; - update.mask = mask; - update.val = val; - widget->dapm->update = &update; + widget->dapm->update = &update; - soc_dapm_mixer_update_power(widget, kcontrol, connect); + soc_dapm_mixer_update_power(widget->dapm, kcontrol, connect); - widget->dapm->update = NULL; - } + widget->dapm->update = NULL; } mutex_unlock(&card->dapm_mutex); @@ -2784,7 +2776,6 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, 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; @@ -2802,22 +2793,17 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, 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; - widget->value = val; + update.kcontrol = kcontrol; + 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; + soc_dapm_mux_update_power(widget->dapm, kcontrol, mux, e); - soc_dapm_mux_update_power(widget, kcontrol, mux, e); - - widget->dapm->update = NULL; - } + widget->dapm->update = NULL; } mutex_unlock(&card->dapm_mutex); @@ -2861,7 +2847,6 @@ int snd_soc_dapm_put_enum_virt(struct snd_kcontrol *kcontrol, struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; int change; - int wi; if (ucontrol->value.enumerated.item[0] >= e->max) return -EINVAL; @@ -2870,13 +2855,8 @@ int snd_soc_dapm_put_enum_virt(struct snd_kcontrol *kcontrol, 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]; - - soc_dapm_mux_update_power(widget, kcontrol, widget->value, e); - } + widget->value = ucontrol->value.enumerated.item[0]; + soc_dapm_mux_update_power(widget->dapm, kcontrol, widget->value, e); } mutex_unlock(&card->dapm_mutex); @@ -2949,7 +2929,6 @@ int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol, 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; @@ -2967,22 +2946,17 @@ int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol, 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; + 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.reg = e->reg; + update.mask = mask; + update.val = val; + widget->dapm->update = &update; - soc_dapm_mux_update_power(widget, kcontrol, mux, e); + soc_dapm_mux_update_power(widget->dapm, kcontrol, mux, e); - widget->dapm->update = NULL; - } + widget->dapm->update = NULL; } mutex_unlock(&card->dapm_mutex); -- cgit v0.10.2 From 6b3fc03b3b614ced09df96ca60ab6f627d8c240c Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 24 Jul 2013 15:27:38 +0200 Subject: ASoC: dapm: Add a update parameter to snd_soc_dapm_{mux,mixer}_update_power In order to avoid race conditions the assignment of dapm->update should happen while card->dapm_mutex is being held. To allow CODEC drivers to run a register update when using snd_soc_dapm_mux_update_power() or snd_soc_dapm_mixer_update_power() add a update parameter to these two functions. The update parameter will be assigned to dapm->update while card->dapm_mutex is locked. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 3717ad0..e77c6f5 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -333,6 +333,7 @@ struct snd_soc_dapm_route; struct snd_soc_dapm_context; struct regulator; struct snd_soc_dapm_widget_list; +struct snd_soc_dapm_update; int dapm_reg_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event); @@ -392,9 +393,11 @@ void snd_soc_dapm_shutdown(struct snd_soc_card *card); /* external DAPM widget events */ int snd_soc_dapm_mixer_update_power(struct snd_soc_dapm_context *dapm, - struct snd_kcontrol *kcontrol, int connect); + struct snd_kcontrol *kcontrol, int connect, + struct snd_soc_dapm_update *update); int snd_soc_dapm_mux_update_power(struct snd_soc_dapm_context *dapm, - struct snd_kcontrol *kcontrol, int mux, struct soc_enum *e); + struct snd_kcontrol *kcontrol, int mux, struct soc_enum *e, + struct snd_soc_dapm_update *update); /* dapm sys fs - used by the core */ int snd_soc_dapm_sys_add(struct device *dev); diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 6f8a01b..7587611 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -1982,13 +1982,16 @@ static int soc_dapm_mux_update_power(struct snd_soc_dapm_context *dapm, } int snd_soc_dapm_mux_update_power(struct snd_soc_dapm_context *dapm, - struct snd_kcontrol *kcontrol, int mux, struct soc_enum *e) + struct snd_kcontrol *kcontrol, int mux, struct soc_enum *e, + struct snd_soc_dapm_update *update) { struct snd_soc_card *card = dapm->card; int ret; mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); + dapm->update = update; ret = soc_dapm_mux_update_power(dapm, kcontrol, mux, e); + dapm->update = NULL; mutex_unlock(&card->dapm_mutex); if (ret > 0) soc_dpcm_runtime_update(card); @@ -2022,13 +2025,16 @@ static int soc_dapm_mixer_update_power(struct snd_soc_dapm_context *dapm, } int snd_soc_dapm_mixer_update_power(struct snd_soc_dapm_context *dapm, - struct snd_kcontrol *kcontrol, int connect) + struct snd_kcontrol *kcontrol, int connect, + struct snd_soc_dapm_update *update) { struct snd_soc_card *card = dapm->card; int ret; mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); + dapm->update = update; ret = soc_dapm_mixer_update_power(dapm, kcontrol, connect); + dapm->update = NULL; mutex_unlock(&card->dapm_mutex); if (ret > 0) soc_dpcm_runtime_update(card); -- cgit v0.10.2 From 5d99d778495cb02eafd38292f462c4466fc7189f Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 24 Jul 2013 15:27:39 +0200 Subject: ASoC: tlv320aic3x: Use snd_soc_dapm_mixer_update_power Use snd_soc_dapm_mixer_update_power() instead of reimplementing its functionality. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c index e5b9268..1325c0c 100644 --- a/sound/soc/codecs/tlv320aic3x.c +++ b/sound/soc/codecs/tlv320aic3x.c @@ -147,10 +147,9 @@ static int snd_soc_dapm_put_volsw_aic3x(struct snd_kcontrol *kcontrol, int max = mc->max; unsigned int mask = (1 << fls(max)) - 1; unsigned int invert = mc->invert; - unsigned short val, val_mask; - int ret; - struct snd_soc_dapm_path *path; - int found = 0; + unsigned short val; + struct snd_soc_dapm_update update; + int connect, change; val = (ucontrol->value.integer.value[0] & mask); @@ -158,42 +157,26 @@ static int snd_soc_dapm_put_volsw_aic3x(struct snd_kcontrol *kcontrol, if (val) val = mask; + connect = !!val; + if (invert) val = mask - val; - val_mask = mask << shift; - val = val << shift; - - mutex_lock(&widget->codec->mutex); - if (snd_soc_test_bits(widget->codec, reg, val_mask, val)) { - /* find dapm widget path assoc with kcontrol */ - list_for_each_entry(path, &widget->dapm->card->paths, list) { - if (path->kcontrol != kcontrol) - continue; + mask <<= shift; + val <<= shift; - /* found, now check type */ - found = 1; - if (val) - /* new connection */ - path->connect = invert ? 0 : 1; - else - /* old connection must be powered down */ - path->connect = invert ? 1 : 0; + change = snd_soc_test_bits(widget->codec, val, mask, reg); + if (change) { + update.kcontrol = kcontrol; + update.reg = reg; + update.mask = mask; + update.val = val; - dapm_mark_dirty(path->source, "tlv320aic3x source"); - dapm_mark_dirty(path->sink, "tlv320aic3x sink"); - - break; - } + snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, connect, + &update); } - mutex_unlock(&widget->codec->mutex); - - if (found) - snd_soc_dapm_sync(widget->dapm); - - ret = snd_soc_update_bits_locked(widget->codec, reg, val_mask, val); - return ret; + return change; } /* -- cgit v0.10.2 From da7db6ad4da05a3109d0a31100e1ecd746a90fee Mon Sep 17 00:00:00 2001 From: Wang Xingchao Date: Mon, 22 Jul 2013 03:19:18 -0400 Subject: ALSA: hda - use azx_writew() for 16-bit length register Register STATESTS is 16-bit length, use correct API for read/write. Signed-off-by: Wang Xingchao Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 8860dd5..3f16c4b 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -1160,7 +1160,7 @@ static int azx_reset(struct azx *chip, int full_reset) goto __skip; /* clear STATESTS */ - azx_writeb(chip, STATESTS, STATESTS_INT_MASK); + azx_writew(chip, STATESTS, STATESTS_INT_MASK); /* reset controller */ azx_enter_link_reset(chip); @@ -1242,7 +1242,7 @@ static void azx_int_clear(struct azx *chip) } /* clear STATESTS */ - azx_writeb(chip, STATESTS, STATESTS_INT_MASK); + azx_writew(chip, STATESTS, STATESTS_INT_MASK); /* clear rirb status */ azx_writeb(chip, RIRBSTS, RIRB_INT_MASK); @@ -1451,8 +1451,8 @@ static irqreturn_t azx_interrupt(int irq, void *dev_id) #if 0 /* clear state status int */ - if (azx_readb(chip, STATESTS) & 0x04) - azx_writeb(chip, STATESTS, 0x04); + if (azx_readw(chip, STATESTS) & 0x04) + azx_writew(chip, STATESTS, 0x04); #endif spin_unlock(&chip->reg_lock); -- cgit v0.10.2 From 60db923dad880f973d8e1aa7f654f2b928b34ad1 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Tue, 23 Jul 2013 20:01:54 +0200 Subject: ASoC: au1x: don't check resource with devm_ioremap_resource devm_ioremap_resource does sanity checks on the given resource. No need to duplicate this in the driver. Signed-off-by: Wolfram Sang Signed-off-by: Mark Brown diff --git a/sound/soc/au1x/psc-ac97.c b/sound/soc/au1x/psc-ac97.c index a822ab8..986dcec 100644 --- a/sound/soc/au1x/psc-ac97.c +++ b/sound/soc/au1x/psc-ac97.c @@ -379,9 +379,6 @@ static int au1xpsc_ac97_drvprobe(struct platform_device *pdev) mutex_init(&wd->lock); iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!iores) - return -ENODEV; - wd->mmio = devm_ioremap_resource(&pdev->dev, iores); if (IS_ERR(wd->mmio)) return PTR_ERR(wd->mmio); -- cgit v0.10.2 From bd23ee0c5e2af1d3cdef813039384abd400dcffc Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Tue, 23 Jul 2013 20:01:55 +0200 Subject: ASoC: ep93xx: don't check resource with devm_ioremap_resource devm_ioremap_resource does sanity checks on the given resource. No need to duplicate this in the driver. Signed-off-by: Wolfram Sang Signed-off-by: Mark Brown diff --git a/sound/soc/cirrus/ep93xx-ac97.c b/sound/soc/cirrus/ep93xx-ac97.c index ac73c60..2a5cdae 100644 --- a/sound/soc/cirrus/ep93xx-ac97.c +++ b/sound/soc/cirrus/ep93xx-ac97.c @@ -363,9 +363,6 @@ static int ep93xx_ac97_probe(struct platform_device *pdev) return -ENOMEM; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - return -ENODEV; - info->regs = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(info->regs)) return PTR_ERR(info->regs); diff --git a/sound/soc/cirrus/ep93xx-i2s.c b/sound/soc/cirrus/ep93xx-i2s.c index 17ad70b..f23f331 100644 --- a/sound/soc/cirrus/ep93xx-i2s.c +++ b/sound/soc/cirrus/ep93xx-i2s.c @@ -376,9 +376,6 @@ static int ep93xx_i2s_probe(struct platform_device *pdev) return -ENOMEM; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - return -ENODEV; - info->regs = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(info->regs)) return PTR_ERR(info->regs); -- cgit v0.10.2 From 916dd4130c9d045b8b50c16211b4d68ce147e50f Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Tue, 23 Jul 2013 20:01:56 +0200 Subject: ASoC: nuc900: don't check resource with devm_ioremap_resource devm_ioremap_resource does sanity checks on the given resource. No need to duplicate this in the driver. Signed-off-by: Wolfram Sang Signed-off-by: Mark Brown diff --git a/sound/soc/nuc900/nuc900-ac97.c b/sound/soc/nuc900/nuc900-ac97.c index f4c2417..8987bf9 100644 --- a/sound/soc/nuc900/nuc900-ac97.c +++ b/sound/soc/nuc900/nuc900-ac97.c @@ -333,9 +333,6 @@ static int nuc900_ac97_drvprobe(struct platform_device *pdev) spin_lock_init(&nuc900_audio->lock); nuc900_audio->res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!nuc900_audio->res) - return ret; - nuc900_audio->mmio = devm_ioremap_resource(&pdev->dev, nuc900_audio->res); if (IS_ERR(nuc900_audio->mmio)) -- cgit v0.10.2 From 0b4fa3374172c07691dcf568e72f63fb41e82561 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Tue, 23 Jul 2013 20:01:57 +0200 Subject: ASoC: pxa: don't check resource with devm_ioremap_resource devm_ioremap_resource does sanity checks on the given resource. No need to duplicate this in the driver. Signed-off-by: Wolfram Sang Signed-off-by: Mark Brown diff --git a/sound/soc/pxa/mmp-sspa.c b/sound/soc/pxa/mmp-sspa.c index 62142ce..1605934 100644 --- a/sound/soc/pxa/mmp-sspa.c +++ b/sound/soc/pxa/mmp-sspa.c @@ -430,9 +430,6 @@ static int asoc_mmp_sspa_probe(struct platform_device *pdev) return -ENOMEM; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (res == NULL) - return -ENOMEM; - priv->sspa->mmio_base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(priv->sspa->mmio_base)) return PTR_ERR(priv->sspa->mmio_base); -- cgit v0.10.2 From 06c77ea65b867e9b7ced67636b9a2854e0ce8558 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Tue, 23 Jul 2013 20:01:59 +0200 Subject: ASoC: txx9: don't check resource with devm_ioremap_resource devm_ioremap_resource does sanity checks on the given resource. No need to duplicate this in the driver. Signed-off-by: Wolfram Sang Signed-off-by: Mark Brown diff --git a/sound/soc/txx9/txx9aclc-ac97.c b/sound/soc/txx9/txx9aclc-ac97.c index 4bcce8a..e0305a1 100644 --- a/sound/soc/txx9/txx9aclc-ac97.c +++ b/sound/soc/txx9/txx9aclc-ac97.c @@ -184,9 +184,6 @@ static int txx9aclc_ac97_dev_probe(struct platform_device *pdev) if (irq < 0) return irq; r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!r) - return -EBUSY; - drvdata->base = devm_ioremap_resource(&pdev->dev, r); if (IS_ERR(drvdata->base)) return PTR_ERR(drvdata->base); -- cgit v0.10.2 From 32bd8cd25759411d3e11351db59be05446092f80 Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Thu, 25 Jul 2013 17:41:41 +0800 Subject: ASoC: fsl: Set sdma peripheral type directly Let CPU DAI drivers set SDMA periperal type directly to support more dma types(SPDIF, ESAI) other than only two for SSI. This will easily allow some non-SSI drivers to use the imx-pcm-dma as well. Signed-off-by: Nicolin Chen Acked-by: Shawn Guo Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 11469fe..4d78df7 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -775,9 +775,9 @@ static int fsl_ssi_probe(struct platform_device *pdev) "fsl,spba-bus"); imx_pcm_dma_params_init_data(&ssi_private->filter_data_tx, - dma_events[0], shared); + dma_events[0], shared ? IMX_DMATYPE_SSI_SP : IMX_DMATYPE_SSI); imx_pcm_dma_params_init_data(&ssi_private->filter_data_rx, - dma_events[1], shared); + dma_events[1], shared ? IMX_DMATYPE_SSI_SP : IMX_DMATYPE_SSI); } /* Initialize the the device_attribute structure */ diff --git a/sound/soc/fsl/imx-pcm.h b/sound/soc/fsl/imx-pcm.h index fd56cad..9136625 100644 --- a/sound/soc/fsl/imx-pcm.h +++ b/sound/soc/fsl/imx-pcm.h @@ -22,14 +22,11 @@ static inline void imx_pcm_dma_params_init_data(struct imx_dma_data *dma_data, - int dma, bool shared) + int dma, enum sdma_peripheral_type peripheral_type) { dma_data->dma_request = dma; dma_data->priority = DMA_PRIO_HIGH; - if (shared) - dma_data->peripheral_type = IMX_DMATYPE_SSI_SP; - else - dma_data->peripheral_type = IMX_DMATYPE_SSI; + dma_data->peripheral_type = peripheral_type; } struct imx_pcm_fiq_params { diff --git a/sound/soc/fsl/imx-ssi.c b/sound/soc/fsl/imx-ssi.c index f029e27..f58bcd8 100644 --- a/sound/soc/fsl/imx-ssi.c +++ b/sound/soc/fsl/imx-ssi.c @@ -571,13 +571,13 @@ static int imx_ssi_probe(struct platform_device *pdev) res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "tx0"); if (res) { imx_pcm_dma_params_init_data(&ssi->filter_data_tx, res->start, - false); + IMX_DMATYPE_SSI); } res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "rx0"); if (res) { imx_pcm_dma_params_init_data(&ssi->filter_data_rx, res->start, - false); + IMX_DMATYPE_SSI); } platform_set_drvdata(pdev, ssi); -- cgit v0.10.2 From b60be4aa40cff1ebfffc09f92b9007ac1fa24fb4 Mon Sep 17 00:00:00 2001 From: Padmavathi Venna Date: Fri, 26 Jul 2013 19:06:48 +0530 Subject: ASoC: Samsung: I2S: Modify driver to give more flexibility This patch modifies the i2s driver to give flexibility towards register handling. This is a pre requirement for enabling i2s support on Exynos5420. This patch modifies only the required registers as a pre-requirement to support on Exynos5420. Signed-off-by: Padmavathi Venna Signed-off-by: Mark Brown diff --git a/sound/soc/samsung/i2s-regs.h b/sound/soc/samsung/i2s-regs.h index c0e6d9a..30513b7 100644 --- a/sound/soc/samsung/i2s-regs.h +++ b/sound/soc/samsung/i2s-regs.h @@ -95,22 +95,26 @@ #define MOD_RXONLY (1 << 8) #define MOD_TXRX (2 << 8) #define MOD_MASK (3 << 8) -#define MOD_LR_LLOW (0 << 7) -#define MOD_LR_RLOW (1 << 7) -#define MOD_SDF_IIS (0 << 5) -#define MOD_SDF_MSB (1 << 5) -#define MOD_SDF_LSB (2 << 5) -#define MOD_SDF_MASK (3 << 5) -#define MOD_RCLK_256FS (0 << 3) -#define MOD_RCLK_512FS (1 << 3) -#define MOD_RCLK_384FS (2 << 3) -#define MOD_RCLK_768FS (3 << 3) -#define MOD_RCLK_MASK (3 << 3) -#define MOD_BCLK_32FS (0 << 1) -#define MOD_BCLK_48FS (1 << 1) -#define MOD_BCLK_16FS (2 << 1) -#define MOD_BCLK_24FS (3 << 1) -#define MOD_BCLK_MASK (3 << 1) +#define MOD_LRP_SHIFT 7 +#define MOD_LR_LLOW 0 +#define MOD_LR_RLOW 1 +#define MOD_SDF_SHIFT 5 +#define MOD_SDF_IIS 0 +#define MOD_SDF_MSB 1 +#define MOD_SDF_LSB 2 +#define MOD_SDF_MASK 3 +#define MOD_RCLK_SHIFT 3 +#define MOD_RCLK_256FS 0 +#define MOD_RCLK_512FS 1 +#define MOD_RCLK_384FS 2 +#define MOD_RCLK_768FS 3 +#define MOD_RCLK_MASK 3 +#define MOD_BCLK_SHIFT 1 +#define MOD_BCLK_32FS 0 +#define MOD_BCLK_48FS 1 +#define MOD_BCLK_16FS 2 +#define MOD_BCLK_24FS 3 +#define MOD_BCLK_MASK 3 #define MOD_8BIT (1 << 0) #define MOD_CDCLKCON (1 << 12) diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index 7a17346..9737358 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -198,7 +198,8 @@ static inline bool is_manager(struct i2s_dai *i2s) /* Read RCLK of I2S (in multiples of LRCLK) */ static inline unsigned get_rfs(struct i2s_dai *i2s) { - u32 rfs = (readl(i2s->addr + I2SMOD) >> 3) & 0x3; + u32 rfs = (readl(i2s->addr + I2SMOD) >> MOD_RCLK_SHIFT); + rfs &= MOD_RCLK_MASK; switch (rfs) { case 3: return 768; @@ -212,21 +213,22 @@ static inline unsigned get_rfs(struct i2s_dai *i2s) static inline void set_rfs(struct i2s_dai *i2s, unsigned rfs) { u32 mod = readl(i2s->addr + I2SMOD); + int rfs_shift = MOD_RCLK_SHIFT; - mod &= ~MOD_RCLK_MASK; + mod &= ~(MOD_RCLK_MASK << rfs_shift); switch (rfs) { case 768: - mod |= MOD_RCLK_768FS; + mod |= (MOD_RCLK_768FS << rfs_shift); break; case 512: - mod |= MOD_RCLK_512FS; + mod |= (MOD_RCLK_512FS << rfs_shift); break; case 384: - mod |= MOD_RCLK_384FS; + mod |= (MOD_RCLK_384FS << rfs_shift); break; default: - mod |= MOD_RCLK_256FS; + mod |= (MOD_RCLK_256FS << rfs_shift); break; } @@ -236,7 +238,8 @@ static inline void set_rfs(struct i2s_dai *i2s, unsigned rfs) /* Read Bit-Clock of I2S (in multiples of LRCLK) */ static inline unsigned get_bfs(struct i2s_dai *i2s) { - u32 bfs = (readl(i2s->addr + I2SMOD) >> 1) & 0x3; + u32 bfs = readl(i2s->addr + I2SMOD) >> MOD_BCLK_SHIFT; + bfs &= MOD_BCLK_MASK; switch (bfs) { case 3: return 24; @@ -250,21 +253,22 @@ static inline unsigned get_bfs(struct i2s_dai *i2s) static inline void set_bfs(struct i2s_dai *i2s, unsigned bfs) { u32 mod = readl(i2s->addr + I2SMOD); + int bfs_shift = MOD_BCLK_SHIFT; - mod &= ~MOD_BCLK_MASK; + mod &= ~(MOD_BCLK_MASK << bfs_shift); switch (bfs) { case 48: - mod |= MOD_BCLK_48FS; + mod |= (MOD_BCLK_48FS << bfs_shift); break; case 32: - mod |= MOD_BCLK_32FS; + mod |= (MOD_BCLK_32FS << bfs_shift); break; case 24: - mod |= MOD_BCLK_24FS; + mod |= (MOD_BCLK_24FS << bfs_shift); break; case 16: - mod |= MOD_BCLK_16FS; + mod |= (MOD_BCLK_16FS << bfs_shift); break; default: dev_err(&i2s->pdev->dev, "Wrong BCLK Divider!\n"); @@ -491,20 +495,25 @@ static int i2s_set_fmt(struct snd_soc_dai *dai, { struct i2s_dai *i2s = to_info(dai); u32 mod = readl(i2s->addr + I2SMOD); + int lrp_shift = MOD_LRP_SHIFT, sdf_shift = MOD_SDF_SHIFT; + int sdf_mask, lrp_rlow; u32 tmp = 0; + sdf_mask = MOD_SDF_MASK << sdf_shift; + lrp_rlow = MOD_LR_RLOW << lrp_shift; + /* Format is priority */ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_RIGHT_J: - tmp |= MOD_LR_RLOW; - tmp |= MOD_SDF_MSB; + tmp |= lrp_rlow; + tmp |= (MOD_SDF_MSB << sdf_shift); break; case SND_SOC_DAIFMT_LEFT_J: - tmp |= MOD_LR_RLOW; - tmp |= MOD_SDF_LSB; + tmp |= lrp_rlow; + tmp |= (MOD_SDF_LSB << sdf_shift); break; case SND_SOC_DAIFMT_I2S: - tmp |= MOD_SDF_IIS; + tmp |= (MOD_SDF_IIS << sdf_shift); break; default: dev_err(&i2s->pdev->dev, "Format not supported\n"); @@ -519,10 +528,10 @@ static int i2s_set_fmt(struct snd_soc_dai *dai, case SND_SOC_DAIFMT_NB_NF: break; case SND_SOC_DAIFMT_NB_IF: - if (tmp & MOD_LR_RLOW) - tmp &= ~MOD_LR_RLOW; + if (tmp & lrp_rlow) + tmp &= ~lrp_rlow; else - tmp |= MOD_LR_RLOW; + tmp |= lrp_rlow; break; default: dev_err(&i2s->pdev->dev, "Polarity not supported\n"); @@ -544,15 +553,18 @@ static int i2s_set_fmt(struct snd_soc_dai *dai, return -EINVAL; } + /* + * Don't change the I2S mode if any controller is active on this + * channel. + */ if (any_active(i2s) && - ((mod & (MOD_SDF_MASK | MOD_LR_RLOW - | MOD_SLAVE)) != tmp)) { + ((mod & (sdf_mask | lrp_rlow | MOD_SLAVE)) != tmp)) { dev_err(&i2s->pdev->dev, "%s:%d Other DAI busy\n", __func__, __LINE__); return -EAGAIN; } - mod &= ~(MOD_SDF_MASK | MOD_LR_RLOW | MOD_SLAVE); + mod &= ~(sdf_mask | lrp_rlow | MOD_SLAVE); mod |= tmp; writel(mod, i2s->addr + I2SMOD); -- cgit v0.10.2 From 087ee0934e23c8dce0f6a0f237f919efdbbb4285 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 25 Jul 2013 15:29:49 +0100 Subject: ASoC: wm8994: Add clock bindings to the device tree Due to the variable availability of the clock API there is no code in the driver to use these at present, for the time being the machine drivers will need to handle requesting clocks. Signed-off-by: Mark Brown Acked-by: Mark Rutland diff --git a/Documentation/devicetree/bindings/sound/wm8994.txt b/Documentation/devicetree/bindings/sound/wm8994.txt index f2f3e80..e045e90 100644 --- a/Documentation/devicetree/bindings/sound/wm8994.txt +++ b/Documentation/devicetree/bindings/sound/wm8994.txt @@ -32,6 +32,10 @@ Optional properties: The second cell is the flags, encoded as the trigger masks from Documentation/devicetree/bindings/interrupts.txt + - clocks : A list of up to two phandle and clock specifier pairs + - clock-names : A list of clock names sorted in the same order as clocks. + Valid clock names are "MCLK1" and "MCLK2". + - wlf,gpio-cfg : A list of GPIO configuration register values. If absent, no configuration of these registers is performed. If any value is over 0xffff then the register will be left as default. If present 11 -- cgit v0.10.2 From ba51cbb8206cdba789a1f65b06526bb20f51d594 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 25 Jul 2013 19:40:17 +0300 Subject: ASoC: adau1701: type bug with ADAU1707_CLKDIV_UNSET ADAU1707_CLKDIV_UNSET is always compared against an unsigned int and not an unsigned long. The current tests are always false. Signed-off-by: Dan Carpenter Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/adau1701.c b/sound/soc/codecs/adau1701.c index 44d8a95..2c10252 100644 --- a/sound/soc/codecs/adau1701.c +++ b/sound/soc/codecs/adau1701.c @@ -91,7 +91,7 @@ #define ADAU1701_OSCIPOW_OPD 0x04 #define ADAU1701_DACSET_DACINIT 1 -#define ADAU1707_CLKDIV_UNSET (-1UL) +#define ADAU1707_CLKDIV_UNSET (-1U) #define ADAU1701_FIRMWARE "adau1701.bin" -- cgit v0.10.2 From 02bd90e86dc63728feebaf2b238684208ccb19eb Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Sun, 28 Jul 2013 20:06:15 +0530 Subject: ASoC: compress: use soc_xxx handlers for metadata the compress metadata handlers were wrongly named sst_xxx Signed-off-by: Vinod Koul Signed-off-by: Mark Brown diff --git a/sound/soc/soc-compress.c b/sound/soc/soc-compress.c index 06a8000..d220150 100644 --- a/sound/soc/soc-compress.c +++ b/sound/soc/soc-compress.c @@ -334,7 +334,7 @@ static int soc_compr_copy(struct snd_compr_stream *cstream, return ret; } -static int sst_compr_set_metadata(struct snd_compr_stream *cstream, +static int soc_compr_set_metadata(struct snd_compr_stream *cstream, struct snd_compr_metadata *metadata) { struct snd_soc_pcm_runtime *rtd = cstream->private_data; @@ -347,7 +347,7 @@ static int sst_compr_set_metadata(struct snd_compr_stream *cstream, return ret; } -static int sst_compr_get_metadata(struct snd_compr_stream *cstream, +static int soc_compr_get_metadata(struct snd_compr_stream *cstream, struct snd_compr_metadata *metadata) { struct snd_soc_pcm_runtime *rtd = cstream->private_data; @@ -364,8 +364,8 @@ static struct snd_compr_ops soc_compr_ops = { .open = soc_compr_open, .free = soc_compr_free, .set_params = soc_compr_set_params, - .set_metadata = sst_compr_set_metadata, - .get_metadata = sst_compr_get_metadata, + .set_metadata = soc_compr_set_metadata, + .get_metadata = soc_compr_get_metadata, .get_params = soc_compr_get_params, .trigger = soc_compr_trigger, .pointer = soc_compr_pointer, -- cgit v0.10.2 From 07ccc0f4f190070aaba8fb587307f7fefad97981 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 28 Jul 2013 18:45:28 +0200 Subject: ASoC: lm4857: Use table based setup for DAPM and controls Let the ASoC core take care of registering the DAPM widget and routes as well as the controls. This makes the code a bit shorter. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/lm4857.c b/sound/soc/codecs/lm4857.c index 9f9f595..5ea2ed0 100644 --- a/sound/soc/codecs/lm4857.c +++ b/sound/soc/codecs/lm4857.c @@ -174,28 +174,9 @@ static const struct snd_soc_dapm_route lm4857_routes[] = { static int lm4857_probe(struct snd_soc_codec *codec) { struct lm4857 *lm4857 = snd_soc_codec_get_drvdata(codec); - struct snd_soc_dapm_context *dapm = &codec->dapm; - int ret; codec->control_data = lm4857->i2c; - ret = snd_soc_add_codec_controls(codec, lm4857_controls, - ARRAY_SIZE(lm4857_controls)); - if (ret) - return ret; - - ret = snd_soc_dapm_new_controls(dapm, lm4857_dapm_widgets, - ARRAY_SIZE(lm4857_dapm_widgets)); - if (ret) - return ret; - - ret = snd_soc_dapm_add_routes(dapm, lm4857_routes, - ARRAY_SIZE(lm4857_routes)); - if (ret) - return ret; - - snd_soc_dapm_new_widgets(dapm); - return 0; } @@ -207,6 +188,13 @@ static struct snd_soc_codec_driver soc_codec_dev_lm4857 = { .reg_word_size = sizeof(uint8_t), .reg_cache_default = lm4857_default_regs, .set_bias_level = lm4857_set_bias_level, + + .controls = lm4857_controls, + .num_controls = ARRAY_SIZE(lm4857_controls), + .dapm_widgets = lm4857_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(lm4857_dapm_widgets), + .dapm_routes = lm4857_routes, + .num_dapm_routes = ARRAY_SIZE(lm4857_routes), }; static int lm4857_i2c_probe(struct i2c_client *i2c, -- cgit v0.10.2 From 9b2709687a81297bca53f98100b6f13cd5e05b44 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 28 Jul 2013 18:45:29 +0200 Subject: ASoC: lm4857: Convert to regmap Use regmap for IO for the lm4857 driver instead of open-coding the IO read/write functions. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/lm4857.c b/sound/soc/codecs/lm4857.c index 5ea2ed0..0e5743e 100644 --- a/sound/soc/codecs/lm4857.c +++ b/sound/soc/codecs/lm4857.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -23,12 +24,15 @@ #include struct lm4857 { - struct i2c_client *i2c; + struct regmap *regmap; uint8_t mode; }; -static const uint8_t lm4857_default_regs[] = { - 0x00, 0x00, 0x00, 0x00, +static const struct reg_default lm4857_default_regs[] = { + { 0x0, 0x00 }, + { 0x1, 0x00 }, + { 0x2, 0x00 }, + { 0x3, 0x00 }, }; /* The register offsets in the cache array */ @@ -42,39 +46,6 @@ static const uint8_t lm4857_default_regs[] = { #define LM4857_WAKEUP 5 #define LM4857_EPGAIN 4 -static int lm4857_write(struct snd_soc_codec *codec, unsigned int reg, - unsigned int value) -{ - uint8_t data; - int ret; - - ret = snd_soc_cache_write(codec, reg, value); - if (ret < 0) - return ret; - - data = (reg << 6) | value; - ret = i2c_master_send(codec->control_data, &data, 1); - if (ret != 1) { - dev_err(codec->dev, "Failed to write register: %d\n", ret); - return ret; - } - - return 0; -} - -static unsigned int lm4857_read(struct snd_soc_codec *codec, - unsigned int reg) -{ - unsigned int val; - int ret; - - ret = snd_soc_cache_read(codec, reg, &val); - if (ret) - return -1; - - return val; -} - static int lm4857_get_mode(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -96,7 +67,7 @@ static int lm4857_set_mode(struct snd_kcontrol *kcontrol, lm4857->mode = value; if (codec->dapm.bias_level == SND_SOC_BIAS_ON) - snd_soc_update_bits(codec, LM4857_CTRL, 0x0F, value + 6); + regmap_update_bits(lm4857->regmap, LM4857_CTRL, 0x0F, value + 6); return 1; } @@ -108,10 +79,11 @@ static int lm4857_set_bias_level(struct snd_soc_codec *codec, switch (level) { case SND_SOC_BIAS_ON: - snd_soc_update_bits(codec, LM4857_CTRL, 0x0F, lm4857->mode + 6); + regmap_update_bits(lm4857->regmap, LM4857_CTRL, 0x0F, + lm4857->mode + 6); break; case SND_SOC_BIAS_STANDBY: - snd_soc_update_bits(codec, LM4857_CTRL, 0x0F, 0); + regmap_update_bits(lm4857->regmap, LM4857_CTRL, 0x0F, 0); break; default: break; @@ -171,22 +143,7 @@ static const struct snd_soc_dapm_route lm4857_routes[] = { {"EP", NULL, "IN"}, }; -static int lm4857_probe(struct snd_soc_codec *codec) -{ - struct lm4857 *lm4857 = snd_soc_codec_get_drvdata(codec); - - codec->control_data = lm4857->i2c; - - return 0; -} - static struct snd_soc_codec_driver soc_codec_dev_lm4857 = { - .write = lm4857_write, - .read = lm4857_read, - .probe = lm4857_probe, - .reg_cache_size = ARRAY_SIZE(lm4857_default_regs), - .reg_word_size = sizeof(uint8_t), - .reg_cache_default = lm4857_default_regs, .set_bias_level = lm4857_set_bias_level, .controls = lm4857_controls, @@ -197,11 +154,21 @@ static struct snd_soc_codec_driver soc_codec_dev_lm4857 = { .num_dapm_routes = ARRAY_SIZE(lm4857_routes), }; +static const struct regmap_config lm4857_regmap_config = { + .val_bits = 6, + .reg_bits = 2, + + .max_register = LM4857_CTRL, + + .cache_type = REGCACHE_FLAT, + .reg_defaults = lm4857_default_regs, + .num_reg_defaults = ARRAY_SIZE(lm4857_default_regs), +}; + static int lm4857_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { struct lm4857 *lm4857; - int ret; lm4857 = devm_kzalloc(&i2c->dev, sizeof(*lm4857), GFP_KERNEL); if (!lm4857) @@ -209,11 +176,11 @@ static int lm4857_i2c_probe(struct i2c_client *i2c, i2c_set_clientdata(i2c, lm4857); - lm4857->i2c = i2c; - - ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_lm4857, NULL, 0); + lm4857->regmap = devm_regmap_init_i2c(i2c, &lm4857_regmap_config); + if (IS_ERR(lm4857->regmap)) + return PTR_ERR(lm4857->regmap); - return ret; + return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_lm4857, NULL, 0); } static int lm4857_i2c_remove(struct i2c_client *i2c) -- cgit v0.10.2 From 1536a968892aa9095aada4b6d2ed326432cd71c8 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Sun, 21 Jul 2013 21:35:52 -0700 Subject: ASoC: add Renesas R-Car core feature Renesas R-Car series sound circuit consists of SSI and its peripheral. But this peripheral circuits are different between R-Car Generation1 (E1/M1/H1) and Generation2 (E2/M2/H2). (Actually, there are many difference in Generation1 chips) Basically, for the future, Renesas R-Car series will use Gen2 style sound circuit, but driver should care Gen1 also. The main differences between Gen1 and Gen2 peripheral are 1) register offset, 2) data path. This patch adds basic (core) feature for R-Car series sound driver as prototype Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown diff --git a/include/sound/rcar_snd.h b/include/sound/rcar_snd.h new file mode 100644 index 0000000..7272b2e --- /dev/null +++ b/include/sound/rcar_snd.h @@ -0,0 +1,33 @@ +/* + * Renesas R-Car SRU/SCU/SSIU/SSI support + * + * Copyright (C) 2013 Renesas Solutions Corp. + * Kuninori Morimoto + * + * 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. + */ + +#ifndef RCAR_SND_H +#define RCAR_SND_H + +#include + + +#define RSND_BASE_MAX 0 + +struct rsnd_dai_platform_info { + int ssi_id_playback; + int ssi_id_capture; +}; + +struct rcar_snd_info { + u32 flags; + struct rsnd_dai_platform_info *dai_info; + int dai_info_nr; + int (*start)(int id); + int (*stop)(int id); +}; + +#endif diff --git a/sound/soc/sh/Kconfig b/sound/soc/sh/Kconfig index 6bcb116..56d8ff6 100644 --- a/sound/soc/sh/Kconfig +++ b/sound/soc/sh/Kconfig @@ -34,6 +34,13 @@ config SND_SOC_SH4_SIU select SH_DMAE select FW_LOADER +config SND_SOC_RCAR + tristate "R-Car series SRU/SCU/SSIU/SSI support" + select SND_SIMPLE_CARD + select RCAR_CLK_ADG + help + This option enables R-Car SUR/SCU/SSIU/SSI sound support + ## ## Boards ## diff --git a/sound/soc/sh/Makefile b/sound/soc/sh/Makefile index 849b387..aaf3dcd 100644 --- a/sound/soc/sh/Makefile +++ b/sound/soc/sh/Makefile @@ -12,6 +12,9 @@ obj-$(CONFIG_SND_SOC_SH4_SSI) += snd-soc-ssi.o obj-$(CONFIG_SND_SOC_SH4_FSI) += snd-soc-fsi.o obj-$(CONFIG_SND_SOC_SH4_SIU) += snd-soc-siu.o +## audio units for R-Car +obj-$(CONFIG_SND_SOC_RCAR) += rcar/ + ## boards snd-soc-sh7760-ac97-objs := sh7760-ac97.o snd-soc-migor-objs := migor.o diff --git a/sound/soc/sh/rcar/Makefile b/sound/soc/sh/rcar/Makefile new file mode 100644 index 0000000..cd8089f --- /dev/null +++ b/sound/soc/sh/rcar/Makefile @@ -0,0 +1,2 @@ +snd-soc-rcar-objs := core.o +obj-$(CONFIG_SND_SOC_RCAR) += snd-soc-rcar.o diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c new file mode 100644 index 0000000..13b5d50 --- /dev/null +++ b/sound/soc/sh/rcar/core.c @@ -0,0 +1,554 @@ +/* + * Renesas R-Car SRU/SCU/SSIU/SSI support + * + * Copyright (C) 2013 Renesas Solutions Corp. + * Kuninori Morimoto + * + * Based on fsi.c + * Kuninori Morimoto + * + * 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. + */ + +/* + * Renesas R-Car sound device structure + * + * Gen1 + * + * SRU : Sound Routing Unit + * - SRC : Sampling Rate Converter + * - CMD + * - CTU : Channel Count Conversion Unit + * - MIX : Mixer + * - DVC : Digital Volume and Mute Function + * - SSI : Serial Sound Interface + * + * Gen2 + * + * SCU : Sampling Rate Converter Unit + * - SRC : Sampling Rate Converter + * - CMD + * - CTU : Channel Count Conversion Unit + * - MIX : Mixer + * - DVC : Digital Volume and Mute Function + * SSIU : Serial Sound Interface Unit + * - SSI : Serial Sound Interface + */ + +/* + * driver data Image + * + * rsnd_priv + * | + * | ** this depends on Gen1/Gen2 + * | + * +- gen + * | + * | ** these depend on data path + * | ** gen and platform data control it + * | + * +- rdai[0] + * | | sru ssiu ssi + * | +- playback -> [mod] -> [mod] -> [mod] -> ... + * | | + * | | sru ssiu ssi + * | +- capture -> [mod] -> [mod] -> [mod] -> ... + * | + * +- rdai[1] + * | | sru ssiu ssi + * | +- playback -> [mod] -> [mod] -> [mod] -> ... + * | | + * | | sru ssiu ssi + * | +- capture -> [mod] -> [mod] -> [mod] -> ... + * ... + * | + * | ** these control ssi + * | + * +- ssi + * | | + * | +- ssi[0] + * | +- ssi[1] + * | +- ssi[2] + * | ... + * | + * | ** these control scu + * | + * +- scu + * | + * +- scu[0] + * +- scu[1] + * +- scu[2] + * ... + * + * + * for_each_rsnd_dai(xx, priv, xx) + * rdai[0] => rdai[1] => rdai[2] => ... + * + * for_each_rsnd_mod(xx, rdai, xx) + * [mod] => [mod] => [mod] => ... + * + * rsnd_dai_call(xxx, fn ) + * [mod]->fn() -> [mod]->fn() -> [mod]->fn()... + * + */ +#include +#include "rsnd.h" + +#define RSND_RATES SNDRV_PCM_RATE_8000_96000 +#define RSND_FMTS (SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE) + +/* + * rsnd_platform functions + */ +#define rsnd_platform_call(priv, dai, func, param...) \ + (!(priv->info->func) ? -ENODEV : \ + priv->info->func(param)) + + +/* + * rsnd_dai functions + */ +struct rsnd_dai *rsnd_dai_get(struct rsnd_priv *priv, int id) +{ + return priv->rdai + id; +} + +static struct rsnd_dai *rsnd_dai_to_rdai(struct snd_soc_dai *dai) +{ + struct rsnd_priv *priv = snd_soc_dai_get_drvdata(dai); + + return rsnd_dai_get(priv, dai->id); +} + +int rsnd_dai_is_play(struct rsnd_dai *rdai, struct rsnd_dai_stream *io) +{ + return &rdai->playback == io; +} + +/* + * rsnd_soc_dai functions + */ +int rsnd_dai_pointer_offset(struct rsnd_dai_stream *io, int additional) +{ + struct snd_pcm_substream *substream = io->substream; + struct snd_pcm_runtime *runtime = substream->runtime; + int pos = io->byte_pos + additional; + + pos %= (runtime->periods * io->byte_per_period); + + return pos; +} + +void rsnd_dai_pointer_update(struct rsnd_dai_stream *io, int byte) +{ + io->byte_pos += byte; + + if (io->byte_pos >= io->next_period_byte) { + struct snd_pcm_substream *substream = io->substream; + struct snd_pcm_runtime *runtime = substream->runtime; + + io->period_pos++; + io->next_period_byte += io->byte_per_period; + + if (io->period_pos >= runtime->periods) { + io->byte_pos = 0; + io->period_pos = 0; + io->next_period_byte = io->byte_per_period; + } + + snd_pcm_period_elapsed(substream); + } +} + +static int rsnd_dai_stream_init(struct rsnd_dai_stream *io, + struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + + if (!list_empty(&io->head)) + return -EIO; + + INIT_LIST_HEAD(&io->head); + io->substream = substream; + io->byte_pos = 0; + io->period_pos = 0; + io->byte_per_period = runtime->period_size * + runtime->channels * + samples_to_bytes(runtime, 1); + io->next_period_byte = io->byte_per_period; + + return 0; +} + +static +struct snd_soc_dai *rsnd_substream_to_dai(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + + return rtd->cpu_dai; +} + +static +struct rsnd_dai_stream *rsnd_rdai_to_io(struct rsnd_dai *rdai, + struct snd_pcm_substream *substream) +{ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + return &rdai->playback; + else + return &rdai->capture; +} + +static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct rsnd_priv *priv = snd_soc_dai_get_drvdata(dai); + struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai); + struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream); + struct rsnd_dai_platform_info *info = rsnd_dai_get_platform_info(rdai); + int ssi_id = rsnd_dai_is_play(rdai, io) ? info->ssi_id_playback : + info->ssi_id_capture; + int ret; + unsigned long flags; + + rsnd_lock(priv, flags); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + ret = rsnd_dai_stream_init(io, substream); + if (ret < 0) + goto dai_trigger_end; + + ret = rsnd_platform_call(priv, dai, start, ssi_id); + if (ret < 0) + goto dai_trigger_end; + + break; + case SNDRV_PCM_TRIGGER_STOP: + ret = rsnd_platform_call(priv, dai, stop, ssi_id); + if (ret < 0) + goto dai_trigger_end; + + break; + default: + ret = -EINVAL; + } + +dai_trigger_end: + rsnd_unlock(priv, flags); + + return ret; +} + +static int rsnd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai); + + /* set master/slave audio interface */ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + rdai->clk_master = 1; + break; + case SND_SOC_DAIFMT_CBS_CFS: + rdai->clk_master = 0; + break; + default: + return -EINVAL; + } + + /* set clock inversion */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_IF: + rdai->bit_clk_inv = 0; + rdai->frm_clk_inv = 1; + break; + case SND_SOC_DAIFMT_IB_NF: + rdai->bit_clk_inv = 1; + rdai->frm_clk_inv = 0; + break; + case SND_SOC_DAIFMT_IB_IF: + rdai->bit_clk_inv = 1; + rdai->frm_clk_inv = 1; + break; + case SND_SOC_DAIFMT_NB_NF: + default: + rdai->bit_clk_inv = 0; + rdai->frm_clk_inv = 0; + break; + } + + /* set format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + rdai->sys_delay = 0; + rdai->data_alignment = 0; + break; + case SND_SOC_DAIFMT_LEFT_J: + rdai->sys_delay = 1; + rdai->data_alignment = 0; + break; + case SND_SOC_DAIFMT_RIGHT_J: + rdai->sys_delay = 1; + rdai->data_alignment = 1; + break; + } + + return 0; +} + +static const struct snd_soc_dai_ops rsnd_soc_dai_ops = { + .trigger = rsnd_soc_dai_trigger, + .set_fmt = rsnd_soc_dai_set_fmt, +}; + +static int rsnd_dai_probe(struct platform_device *pdev, + struct rcar_snd_info *info, + struct rsnd_priv *priv) +{ + struct snd_soc_dai_driver *drv; + struct rsnd_dai *rdai; + struct device *dev = rsnd_priv_to_dev(priv); + struct rsnd_dai_platform_info *dai_info; + int dai_nr = info->dai_info_nr; + int i, pid, cid; + + drv = devm_kzalloc(dev, sizeof(*drv) * dai_nr, GFP_KERNEL); + rdai = devm_kzalloc(dev, sizeof(*rdai) * dai_nr, GFP_KERNEL); + if (!drv || !rdai) { + dev_err(dev, "dai allocate failed\n"); + return -ENOMEM; + } + + for (i = 0; i < dai_nr; i++) { + dai_info = &info->dai_info[i]; + + pid = dai_info->ssi_id_playback; + cid = dai_info->ssi_id_capture; + + /* + * init rsnd_dai + */ + INIT_LIST_HEAD(&rdai[i].playback.head); + INIT_LIST_HEAD(&rdai[i].capture.head); + + rdai[i].info = dai_info; + + snprintf(rdai[i].name, RSND_DAI_NAME_SIZE, "rsnd-dai.%d", i); + + /* + * init snd_soc_dai_driver + */ + drv[i].name = rdai[i].name; + drv[i].ops = &rsnd_soc_dai_ops; + if (pid >= 0) { + drv[i].playback.rates = RSND_RATES; + drv[i].playback.formats = RSND_FMTS; + drv[i].playback.channels_min = 2; + drv[i].playback.channels_max = 2; + } + if (cid >= 0) { + drv[i].capture.rates = RSND_RATES; + drv[i].capture.formats = RSND_FMTS; + drv[i].capture.channels_min = 2; + drv[i].capture.channels_max = 2; + } + + dev_dbg(dev, "%s (%d, %d) probed", rdai[i].name, pid, cid); + } + + priv->dai_nr = dai_nr; + priv->daidrv = drv; + priv->rdai = rdai; + + return 0; +} + +static void rsnd_dai_remove(struct platform_device *pdev, + struct rsnd_priv *priv) +{ +} + +/* + * pcm ops + */ +static struct snd_pcm_hardware rsnd_pcm_hardware = { + .info = SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE, + .formats = RSND_FMTS, + .rates = RSND_RATES, + .rate_min = 8000, + .rate_max = 192000, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = 64 * 1024, + .period_bytes_min = 32, + .period_bytes_max = 8192, + .periods_min = 1, + .periods_max = 32, + .fifo_size = 256, +}; + +static int rsnd_pcm_open(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + int ret = 0; + + snd_soc_set_runtime_hwparams(substream, &rsnd_pcm_hardware); + + ret = snd_pcm_hw_constraint_integer(runtime, + SNDRV_PCM_HW_PARAM_PERIODS); + + return ret; +} + +static int rsnd_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + return snd_pcm_lib_malloc_pages(substream, + params_buffer_bytes(hw_params)); +} + +static snd_pcm_uframes_t rsnd_pointer(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_dai *dai = rsnd_substream_to_dai(substream); + struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai); + struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream); + + return bytes_to_frames(runtime, io->byte_pos); +} + +static struct snd_pcm_ops rsnd_pcm_ops = { + .open = rsnd_pcm_open, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = rsnd_hw_params, + .hw_free = snd_pcm_lib_free_pages, + .pointer = rsnd_pointer, +}; + +/* + * snd_soc_platform + */ + +#define PREALLOC_BUFFER (32 * 1024) +#define PREALLOC_BUFFER_MAX (32 * 1024) + +static int rsnd_pcm_new(struct snd_soc_pcm_runtime *rtd) +{ + return snd_pcm_lib_preallocate_pages_for_all( + rtd->pcm, + SNDRV_DMA_TYPE_DEV, + rtd->card->snd_card->dev, + PREALLOC_BUFFER, PREALLOC_BUFFER_MAX); +} + +static void rsnd_pcm_free(struct snd_pcm *pcm) +{ + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +static struct snd_soc_platform_driver rsnd_soc_platform = { + .ops = &rsnd_pcm_ops, + .pcm_new = rsnd_pcm_new, + .pcm_free = rsnd_pcm_free, +}; + +static const struct snd_soc_component_driver rsnd_soc_component = { + .name = "rsnd", +}; + +/* + * rsnd probe + */ +static int rsnd_probe(struct platform_device *pdev) +{ + struct rcar_snd_info *info; + struct rsnd_priv *priv; + struct device *dev = &pdev->dev; + int ret; + + info = pdev->dev.platform_data; + if (!info) { + dev_err(dev, "driver needs R-Car sound information\n"); + return -ENODEV; + } + + /* + * init priv data + */ + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) { + dev_err(dev, "priv allocate failed\n"); + return -ENODEV; + } + + priv->dev = dev; + priv->info = info; + spin_lock_init(&priv->lock); + + /* + * init each module + */ + ret = rsnd_dai_probe(pdev, info, priv); + if (ret < 0) + return ret; + + /* + * asoc register + */ + ret = snd_soc_register_platform(dev, &rsnd_soc_platform); + if (ret < 0) { + dev_err(dev, "cannot snd soc register\n"); + return ret; + } + + ret = snd_soc_register_component(dev, &rsnd_soc_component, + priv->daidrv, rsnd_dai_nr(priv)); + if (ret < 0) { + dev_err(dev, "cannot snd dai register\n"); + goto exit_snd_soc; + } + + dev_set_drvdata(dev, priv); + + pm_runtime_enable(dev); + + dev_info(dev, "probed\n"); + return ret; + +exit_snd_soc: + snd_soc_unregister_platform(dev); + + return ret; +} + +static int rsnd_remove(struct platform_device *pdev) +{ + struct rsnd_priv *priv = dev_get_drvdata(&pdev->dev); + + pm_runtime_disable(&pdev->dev); + + /* + * remove each module + */ + rsnd_dai_remove(pdev, priv); + + return 0; +} + +static struct platform_driver rsnd_driver = { + .driver = { + .name = "rcar_sound", + }, + .probe = rsnd_probe, + .remove = rsnd_remove, +}; +module_platform_driver(rsnd_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Renesas R-Car audio driver"); +MODULE_AUTHOR("Kuninori Morimoto "); +MODULE_ALIAS("platform:rcar-pcm-audio"); diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h new file mode 100644 index 0000000..8d04fd0 --- /dev/null +++ b/sound/soc/sh/rcar/rsnd.h @@ -0,0 +1,94 @@ +/* + * Renesas R-Car + * + * Copyright (C) 2013 Renesas Solutions Corp. + * Kuninori Morimoto + * + * 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. + */ +#ifndef RSND_H +#define RSND_H + +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * pseudo register + * + * The register address offsets SRU/SCU/SSIU on Gen1/Gen2 are very different. + * This driver uses pseudo register in order to hide it. + * see gen1/gen2 for detail + */ +struct rsnd_priv; +struct rsnd_dai; +struct rsnd_dai_stream; + +/* + * R-Car sound DAI + */ +#define RSND_DAI_NAME_SIZE 16 +struct rsnd_dai_stream { + struct list_head head; /* head of rsnd_mod list */ + struct snd_pcm_substream *substream; + int byte_pos; + int period_pos; + int byte_per_period; + int next_period_byte; +}; + +struct rsnd_dai { + char name[RSND_DAI_NAME_SIZE]; + struct rsnd_dai_platform_info *info; /* rcar_snd.h */ + struct rsnd_dai_stream playback; + struct rsnd_dai_stream capture; + + int clk_master:1; + int bit_clk_inv:1; + int frm_clk_inv:1; + int sys_delay:1; + int data_alignment:1; +}; + +#define rsnd_dai_nr(priv) ((priv)->dai_nr) +#define for_each_rsnd_dai(rdai, priv, i) \ + for (i = 0, (rdai) = rsnd_dai_get(priv, i); \ + i < rsnd_dai_nr(priv); \ + i++, (rdai) = rsnd_dai_get(priv, i)) + +struct rsnd_dai *rsnd_dai_get(struct rsnd_priv *priv, int id); +int rsnd_dai_is_play(struct rsnd_dai *rdai, struct rsnd_dai_stream *io); +#define rsnd_dai_get_platform_info(rdai) ((rdai)->info) + +void rsnd_dai_pointer_update(struct rsnd_dai_stream *io, int cnt); +int rsnd_dai_pointer_offset(struct rsnd_dai_stream *io, int additional); + +/* + * R-Car sound priv + */ +struct rsnd_priv { + + struct device *dev; + struct rcar_snd_info *info; + spinlock_t lock; + + /* + * below value will be filled on rsnd_dai_probe() + */ + struct snd_soc_dai_driver *daidrv; + struct rsnd_dai *rdai; + int dai_nr; +}; + +#define rsnd_priv_to_dev(priv) ((priv)->dev) +#define rsnd_lock(priv, flags) spin_lock_irqsave(&priv->lock, flags) +#define rsnd_unlock(priv, flags) spin_unlock_irqrestore(&priv->lock, flags) + +#endif -- cgit v0.10.2 From cdaa3cdfb4a710545a53740b1780a683b043618a Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Sun, 21 Jul 2013 21:36:03 -0700 Subject: ASoC: add Renesas R-Car module feature Renesas R-Car series sound circuit consists of SSI and its peripheral. But this peripheral circuit is different between R-Car Generation1 (E1/M1/H1) and Generation2 (E2/M2/H2) (Actually, there are many difference in Generation1 chips) Gen1 series consists of SRU/SSI/ADG, and Gen2 series consists of SCU/SSIU/SSI/ADG. In order to control these by same method, these are treated as "mod" on this driver. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index 13b5d50..a47fda2 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -108,8 +108,73 @@ /* + * rsnd_mod functions + */ +char *rsnd_mod_name(struct rsnd_mod *mod) +{ + if (!mod || !mod->ops) + return "unknown"; + + return mod->ops->name; +} + +void rsnd_mod_init(struct rsnd_priv *priv, + struct rsnd_mod *mod, + struct rsnd_mod_ops *ops, + int id) +{ + mod->priv = priv; + mod->id = id; + mod->ops = ops; + INIT_LIST_HEAD(&mod->list); +} + +/* * rsnd_dai functions */ +#define rsnd_dai_call(rdai, io, fn) \ +({ \ + struct rsnd_mod *mod, *n; \ + int ret = 0; \ + for_each_rsnd_mod(mod, n, io) { \ + ret = rsnd_mod_call(mod, fn, rdai, io); \ + if (ret < 0) \ + break; \ + } \ + ret; \ +}) + +int rsnd_dai_connect(struct rsnd_dai *rdai, + struct rsnd_mod *mod, + struct rsnd_dai_stream *io) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct device *dev = rsnd_priv_to_dev(priv); + + if (!mod) { + dev_err(dev, "NULL mod\n"); + return -EIO; + } + + if (!list_empty(&mod->list)) { + dev_err(dev, "%s%d is not empty\n", + rsnd_mod_name(mod), + rsnd_mod_id(mod)); + return -EIO; + } + + list_add_tail(&mod->list, &io->head); + + return 0; +} + +int rsnd_dai_disconnect(struct rsnd_mod *mod) +{ + list_del_init(&mod->list); + + return 0; +} + struct rsnd_dai *rsnd_dai_get(struct rsnd_priv *priv, int id) { return priv->rdai + id; @@ -224,8 +289,23 @@ static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd, if (ret < 0) goto dai_trigger_end; + ret = rsnd_dai_call(rdai, io, init); + if (ret < 0) + goto dai_trigger_end; + + ret = rsnd_dai_call(rdai, io, start); + if (ret < 0) + goto dai_trigger_end; break; case SNDRV_PCM_TRIGGER_STOP: + ret = rsnd_dai_call(rdai, io, stop); + if (ret < 0) + goto dai_trigger_end; + + ret = rsnd_dai_call(rdai, io, quit); + if (ret < 0) + goto dai_trigger_end; + ret = rsnd_platform_call(priv, dai, stop, ssi_id); if (ret < 0) goto dai_trigger_end; diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 8d04fd0..65d3835 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -28,10 +28,53 @@ * see gen1/gen2 for detail */ struct rsnd_priv; +struct rsnd_mod; struct rsnd_dai; struct rsnd_dai_stream; /* + * R-Car sound mod + */ + +struct rsnd_mod_ops { + char *name; + int (*init)(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io); + int (*quit)(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io); + int (*start)(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io); + int (*stop)(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io); +}; + +struct rsnd_mod { + int id; + struct rsnd_priv *priv; + struct rsnd_mod_ops *ops; + struct list_head list; /* connect to rsnd_dai playback/capture */ +}; + +#define rsnd_mod_to_priv(mod) ((mod)->priv) +#define rsnd_mod_id(mod) ((mod)->id) +#define for_each_rsnd_mod(pos, n, io) \ + list_for_each_entry_safe(pos, n, &(io)->head, list) +#define rsnd_mod_call(mod, func, rdai, io) \ + (!(mod) ? -ENODEV : \ + !((mod)->ops->func) ? 0 : \ + (mod)->ops->func(mod, rdai, io)) + +void rsnd_mod_init(struct rsnd_priv *priv, + struct rsnd_mod *mod, + struct rsnd_mod_ops *ops, + int id); +char *rsnd_mod_name(struct rsnd_mod *mod); + +/* * R-Car sound DAI */ #define RSND_DAI_NAME_SIZE 16 @@ -64,6 +107,9 @@ struct rsnd_dai { i++, (rdai) = rsnd_dai_get(priv, i)) struct rsnd_dai *rsnd_dai_get(struct rsnd_priv *priv, int id); +int rsnd_dai_disconnect(struct rsnd_mod *mod); +int rsnd_dai_connect(struct rsnd_dai *rdai, struct rsnd_mod *mod, + struct rsnd_dai_stream *io); int rsnd_dai_is_play(struct rsnd_dai *rdai, struct rsnd_dai_stream *io); #define rsnd_dai_get_platform_info(rdai) ((rdai)->info) -- cgit v0.10.2 From 3337744ac41bee00b0068ad5f926dd9c27540809 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Sun, 21 Jul 2013 21:36:21 -0700 Subject: ASoC: add Renesas R-Car Generation feature Renesas R-Car series sound circuit consists of SSI and its peripheral. But this peripheral circuit is different between R-Car Generation1 (E1/M1/H1) and Generation2 (E2/M2/H2) (Actually, there are many difference in Generation1 chips) The main difference between Gen1 and Gen2 are 1) register offset, 2) data path In order to control Gen1/Gen2 by same method, this patch adds gen.c. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown diff --git a/include/sound/rcar_snd.h b/include/sound/rcar_snd.h index 7272b2e..14942a8 100644 --- a/include/sound/rcar_snd.h +++ b/include/sound/rcar_snd.h @@ -22,6 +22,16 @@ struct rsnd_dai_platform_info { int ssi_id_capture; }; +/* + * flags + * + * 0x0000000A + * + * A : generation + */ +#define RSND_GEN1 (1 << 0) /* fixme */ +#define RSND_GEN2 (2 << 0) /* fixme */ + struct rcar_snd_info { u32 flags; struct rsnd_dai_platform_info *dai_info; diff --git a/sound/soc/sh/rcar/Makefile b/sound/soc/sh/rcar/Makefile index cd8089f..b2d313b 100644 --- a/sound/soc/sh/rcar/Makefile +++ b/sound/soc/sh/rcar/Makefile @@ -1,2 +1,2 @@ -snd-soc-rcar-objs := core.o +snd-soc-rcar-objs := core.o gen.o obj-$(CONFIG_SND_SOC_RCAR) += snd-soc-rcar.o diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index a47fda2..bb8959f 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -108,6 +108,50 @@ /* + * basic function + */ +u32 rsnd_read(struct rsnd_priv *priv, + struct rsnd_mod *mod, enum rsnd_reg reg) +{ + void __iomem *base = rsnd_gen_reg_get(priv, mod, reg); + + BUG_ON(!base); + + return ioread32(base); +} + +void rsnd_write(struct rsnd_priv *priv, + struct rsnd_mod *mod, + enum rsnd_reg reg, u32 data) +{ + void __iomem *base = rsnd_gen_reg_get(priv, mod, reg); + struct device *dev = rsnd_priv_to_dev(priv); + + BUG_ON(!base); + + dev_dbg(dev, "w %p : %08x\n", base, data); + + iowrite32(data, base); +} + +void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod, + enum rsnd_reg reg, u32 mask, u32 data) +{ + void __iomem *base = rsnd_gen_reg_get(priv, mod, reg); + struct device *dev = rsnd_priv_to_dev(priv); + u32 val; + + BUG_ON(!base); + + val = ioread32(base); + val &= ~mask; + val |= data & mask; + iowrite32(val, base); + + dev_dbg(dev, "s %p : %08x\n", base, val); +} + +/* * rsnd_mod functions */ char *rsnd_mod_name(struct rsnd_mod *mod) @@ -289,6 +333,10 @@ static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd, if (ret < 0) goto dai_trigger_end; + ret = rsnd_gen_path_init(priv, rdai, io); + if (ret < 0) + goto dai_trigger_end; + ret = rsnd_dai_call(rdai, io, init); if (ret < 0) goto dai_trigger_end; @@ -306,10 +354,13 @@ static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd, if (ret < 0) goto dai_trigger_end; - ret = rsnd_platform_call(priv, dai, stop, ssi_id); + ret = rsnd_gen_path_exit(priv, rdai, io); if (ret < 0) goto dai_trigger_end; + ret = rsnd_platform_call(priv, dai, stop, ssi_id); + if (ret < 0) + goto dai_trigger_end; break; default: ret = -EINVAL; @@ -572,6 +623,10 @@ static int rsnd_probe(struct platform_device *pdev) /* * init each module */ + ret = rsnd_gen_probe(pdev, info, priv); + if (ret < 0) + return ret; + ret = rsnd_dai_probe(pdev, info, priv); if (ret < 0) return ret; @@ -615,6 +670,7 @@ static int rsnd_remove(struct platform_device *pdev) * remove each module */ rsnd_dai_remove(pdev, priv); + rsnd_gen_remove(pdev, priv); return 0; } diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c new file mode 100644 index 0000000..ec67a79 --- /dev/null +++ b/sound/soc/sh/rcar/gen.c @@ -0,0 +1,154 @@ +/* + * Renesas R-Car Gen1 SRU/SSI support + * + * Copyright (C) 2013 Renesas Solutions Corp. + * Kuninori Morimoto + * + * 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. + */ +#include "rsnd.h" + +struct rsnd_gen_ops { + int (*path_init)(struct rsnd_priv *priv, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io); + int (*path_exit)(struct rsnd_priv *priv, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io); +}; + +struct rsnd_gen_reg_map { + int index; /* -1 : not supported */ + u32 offset_id; /* offset of ssi0, ssi1, ssi2... */ + u32 offset_adr; /* offset of SSICR, SSISR, ... */ +}; + +struct rsnd_gen { + void __iomem *base[RSND_BASE_MAX]; + + struct rsnd_gen_reg_map reg_map[RSND_REG_MAX]; + struct rsnd_gen_ops *ops; +}; + +#define rsnd_priv_to_gen(p) ((struct rsnd_gen *)(p)->gen) + +#define rsnd_is_gen1(s) ((s)->info->flags & RSND_GEN1) +#define rsnd_is_gen2(s) ((s)->info->flags & RSND_GEN2) + +/* + * Gen2 + * will be filled in the future + */ + +/* + * Gen1 + */ +static int rsnd_gen1_probe(struct platform_device *pdev, + struct rcar_snd_info *info, + struct rsnd_priv *priv) +{ + return 0; +} + +static void rsnd_gen1_remove(struct platform_device *pdev, + struct rsnd_priv *priv) +{ +} + +/* + * Gen + */ +int rsnd_gen_path_init(struct rsnd_priv *priv, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_gen *gen = rsnd_priv_to_gen(priv); + + return gen->ops->path_init(priv, rdai, io); +} + +int rsnd_gen_path_exit(struct rsnd_priv *priv, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_gen *gen = rsnd_priv_to_gen(priv); + + return gen->ops->path_exit(priv, rdai, io); +} + +void __iomem *rsnd_gen_reg_get(struct rsnd_priv *priv, + struct rsnd_mod *mod, + enum rsnd_reg reg) +{ + struct rsnd_gen *gen = rsnd_priv_to_gen(priv); + struct device *dev = rsnd_priv_to_dev(priv); + int index; + u32 offset_id, offset_adr; + + if (reg >= RSND_REG_MAX) { + dev_err(dev, "rsnd_reg reg error\n"); + return NULL; + } + + index = gen->reg_map[reg].index; + offset_id = gen->reg_map[reg].offset_id; + offset_adr = gen->reg_map[reg].offset_adr; + + if (index < 0) { + dev_err(dev, "unsupported reg access %d\n", reg); + return NULL; + } + + if (offset_id && mod) + offset_id *= rsnd_mod_id(mod); + + /* + * index/offset were set on gen1/gen2 + */ + + return gen->base[index] + offset_id + offset_adr; +} + +int rsnd_gen_probe(struct platform_device *pdev, + struct rcar_snd_info *info, + struct rsnd_priv *priv) +{ + struct device *dev = rsnd_priv_to_dev(priv); + struct rsnd_gen *gen; + int i; + + gen = devm_kzalloc(dev, sizeof(*gen), GFP_KERNEL); + if (!gen) { + dev_err(dev, "GEN allocate failed\n"); + return -ENOMEM; + } + + priv->gen = gen; + + /* + * see + * rsnd_reg_get() + * rsnd_gen_probe() + */ + for (i = 0; i < RSND_REG_MAX; i++) + gen->reg_map[i].index = -1; + + /* + * init each module + */ + if (rsnd_is_gen1(priv)) + return rsnd_gen1_probe(pdev, info, priv); + + dev_err(dev, "unknown generation R-Car sound device\n"); + + return -ENODEV; +} + +void rsnd_gen_remove(struct platform_device *pdev, + struct rsnd_priv *priv) +{ + if (rsnd_is_gen1(priv)) + rsnd_gen1_remove(pdev, priv); +} diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 65d3835..8cc3641 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -27,12 +27,36 @@ * This driver uses pseudo register in order to hide it. * see gen1/gen2 for detail */ +enum rsnd_reg { + RSND_REG_MAX, +}; + struct rsnd_priv; struct rsnd_mod; struct rsnd_dai; struct rsnd_dai_stream; /* + * R-Car basic functions + */ +#define rsnd_mod_read(m, r) \ + rsnd_read(rsnd_mod_to_priv(m), m, RSND_REG_##r) +#define rsnd_mod_write(m, r, d) \ + rsnd_write(rsnd_mod_to_priv(m), m, RSND_REG_##r, d) +#define rsnd_mod_bset(m, r, s, d) \ + rsnd_bset(rsnd_mod_to_priv(m), m, RSND_REG_##r, s, d) + +#define rsnd_priv_read(p, r) rsnd_read(p, NULL, RSND_REG_##r) +#define rsnd_priv_write(p, r, d) rsnd_write(p, NULL, RSND_REG_##r, d) +#define rsnd_priv_bset(p, r, s, d) rsnd_bset(p, NULL, RSND_REG_##r, s, d) + +u32 rsnd_read(struct rsnd_priv *priv, struct rsnd_mod *mod, enum rsnd_reg reg); +void rsnd_write(struct rsnd_priv *priv, struct rsnd_mod *mod, + enum rsnd_reg reg, u32 data); +void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod, enum rsnd_reg reg, + u32 mask, u32 data); + +/* * R-Car sound mod */ @@ -117,6 +141,24 @@ void rsnd_dai_pointer_update(struct rsnd_dai_stream *io, int cnt); int rsnd_dai_pointer_offset(struct rsnd_dai_stream *io, int additional); /* + * R-Car Gen1/Gen2 + */ +int rsnd_gen_probe(struct platform_device *pdev, + struct rcar_snd_info *info, + struct rsnd_priv *priv); +void rsnd_gen_remove(struct platform_device *pdev, + struct rsnd_priv *priv); +int rsnd_gen_path_init(struct rsnd_priv *priv, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io); +int rsnd_gen_path_exit(struct rsnd_priv *priv, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io); +void __iomem *rsnd_gen_reg_get(struct rsnd_priv *priv, + struct rsnd_mod *mod, + enum rsnd_reg reg); + +/* * R-Car sound priv */ struct rsnd_priv { @@ -126,6 +168,11 @@ struct rsnd_priv { spinlock_t lock; /* + * below value will be filled on rsnd_gen_probe() + */ + void *gen; + + /* * below value will be filled on rsnd_dai_probe() */ struct snd_soc_dai_driver *daidrv; -- cgit v0.10.2 From 07539c1de82cdc0ecbe72b413762b2e920407227 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Sun, 21 Jul 2013 21:36:35 -0700 Subject: ASoC: add Renesas R-Car SCU feature Renesas R-Car series sound circuit consists of SSI and its peripheral. But this peripheral circuit is different between R-Car Generation1 (E1/M1/H1) and Generation2 (E2/M2/H2) (Actually, there are many difference in Generation1 chips) This patch adds SCU feature on this driver. But, it defines SCU style only, does nothing at this point. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown diff --git a/include/sound/rcar_snd.h b/include/sound/rcar_snd.h index 14942a8..01f2e45 100644 --- a/include/sound/rcar_snd.h +++ b/include/sound/rcar_snd.h @@ -14,8 +14,15 @@ #include +#define RSND_GEN1_SRU 0 -#define RSND_BASE_MAX 0 +#define RSND_GEN2_SRU 0 + +#define RSND_BASE_MAX 1 + +struct rsnd_scu_platform_info { + u32 flags; +}; struct rsnd_dai_platform_info { int ssi_id_playback; @@ -34,6 +41,8 @@ struct rsnd_dai_platform_info { struct rcar_snd_info { u32 flags; + struct rsnd_scu_platform_info *scu_info; + int scu_info_nr; struct rsnd_dai_platform_info *dai_info; int dai_info_nr; int (*start)(int id); diff --git a/sound/soc/sh/rcar/Makefile b/sound/soc/sh/rcar/Makefile index b2d313b..112b2cf 100644 --- a/sound/soc/sh/rcar/Makefile +++ b/sound/soc/sh/rcar/Makefile @@ -1,2 +1,2 @@ -snd-soc-rcar-objs := core.o gen.o -obj-$(CONFIG_SND_SOC_RCAR) += snd-soc-rcar.o +snd-soc-rcar-objs := core.o gen.o scu.o +obj-$(CONFIG_SND_SOC_RCAR) += snd-soc-rcar.o \ No newline at end of file diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index bb8959f..02d736b 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -631,6 +631,10 @@ static int rsnd_probe(struct platform_device *pdev) if (ret < 0) return ret; + ret = rsnd_scu_probe(pdev, info, priv); + if (ret < 0) + return ret; + /* * asoc register */ @@ -669,6 +673,7 @@ static int rsnd_remove(struct platform_device *pdev) /* * remove each module */ + rsnd_scu_remove(pdev, priv); rsnd_dai_remove(pdev, priv); rsnd_gen_remove(pdev, priv); diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c index ec67a79..2934c0d 100644 --- a/sound/soc/sh/rcar/gen.c +++ b/sound/soc/sh/rcar/gen.c @@ -45,10 +45,105 @@ struct rsnd_gen { /* * Gen1 */ +static int rsnd_gen1_path_init(struct rsnd_priv *priv, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_dai_platform_info *info = rsnd_dai_get_platform_info(rdai); + struct rsnd_mod *mod; + int ret; + int id; + + /* + * Gen1 is created by SRU/SSI, and this SRU is base module of + * Gen2's SCU/SSIU/SSI. (Gen2 SCU/SSIU came from SRU) + * + * Easy image is.. + * Gen1 SRU = Gen2 SCU + SSIU + etc + * + * Gen2 SCU path is very flexible, but, Gen1 SRU (SCU parts) is + * using fixed path. + * + * Then, SSI id = SCU id here + */ + + if (rsnd_dai_is_play(rdai, io)) + id = info->ssi_id_playback; + else + id = info->ssi_id_capture; + + /* SCU */ + mod = rsnd_scu_mod_get(priv, id); + ret = rsnd_dai_connect(rdai, mod, io); + + return ret; +} + +static int rsnd_gen1_path_exit(struct rsnd_priv *priv, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_mod *mod, *n; + int ret = 0; + + /* + * remove all mod from rdai + */ + for_each_rsnd_mod(mod, n, io) + ret |= rsnd_dai_disconnect(mod); + + return ret; +} + +static struct rsnd_gen_ops rsnd_gen1_ops = { + .path_init = rsnd_gen1_path_init, + .path_exit = rsnd_gen1_path_exit, +}; + +#define RSND_GEN1_REG_MAP(g, s, i, oi, oa) \ + do { \ + (g)->reg_map[RSND_REG_##i].index = RSND_GEN1_##s; \ + (g)->reg_map[RSND_REG_##i].offset_id = oi; \ + (g)->reg_map[RSND_REG_##i].offset_adr = oa; \ + } while (0) + +static void rsnd_gen1_reg_map_init(struct rsnd_gen *gen) +{ + RSND_GEN1_REG_MAP(gen, SRU, SSI_MODE0, 0x0, 0xD0); + RSND_GEN1_REG_MAP(gen, SRU, SSI_MODE1, 0x0, 0xD4); +} + static int rsnd_gen1_probe(struct platform_device *pdev, struct rcar_snd_info *info, struct rsnd_priv *priv) { + struct device *dev = rsnd_priv_to_dev(priv); + struct rsnd_gen *gen = rsnd_priv_to_gen(priv); + struct resource *sru_res; + + /* + * map address + */ + sru_res = platform_get_resource(pdev, IORESOURCE_MEM, RSND_GEN1_SRU); + if (!sru_res) { + dev_err(dev, "Not enough SRU/SSI/ADG platform resources.\n"); + return -ENODEV; + } + + gen->ops = &rsnd_gen1_ops; + + gen->base[RSND_GEN1_SRU] = devm_ioremap_resource(dev, sru_res); + if (!gen->base[RSND_GEN1_SRU]) { + dev_err(dev, "SRU/SSI/ADG ioremap failed\n"); + return -ENODEV; + } + + rsnd_gen1_reg_map_init(gen); + + dev_dbg(dev, "Gen1 device probed\n"); + dev_dbg(dev, "SRU : %08x => %p\n", sru_res->start, + gen->base[RSND_GEN1_SRU]); + return 0; } diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 8cc3641..95a391f 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -28,6 +28,10 @@ * see gen1/gen2 for detail */ enum rsnd_reg { + /* SRU/SCU */ + RSND_REG_SSI_MODE0, + RSND_REG_SSI_MODE1, + RSND_REG_MAX, }; @@ -173,6 +177,12 @@ struct rsnd_priv { void *gen; /* + * below value will be filled on rsnd_scu_probe() + */ + void *scu; + int scu_nr; + + /* * below value will be filled on rsnd_dai_probe() */ struct snd_soc_dai_driver *daidrv; @@ -184,4 +194,15 @@ struct rsnd_priv { #define rsnd_lock(priv, flags) spin_lock_irqsave(&priv->lock, flags) #define rsnd_unlock(priv, flags) spin_unlock_irqrestore(&priv->lock, flags) +/* + * R-Car SCU + */ +int rsnd_scu_probe(struct platform_device *pdev, + struct rcar_snd_info *info, + struct rsnd_priv *priv); +void rsnd_scu_remove(struct platform_device *pdev, + struct rsnd_priv *priv); +struct rsnd_mod *rsnd_scu_mod_get(struct rsnd_priv *priv, int id); +#define rsnd_scu_nr(priv) ((priv)->scu_nr) + #endif diff --git a/sound/soc/sh/rcar/scu.c b/sound/soc/sh/rcar/scu.c new file mode 100644 index 0000000..c12e65f --- /dev/null +++ b/sound/soc/sh/rcar/scu.c @@ -0,0 +1,125 @@ +/* + * Renesas R-Car SCU support + * + * Copyright (C) 2013 Renesas Solutions Corp. + * Kuninori Morimoto + * + * 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. + */ +#include "rsnd.h" + +struct rsnd_scu { + struct rsnd_scu_platform_info *info; /* rcar_snd.h */ + struct rsnd_mod mod; +}; + +#define rsnd_mod_to_scu(_mod) \ + container_of((_mod), struct rsnd_scu, mod) + +#define for_each_rsnd_scu(pos, priv, i) \ + for ((i) = 0; \ + ((i) < rsnd_scu_nr(priv)) && \ + ((pos) = (struct rsnd_scu *)(priv)->scu + i); \ + i++) + +static int rsnd_scu_init(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct device *dev = rsnd_priv_to_dev(priv); + + dev_dbg(dev, "%s.%d init\n", rsnd_mod_name(mod), rsnd_mod_id(mod)); + + return 0; +} + +static int rsnd_scu_quit(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct device *dev = rsnd_priv_to_dev(priv); + + dev_dbg(dev, "%s.%d quit\n", rsnd_mod_name(mod), rsnd_mod_id(mod)); + + return 0; +} + +static int rsnd_scu_start(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct device *dev = rsnd_priv_to_dev(priv); + + dev_dbg(dev, "%s.%d start\n", rsnd_mod_name(mod), rsnd_mod_id(mod)); + + return 0; +} + +static int rsnd_scu_stop(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct device *dev = rsnd_priv_to_dev(priv); + + dev_dbg(dev, "%s.%d stop\n", rsnd_mod_name(mod), rsnd_mod_id(mod)); + + return 0; +} + +static struct rsnd_mod_ops rsnd_scu_ops = { + .name = "scu", + .init = rsnd_scu_init, + .quit = rsnd_scu_quit, + .start = rsnd_scu_start, + .stop = rsnd_scu_stop, +}; + +struct rsnd_mod *rsnd_scu_mod_get(struct rsnd_priv *priv, int id) +{ + BUG_ON(id < 0 || id >= rsnd_scu_nr(priv)); + + return &((struct rsnd_scu *)(priv->scu) + id)->mod; +} + +int rsnd_scu_probe(struct platform_device *pdev, + struct rcar_snd_info *info, + struct rsnd_priv *priv) +{ + struct device *dev = rsnd_priv_to_dev(priv); + struct rsnd_scu *scu; + int i, nr; + + /* + * init SCU + */ + nr = info->scu_info_nr; + scu = devm_kzalloc(dev, sizeof(*scu) * nr, GFP_KERNEL); + if (!scu) { + dev_err(dev, "SCU allocate failed\n"); + return -ENOMEM; + } + + priv->scu_nr = nr; + priv->scu = scu; + + for_each_rsnd_scu(scu, priv, i) { + rsnd_mod_init(priv, &scu->mod, + &rsnd_scu_ops, i); + scu->info = &info->scu_info[i]; + } + + dev_dbg(dev, "scu probed\n"); + + return 0; +} + +void rsnd_scu_remove(struct platform_device *pdev, + struct rsnd_priv *priv) +{ +} -- cgit v0.10.2 From dfc9403b7c1f566bb099a12c58aee20589e390f1 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Sun, 21 Jul 2013 21:36:46 -0700 Subject: ASoC: add Renesas R-Car ADG feature Renesas R-Car series sound circuit consists of SSI and its peripheral. But this peripheral circuit is different between R-Car Generation1 (E1/M1/H1) and Generation2 (E2/M2/H2) (Actually, there are many difference in Generation1 chips) This patch adds ADG feature which controls sound clock Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown diff --git a/include/sound/rcar_snd.h b/include/sound/rcar_snd.h index 01f2e45..6babd6f 100644 --- a/include/sound/rcar_snd.h +++ b/include/sound/rcar_snd.h @@ -15,10 +15,12 @@ #include #define RSND_GEN1_SRU 0 +#define RSND_GEN1_ADG 1 #define RSND_GEN2_SRU 0 +#define RSND_GEN2_ADG 1 -#define RSND_BASE_MAX 1 +#define RSND_BASE_MAX 2 struct rsnd_scu_platform_info { u32 flags; diff --git a/sound/soc/sh/rcar/Makefile b/sound/soc/sh/rcar/Makefile index 112b2cf..c11280c 100644 --- a/sound/soc/sh/rcar/Makefile +++ b/sound/soc/sh/rcar/Makefile @@ -1,2 +1,2 @@ -snd-soc-rcar-objs := core.o gen.o scu.o +snd-soc-rcar-objs := core.o gen.o scu.o adg.o obj-$(CONFIG_SND_SOC_RCAR) += snd-soc-rcar.o \ No newline at end of file diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c new file mode 100644 index 0000000..d80deb7 --- /dev/null +++ b/sound/soc/sh/rcar/adg.c @@ -0,0 +1,234 @@ +/* + * Helper routines for R-Car sound ADG. + * + * Copyright (C) 2013 Kuninori Morimoto + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ +#include +#include +#include "rsnd.h" + +#define CLKA 0 +#define CLKB 1 +#define CLKC 2 +#define CLKI 3 +#define CLKMAX 4 + +struct rsnd_adg { + struct clk *clk[CLKMAX]; + + int rate_of_441khz_div_6; + int rate_of_48khz_div_6; +}; + +#define for_each_rsnd_clk(pos, adg, i) \ + for (i = 0, (pos) = adg->clk[i]; \ + i < CLKMAX; \ + i++, (pos) = adg->clk[i]) +#define rsnd_priv_to_adg(priv) ((struct rsnd_adg *)(priv)->adg) + +static enum rsnd_reg rsnd_adg_ssi_reg_get(int id) +{ + enum rsnd_reg reg; + + /* + * SSI 8 is not connected to ADG. + * it works with SSI 7 + */ + if (id == 8) + return RSND_REG_MAX; + + if (0 <= id && id <= 3) + reg = RSND_REG_AUDIO_CLK_SEL0; + else if (4 <= id && id <= 7) + reg = RSND_REG_AUDIO_CLK_SEL1; + else + reg = RSND_REG_AUDIO_CLK_SEL2; + + return reg; +} + +int rsnd_adg_ssi_clk_stop(struct rsnd_mod *mod) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + enum rsnd_reg reg; + int id; + + /* + * "mod" = "ssi" here. + * we can get "ssi id" from mod + */ + id = rsnd_mod_id(mod); + reg = rsnd_adg_ssi_reg_get(id); + + rsnd_write(priv, mod, reg, 0); + + return 0; +} + +int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *mod, unsigned int rate) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct rsnd_adg *adg = rsnd_priv_to_adg(priv); + struct device *dev = rsnd_priv_to_dev(priv); + struct clk *clk; + enum rsnd_reg reg; + int id, shift, i; + u32 data; + int sel_table[] = { + [CLKA] = 0x1, + [CLKB] = 0x2, + [CLKC] = 0x3, + [CLKI] = 0x0, + }; + + dev_dbg(dev, "request clock = %d\n", rate); + + /* + * find suitable clock from + * AUDIO_CLKA/AUDIO_CLKB/AUDIO_CLKC/AUDIO_CLKI. + */ + data = 0; + for_each_rsnd_clk(clk, adg, i) { + if (rate == clk_get_rate(clk)) { + data = sel_table[i]; + goto found_clock; + } + } + + /* + * find 1/6 clock from BRGA/BRGB + */ + if (rate == adg->rate_of_441khz_div_6) { + data = 0x10; + goto found_clock; + } + + if (rate == adg->rate_of_48khz_div_6) { + data = 0x20; + goto found_clock; + } + + return -EIO; + +found_clock: + + /* + * This "mod" = "ssi" here. + * we can get "ssi id" from mod + */ + id = rsnd_mod_id(mod); + reg = rsnd_adg_ssi_reg_get(id); + + dev_dbg(dev, "ADG: ssi%d selects clk%d = %d", id, i, rate); + + /* + * Enable SSIx clock + */ + shift = (id % 4) * 8; + + rsnd_bset(priv, mod, reg, + 0xFF << shift, + data << shift); + + return 0; +} + +static void rsnd_adg_ssi_clk_init(struct rsnd_priv *priv, struct rsnd_adg *adg) +{ + struct clk *clk; + unsigned long rate; + u32 ckr; + int i; + int brg_table[] = { + [CLKA] = 0x0, + [CLKB] = 0x1, + [CLKC] = 0x4, + [CLKI] = 0x2, + }; + + /* + * This driver is assuming that AUDIO_CLKA/AUDIO_CLKB/AUDIO_CLKC + * have 44.1kHz or 48kHz base clocks for now. + * + * SSI itself can divide parent clock by 1/1 - 1/16 + * So, BRGA outputs 44.1kHz base parent clock 1/32, + * and, BRGB outputs 48.0kHz base parent clock 1/32 here. + * see + * rsnd_adg_ssi_clk_try_start() + */ + ckr = 0; + adg->rate_of_441khz_div_6 = 0; + adg->rate_of_48khz_div_6 = 0; + for_each_rsnd_clk(clk, adg, i) { + rate = clk_get_rate(clk); + + if (0 == rate) /* not used */ + continue; + + /* RBGA */ + if (!adg->rate_of_441khz_div_6 && (0 == rate % 44100)) { + adg->rate_of_441khz_div_6 = rate / 6; + ckr |= brg_table[i] << 20; + } + + /* RBGB */ + if (!adg->rate_of_48khz_div_6 && (0 == rate % 48000)) { + adg->rate_of_48khz_div_6 = rate / 6; + ckr |= brg_table[i] << 16; + } + } + + rsnd_priv_bset(priv, SSICKR, 0x00FF0000, ckr); + rsnd_priv_write(priv, BRRA, 0x00000002); /* 1/6 */ + rsnd_priv_write(priv, BRRB, 0x00000002); /* 1/6 */ +} + +int rsnd_adg_probe(struct platform_device *pdev, + struct rcar_snd_info *info, + struct rsnd_priv *priv) +{ + struct rsnd_adg *adg; + struct device *dev = rsnd_priv_to_dev(priv); + struct clk *clk; + int i; + + adg = devm_kzalloc(dev, sizeof(*adg), GFP_KERNEL); + if (!adg) { + dev_err(dev, "ADG allocate failed\n"); + return -ENOMEM; + } + + adg->clk[CLKA] = clk_get(NULL, "audio_clk_a"); + adg->clk[CLKB] = clk_get(NULL, "audio_clk_b"); + adg->clk[CLKC] = clk_get(NULL, "audio_clk_c"); + adg->clk[CLKI] = clk_get(NULL, "audio_clk_internal"); + for_each_rsnd_clk(clk, adg, i) { + if (IS_ERR(clk)) { + dev_err(dev, "Audio clock failed\n"); + return -EIO; + } + } + + rsnd_adg_ssi_clk_init(priv, adg); + + priv->adg = adg; + + dev_dbg(dev, "adg probed\n"); + + return 0; +} + +void rsnd_adg_remove(struct platform_device *pdev, + struct rsnd_priv *priv) +{ + struct rsnd_adg *adg = priv->adg; + struct clk *clk; + int i; + + for_each_rsnd_clk(clk, adg, i) + clk_put(clk); +} diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index 02d736b..e588d8a 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -635,6 +635,10 @@ static int rsnd_probe(struct platform_device *pdev) if (ret < 0) return ret; + ret = rsnd_adg_probe(pdev, info, priv); + if (ret < 0) + return ret; + /* * asoc register */ @@ -673,6 +677,7 @@ static int rsnd_remove(struct platform_device *pdev) /* * remove each module */ + rsnd_adg_remove(pdev, priv); rsnd_scu_remove(pdev, priv); rsnd_dai_remove(pdev, priv); rsnd_gen_remove(pdev, priv); diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c index 2934c0d..ed21a13 100644 --- a/sound/soc/sh/rcar/gen.c +++ b/sound/soc/sh/rcar/gen.c @@ -111,6 +111,15 @@ static void rsnd_gen1_reg_map_init(struct rsnd_gen *gen) { RSND_GEN1_REG_MAP(gen, SRU, SSI_MODE0, 0x0, 0xD0); RSND_GEN1_REG_MAP(gen, SRU, SSI_MODE1, 0x0, 0xD4); + + RSND_GEN1_REG_MAP(gen, ADG, BRRA, 0x0, 0x00); + RSND_GEN1_REG_MAP(gen, ADG, BRRB, 0x0, 0x04); + RSND_GEN1_REG_MAP(gen, ADG, SSICKR, 0x0, 0x08); + RSND_GEN1_REG_MAP(gen, ADG, AUDIO_CLK_SEL0, 0x0, 0x0c); + RSND_GEN1_REG_MAP(gen, ADG, AUDIO_CLK_SEL1, 0x0, 0x10); + RSND_GEN1_REG_MAP(gen, ADG, AUDIO_CLK_SEL3, 0x0, 0x18); + RSND_GEN1_REG_MAP(gen, ADG, AUDIO_CLK_SEL4, 0x0, 0x1c); + RSND_GEN1_REG_MAP(gen, ADG, AUDIO_CLK_SEL5, 0x0, 0x20); } static int rsnd_gen1_probe(struct platform_device *pdev, @@ -120,12 +129,15 @@ static int rsnd_gen1_probe(struct platform_device *pdev, struct device *dev = rsnd_priv_to_dev(priv); struct rsnd_gen *gen = rsnd_priv_to_gen(priv); struct resource *sru_res; + struct resource *adg_res; /* * map address */ sru_res = platform_get_resource(pdev, IORESOURCE_MEM, RSND_GEN1_SRU); - if (!sru_res) { + adg_res = platform_get_resource(pdev, IORESOURCE_MEM, RSND_GEN1_ADG); + if (!sru_res || + !adg_res) { dev_err(dev, "Not enough SRU/SSI/ADG platform resources.\n"); return -ENODEV; } @@ -133,7 +145,9 @@ static int rsnd_gen1_probe(struct platform_device *pdev, gen->ops = &rsnd_gen1_ops; gen->base[RSND_GEN1_SRU] = devm_ioremap_resource(dev, sru_res); - if (!gen->base[RSND_GEN1_SRU]) { + gen->base[RSND_GEN1_ADG] = devm_ioremap_resource(dev, adg_res); + if (!gen->base[RSND_GEN1_SRU] || + !gen->base[RSND_GEN1_ADG]) { dev_err(dev, "SRU/SSI/ADG ioremap failed\n"); return -ENODEV; } @@ -143,6 +157,8 @@ static int rsnd_gen1_probe(struct platform_device *pdev, dev_dbg(dev, "Gen1 device probed\n"); dev_dbg(dev, "SRU : %08x => %p\n", sru_res->start, gen->base[RSND_GEN1_SRU]); + dev_dbg(dev, "ADG : %08x => %p\n", adg_res->start, + gen->base[RSND_GEN1_ADG]); return 0; } diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 95a391f..344fd59 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -32,6 +32,17 @@ enum rsnd_reg { RSND_REG_SSI_MODE0, RSND_REG_SSI_MODE1, + /* ADG */ + RSND_REG_BRRA, + RSND_REG_BRRB, + RSND_REG_SSICKR, + RSND_REG_AUDIO_CLK_SEL0, + RSND_REG_AUDIO_CLK_SEL1, + RSND_REG_AUDIO_CLK_SEL2, + RSND_REG_AUDIO_CLK_SEL3, + RSND_REG_AUDIO_CLK_SEL4, + RSND_REG_AUDIO_CLK_SEL5, + RSND_REG_MAX, }; @@ -163,6 +174,17 @@ void __iomem *rsnd_gen_reg_get(struct rsnd_priv *priv, enum rsnd_reg reg); /* + * R-Car ADG + */ +int rsnd_adg_ssi_clk_stop(struct rsnd_mod *mod); +int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *mod, unsigned int rate); +int rsnd_adg_probe(struct platform_device *pdev, + struct rcar_snd_info *info, + struct rsnd_priv *priv); +void rsnd_adg_remove(struct platform_device *pdev, + struct rsnd_priv *priv); + +/* * R-Car sound priv */ struct rsnd_priv { @@ -183,6 +205,11 @@ struct rsnd_priv { int scu_nr; /* + * below value will be filled on rsnd_adg_probe() + */ + void *adg; + + /* * below value will be filled on rsnd_dai_probe() */ struct snd_soc_dai_driver *daidrv; -- cgit v0.10.2 From ae5c322303fff50b93d60e34c6563f1264a5941b Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Sun, 21 Jul 2013 21:36:57 -0700 Subject: ASoC: add Renesas R-Car SSI feature Renesas R-Car series sound circuit consists of SSI and its peripheral. But this peripheral circuit is different between R-Car Generation1 (E1/M1/H1) and Generation2 (E2/M2/H2) (Actually, there are many difference in Generation1 chips) As 1st protype, this patch adds SSI feature on this driver. But, it is PIO sound playback support only at this point. The DMA transfer, and capture feature will be supported in the future Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown diff --git a/include/sound/rcar_snd.h b/include/sound/rcar_snd.h index 6babd6f..99d8dd0 100644 --- a/include/sound/rcar_snd.h +++ b/include/sound/rcar_snd.h @@ -16,11 +16,30 @@ #define RSND_GEN1_SRU 0 #define RSND_GEN1_ADG 1 +#define RSND_GEN1_SSI 2 #define RSND_GEN2_SRU 0 #define RSND_GEN2_ADG 1 +#define RSND_GEN2_SSIU 2 +#define RSND_GEN2_SSI 3 -#define RSND_BASE_MAX 2 +#define RSND_BASE_MAX 4 + +/* + * flags + * + * 0xA0000000 + * + * A : clock sharing settings + */ +#define RSND_SSI_CLK_PIN_SHARE (1 << 31) +#define RSND_SSI_CLK_FROM_ADG (1 << 30) /* clock parent is master */ +#define RSND_SSI_SYNC (1 << 29) /* SSI34_sync etc */ + +struct rsnd_ssi_platform_info { + int pio_irq; + u32 flags; +}; struct rsnd_scu_platform_info { u32 flags; @@ -43,6 +62,8 @@ struct rsnd_dai_platform_info { struct rcar_snd_info { u32 flags; + struct rsnd_ssi_platform_info *ssi_info; + int ssi_info_nr; struct rsnd_scu_platform_info *scu_info; int scu_info_nr; struct rsnd_dai_platform_info *dai_info; diff --git a/sound/soc/sh/rcar/Makefile b/sound/soc/sh/rcar/Makefile index c11280c..0ff492d 100644 --- a/sound/soc/sh/rcar/Makefile +++ b/sound/soc/sh/rcar/Makefile @@ -1,2 +1,2 @@ -snd-soc-rcar-objs := core.o gen.o scu.o adg.o +snd-soc-rcar-objs := core.o gen.o scu.o adg.o ssi.o obj-$(CONFIG_SND_SOC_RCAR) += snd-soc-rcar.o \ No newline at end of file diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index e588d8a..9a5469d 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -639,6 +639,10 @@ static int rsnd_probe(struct platform_device *pdev) if (ret < 0) return ret; + ret = rsnd_ssi_probe(pdev, info, priv); + if (ret < 0) + return ret; + /* * asoc register */ @@ -677,6 +681,7 @@ static int rsnd_remove(struct platform_device *pdev) /* * remove each module */ + rsnd_ssi_remove(pdev, priv); rsnd_adg_remove(pdev, priv); rsnd_scu_remove(pdev, priv); rsnd_dai_remove(pdev, priv); diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c index ed21a13..5e4ae0d 100644 --- a/sound/soc/sh/rcar/gen.c +++ b/sound/soc/sh/rcar/gen.c @@ -72,6 +72,12 @@ static int rsnd_gen1_path_init(struct rsnd_priv *priv, else id = info->ssi_id_capture; + /* SSI */ + mod = rsnd_ssi_mod_get(priv, id); + ret = rsnd_dai_connect(rdai, mod, io); + if (ret < 0) + return ret; + /* SCU */ mod = rsnd_scu_mod_get(priv, id); ret = rsnd_dai_connect(rdai, mod, io); @@ -120,6 +126,12 @@ static void rsnd_gen1_reg_map_init(struct rsnd_gen *gen) RSND_GEN1_REG_MAP(gen, ADG, AUDIO_CLK_SEL3, 0x0, 0x18); RSND_GEN1_REG_MAP(gen, ADG, AUDIO_CLK_SEL4, 0x0, 0x1c); RSND_GEN1_REG_MAP(gen, ADG, AUDIO_CLK_SEL5, 0x0, 0x20); + + RSND_GEN1_REG_MAP(gen, SSI, SSICR, 0x40, 0x00); + RSND_GEN1_REG_MAP(gen, SSI, SSISR, 0x40, 0x04); + RSND_GEN1_REG_MAP(gen, SSI, SSITDR, 0x40, 0x08); + RSND_GEN1_REG_MAP(gen, SSI, SSIRDR, 0x40, 0x0c); + RSND_GEN1_REG_MAP(gen, SSI, SSIWSR, 0x40, 0x20); } static int rsnd_gen1_probe(struct platform_device *pdev, @@ -130,14 +142,17 @@ static int rsnd_gen1_probe(struct platform_device *pdev, struct rsnd_gen *gen = rsnd_priv_to_gen(priv); struct resource *sru_res; struct resource *adg_res; + struct resource *ssi_res; /* * map address */ sru_res = platform_get_resource(pdev, IORESOURCE_MEM, RSND_GEN1_SRU); adg_res = platform_get_resource(pdev, IORESOURCE_MEM, RSND_GEN1_ADG); + ssi_res = platform_get_resource(pdev, IORESOURCE_MEM, RSND_GEN1_SSI); if (!sru_res || - !adg_res) { + !adg_res || + !ssi_res) { dev_err(dev, "Not enough SRU/SSI/ADG platform resources.\n"); return -ENODEV; } @@ -146,8 +161,10 @@ static int rsnd_gen1_probe(struct platform_device *pdev, gen->base[RSND_GEN1_SRU] = devm_ioremap_resource(dev, sru_res); gen->base[RSND_GEN1_ADG] = devm_ioremap_resource(dev, adg_res); + gen->base[RSND_GEN1_SSI] = devm_ioremap_resource(dev, ssi_res); if (!gen->base[RSND_GEN1_SRU] || - !gen->base[RSND_GEN1_ADG]) { + !gen->base[RSND_GEN1_ADG] || + !gen->base[RSND_GEN1_SSI]) { dev_err(dev, "SRU/SSI/ADG ioremap failed\n"); return -ENODEV; } @@ -159,8 +176,11 @@ static int rsnd_gen1_probe(struct platform_device *pdev, gen->base[RSND_GEN1_SRU]); dev_dbg(dev, "ADG : %08x => %p\n", adg_res->start, gen->base[RSND_GEN1_ADG]); + dev_dbg(dev, "SSI : %08x => %p\n", ssi_res->start, + gen->base[RSND_GEN1_SSI]); return 0; + } static void rsnd_gen1_remove(struct platform_device *pdev, diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 344fd59..0e7727c 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -43,6 +43,13 @@ enum rsnd_reg { RSND_REG_AUDIO_CLK_SEL4, RSND_REG_AUDIO_CLK_SEL5, + /* SSI */ + RSND_REG_SSICR, + RSND_REG_SSISR, + RSND_REG_SSITDR, + RSND_REG_SSIRDR, + RSND_REG_SSIWSR, + RSND_REG_MAX, }; @@ -151,6 +158,7 @@ int rsnd_dai_connect(struct rsnd_dai *rdai, struct rsnd_mod *mod, struct rsnd_dai_stream *io); int rsnd_dai_is_play(struct rsnd_dai *rdai, struct rsnd_dai_stream *io); #define rsnd_dai_get_platform_info(rdai) ((rdai)->info) +#define rsnd_io_to_runtime(io) ((io)->substream->runtime) void rsnd_dai_pointer_update(struct rsnd_dai_stream *io, int cnt); int rsnd_dai_pointer_offset(struct rsnd_dai_stream *io, int additional); @@ -210,6 +218,11 @@ struct rsnd_priv { void *adg; /* + * below value will be filled on rsnd_ssi_probe() + */ + void *ssiu; + + /* * below value will be filled on rsnd_dai_probe() */ struct snd_soc_dai_driver *daidrv; @@ -232,4 +245,14 @@ void rsnd_scu_remove(struct platform_device *pdev, struct rsnd_mod *rsnd_scu_mod_get(struct rsnd_priv *priv, int id); #define rsnd_scu_nr(priv) ((priv)->scu_nr) +/* + * R-Car SSI + */ +int rsnd_ssi_probe(struct platform_device *pdev, + struct rcar_snd_info *info, + struct rsnd_priv *priv); +void rsnd_ssi_remove(struct platform_device *pdev, + struct rsnd_priv *priv); +struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id); + #endif diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c new file mode 100644 index 0000000..061ac7e --- /dev/null +++ b/sound/soc/sh/rcar/ssi.c @@ -0,0 +1,588 @@ +/* + * Renesas R-Car SSIU/SSI support + * + * Copyright (C) 2013 Renesas Solutions Corp. + * Kuninori Morimoto + * + * Based on fsi.c + * Kuninori Morimoto + * + * 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. + */ +#include +#include "rsnd.h" +#define RSND_SSI_NAME_SIZE 16 + +/* + * SSICR + */ +#define FORCE (1 << 31) /* Fixed */ +#define UIEN (1 << 27) /* Underflow Interrupt Enable */ +#define OIEN (1 << 26) /* Overflow Interrupt Enable */ +#define IIEN (1 << 25) /* Idle Mode Interrupt Enable */ +#define DIEN (1 << 24) /* Data Interrupt Enable */ + +#define DWL_8 (0 << 19) /* Data Word Length */ +#define DWL_16 (1 << 19) /* Data Word Length */ +#define DWL_18 (2 << 19) /* Data Word Length */ +#define DWL_20 (3 << 19) /* Data Word Length */ +#define DWL_22 (4 << 19) /* Data Word Length */ +#define DWL_24 (5 << 19) /* Data Word Length */ +#define DWL_32 (6 << 19) /* Data Word Length */ + +#define SWL_32 (3 << 16) /* R/W System Word Length */ +#define SCKD (1 << 15) /* Serial Bit Clock Direction */ +#define SWSD (1 << 14) /* Serial WS Direction */ +#define SCKP (1 << 13) /* Serial Bit Clock Polarity */ +#define SWSP (1 << 12) /* Serial WS Polarity */ +#define SDTA (1 << 10) /* Serial Data Alignment */ +#define DEL (1 << 8) /* Serial Data Delay */ +#define CKDV(v) (v << 4) /* Serial Clock Division Ratio */ +#define TRMD (1 << 1) /* Transmit/Receive Mode Select */ +#define EN (1 << 0) /* SSI Module Enable */ + +/* + * SSISR + */ +#define UIRQ (1 << 27) /* Underflow Error Interrupt Status */ +#define OIRQ (1 << 26) /* Overflow Error Interrupt Status */ +#define IIRQ (1 << 25) /* Idle Mode Interrupt Status */ +#define DIRQ (1 << 24) /* Data Interrupt Status Flag */ + +struct rsnd_ssi { + struct clk *clk; + struct rsnd_ssi_platform_info *info; /* rcar_snd.h */ + struct rsnd_ssi *parent; + struct rsnd_mod mod; + + struct rsnd_dai *rdai; + struct rsnd_dai_stream *io; + u32 cr_own; + u32 cr_clk; + u32 cr_etc; + int err; + unsigned int usrcnt; + unsigned int rate; +}; + +struct rsnd_ssiu { + u32 ssi_mode0; + u32 ssi_mode1; + + int ssi_nr; + struct rsnd_ssi *ssi; +}; + +#define for_each_rsnd_ssi(pos, priv, i) \ + for (i = 0; \ + (i < rsnd_ssi_nr(priv)) && \ + ((pos) = ((struct rsnd_ssiu *)((priv)->ssiu))->ssi + i); \ + i++) + +#define rsnd_ssi_nr(priv) (((struct rsnd_ssiu *)((priv)->ssiu))->ssi_nr) +#define rsnd_mod_to_ssi(_mod) container_of((_mod), struct rsnd_ssi, mod) +#define rsnd_ssi_is_pio(ssi) ((ssi)->info->pio_irq > 0) +#define rsnd_ssi_clk_from_parent(ssi) ((ssi)->parent) +#define rsnd_rdai_is_clk_master(rdai) ((rdai)->clk_master) +#define rsnd_ssi_mode_flags(p) ((p)->info->flags) +#define rsnd_ssi_to_ssiu(ssi)\ + (((struct rsnd_ssiu *)((ssi) - rsnd_mod_id(&(ssi)->mod))) - 1) + +static void rsnd_ssi_mode_init(struct rsnd_priv *priv, + struct rsnd_ssiu *ssiu) +{ + struct rsnd_ssi *ssi; + u32 flags; + u32 val; + int i; + + /* + * SSI_MODE0 + */ + ssiu->ssi_mode0 = 0; + for_each_rsnd_ssi(ssi, priv, i) + ssiu->ssi_mode0 |= (1 << i); + + /* + * SSI_MODE1 + */ +#define ssi_parent_set(p, sync, adg, ext) \ + do { \ + ssi->parent = ssiu->ssi + p; \ + if (flags & RSND_SSI_CLK_FROM_ADG) \ + val = adg; \ + else \ + val = ext; \ + if (flags & RSND_SSI_SYNC) \ + val |= sync; \ + } while (0) + + ssiu->ssi_mode1 = 0; + for_each_rsnd_ssi(ssi, priv, i) { + flags = rsnd_ssi_mode_flags(ssi); + + if (!(flags & RSND_SSI_CLK_PIN_SHARE)) + continue; + + val = 0; + switch (i) { + case 1: + ssi_parent_set(0, (1 << 4), (0x2 << 0), (0x1 << 0)); + break; + case 2: + ssi_parent_set(0, (1 << 4), (0x2 << 2), (0x1 << 2)); + break; + case 4: + ssi_parent_set(3, (1 << 20), (0x2 << 16), (0x1 << 16)); + break; + case 8: + ssi_parent_set(7, 0, 0, 0); + break; + } + + ssiu->ssi_mode1 |= val; + } +} + +static void rsnd_ssi_mode_set(struct rsnd_ssi *ssi) +{ + struct rsnd_ssiu *ssiu = rsnd_ssi_to_ssiu(ssi); + + rsnd_mod_write(&ssi->mod, SSI_MODE0, ssiu->ssi_mode0); + rsnd_mod_write(&ssi->mod, SSI_MODE1, ssiu->ssi_mode1); +} + +static void rsnd_ssi_status_check(struct rsnd_mod *mod, + u32 bit) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct device *dev = rsnd_priv_to_dev(priv); + u32 status; + int i; + + for (i = 0; i < 1024; i++) { + status = rsnd_mod_read(mod, SSISR); + if (status & bit) + return; + + udelay(50); + } + + dev_warn(dev, "status check failed\n"); +} + +static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi, + unsigned int rate) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(&ssi->mod); + struct device *dev = rsnd_priv_to_dev(priv); + int i, j, ret; + int adg_clk_div_table[] = { + 1, 6, /* see adg.c */ + }; + int ssi_clk_mul_table[] = { + 1, 2, 4, 8, 16, 6, 12, + }; + unsigned int main_rate; + + /* + * Find best clock, and try to start ADG + */ + for (i = 0; i < ARRAY_SIZE(adg_clk_div_table); i++) { + for (j = 0; j < ARRAY_SIZE(ssi_clk_mul_table); j++) { + + /* + * this driver is assuming that + * system word is 64fs (= 2 x 32bit) + * see rsnd_ssi_start() + */ + main_rate = rate / adg_clk_div_table[i] + * 32 * 2 * ssi_clk_mul_table[j]; + + ret = rsnd_adg_ssi_clk_try_start(&ssi->mod, main_rate); + if (0 == ret) { + ssi->rate = rate; + ssi->cr_clk = FORCE | SWL_32 | + SCKD | SWSD | CKDV(j); + + dev_dbg(dev, "ssi%d outputs %u Hz\n", + rsnd_mod_id(&ssi->mod), rate); + + return 0; + } + } + } + + dev_err(dev, "unsupported clock rate\n"); + return -EIO; +} + +static void rsnd_ssi_master_clk_stop(struct rsnd_ssi *ssi) +{ + ssi->rate = 0; + ssi->cr_clk = 0; + rsnd_adg_ssi_clk_stop(&ssi->mod); +} + +static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(&ssi->mod); + struct device *dev = rsnd_priv_to_dev(priv); + u32 cr; + + if (0 == ssi->usrcnt) { + clk_enable(ssi->clk); + + if (rsnd_rdai_is_clk_master(rdai)) { + struct snd_pcm_runtime *runtime; + + runtime = rsnd_io_to_runtime(io); + + if (rsnd_ssi_clk_from_parent(ssi)) + rsnd_ssi_hw_start(ssi->parent, rdai, io); + else + rsnd_ssi_master_clk_start(ssi, runtime->rate); + } + } + + cr = ssi->cr_own | + ssi->cr_clk | + ssi->cr_etc | + EN; + + rsnd_mod_write(&ssi->mod, SSICR, cr); + + ssi->usrcnt++; + + dev_dbg(dev, "ssi%d hw started\n", rsnd_mod_id(&ssi->mod)); +} + +static void rsnd_ssi_hw_stop(struct rsnd_ssi *ssi, + struct rsnd_dai *rdai) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(&ssi->mod); + struct device *dev = rsnd_priv_to_dev(priv); + u32 cr; + + if (0 == ssi->usrcnt) /* stop might be called without start */ + return; + + ssi->usrcnt--; + + if (0 == ssi->usrcnt) { + /* + * disable all IRQ, + * and, wait all data was sent + */ + cr = ssi->cr_own | + ssi->cr_clk; + + rsnd_mod_write(&ssi->mod, SSICR, cr | EN); + rsnd_ssi_status_check(&ssi->mod, DIRQ); + + /* + * disable SSI, + * and, wait idle state + */ + rsnd_mod_write(&ssi->mod, SSICR, cr); /* disabled all */ + rsnd_ssi_status_check(&ssi->mod, IIRQ); + + if (rsnd_rdai_is_clk_master(rdai)) { + if (rsnd_ssi_clk_from_parent(ssi)) + rsnd_ssi_hw_stop(ssi->parent, rdai); + else + rsnd_ssi_master_clk_stop(ssi); + } + + clk_disable(ssi->clk); + } + + dev_dbg(dev, "ssi%d hw stopped\n", rsnd_mod_id(&ssi->mod)); +} + +/* + * SSI mod common functions + */ +static int rsnd_ssi_init(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct device *dev = rsnd_priv_to_dev(priv); + struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); + u32 cr; + + cr = FORCE; + + /* + * always use 32bit system word for easy clock calculation. + * see also rsnd_ssi_master_clk_enable() + */ + cr |= SWL_32; + + /* + * init clock settings for SSICR + */ + switch (runtime->sample_bits) { + case 16: + cr |= DWL_16; + break; + case 32: + cr |= DWL_24; + break; + default: + return -EIO; + } + + if (rdai->bit_clk_inv) + cr |= SCKP; + if (rdai->frm_clk_inv) + cr |= SWSP; + if (rdai->data_alignment) + cr |= SDTA; + if (rdai->sys_delay) + cr |= DEL; + if (rsnd_dai_is_play(rdai, io)) + cr |= TRMD; + + /* + * set ssi parameter + */ + ssi->rdai = rdai; + ssi->io = io; + ssi->cr_own = cr; + ssi->err = -1; /* ignore 1st error */ + + rsnd_ssi_mode_set(ssi); + + dev_dbg(dev, "%s.%d init\n", rsnd_mod_name(mod), rsnd_mod_id(mod)); + + return 0; +} + +static int rsnd_ssi_quit(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct device *dev = rsnd_priv_to_dev(priv); + + dev_dbg(dev, "%s.%d quit\n", rsnd_mod_name(mod), rsnd_mod_id(mod)); + + if (ssi->err > 0) + dev_warn(dev, "ssi under/over flow err = %d\n", ssi->err); + + ssi->rdai = NULL; + ssi->io = NULL; + ssi->cr_own = 0; + ssi->err = 0; + + return 0; +} + +static void rsnd_ssi_record_error(struct rsnd_ssi *ssi, u32 status) +{ + /* under/over flow error */ + if (status & (UIRQ | OIRQ)) { + ssi->err++; + + /* clear error status */ + rsnd_mod_write(&ssi->mod, SSISR, 0); + } +} + +/* + * SSI PIO + */ +static irqreturn_t rsnd_ssi_pio_interrupt(int irq, void *data) +{ + struct rsnd_ssi *ssi = data; + struct rsnd_dai_stream *io = ssi->io; + u32 status = rsnd_mod_read(&ssi->mod, SSISR); + irqreturn_t ret = IRQ_NONE; + + if (io && (status & DIRQ)) { + struct rsnd_dai *rdai = ssi->rdai; + struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); + u32 *buf = (u32 *)(runtime->dma_area + + rsnd_dai_pointer_offset(io, 0)); + + rsnd_ssi_record_error(ssi, status); + + /* + * 8/16/32 data can be assesse to TDR/RDR register + * directly as 32bit data + * see rsnd_ssi_init() + */ + if (rsnd_dai_is_play(rdai, io)) + rsnd_mod_write(&ssi->mod, SSITDR, *buf); + else + *buf = rsnd_mod_read(&ssi->mod, SSIRDR); + + rsnd_dai_pointer_update(io, sizeof(*buf)); + + ret = IRQ_HANDLED; + } + + return ret; +} + +static int rsnd_ssi_pio_start(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); + struct device *dev = rsnd_priv_to_dev(priv); + + /* enable PIO IRQ */ + ssi->cr_etc = UIEN | OIEN | DIEN; + + rsnd_ssi_hw_start(ssi, rdai, io); + + dev_dbg(dev, "%s.%d start\n", rsnd_mod_name(mod), rsnd_mod_id(mod)); + + return 0; +} + +static int rsnd_ssi_pio_stop(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct device *dev = rsnd_priv_to_dev(priv); + struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); + + dev_dbg(dev, "%s.%d stop\n", rsnd_mod_name(mod), rsnd_mod_id(mod)); + + ssi->cr_etc = 0; + + rsnd_ssi_hw_stop(ssi, rdai); + + return 0; +} + +static struct rsnd_mod_ops rsnd_ssi_pio_ops = { + .name = "ssi (pio)", + .init = rsnd_ssi_init, + .quit = rsnd_ssi_quit, + .start = rsnd_ssi_pio_start, + .stop = rsnd_ssi_pio_stop, +}; + +/* + * Non SSI + */ +static int rsnd_ssi_non(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct device *dev = rsnd_priv_to_dev(priv); + + dev_dbg(dev, "%s\n", __func__); + + return 0; +} + +static struct rsnd_mod_ops rsnd_ssi_non_ops = { + .name = "ssi (non)", + .init = rsnd_ssi_non, + .quit = rsnd_ssi_non, + .start = rsnd_ssi_non, + .stop = rsnd_ssi_non, +}; + +/* + * ssi mod function + */ +struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id) +{ + BUG_ON(id < 0 || id >= rsnd_ssi_nr(priv)); + + return &(((struct rsnd_ssiu *)(priv->ssiu))->ssi + id)->mod; +} + +int rsnd_ssi_probe(struct platform_device *pdev, + struct rcar_snd_info *info, + struct rsnd_priv *priv) +{ + struct rsnd_ssi_platform_info *pinfo; + struct device *dev = rsnd_priv_to_dev(priv); + struct rsnd_mod_ops *ops; + struct clk *clk; + struct rsnd_ssiu *ssiu; + struct rsnd_ssi *ssi; + char name[RSND_SSI_NAME_SIZE]; + int i, nr, ret; + + /* + * init SSI + */ + nr = info->ssi_info_nr; + ssiu = devm_kzalloc(dev, sizeof(*ssiu) + (sizeof(*ssi) * nr), + GFP_KERNEL); + if (!ssiu) { + dev_err(dev, "SSI allocate failed\n"); + return -ENOMEM; + } + + priv->ssiu = ssiu; + ssiu->ssi = (struct rsnd_ssi *)(ssiu + 1); + ssiu->ssi_nr = nr; + + for_each_rsnd_ssi(ssi, priv, i) { + pinfo = &info->ssi_info[i]; + + snprintf(name, RSND_SSI_NAME_SIZE, "ssi.%d", i); + + clk = clk_get(dev, name); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + ssi->info = pinfo; + ssi->clk = clk; + + ops = &rsnd_ssi_non_ops; + + /* + * SSI PIO case + */ + if (rsnd_ssi_is_pio(ssi)) { + ret = devm_request_irq(dev, pinfo->pio_irq, + &rsnd_ssi_pio_interrupt, + IRQF_SHARED, + dev_name(dev), ssi); + if (ret) { + dev_err(dev, "SSI request interrupt failed\n"); + return ret; + } + + ops = &rsnd_ssi_pio_ops; + } + + rsnd_mod_init(priv, &ssi->mod, ops, i); + } + + rsnd_ssi_mode_init(priv, ssiu); + + dev_dbg(dev, "ssi probed\n"); + + return 0; +} + +void rsnd_ssi_remove(struct platform_device *pdev, + struct rsnd_priv *priv) +{ + struct rsnd_ssi *ssi; + int i; + + for_each_rsnd_ssi(ssi, priv, i) + clk_put(ssi->clk); +} -- cgit v0.10.2 From 85054b2153f18eac16df9ff88913c98adea6a23e Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Sun, 28 Jul 2013 23:27:38 +0300 Subject: ALSA: usx2y: remove an unneeded check The test here is always true because S[i].urb is an array not a pointer. Also it's bogus because the intent was to test: if (S->urb[i]) { instead of: if (S[i].urb) { Anyway, usb_kill_urb() and usb_free_urb() accept NULL pointers so we can just remove this. Signed-off-by: Dan Carpenter Signed-off-by: Takashi Iwai diff --git a/sound/usb/usx2y/usbusx2y.c b/sound/usb/usx2y/usbusx2y.c index 1f9bbd5..5a51b18 100644 --- a/sound/usb/usx2y/usbusx2y.c +++ b/sound/usb/usx2y/usbusx2y.c @@ -305,11 +305,9 @@ static void usX2Y_unlinkSeq(struct snd_usX2Y_AsyncSeq *S) { int i; for (i = 0; i < URBS_AsyncSeq; ++i) { - if (S[i].urb) { - usb_kill_urb(S->urb[i]); - usb_free_urb(S->urb[i]); - S->urb[i] = NULL; - } + usb_kill_urb(S->urb[i]); + usb_free_urb(S->urb[i]); + S->urb[i] = NULL; } kfree(S->buffer); } -- cgit v0.10.2 From 7eaa9161edd1bb41c026db252bb7e7dfe97ab90a Mon Sep 17 00:00:00 2001 From: Wang Xingchao Date: Thu, 25 Jul 2013 23:34:44 -0400 Subject: ALSA: hda - Clearing jackpoll_interval avoid pending work Clearing jackpoll_interval before calling cancel_delayed_work_sync(), otherwise the work will be triggered again and cause impact in hda_jackpoll_work(). The next patch will poll jack once even with jackpoll_interval=0. Signed-off-by: Wang Xingchao Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index e2481ba..0bc20ef 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -207,9 +207,9 @@ static void vt1708_stop_hp_work(struct hda_codec *codec) return; if (spec->hp_work_active) { snd_hda_codec_write(codec, 0x1, 0, 0xf81, 1); + codec->jackpoll_interval = 0; cancel_delayed_work_sync(&codec->jackpoll_work); spec->hp_work_active = false; - codec->jackpoll_interval = 0; } } -- cgit v0.10.2 From 18e606275691726cce06ad803072ac54315740f7 Mon Sep 17 00:00:00 2001 From: Wang Xingchao Date: Thu, 25 Jul 2013 23:34:45 -0400 Subject: ALSA: hda - jack poll once if jackpoll_interval==0 With jackpoll_interval != 0, it's used to poll jack event periodically in a delayed work. if it's 0, give the caller chance to probe jack status but will not restart the delayed work. In the next patch which enable WAKEEN feature, HDA controller was able to wake up system when it's in D3, it's useful to detect Jack hotplug event and notify userspace. By default the jackpoll_interval=0, this patch let jack poll once without starting the delayed work. Signed-off-by: Wang Xingchao Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 8a005f0..fdbb09a 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -1216,11 +1216,13 @@ static void hda_jackpoll_work(struct work_struct *work) { struct hda_codec *codec = container_of(work, struct hda_codec, jackpoll_work.work); - if (!codec->jackpoll_interval) - return; snd_hda_jack_set_dirty_all(codec); snd_hda_jack_poll_all(codec); + + if (!codec->jackpoll_interval) + return; + queue_delayed_work(codec->bus->workq, &codec->jackpoll_work, codec->jackpoll_interval); } -- cgit v0.10.2 From 7d4f606c50ffaaa3ac60b7faf770dc6e84af3207 Mon Sep 17 00:00:00 2001 From: Wang Xingchao Date: Thu, 25 Jul 2013 23:34:46 -0400 Subject: ALSA: hda - WAKEEN feature enabling for runtime pm With runtime power save feature enabled, Headphone hotplug event will not be detected while controller/codec in D3. HDA has feature WAKEEN to let codec wake up system if controller is in D3 or system in S3.(HDA Spec 4.5.9.2/3). Codec can send out INT or wake up controller depending on whether CIE or GIE enabled.(Figure 4, Interupt structure). The controller must be in RESET mode after enter runtime-suspend, otherwise it will not be waken up even if codec send out wake-up event. And STATESTS will be cleared after controller brought out of RESET mode. This patch only enable WAKEEN for runtime-suspend(Controller D3) mode, not for system S3 mode. with tool "evtest", Headphone hotplug events could be cought and reported successfully. [fixed an unused variable warning by tiwai] Signed-off-by: Wang Xingchao Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 3f16c4b..7f9e406 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -2971,6 +2971,10 @@ static int azx_runtime_suspend(struct device *dev) struct snd_card *card = dev_get_drvdata(dev); struct azx *chip = card->private_data; + /* enable controller wake up event */ + azx_writew(chip, WAKEEN, azx_readw(chip, WAKEEN) | + STATESTS_INT_MASK); + azx_stop_chip(chip); azx_enter_link_reset(chip); azx_clear_irq_pending(chip); @@ -2983,11 +2987,31 @@ static int azx_runtime_resume(struct device *dev) { struct snd_card *card = dev_get_drvdata(dev); struct azx *chip = card->private_data; + struct hda_bus *bus; + struct hda_codec *codec; + int status; if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) hda_display_power(true); + + /* Read STATESTS before controller reset */ + status = azx_readw(chip, STATESTS); + azx_init_pci(chip); azx_init_chip(chip, 1); + + bus = chip->bus; + if (status && bus) { + list_for_each_entry(codec, &bus->codec_list, list) + if (status & (1 << codec->addr)) + queue_delayed_work(codec->bus->workq, + &codec->jackpoll_work, codec->jackpoll_interval); + } + + /* disable controller Wake Up event*/ + azx_writew(chip, WAKEEN, azx_readw(chip, WAKEEN) & + ~STATESTS_INT_MASK); + return 0; } -- cgit v0.10.2 From eefb8be4a4fb4aa9005fc092a88d66fe7cf1adc2 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 29 Jul 2013 16:26:15 +0200 Subject: ALSA: hda - Remove analog mic pin override from STAC9228 dell-bios quirk The current fixup for dell-bios model with STAC9228 codec contains the override of pin 0x0c for analog mic. But this is actually just adding a bogus pin and confuses the parser. Better to remove it for the auto-mic switching. Meanwhile, for a possible regression, keep the old configuration as model=dell-bios-amic, so that people can test it again quickly. Tested on Dell 1420n laptop. Reported-and-tested-by: Eric Shattow Signed-off-by: Takashi Iwai diff --git a/Documentation/sound/alsa/HD-Audio-Models.txt b/Documentation/sound/alsa/HD-Audio-Models.txt index 809d72b..a46ddb8 100644 --- a/Documentation/sound/alsa/HD-Audio-Models.txt +++ b/Documentation/sound/alsa/HD-Audio-Models.txt @@ -244,6 +244,7 @@ STAC9227/9228/9229/927x 5stack-no-fp D965 5stack without front panel dell-3stack Dell Dimension E520 dell-bios Fixes with Dell BIOS setup + dell-bios-amic Fixes with Dell BIOS setup including analog mic volknob Fixes with volume-knob widget 0x24 auto BIOS setup (default) diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index e2f8359..8f6c357 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -158,6 +158,7 @@ enum { STAC_D965_VERBS, STAC_DELL_3ST, STAC_DELL_BIOS, + STAC_DELL_BIOS_AMIC, STAC_DELL_BIOS_SPDIF, STAC_927X_DELL_DMIC, STAC_927X_VOLKNOB, @@ -3228,8 +3229,6 @@ static const struct hda_fixup stac927x_fixups[] = { [STAC_DELL_BIOS] = { .type = HDA_FIXUP_PINS, .v.pins = (const struct hda_pintbl[]) { - /* configure the analog microphone on some laptops */ - { 0x0c, 0x90a79130 }, /* correct the front output jack as a hp out */ { 0x0f, 0x0227011f }, /* correct the front input jack as a mic */ @@ -3239,6 +3238,16 @@ static const struct hda_fixup stac927x_fixups[] = { .chained = true, .chain_id = STAC_927X_DELL_DMIC, }, + [STAC_DELL_BIOS_AMIC] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + /* configure the analog microphone on some laptops */ + { 0x0c, 0x90a79130 }, + {} + }, + .chained = true, + .chain_id = STAC_DELL_BIOS, + }, [STAC_DELL_BIOS_SPDIF] = { .type = HDA_FIXUP_PINS, .v.pins = (const struct hda_pintbl[]) { @@ -3267,6 +3276,7 @@ static const struct hda_model_fixup stac927x_models[] = { { .id = STAC_D965_5ST_NO_FP, .name = "5stack-no-fp" }, { .id = STAC_DELL_3ST, .name = "dell-3stack" }, { .id = STAC_DELL_BIOS, .name = "dell-bios" }, + { .id = STAC_DELL_BIOS_AMIC, .name = "dell-bios-amic" }, { .id = STAC_927X_VOLKNOB, .name = "volknob" }, {} }; -- cgit v0.10.2 From 4fefd69853a4e83040ddaa98d3b6e5e12cc4f97a Mon Sep 17 00:00:00 2001 From: Dimitris Papastamos Date: Mon, 29 Jul 2013 13:51:58 +0100 Subject: ASoC: core: Add snd_soc_card_get_kcontrol() This is useful for drivers who want to grab a pointer to snd_kcontrol outside of the kcontrol callbacks. Signed-off-by: Dimitris Papastamos Signed-off-by: Mark Brown diff --git a/include/sound/soc.h b/include/sound/soc.h index 6eabee7..b33d1de 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -475,6 +475,8 @@ int snd_soc_set_ac97_ops(struct snd_ac97_bus_ops *ops); struct snd_kcontrol *snd_soc_cnew(const struct snd_kcontrol_new *_template, void *data, const char *long_name, const char *prefix); +struct snd_kcontrol *snd_soc_card_get_kcontrol(struct snd_soc_card *soc_card, + const char *name); int snd_soc_add_codec_controls(struct snd_soc_codec *codec, const struct snd_kcontrol_new *controls, int num_controls); int snd_soc_add_platform_controls(struct snd_soc_platform *platform, diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 0ec070c..cef714e 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -2299,6 +2299,22 @@ static int snd_soc_add_controls(struct snd_card *card, struct device *dev, return 0; } +struct snd_kcontrol *snd_soc_card_get_kcontrol(struct snd_soc_card *soc_card, + const char *name) +{ + struct snd_card *card = soc_card->snd_card; + struct snd_kcontrol *kctl; + + if (unlikely(!name)) + return NULL; + + list_for_each_entry(kctl, &card->controls, list) + if (!strncmp(kctl->id.name, name, sizeof(kctl->id.name))) + return kctl; + return NULL; +} +EXPORT_SYMBOL_GPL(snd_soc_card_get_kcontrol); + /** * snd_soc_add_codec_controls - add an array of controls to a codec. * Convenience function to add a list of controls. Many codecs were -- cgit v0.10.2 From 81ad93ecfda64cb37129d29adb384affd0d0fa5b Mon Sep 17 00:00:00 2001 From: Dimitris Papastamos Date: Mon, 29 Jul 2013 13:51:59 +0100 Subject: ASoC: wm_adsp: Simplify kcontrol handling Get rid off the wm_coeff struct and the wm_coeff_add_kcontrol() function. We are now using the snd_soc_card_kcontrol() function to get the kcontrol pointers. No need to call into ALSA code to register the kcontrols. Signed-off-by: Dimitris Papastamos Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 05252ac..3168224 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -225,15 +225,9 @@ struct wm_coeff_ctl_ops { struct snd_ctl_elem_info *uinfo); }; -struct wm_coeff { - struct device *dev; - struct list_head ctl_list; - struct regmap *regmap; -}; - struct wm_coeff_ctl { const char *name; - struct snd_card *card; + struct snd_soc_card *card; struct wm_adsp_alg_region region; struct wm_coeff_ctl_ops ops; struct wm_adsp *adsp; @@ -378,7 +372,6 @@ static int wm_coeff_info(struct snd_kcontrol *kcontrol, static int wm_coeff_write_control(struct snd_kcontrol *kcontrol, const void *buf, size_t len) { - struct wm_coeff *wm_coeff= snd_kcontrol_chip(kcontrol); struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value; struct wm_adsp_alg_region *region = &ctl->region; const struct wm_adsp_region *mem; @@ -401,7 +394,7 @@ static int wm_coeff_write_control(struct snd_kcontrol *kcontrol, if (!scratch) return -ENOMEM; - ret = regmap_raw_write(wm_coeff->regmap, reg, scratch, + ret = regmap_raw_write(adsp->regmap, reg, scratch, ctl->len); if (ret) { adsp_err(adsp, "Failed to write %zu bytes to %x\n", @@ -434,7 +427,6 @@ static int wm_coeff_put(struct snd_kcontrol *kcontrol, static int wm_coeff_read_control(struct snd_kcontrol *kcontrol, void *buf, size_t len) { - struct wm_coeff *wm_coeff= snd_kcontrol_chip(kcontrol); struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value; struct wm_adsp_alg_region *region = &ctl->region; const struct wm_adsp_region *mem; @@ -457,7 +449,7 @@ static int wm_coeff_read_control(struct snd_kcontrol *kcontrol, if (!scratch) return -ENOMEM; - ret = regmap_raw_read(wm_coeff->regmap, reg, scratch, ctl->len); + ret = regmap_raw_read(adsp->regmap, reg, scratch, ctl->len); if (ret) { adsp_err(adsp, "Failed to read %zu bytes from %x\n", ctl->len, reg); @@ -481,37 +473,18 @@ static int wm_coeff_get(struct snd_kcontrol *kcontrol, return 0; } -static int wm_coeff_add_kcontrol(struct wm_coeff *wm_coeff, - struct wm_coeff_ctl *ctl, - const struct snd_kcontrol_new *kctl) -{ - int ret; - struct snd_kcontrol *kcontrol; - - kcontrol = snd_ctl_new1(kctl, wm_coeff); - ret = snd_ctl_add(ctl->card, kcontrol); - if (ret < 0) { - dev_err(wm_coeff->dev, "Failed to add %s: %d\n", - kctl->name, ret); - return ret; - } - ctl->kcontrol = kcontrol; - return 0; -} - struct wmfw_ctl_work { - struct wm_coeff *wm_coeff; + struct wm_adsp *adsp; struct wm_coeff_ctl *ctl; struct work_struct work; }; -static int wmfw_add_ctl(struct wm_coeff *wm_coeff, - struct wm_coeff_ctl *ctl) +static int wmfw_add_ctl(struct wm_adsp *adsp, struct wm_coeff_ctl *ctl) { struct snd_kcontrol_new *kcontrol; int ret; - if (!wm_coeff || !ctl || !ctl->name || !ctl->card) + if (!ctl || !ctl->name || !ctl->card) return -EINVAL; kcontrol = kzalloc(sizeof(*kcontrol), GFP_KERNEL); @@ -525,14 +498,17 @@ static int wmfw_add_ctl(struct wm_coeff *wm_coeff, kcontrol->put = wm_coeff_put; kcontrol->private_value = (unsigned long)ctl; - ret = wm_coeff_add_kcontrol(wm_coeff, - ctl, kcontrol); + ret = snd_soc_add_card_controls(ctl->card, + kcontrol, 1); if (ret < 0) goto err_kcontrol; kfree(kcontrol); - list_add(&ctl->list, &wm_coeff->ctl_list); + ctl->kcontrol = snd_soc_card_get_kcontrol(ctl->card, + ctl->name); + + list_add(&ctl->list, &adsp->ctl_list); return 0; err_kcontrol: @@ -753,13 +729,12 @@ out: return ret; } -static int wm_coeff_init_control_caches(struct wm_coeff *wm_coeff) +static int wm_coeff_init_control_caches(struct wm_adsp *adsp) { struct wm_coeff_ctl *ctl; int ret; - list_for_each_entry(ctl, &wm_coeff->ctl_list, - list) { + list_for_each_entry(ctl, &adsp->ctl_list, list) { if (!ctl->enabled || ctl->set) continue; ret = wm_coeff_read_control(ctl->kcontrol, @@ -772,13 +747,12 @@ static int wm_coeff_init_control_caches(struct wm_coeff *wm_coeff) return 0; } -static int wm_coeff_sync_controls(struct wm_coeff *wm_coeff) +static int wm_coeff_sync_controls(struct wm_adsp *adsp) { struct wm_coeff_ctl *ctl; int ret; - list_for_each_entry(ctl, &wm_coeff->ctl_list, - list) { + list_for_each_entry(ctl, &adsp->ctl_list, list) { if (!ctl->enabled) continue; if (ctl->set) { @@ -799,7 +773,7 @@ static void wm_adsp_ctl_work(struct work_struct *work) struct wmfw_ctl_work, work); - wmfw_add_ctl(ctl_work->wm_coeff, ctl_work->ctl); + wmfw_add_ctl(ctl_work->adsp, ctl_work->ctl); kfree(ctl_work); } @@ -842,7 +816,7 @@ static int wm_adsp_create_control(struct snd_soc_codec *codec, snprintf(name, PAGE_SIZE, "DSP%d %s %x", dsp->num, region_name, region->alg); - list_for_each_entry(ctl, &dsp->wm_coeff->ctl_list, + list_for_each_entry(ctl, &dsp->ctl_list, list) { if (!strcmp(ctl->name, name)) { if (!ctl->enabled) @@ -866,7 +840,7 @@ static int wm_adsp_create_control(struct snd_soc_codec *codec, ctl->set = 0; ctl->ops.xget = wm_coeff_get; ctl->ops.xput = wm_coeff_put; - ctl->card = codec->card->snd_card; + ctl->card = codec->card; ctl->adsp = dsp; ctl->len = region->len; @@ -882,7 +856,7 @@ static int wm_adsp_create_control(struct snd_soc_codec *codec, goto err_ctl_cache; } - ctl_work->wm_coeff = dsp->wm_coeff; + ctl_work->adsp = dsp; ctl_work->ctl = ctl; INIT_WORK(&ctl_work->work, wm_adsp_ctl_work); schedule_work(&ctl_work->work); @@ -1434,12 +1408,12 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w, goto err; /* Initialize caches for enabled and unset controls */ - ret = wm_coeff_init_control_caches(dsp->wm_coeff); + ret = wm_coeff_init_control_caches(dsp); if (ret != 0) goto err; /* Sync set controls */ - ret = wm_coeff_sync_controls(dsp->wm_coeff); + ret = wm_coeff_sync_controls(dsp); if (ret != 0) goto err; @@ -1460,10 +1434,8 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w, regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, ADSP1_SYS_ENA, 0); - list_for_each_entry(ctl, &dsp->wm_coeff->ctl_list, - list) { + list_for_each_entry(ctl, &dsp->ctl_list, list) ctl->enabled = 0; - } break; default: @@ -1591,12 +1563,12 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w, goto err; /* Initialize caches for enabled and unset controls */ - ret = wm_coeff_init_control_caches(dsp->wm_coeff); + ret = wm_coeff_init_control_caches(dsp); if (ret != 0) goto err; /* Sync set controls */ - ret = wm_coeff_sync_controls(dsp->wm_coeff); + ret = wm_coeff_sync_controls(dsp); if (ret != 0) goto err; @@ -1637,10 +1609,8 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w, ret); } - list_for_each_entry(ctl, &dsp->wm_coeff->ctl_list, - list) { + list_for_each_entry(ctl, &dsp->ctl_list, list) ctl->enabled = 0; - } while (!list_empty(&dsp->alg_regions)) { alg_region = list_first_entry(&dsp->alg_regions, @@ -1679,49 +1649,38 @@ int wm_adsp2_init(struct wm_adsp *adsp, bool dvfs) } INIT_LIST_HEAD(&adsp->alg_regions); - - adsp->wm_coeff = kzalloc(sizeof(*adsp->wm_coeff), - GFP_KERNEL); - if (!adsp->wm_coeff) - return -ENOMEM; - adsp->wm_coeff->regmap = adsp->regmap; - adsp->wm_coeff->dev = adsp->dev; - INIT_LIST_HEAD(&adsp->wm_coeff->ctl_list); + INIT_LIST_HEAD(&adsp->ctl_list); if (dvfs) { adsp->dvfs = devm_regulator_get(adsp->dev, "DCVDD"); if (IS_ERR(adsp->dvfs)) { ret = PTR_ERR(adsp->dvfs); dev_err(adsp->dev, "Failed to get DCVDD: %d\n", ret); - goto out_coeff; + return ret; } ret = regulator_enable(adsp->dvfs); if (ret != 0) { dev_err(adsp->dev, "Failed to enable DCVDD: %d\n", ret); - goto out_coeff; + return ret; } ret = regulator_set_voltage(adsp->dvfs, 1200000, 1800000); if (ret != 0) { dev_err(adsp->dev, "Failed to initialise DVFS: %d\n", ret); - goto out_coeff; + return ret; } ret = regulator_disable(adsp->dvfs); if (ret != 0) { dev_err(adsp->dev, "Failed to disable DCVDD: %d\n", ret); - goto out_coeff; + return ret; } } return 0; - -out_coeff: - kfree(adsp->wm_coeff); - return ret; } EXPORT_SYMBOL_GPL(wm_adsp2_init); diff --git a/sound/soc/codecs/wm_adsp.h b/sound/soc/codecs/wm_adsp.h index 9f922c8..64087fb 100644 --- a/sound/soc/codecs/wm_adsp.h +++ b/sound/soc/codecs/wm_adsp.h @@ -57,7 +57,7 @@ struct wm_adsp { struct regulator *dvfs; - struct wm_coeff *wm_coeff; + struct list_head ctl_list; }; #define WM_ADSP1(wname, num) \ -- cgit v0.10.2 From da96fb5b0185d27faab0746f872d22b0cee7b026 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 29 Jul 2013 16:54:36 +0200 Subject: ALSA: hda - Fix invalid multi-io creation on VAIO-Z laptops VAIO-Z laptops need to use the specific DAC for the speaker output by some unknown reason although the codec itself supports the flexible connection. So we implemented a workaround by a new flag, no_primary_hp, for assigning the speaker pin first. This worked until 3.8 kernel, but it got broken because the driver learned for a better multi-io pin mapping, and not it can assign two mic pins for multi-io. Since the multi-io requires to be the primary output, the hp and two mic pins are assigned in prior to the speaker in the end. Although the machine has two mic pins, one of them is used as a noise- canceling headphone, thus it's no real retaskable mic jack. Thus, at best, we can disable the multi-io assignment and make the parser behavior back to the state before the multi-io. This patch adds again a new flag, no_multi_io, to indicate that the device has no multi-io capability, and set it in the fixup for VAIO-Z. The no_multi_io flag itself can be used generically, added via a helper line, too. Reported-by: Tormen Reported-by: Adam Williamson Signed-off-by: Takashi Iwai diff --git a/Documentation/sound/alsa/HD-Audio.txt b/Documentation/sound/alsa/HD-Audio.txt index c3c912d..42a0a39 100644 --- a/Documentation/sound/alsa/HD-Audio.txt +++ b/Documentation/sound/alsa/HD-Audio.txt @@ -454,6 +454,8 @@ The generic parser supports the following hints: - need_dac_fix (bool): limits the DACs depending on the channel count - primary_hp (bool): probe headphone jacks as the primary outputs; default true +- multi_io (bool): try probing multi-I/O config (e.g. shared + line-in/surround, mic/clfe jacks) - multi_cap_vol (bool): provide multiple capture volumes - inv_dmic_split (bool): provide split internal mic volume/switch for phase-inverted digital mics diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index f5c2d1f..f6c0344 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -142,6 +142,9 @@ static void parse_user_hints(struct hda_codec *codec) val = snd_hda_get_bool_hint(codec, "primary_hp"); if (val >= 0) spec->no_primary_hp = !val; + val = snd_hda_get_bool_hint(codec, "multi_io"); + if (val >= 0) + spec->no_multi_io = !val; val = snd_hda_get_bool_hint(codec, "multi_cap_vol"); if (val >= 0) spec->multi_cap_vol = !!val; @@ -1541,7 +1544,8 @@ static int fill_and_eval_dacs(struct hda_codec *codec, cfg->speaker_pins, spec->multiout.extra_out_nid, spec->speaker_paths); - if (fill_mio_first && cfg->line_outs == 1 && + if (!spec->no_multi_io && + fill_mio_first && cfg->line_outs == 1 && cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { err = fill_multi_ios(codec, cfg->line_out_pins[0], true); if (!err) @@ -1554,7 +1558,7 @@ static int fill_and_eval_dacs(struct hda_codec *codec, spec->private_dac_nids, spec->out_paths, spec->main_out_badness); - if (fill_mio_first && + if (!spec->no_multi_io && fill_mio_first && cfg->line_outs == 1 && cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { /* try to fill multi-io first */ err = fill_multi_ios(codec, cfg->line_out_pins[0], false); @@ -1582,7 +1586,8 @@ static int fill_and_eval_dacs(struct hda_codec *codec, return err; badness += err; } - if (cfg->line_outs == 1 && cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { + if (!spec->no_multi_io && + cfg->line_outs == 1 && cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { err = fill_multi_ios(codec, cfg->line_out_pins[0], false); if (err < 0) return err; @@ -1600,7 +1605,8 @@ static int fill_and_eval_dacs(struct hda_codec *codec, check_aamix_out_path(codec, spec->speaker_paths[0]); } - if (cfg->hp_outs && cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) + if (!spec->no_multi_io && + cfg->hp_outs && cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) if (count_multiio_pins(codec, cfg->hp_pins[0]) >= 2) spec->multi_ios = 1; /* give badness */ diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index e199a85..48d4402 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -220,6 +220,7 @@ struct hda_gen_spec { unsigned int hp_mic:1; /* Allow HP as a mic-in */ unsigned int suppress_hp_mic_detect:1; /* Don't detect HP/mic */ unsigned int no_primary_hp:1; /* Don't prefer HP pins to speaker pins */ + unsigned int no_multi_io:1; /* Don't try multi I/O config */ unsigned int multi_cap_vol:1; /* allow multiple capture xxx volumes */ unsigned int inv_dmic_split:1; /* inverted dmic w/a for conexant */ unsigned int own_eapd_ctl:1; /* set EAPD by own function */ diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 04a69e3..ad7a098 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -1845,8 +1845,10 @@ static void alc882_fixup_no_primary_hp(struct hda_codec *codec, const struct hda_fixup *fix, int action) { struct alc_spec *spec = codec->spec; - if (action == HDA_FIXUP_ACT_PRE_PROBE) + if (action == HDA_FIXUP_ACT_PRE_PROBE) { spec->gen.no_primary_hp = 1; + spec->gen.no_multi_io = 1; + } } static const struct hda_fixup alc882_fixups[] = { -- cgit v0.10.2 From 0d47acc4ffaa9b63d96183d69d38bdb388314d7d Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 17 Jul 2013 17:20:19 +0100 Subject: ASoC: smdk_wm8994: Make driver name more unique Avoid collisions with other SMDK audio by using the CODEC name. Signed-off-by: Mark Brown diff --git a/sound/soc/samsung/smdk_wm8994.c b/sound/soc/samsung/smdk_wm8994.c index 581ea4a..05c479c 100644 --- a/sound/soc/samsung/smdk_wm8994.c +++ b/sound/soc/samsung/smdk_wm8994.c @@ -200,7 +200,7 @@ MODULE_DEVICE_TABLE(of, samsung_wm8994_of_match); static struct platform_driver smdk_audio_driver = { .driver = { - .name = "smdk-audio", + .name = "smdk-audio-wm8894", .owner = THIS_MODULE, .of_match_table = of_match_ptr(samsung_wm8994_of_match), }, @@ -212,4 +212,4 @@ module_platform_driver(smdk_audio_driver); MODULE_DESCRIPTION("ALSA SoC SMDK WM8994"); MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:smdk-audio"); +MODULE_ALIAS("platform:smdk-audio-wm8994"); -- cgit v0.10.2 From f6ecf50b5e33119620b446dd0bce8b0a01a39669 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 26 Jul 2013 12:01:53 +0100 Subject: ASoC: smdk_wm8994: Configure DAI format at init time Initialise the DAI format from the data link, saving code and repeated work. Signed-off-by: Mark Brown diff --git a/sound/soc/samsung/smdk_wm8994.c b/sound/soc/samsung/smdk_wm8994.c index 05c479c..a561175 100644 --- a/sound/soc/samsung/smdk_wm8994.c +++ b/sound/soc/samsung/smdk_wm8994.c @@ -41,7 +41,6 @@ static int smdk_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 *cpu_dai = rtd->cpu_dai; struct snd_soc_dai *codec_dai = rtd->codec_dai; unsigned int pll_out; int ret; @@ -54,18 +53,6 @@ static int smdk_hw_params(struct snd_pcm_substream *substream, else pll_out = params_rate(params) * 256; - ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S - | SND_SOC_DAIFMT_NB_NF - | SND_SOC_DAIFMT_CBM_CFM); - if (ret < 0) - return ret; - - ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S - | SND_SOC_DAIFMT_NB_NF - | SND_SOC_DAIFMT_CBM_CFM); - if (ret < 0) - return ret; - ret = snd_soc_dai_set_pll(codec_dai, WM8994_FLL1, WM8994_FLL_SRC_MCLK1, SMDK_WM8994_FREQ, pll_out); if (ret < 0) @@ -131,6 +118,8 @@ static struct snd_soc_dai_link smdk_dai[] = { .platform_name = "samsung-i2s.0", .codec_name = "wm8994-codec", .init = smdk_wm8994_init_paiftx, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBM_CFM, .ops = &smdk_ops, }, { /* Sec_Fifo Playback i/f */ .name = "Sec_FIFO TX", @@ -139,6 +128,8 @@ static struct snd_soc_dai_link smdk_dai[] = { .codec_dai_name = "wm8994-aif1", .platform_name = "samsung-i2s-sec", .codec_name = "wm8994-codec", + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBM_CFM, .ops = &smdk_ops, }, }; -- cgit v0.10.2 From d30c148bb1cab23d3c330e6352b8d882575a0c3a Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 26 Jul 2013 12:42:19 +0100 Subject: ASoC: smdk_wm8994: Configure the MCLK1 rate based on the board Make the code more generally applicable by refactoring so that the MCLK1 rate can be selected based on the compatible string provided by the device tree, allowing use on other boards which have different rates or use other information sources. Signed-off-by: Mark Brown diff --git a/sound/soc/samsung/smdk_wm8994.c b/sound/soc/samsung/smdk_wm8994.c index a561175..5fd7a05 100644 --- a/sound/soc/samsung/smdk_wm8994.c +++ b/sound/soc/samsung/smdk_wm8994.c @@ -11,6 +11,7 @@ #include #include #include +#include /* * Default CFG switch settings to use this driver: @@ -37,6 +38,15 @@ /* SMDK has a 16.934MHZ crystal attached to WM8994 */ #define SMDK_WM8994_FREQ 16934000 +struct smdk_wm8994_data { + int mclk1_rate; +}; + +/* Default SMDKs */ +static struct smdk_wm8994_data smdk_board_data = { + .mclk1_rate = SMDK_WM8994_FREQ, +}; + static int smdk_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { @@ -141,15 +151,28 @@ static struct snd_soc_card smdk = { .num_links = ARRAY_SIZE(smdk_dai), }; +#ifdef CONFIG_OF +static const struct of_device_id samsung_wm8994_of_match[] = { + { .compatible = "samsung,smdk-wm8994", .data = &smdk_board_data }, + {}, +}; +MODULE_DEVICE_TABLE(of, samsung_wm8994_of_match); +#endif /* CONFIG_OF */ static int smdk_audio_probe(struct platform_device *pdev) { int ret; struct device_node *np = pdev->dev.of_node; struct snd_soc_card *card = &smdk; + struct smdk_wm8994_data *board; + const struct of_device_id *id; card->dev = &pdev->dev; + board = devm_kzalloc(&pdev->dev, sizeof(*board), GFP_KERNEL); + if (!board) + return -ENOMEM; + if (np) { smdk_dai[0].cpu_dai_name = NULL; smdk_dai[0].cpu_of_node = of_parse_phandle(np, @@ -164,6 +187,12 @@ static int smdk_audio_probe(struct platform_device *pdev) smdk_dai[0].platform_of_node = smdk_dai[0].cpu_of_node; } + id = of_match_device(samsung_wm8994_of_match, &pdev->dev); + if (id) + *board = *((struct smdk_wm8994_data *)id->data); + + platform_set_drvdata(pdev, board); + ret = snd_soc_register_card(card); if (ret) @@ -181,14 +210,6 @@ static int smdk_audio_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_OF -static const struct of_device_id samsung_wm8994_of_match[] = { - { .compatible = "samsung,smdk-wm8994", }, - {}, -}; -MODULE_DEVICE_TABLE(of, samsung_wm8994_of_match); -#endif /* CONFIG_OF */ - static struct platform_driver smdk_audio_driver = { .driver = { .name = "smdk-audio-wm8894", -- cgit v0.10.2 From 564c65049eddb1a95b48958080db97eda88c98dd Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 29 Jul 2013 17:13:55 +0200 Subject: ASoC: dapm: Move snd_soc_dapm_update from dapm context to card The update field of a DAPM context is only assigned while the card's dapm_mutex is locked, the field is also cleared again while the mutex is stil locked. So there will only ever be one DAPM context at a time with a non-NULL update field. So it is safe to move the update field from the DAPM context struct to the card struct. Doing so will allow further cleanups in this area. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index e77c6f5..3397292 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -575,8 +575,6 @@ struct snd_soc_dapm_context { struct delayed_work delayed_work; unsigned int idle_bias_off:1; /* Use BIAS_OFF instead of STANDBY */ - struct snd_soc_dapm_update *update; - void (*seq_notifier)(struct snd_soc_dapm_context *, enum snd_soc_dapm_type, int); diff --git a/include/sound/soc.h b/include/sound/soc.h index 6eabee7..b1e1f96 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1042,6 +1042,7 @@ struct snd_soc_card { /* Generic DAPM context for the card */ struct snd_soc_dapm_context dapm; struct snd_soc_dapm_stats dapm_stats; + struct snd_soc_dapm_update *update; #ifdef CONFIG_DEBUG_FS struct dentry *debugfs_card_root; diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 366daef..7449e27 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -1425,7 +1425,7 @@ static void dapm_seq_run(struct snd_soc_dapm_context *dapm, static void dapm_widget_update(struct snd_soc_dapm_context *dapm) { - struct snd_soc_dapm_update *update = dapm->update; + struct snd_soc_dapm_update *update = dapm->card->update; struct snd_soc_dapm_widget_list *wlist; struct snd_soc_dapm_widget *w = NULL; unsigned int wi; @@ -1959,9 +1959,9 @@ int snd_soc_dapm_mux_update_power(struct snd_soc_dapm_context *dapm, int ret; mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); - dapm->update = update; + card->update = update; ret = soc_dapm_mux_update_power(dapm, kcontrol, mux, e); - dapm->update = NULL; + card->update = NULL; mutex_unlock(&card->dapm_mutex); if (ret > 0) soc_dpcm_runtime_update(card); @@ -2002,9 +2002,9 @@ int snd_soc_dapm_mixer_update_power(struct snd_soc_dapm_context *dapm, int ret; mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); - dapm->update = update; + card->update = update; ret = soc_dapm_mixer_update_power(dapm, kcontrol, connect); - dapm->update = NULL; + card->update = NULL; mutex_unlock(&card->dapm_mutex); if (ret > 0) soc_dpcm_runtime_update(card); @@ -2693,11 +2693,11 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, update.mask = mask; update.val = val; - widget->dapm->update = &update; + card->update = &update; soc_dapm_mixer_update_power(widget->dapm, kcontrol, connect); - widget->dapm->update = NULL; + card->update = NULL; } mutex_unlock(&card->dapm_mutex); @@ -2775,11 +2775,11 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, update.reg = e->reg; update.mask = mask; update.val = val; - widget->dapm->update = &update; + card->update = &update; soc_dapm_mux_update_power(widget->dapm, kcontrol, mux, e); - widget->dapm->update = NULL; + card->update = NULL; } mutex_unlock(&card->dapm_mutex); @@ -2928,11 +2928,11 @@ int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol, update.reg = e->reg; update.mask = mask; update.val = val; - widget->dapm->update = &update; + card->update = &update; soc_dapm_mux_update_power(widget->dapm, kcontrol, mux, e); - widget->dapm->update = NULL; + card->update = NULL; } mutex_unlock(&card->dapm_mutex); -- cgit v0.10.2 From 95dd5cd6e16d86786f7dc9da404ae477403d8f83 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 29 Jul 2013 17:13:56 +0200 Subject: ASoC: dapm: Pass card instead of dapm context to dapm_power_widgets() DAPM operations are always performed on the card as a whole. Yet (primarily for historic reasons) dapm_power_widgets() takes a DAPM context as its parameter. The DAPM context is mainly used to look up a pointer to the card. The same is true for a couple of functions that are being called from dapm_power_widgets(). This patch changes the signature of dapm_power_widgets() and a couple of related functions to take a snd_soc_card instead of a snd_soc_dapm_context. Some of the functions also use the DAPM's device to print error and debug messages. This can be a bit confusing though since this means the messages for all widgets, also those from other contexts, will be printed with that device. The patch updates those cases to use the device of the widget's DAPM context. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 7449e27..5db8df2 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -1213,10 +1213,9 @@ static void dapm_seq_insert(struct snd_soc_dapm_widget *new_widget, list_add_tail(&new_widget->power_list, list); } -static void dapm_seq_check_event(struct snd_soc_dapm_context *dapm, +static void dapm_seq_check_event(struct snd_soc_card *card, struct snd_soc_dapm_widget *w, int event) { - struct snd_soc_card *card = dapm->card; const char *ev_name; int power, ret; @@ -1254,22 +1253,21 @@ static void dapm_seq_check_event(struct snd_soc_dapm_context *dapm, return; if (w->event && (w->event_flags & event)) { - pop_dbg(dapm->dev, card->pop_time, "pop test : %s %s\n", + pop_dbg(w->dapm->dev, card->pop_time, "pop test : %s %s\n", w->name, ev_name); trace_snd_soc_dapm_widget_event_start(w, event); ret = w->event(w, NULL, event); trace_snd_soc_dapm_widget_event_done(w, event); if (ret < 0) - dev_err(dapm->dev, "ASoC: %s: %s event failed: %d\n", + dev_err(w->dapm->dev, "ASoC: %s: %s event failed: %d\n", ev_name, w->name, ret); } } /* Apply the coalesced changes from a DAPM sequence */ -static void dapm_seq_run_coalesced(struct snd_soc_dapm_context *dapm, +static void dapm_seq_run_coalesced(struct snd_soc_card *card, struct list_head *pending) { - struct snd_soc_card *card = dapm->card; struct snd_soc_dapm_widget *w; int reg, power; unsigned int value = 0; @@ -1292,13 +1290,13 @@ static void dapm_seq_run_coalesced(struct snd_soc_dapm_context *dapm, if (power) value |= cur_mask; - pop_dbg(dapm->dev, card->pop_time, + pop_dbg(w->dapm->dev, card->pop_time, "pop test : Queue %s: reg=0x%x, 0x%x/0x%x\n", w->name, reg, value, mask); /* Check for events */ - dapm_seq_check_event(dapm, w, SND_SOC_DAPM_PRE_PMU); - dapm_seq_check_event(dapm, w, SND_SOC_DAPM_PRE_PMD); + dapm_seq_check_event(card, w, SND_SOC_DAPM_PRE_PMU); + dapm_seq_check_event(card, w, SND_SOC_DAPM_PRE_PMD); } if (reg >= 0) { @@ -1308,7 +1306,7 @@ static void dapm_seq_run_coalesced(struct snd_soc_dapm_context *dapm, w = list_first_entry(pending, struct snd_soc_dapm_widget, power_list); - pop_dbg(dapm->dev, card->pop_time, + pop_dbg(w->dapm->dev, card->pop_time, "pop test : Applying 0x%x/0x%x to %x in %dms\n", value, mask, reg, card->pop_time); pop_wait(card->pop_time); @@ -1316,8 +1314,8 @@ static void dapm_seq_run_coalesced(struct snd_soc_dapm_context *dapm, } list_for_each_entry(w, pending, power_list) { - dapm_seq_check_event(dapm, w, SND_SOC_DAPM_POST_PMU); - dapm_seq_check_event(dapm, w, SND_SOC_DAPM_POST_PMD); + dapm_seq_check_event(card, w, SND_SOC_DAPM_POST_PMU); + dapm_seq_check_event(card, w, SND_SOC_DAPM_POST_PMD); } } @@ -1329,8 +1327,8 @@ static void dapm_seq_run_coalesced(struct snd_soc_dapm_context *dapm, * Currently anything that requires more than a single write is not * handled. */ -static void dapm_seq_run(struct snd_soc_dapm_context *dapm, - struct list_head *list, int event, bool power_up) +static void dapm_seq_run(struct snd_soc_card *card, + struct list_head *list, int event, bool power_up) { struct snd_soc_dapm_widget *w, *n; LIST_HEAD(pending); @@ -1353,7 +1351,7 @@ static void dapm_seq_run(struct snd_soc_dapm_context *dapm, if (sort[w->id] != cur_sort || w->reg != cur_reg || w->dapm != cur_dapm || w->subseq != cur_subseq) { if (!list_empty(&pending)) - dapm_seq_run_coalesced(cur_dapm, &pending); + dapm_seq_run_coalesced(card, &pending); if (cur_dapm && cur_dapm->seq_notifier) { for (i = 0; i < ARRAY_SIZE(dapm_up_seq); i++) @@ -1413,7 +1411,7 @@ static void dapm_seq_run(struct snd_soc_dapm_context *dapm, } if (!list_empty(&pending)) - dapm_seq_run_coalesced(cur_dapm, &pending); + dapm_seq_run_coalesced(card, &pending); if (cur_dapm && cur_dapm->seq_notifier) { for (i = 0; i < ARRAY_SIZE(dapm_up_seq); i++) @@ -1423,9 +1421,9 @@ static void dapm_seq_run(struct snd_soc_dapm_context *dapm, } } -static void dapm_widget_update(struct snd_soc_dapm_context *dapm) +static void dapm_widget_update(struct snd_soc_card *card) { - struct snd_soc_dapm_update *update = dapm->card->update; + struct snd_soc_dapm_update *update = card->update; struct snd_soc_dapm_widget_list *wlist; struct snd_soc_dapm_widget *w = NULL; unsigned int wi; @@ -1442,7 +1440,7 @@ static void dapm_widget_update(struct snd_soc_dapm_context *dapm) if (w->event && (w->event_flags & SND_SOC_DAPM_PRE_REG)) { ret = w->event(w, update->kcontrol, SND_SOC_DAPM_PRE_REG); if (ret != 0) - dev_err(dapm->dev, "ASoC: %s DAPM pre-event failed: %d\n", + dev_err(w->dapm->dev, "ASoC: %s DAPM pre-event failed: %d\n", w->name, ret); } } @@ -1453,7 +1451,7 @@ static void dapm_widget_update(struct snd_soc_dapm_context *dapm) ret = soc_widget_update_bits_locked(w, update->reg, update->mask, update->val); if (ret < 0) - dev_err(dapm->dev, "ASoC: %s DAPM update failed: %d\n", + dev_err(w->dapm->dev, "ASoC: %s DAPM update failed: %d\n", w->name, ret); for (wi = 0; wi < wlist->num_widgets; wi++) { @@ -1462,7 +1460,7 @@ static void dapm_widget_update(struct snd_soc_dapm_context *dapm) if (w->event && (w->event_flags & SND_SOC_DAPM_POST_REG)) { ret = w->event(w, update->kcontrol, SND_SOC_DAPM_POST_REG); if (ret != 0) - dev_err(dapm->dev, "ASoC: %s DAPM post-event failed: %d\n", + dev_err(w->dapm->dev, "ASoC: %s DAPM post-event failed: %d\n", w->name, ret); } } @@ -1627,9 +1625,8 @@ static void dapm_power_one_widget(struct snd_soc_dapm_widget *w, * o Input pin to Output pin (bypass, sidetone) * o DAC to ADC (loopback). */ -static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event) +static int dapm_power_widgets(struct snd_soc_card *card, int event) { - struct snd_soc_card *card = dapm->card; struct snd_soc_dapm_widget *w; struct snd_soc_dapm_context *d; LIST_HEAD(up_list); @@ -1711,29 +1708,29 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event) trace_snd_soc_dapm_walk_done(card); /* Run all the bias changes in parallel */ - list_for_each_entry(d, &dapm->card->dapm_list, list) + list_for_each_entry(d, &card->dapm_list, list) async_schedule_domain(dapm_pre_sequence_async, d, &async_domain); async_synchronize_full_domain(&async_domain); list_for_each_entry(w, &down_list, power_list) { - dapm_seq_check_event(dapm, w, SND_SOC_DAPM_WILL_PMD); + dapm_seq_check_event(card, w, SND_SOC_DAPM_WILL_PMD); } list_for_each_entry(w, &up_list, power_list) { - dapm_seq_check_event(dapm, w, SND_SOC_DAPM_WILL_PMU); + dapm_seq_check_event(card, w, SND_SOC_DAPM_WILL_PMU); } /* Power down widgets first; try to avoid amplifying pops. */ - dapm_seq_run(dapm, &down_list, event, false); + dapm_seq_run(card, &down_list, event, false); - dapm_widget_update(dapm); + dapm_widget_update(card); /* Now power up. */ - dapm_seq_run(dapm, &up_list, event, true); + dapm_seq_run(card, &up_list, event, true); /* Run all the bias changes in parallel */ - list_for_each_entry(d, &dapm->card->dapm_list, list) + list_for_each_entry(d, &card->dapm_list, list) async_schedule_domain(dapm_post_sequence_async, d, &async_domain); async_synchronize_full_domain(&async_domain); @@ -1744,7 +1741,7 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event) d->stream_event(d, event); } - pop_dbg(dapm->dev, card->pop_time, + pop_dbg(card->dev, card->pop_time, "DAPM sequencing finished, waiting %dms\n", card->pop_time); pop_wait(card->pop_time); @@ -1917,14 +1914,14 @@ static inline void dapm_debugfs_cleanup(struct snd_soc_dapm_context *dapm) #endif /* test and update the power status of a mux widget */ -static int soc_dapm_mux_update_power(struct snd_soc_dapm_context *dapm, +static int soc_dapm_mux_update_power(struct snd_soc_card *card, struct snd_kcontrol *kcontrol, int mux, struct soc_enum *e) { struct snd_soc_dapm_path *path; int found = 0; /* find dapm widget path assoc with kcontrol */ - list_for_each_entry(path, &dapm->card->paths, list) { + list_for_each_entry(path, &card->paths, list) { if (path->kcontrol != kcontrol) continue; @@ -1946,7 +1943,7 @@ static int soc_dapm_mux_update_power(struct snd_soc_dapm_context *dapm, } if (found) - dapm_power_widgets(dapm, SND_SOC_DAPM_STREAM_NOP); + dapm_power_widgets(card, SND_SOC_DAPM_STREAM_NOP); return found; } @@ -1960,7 +1957,7 @@ int snd_soc_dapm_mux_update_power(struct snd_soc_dapm_context *dapm, mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); card->update = update; - ret = soc_dapm_mux_update_power(dapm, kcontrol, mux, e); + ret = soc_dapm_mux_update_power(card, kcontrol, mux, e); card->update = NULL; mutex_unlock(&card->dapm_mutex); if (ret > 0) @@ -1970,14 +1967,14 @@ int snd_soc_dapm_mux_update_power(struct snd_soc_dapm_context *dapm, EXPORT_SYMBOL_GPL(snd_soc_dapm_mux_update_power); /* test and update the power status of a mixer or switch widget */ -static int soc_dapm_mixer_update_power(struct snd_soc_dapm_context *dapm, +static int soc_dapm_mixer_update_power(struct snd_soc_card *card, struct snd_kcontrol *kcontrol, int connect) { struct snd_soc_dapm_path *path; int found = 0; /* find dapm widget path assoc with kcontrol */ - list_for_each_entry(path, &dapm->card->paths, list) { + list_for_each_entry(path, &card->paths, list) { if (path->kcontrol != kcontrol) continue; @@ -1989,7 +1986,7 @@ static int soc_dapm_mixer_update_power(struct snd_soc_dapm_context *dapm, } if (found) - dapm_power_widgets(dapm, SND_SOC_DAPM_STREAM_NOP); + dapm_power_widgets(card, SND_SOC_DAPM_STREAM_NOP); return found; } @@ -2003,7 +2000,7 @@ int snd_soc_dapm_mixer_update_power(struct snd_soc_dapm_context *dapm, mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); card->update = update; - ret = soc_dapm_mixer_update_power(dapm, kcontrol, connect); + ret = soc_dapm_mixer_update_power(card, kcontrol, connect); card->update = NULL; mutex_unlock(&card->dapm_mutex); if (ret > 0) @@ -2180,7 +2177,7 @@ int snd_soc_dapm_sync(struct snd_soc_dapm_context *dapm) return 0; mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); - ret = dapm_power_widgets(dapm, SND_SOC_DAPM_STREAM_NOP); + ret = dapm_power_widgets(dapm->card, SND_SOC_DAPM_STREAM_NOP); mutex_unlock(&dapm->card->dapm_mutex); return ret; } @@ -2545,12 +2542,13 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_weak_routes); */ int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm) { + struct snd_soc_card *card = dapm->card; struct snd_soc_dapm_widget *w; unsigned int val; - mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT); + mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT); - list_for_each_entry(w, &dapm->card->widgets, list) + list_for_each_entry(w, &card->widgets, list) { if (w->new) continue; @@ -2560,7 +2558,7 @@ int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm) sizeof(struct snd_kcontrol *), GFP_KERNEL); if (!w->kcontrols) { - mutex_unlock(&dapm->card->dapm_mutex); + mutex_unlock(&card->dapm_mutex); return -ENOMEM; } } @@ -2601,8 +2599,8 @@ int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm) dapm_debugfs_add_widget(w); } - dapm_power_widgets(dapm, SND_SOC_DAPM_STREAM_NOP); - mutex_unlock(&dapm->card->dapm_mutex); + dapm_power_widgets(card, SND_SOC_DAPM_STREAM_NOP); + mutex_unlock(&card->dapm_mutex); return 0; } EXPORT_SYMBOL_GPL(snd_soc_dapm_new_widgets); @@ -2695,7 +2693,7 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, card->update = &update; - soc_dapm_mixer_update_power(widget->dapm, kcontrol, connect); + soc_dapm_mixer_update_power(card, kcontrol, connect); card->update = NULL; } @@ -2777,7 +2775,7 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, update.val = val; card->update = &update; - soc_dapm_mux_update_power(widget->dapm, kcontrol, mux, e); + soc_dapm_mux_update_power(card, kcontrol, mux, e); card->update = NULL; } @@ -2832,7 +2830,7 @@ int snd_soc_dapm_put_enum_virt(struct snd_kcontrol *kcontrol, change = widget->value != ucontrol->value.enumerated.item[0]; if (change) { widget->value = ucontrol->value.enumerated.item[0]; - soc_dapm_mux_update_power(widget->dapm, kcontrol, widget->value, e); + soc_dapm_mux_update_power(card, kcontrol, widget->value, e); } mutex_unlock(&card->dapm_mutex); @@ -2930,7 +2928,7 @@ int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol, update.val = val; card->update = &update; - soc_dapm_mux_update_power(widget->dapm, kcontrol, mux, e); + soc_dapm_mux_update_power(card, kcontrol, mux, e); card->update = NULL; } @@ -3478,7 +3476,7 @@ static void soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd, int stream, } } - dapm_power_widgets(&rtd->card->dapm, event); + dapm_power_widgets(rtd->card, event); } /** @@ -3747,7 +3745,7 @@ static void soc_dapm_shutdown_codec(struct snd_soc_dapm_context *dapm) if (dapm->bias_level == SND_SOC_BIAS_ON) snd_soc_dapm_set_bias_level(dapm, SND_SOC_BIAS_PREPARE); - dapm_seq_run(dapm, &down_list, 0, false); + dapm_seq_run(card, &down_list, 0, false); if (dapm->bias_level == SND_SOC_BIAS_PREPARE) snd_soc_dapm_set_bias_level(dapm, SND_SOC_BIAS_STANDBY); -- cgit v0.10.2 From eee5d7f99ae95059e1a3d1cfa2dea3ed8dbd94ac Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 29 Jul 2013 17:13:57 +0200 Subject: ASoC: dapm: Add a helper to get the CODEC for DAPM kcontrol We use the same 3 lines to get the CODEC for a kcontrol in a quite a few places. This patch puts them into a common helper function. Having this encapsulated in a helper function will also make it more easier to eventually change the data layout of the kcontrol's private data. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 3397292..ebfae8f 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -427,6 +427,8 @@ void dapm_mark_io_dirty(struct snd_soc_dapm_context *dapm); int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream, struct snd_soc_dapm_widget_list **list); +struct snd_soc_codec *snd_soc_dapm_kcontrol_codec(struct snd_kcontrol *kcontrol); + /* dapm widget types */ enum snd_soc_dapm_type { snd_soc_dapm_input = 0, /* input pin */ diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c index 1325c0c..fec0db0 100644 --- a/sound/soc/codecs/tlv320aic3x.c +++ b/sound/soc/codecs/tlv320aic3x.c @@ -138,8 +138,7 @@ static const u8 aic3x_reg[AIC3X_CACHEREGNUM] = { static int snd_soc_dapm_put_volsw_aic3x(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 = snd_soc_dapm_kcontrol_codec(kcontrol); struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; unsigned int reg = mc->reg; @@ -165,14 +164,14 @@ static int snd_soc_dapm_put_volsw_aic3x(struct snd_kcontrol *kcontrol, mask <<= shift; val <<= shift; - change = snd_soc_test_bits(widget->codec, val, mask, reg); + change = snd_soc_test_bits(codec, val, mask, reg); if (change) { update.kcontrol = kcontrol; update.reg = reg; update.mask = mask; update.val = val; - snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, connect, + snd_soc_dapm_mixer_update_power(&codec->dapm, kcontrol, connect, &update); } diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c index 44621dd..d6c5bf1 100644 --- a/sound/soc/codecs/twl6040.c +++ b/sound/soc/codecs/twl6040.c @@ -437,9 +437,7 @@ static irqreturn_t twl6040_audio_handler(int irq, void *data) static int twl6040_soc_dapm_put_vibra_enum(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_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; unsigned int val; diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c index fa24ced..eebcb1d 100644 --- a/sound/soc/codecs/wm8903.c +++ b/sound/soc/codecs/wm8903.c @@ -364,9 +364,7 @@ static void wm8903_seq_notifier(struct snd_soc_dapm_context *dapm, static int wm8903_class_w_put(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_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec); u16 reg; int ret; diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index ba832b7..eee2a01 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -1437,9 +1437,7 @@ SOC_DAPM_SINGLE("AIF1.1 Switch", WM8994_DAC2_RIGHT_MIXER_ROUTING, static int wm8994_put_class_w(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 *w = wlist->widgets[0]; - struct snd_soc_codec *codec = w->codec; + struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); int ret; ret = snd_soc_dapm_put_volsw(kcontrol, ucontrol); diff --git a/sound/soc/codecs/wm8995.c b/sound/soc/codecs/wm8995.c index 90a65c4..da2899e6 100644 --- a/sound/soc/codecs/wm8995.c +++ b/sound/soc/codecs/wm8995.c @@ -549,12 +549,9 @@ static int check_clk_sys(struct snd_soc_dapm_widget *source, static int wm8995_put_class_w(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 *w = wlist->widgets[0]; - struct snd_soc_codec *codec; + struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); int ret; - codec = w->codec; ret = snd_soc_dapm_put_volsw(kcontrol, ucontrol); wm8995_update_class_w(codec); return ret; diff --git a/sound/soc/codecs/wm_hubs.c b/sound/soc/codecs/wm_hubs.c index 2d9e099..8b50e59 100644 --- a/sound/soc/codecs/wm_hubs.c +++ b/sound/soc/codecs/wm_hubs.c @@ -699,9 +699,7 @@ EXPORT_SYMBOL_GPL(wm_hubs_update_class_w); static int class_w_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_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); int ret; ret = snd_soc_dapm_put_volsw(kcontrol, ucontrol); @@ -721,9 +719,7 @@ static int class_w_put_volsw(struct snd_kcontrol *kcontrol, static int class_w_put_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_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); int ret; ret = snd_soc_dapm_put_enum_double(kcontrol, ucontrol); diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 5db8df2..b18ac5b 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -174,6 +174,17 @@ static inline struct snd_soc_dapm_widget *dapm_cnew_widget( return kmemdup(_widget, sizeof(*_widget), GFP_KERNEL); } +/** + * snd_soc_dapm_kcontrol_codec() - Returns the codec associated to a kcontrol + * @kcontrol: The kcontrol + */ +struct snd_soc_codec *snd_soc_dapm_kcontrol_codec(struct snd_kcontrol *kcontrol) +{ + struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); + return wlist->widgets[0]->codec; +} +EXPORT_SYMBOL_GPL(snd_soc_dapm_kcontrol_codec); + static void dapm_reset(struct snd_soc_card *card) { struct snd_soc_dapm_widget *w; @@ -2617,8 +2628,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_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; unsigned int reg = mc->reg; @@ -2628,12 +2638,12 @@ int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol, unsigned int invert = mc->invert; if (snd_soc_volsw_is_stereo(mc)) - dev_warn(widget->dapm->dev, + dev_warn(codec->dapm.dev, "ASoC: Control '%s' is stereo, which is not supported\n", kcontrol->id.name); ucontrol->value.integer.value[0] = - (snd_soc_read(widget->codec, reg) >> shift) & mask; + (snd_soc_read(codec, reg) >> shift) & mask; if (invert) ucontrol->value.integer.value[0] = max - ucontrol->value.integer.value[0]; @@ -2654,9 +2664,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_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); struct snd_soc_card *card = codec->card; struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; @@ -2670,7 +2678,7 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, struct snd_soc_dapm_update update; if (snd_soc_volsw_is_stereo(mc)) - dev_warn(widget->dapm->dev, + dev_warn(codec->dapm.dev, "ASoC: Control '%s' is stereo, which is not supported\n", kcontrol->id.name); @@ -2684,7 +2692,7 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); - change = snd_soc_test_bits(widget->codec, reg, mask, val); + change = snd_soc_test_bits(codec, reg, mask, val); if (change) { update.kcontrol = kcontrol; update.reg = reg; @@ -2715,12 +2723,11 @@ 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_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; unsigned int val; - val = snd_soc_read(widget->codec, e->reg); + val = snd_soc_read(codec, e->reg); ucontrol->value.enumerated.item[0] = (val >> e->shift_l) & e->mask; if (e->shift_l != e->shift_r) ucontrol->value.enumerated.item[1] = @@ -2765,7 +2772,7 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); - change = snd_soc_test_bits(widget->codec, e->reg, mask, val); + change = snd_soc_test_bits(codec, e->reg, mask, val); if (change) { widget->value = val; @@ -2854,12 +2861,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_codec *codec = snd_soc_dapm_kcontrol_codec(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 = snd_soc_read(codec, e->reg); val = (reg_val >> e->shift_l) & e->mask; for (mux = 0; mux < e->max; mux++) { if (val == e->values[mux]) @@ -2918,7 +2924,7 @@ int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol, mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); - change = snd_soc_test_bits(widget->codec, e->reg, mask, val); + change = snd_soc_test_bits(codec, e->reg, mask, val); if (change) { widget->value = val; -- cgit v0.10.2 From e84357f7608f230b905acb18fe668609c9b811f0 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 29 Jul 2013 17:13:58 +0200 Subject: ASoC: dapm: Wrap kcontrol widget list access In preparation for adding additional per control data wrap all access to the widget list in helper functions. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index b18ac5b..da35b10 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -174,14 +174,72 @@ static inline struct snd_soc_dapm_widget *dapm_cnew_widget( return kmemdup(_widget, sizeof(*_widget), GFP_KERNEL); } +struct dapm_kcontrol_data { + struct snd_soc_dapm_widget_list wlist; +}; + +static int dapm_kcontrol_data_alloc(struct snd_soc_dapm_widget *widget, + struct snd_kcontrol *kcontrol) +{ + struct dapm_kcontrol_data *data; + + data = kzalloc(sizeof(*data) + sizeof(widget), GFP_KERNEL); + if (!data) { + dev_err(widget->dapm->dev, + "ASoC: can't allocate kcontrol data for %s\n", + widget->name); + return -ENOMEM; + } + + data->wlist.widgets[0] = widget; + data->wlist.num_widgets = 1; + + kcontrol->private_data = data; + + return 0; +} + +static void dapm_kcontrol_free(struct snd_kcontrol *kctl) +{ + struct dapm_kcontrol_data *data = snd_kcontrol_chip(kctl); + kfree(data); +} + +static struct snd_soc_dapm_widget_list *dapm_kcontrol_get_wlist( + const struct snd_kcontrol *kcontrol) +{ + struct dapm_kcontrol_data *data = snd_kcontrol_chip(kcontrol); + + return &data->wlist; +} + +static int dapm_kcontrol_add_widget(struct snd_kcontrol *kcontrol, + struct snd_soc_dapm_widget *widget) +{ + struct dapm_kcontrol_data *data = snd_kcontrol_chip(kcontrol); + struct dapm_kcontrol_data *new_data; + unsigned int n = data->wlist.num_widgets + 1; + + new_data = krealloc(data, sizeof(*data) + sizeof(widget) * n, + GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->wlist.widgets[n - 1] = widget; + data->wlist.num_widgets = n; + + kcontrol->private_data = data; + + return 0; +} + /** * snd_soc_dapm_kcontrol_codec() - Returns the codec associated to a kcontrol * @kcontrol: The kcontrol */ struct snd_soc_codec *snd_soc_dapm_kcontrol_codec(struct snd_kcontrol *kcontrol) { - struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); - return wlist->widgets[0]->codec; + return dapm_kcontrol_get_wlist(kcontrol)->widgets[0]->codec; } EXPORT_SYMBOL_GPL(snd_soc_dapm_kcontrol_codec); @@ -488,11 +546,6 @@ static int dapm_is_shared_kcontrol(struct snd_soc_dapm_context *dapm, return 0; } -static void dapm_kcontrol_free(struct snd_kcontrol *kctl) -{ - kfree(kctl->private_data); -} - /* * Determine if a kcontrol is shared. If it is, look it up. If it isn't, * create it. Either way, add the widget into the control's widget list @@ -506,9 +559,6 @@ static int dapm_create_or_share_mixmux_kcontrol(struct snd_soc_dapm_widget *w, size_t prefix_len; int shared; struct snd_kcontrol *kcontrol; - struct snd_soc_dapm_widget_list *wlist; - int wlistentries; - size_t wlistsize; bool wname_in_long_name, kcname_in_long_name; char *long_name; const char *name; @@ -527,25 +577,6 @@ static int dapm_create_or_share_mixmux_kcontrol(struct snd_soc_dapm_widget *w, shared = dapm_is_shared_kcontrol(dapm, w, &w->kcontrol_news[kci], &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; - if (!kcontrol) { if (shared) { wname_in_long_name = false; @@ -568,7 +599,6 @@ static int dapm_create_or_share_mixmux_kcontrol(struct snd_soc_dapm_widget *w, kcname_in_long_name = false; break; default: - kfree(wlist); return -EINVAL; } } @@ -583,10 +613,8 @@ static int dapm_create_or_share_mixmux_kcontrol(struct snd_soc_dapm_widget *w, long_name = kasprintf(GFP_KERNEL, "%s %s", w->name + prefix_len, w->kcontrol_news[kci].name); - if (long_name == NULL) { - kfree(wlist); + if (long_name == NULL) return -ENOMEM; - } name = long_name; } else if (wname_in_long_name) { @@ -597,21 +625,30 @@ static int dapm_create_or_share_mixmux_kcontrol(struct snd_soc_dapm_widget *w, name = w->kcontrol_news[kci].name; } - kcontrol = snd_soc_cnew(&w->kcontrol_news[kci], wlist, name, + kcontrol = snd_soc_cnew(&w->kcontrol_news[kci], NULL, name, prefix); kcontrol->private_free = dapm_kcontrol_free; kfree(long_name); + + ret = dapm_kcontrol_data_alloc(w, kcontrol); + if (ret) { + snd_ctl_free_one(kcontrol); + return ret; + } + ret = snd_ctl_add(card, kcontrol); if (ret < 0) { dev_err(dapm->dev, "ASoC: failed to add widget %s dapm kcontrol %s: %d\n", w->name, name, ret); - kfree(wlist); return ret; } + } else { + ret = dapm_kcontrol_add_widget(kcontrol, w); + if (ret) + return ret; } - kcontrol->private_data = wlist; w->kcontrols[kci] = kcontrol; path->kcontrol = kcontrol; @@ -1443,7 +1480,7 @@ static void dapm_widget_update(struct snd_soc_card *card) if (!update) return; - wlist = snd_kcontrol_chip(update->kcontrol); + wlist = dapm_kcontrol_get_wlist(update->kcontrol); for (wi = 0; wi < wlist->num_widgets; wi++) { w = wlist->widgets[wi]; @@ -2749,7 +2786,7 @@ 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_list *wlist = dapm_kcontrol_get_wlist(kcontrol); struct snd_soc_dapm_widget *widget = wlist->widgets[0]; struct snd_soc_codec *codec = widget->codec; struct snd_soc_card *card = codec->card; @@ -2802,7 +2839,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_list *wlist = dapm_kcontrol_get_wlist(kcontrol); struct snd_soc_dapm_widget *widget = wlist->widgets[0]; ucontrol->value.enumerated.item[0] = widget->value; @@ -2821,7 +2858,7 @@ 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_list *wlist = dapm_kcontrol_get_wlist(kcontrol); struct snd_soc_dapm_widget *widget = wlist->widgets[0]; struct snd_soc_codec *codec = widget->codec; struct snd_soc_card *card = codec->card; @@ -2901,7 +2938,7 @@ 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_list *wlist = dapm_kcontrol_get_wlist(kcontrol); struct snd_soc_dapm_widget *widget = wlist->widgets[0]; struct snd_soc_codec *codec = widget->codec; struct snd_soc_card *card = codec->card; -- cgit v0.10.2 From cf7c1de20c576477d42deae255cbc6e439bb5dc0 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 29 Jul 2013 17:13:59 +0200 Subject: ASoC: dapm: Move 'value' field from widget to control The 'value' field is really per control and not per widget. Currently it is only used for virtual MUXes, which only have one control per widget. So in that case there is not so much of a difference between whether it is stored per widget or per control. Moving the 'value' field from the widget to the control will allow us to use it also for cases where we have more than one control per widget. E.g. for mixers with multiple input controls. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index ebfae8f..d7d26cc 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -523,7 +523,6 @@ struct snd_soc_dapm_widget { /* dapm control */ int reg; /* negative reg = no direct dapm */ unsigned char shift; /* bits to shift */ - unsigned int value; /* widget current value */ unsigned int mask; /* non-shifted mask */ unsigned int on_val; /* on state value */ unsigned int off_val; /* off state value */ diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index da35b10..bad6f6d 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -175,6 +175,7 @@ static inline struct snd_soc_dapm_widget *dapm_cnew_widget( } struct dapm_kcontrol_data { + unsigned int value; struct snd_soc_dapm_widget_list wlist; }; @@ -233,6 +234,26 @@ static int dapm_kcontrol_add_widget(struct snd_kcontrol *kcontrol, return 0; } +static unsigned int dapm_kcontrol_get_value(const struct snd_kcontrol *kcontrol) +{ + struct dapm_kcontrol_data *data = snd_kcontrol_chip(kcontrol); + + return data->value; +} + +static bool dapm_kcontrol_set_value(const struct snd_kcontrol *kcontrol, + unsigned int value) +{ + struct dapm_kcontrol_data *data = snd_kcontrol_chip(kcontrol); + + if (data->value == value) + return false; + + data->value = value; + + return true; +} + /** * snd_soc_dapm_kcontrol_codec() - Returns the codec associated to a kcontrol * @kcontrol: The kcontrol @@ -2786,9 +2807,7 @@ 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 = dapm_kcontrol_get_wlist(kcontrol); - struct snd_soc_dapm_widget *widget = wlist->widgets[0]; - struct snd_soc_codec *codec = widget->codec; + struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); struct snd_soc_card *card = codec->card; struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; unsigned int val, mux, change; @@ -2811,8 +2830,6 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, change = snd_soc_test_bits(codec, e->reg, mask, val); if (change) { - widget->value = val; - update.kcontrol = kcontrol; update.reg = e->reg; update.mask = mask; @@ -2839,11 +2856,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 = dapm_kcontrol_get_wlist(kcontrol); - struct snd_soc_dapm_widget *widget = wlist->widgets[0]; - - ucontrol->value.enumerated.item[0] = widget->value; - + ucontrol->value.enumerated.item[0] = dapm_kcontrol_get_value(kcontrol); return 0; } EXPORT_SYMBOL_GPL(snd_soc_dapm_get_enum_virt); @@ -2858,10 +2871,9 @@ 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 = dapm_kcontrol_get_wlist(kcontrol); - struct snd_soc_dapm_widget *widget = wlist->widgets[0]; - struct snd_soc_codec *codec = widget->codec; + struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); struct snd_soc_card *card = codec->card; + unsigned int value; struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; int change; @@ -2871,11 +2883,10 @@ int snd_soc_dapm_put_enum_virt(struct snd_kcontrol *kcontrol, mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); - change = widget->value != ucontrol->value.enumerated.item[0]; - if (change) { - widget->value = ucontrol->value.enumerated.item[0]; - soc_dapm_mux_update_power(card, kcontrol, widget->value, e); - } + value = ucontrol->value.enumerated.item[0]; + change = dapm_kcontrol_set_value(kcontrol, value); + if (change) + soc_dapm_mux_update_power(card, kcontrol, value, e); mutex_unlock(&card->dapm_mutex); return change; @@ -2938,9 +2949,7 @@ 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 = dapm_kcontrol_get_wlist(kcontrol); - struct snd_soc_dapm_widget *widget = wlist->widgets[0]; - struct snd_soc_codec *codec = widget->codec; + struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); struct snd_soc_card *card = codec->card; struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; unsigned int val, mux, change; @@ -2963,8 +2972,6 @@ int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol, change = snd_soc_test_bits(codec, e->reg, mask, val); if (change) { - widget->value = val; - update.kcontrol = kcontrol; update.reg = e->reg; update.mask = mask; -- cgit v0.10.2 From 5106b92f80a2cd37c52cffed80b4f5acfb77ccfd Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 29 Jul 2013 17:14:00 +0200 Subject: ASoC: dapm: Keep a list of paths per kcontrol Currently we store for each path which control (if any at all) is associated with that control. But we are only ever interested in the reverse relationship, i.e. we want to know all the paths a certain control is associated with. This is currently implemented by always iterating over all paths. This patch updates the code to keep a list for each control which contains all the paths that are associated with that control. This improves the run time of e.g. soc_dapm_mixer_update_power() and soc_dapm_mux_update_power() from O(n) (with n being the number of paths for the card) to O(1). Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index d7d26cc..693c75b 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -490,7 +490,6 @@ struct snd_soc_dapm_path { /* source (input) and sink (output) widgets */ struct snd_soc_dapm_widget *source; struct snd_soc_dapm_widget *sink; - struct snd_kcontrol *kcontrol; /* status */ u32 connect:1; /* source and sink widgets are connected */ @@ -503,6 +502,7 @@ struct snd_soc_dapm_path { struct list_head list_source; struct list_head list_sink; + struct list_head list_kcontrol; struct list_head list; }; diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index bad6f6d..b779d36 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -176,6 +176,7 @@ static inline struct snd_soc_dapm_widget *dapm_cnew_widget( struct dapm_kcontrol_data { unsigned int value; + struct list_head paths; struct snd_soc_dapm_widget_list wlist; }; @@ -194,6 +195,7 @@ static int dapm_kcontrol_data_alloc(struct snd_soc_dapm_widget *widget, data->wlist.widgets[0] = widget; data->wlist.num_widgets = 1; + INIT_LIST_HEAD(&data->paths); kcontrol->private_data = data; @@ -234,6 +236,26 @@ static int dapm_kcontrol_add_widget(struct snd_kcontrol *kcontrol, return 0; } +static void dapm_kcontrol_add_path(const struct snd_kcontrol *kcontrol, + struct snd_soc_dapm_path *path) +{ + struct dapm_kcontrol_data *data = snd_kcontrol_chip(kcontrol); + + list_add_tail(&path->list_kcontrol, &data->paths); +} + +static struct list_head *dapm_kcontrol_get_path_list( + const struct snd_kcontrol *kcontrol) +{ + struct dapm_kcontrol_data *data = snd_kcontrol_chip(kcontrol); + + return &data->paths; +} + +#define dapm_kcontrol_for_each_path(path, kcontrol) \ + list_for_each_entry(path, dapm_kcontrol_get_path_list(kcontrol), \ + list_kcontrol) + static unsigned int dapm_kcontrol_get_value(const struct snd_kcontrol *kcontrol) { struct dapm_kcontrol_data *data = snd_kcontrol_chip(kcontrol); @@ -671,7 +693,7 @@ static int dapm_create_or_share_mixmux_kcontrol(struct snd_soc_dapm_widget *w, } w->kcontrols[kci] = kcontrol; - path->kcontrol = kcontrol; + dapm_kcontrol_add_path(kcontrol, path); return 0; } @@ -691,7 +713,7 @@ static int dapm_new_mixer(struct snd_soc_dapm_widget *w) continue; if (w->kcontrols[i]) { - path->kcontrol = w->kcontrols[i]; + dapm_kcontrol_add_path(w->kcontrols[i], path); continue; } @@ -730,7 +752,7 @@ static int dapm_new_mux(struct snd_soc_dapm_widget *w) return ret; list_for_each_entry(path, &w->sources, list_sink) - path->kcontrol = w->kcontrols[0]; + dapm_kcontrol_add_path(w->kcontrols[0], path); return 0; } @@ -1990,10 +2012,7 @@ static int soc_dapm_mux_update_power(struct snd_soc_card *card, int found = 0; /* find dapm widget path assoc with kcontrol */ - list_for_each_entry(path, &card->paths, list) { - if (path->kcontrol != kcontrol) - continue; - + dapm_kcontrol_for_each_path(path, kcontrol) { if (!path->name || !e->texts[mux]) continue; @@ -2043,11 +2062,7 @@ static int soc_dapm_mixer_update_power(struct snd_soc_card *card, int found = 0; /* find dapm widget path assoc with kcontrol */ - list_for_each_entry(path, &card->paths, list) { - if (path->kcontrol != kcontrol) - continue; - - /* found, now check type */ + dapm_kcontrol_for_each_path(path, kcontrol) { found = 1; path->connect = connect; dapm_mark_dirty(path->source, "mixer connection"); @@ -2152,6 +2167,7 @@ static void dapm_free_path(struct snd_soc_dapm_path *path) { list_del(&path->list_sink); list_del(&path->list_source); + list_del(&path->list_kcontrol); list_del(&path->list); kfree(path); } -- cgit v0.10.2 From de9ba98b6d2629f53fd271a973176c2fa9736d9c Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 29 Jul 2013 17:14:01 +0200 Subject: ASoC: dapm: Make widget power register settings more flexible Currently the DAPM code is limited to only setting or clearing a single bit in a register to power a widget up or down. This patch extends the DAPM code to be more flexible in that regard and allow widgets to use arbitrary values to be used to put a widget in either on or off state. Since the snd_soc_dapm_widget struct already contains a on_val and off_val field no additional fields need to be added and in fact the invert field can even be removed. Also the generated code is slightly smaller. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 693c75b..3575721 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -70,121 +70,144 @@ struct device; .num_kcontrols = 0, .reg = SND_SOC_NOPM, .event = wevent, \ .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD} +#define SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert) \ + .reg = wreg, .mask = 1, .shift = wshift, \ + .on_val = winvert ? 0 : 1, .off_val = winvert ? 1 : 0 + /* 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} +{ .id = snd_soc_dapm_pga, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .kcontrol_news = 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} +{ .id = snd_soc_dapm_out_drv, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .kcontrol_news = 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} +{ .id = snd_soc_dapm_mixer, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .kcontrol_news = 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, \ - .num_kcontrols = wncontrols} +{ .id = snd_soc_dapm_mixer_named_ctl, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .kcontrol_news = 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} +{ .id = snd_soc_dapm_micbias, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .kcontrol_news = 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} +{ .id = snd_soc_dapm_switch, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .kcontrol_news = 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} +{ .id = snd_soc_dapm_mux, .name = wname, .reg = wreg, \ + .kcontrol_news = 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} +{ .id = snd_soc_dapm_virt_mux, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .kcontrol_news = 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, \ - .num_kcontrols = 1} +{ .id = snd_soc_dapm_value_mux, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .kcontrol_news = 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)} +{ .id = snd_soc_dapm_pga, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .kcontrol_news = 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)} +{ .id = snd_soc_dapm_mixer, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .kcontrol_news = 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, \ - .num_kcontrols = ARRAY_SIZE(wcontrols)} +{ .id = snd_soc_dapm_mixer_named_ctl, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .kcontrol_news = 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, \ +{ .id = snd_soc_dapm_pga, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .kcontrol_news = 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, \ +{ .id = snd_soc_dapm_out_drv, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .kcontrol_news = 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, \ +{ .id = snd_soc_dapm_mixer, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .kcontrol_news = 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, \ +{ .id = snd_soc_dapm_mixer, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .kcontrol_news = wcontrols, \ .num_kcontrols = wncontrols, .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, \ +{ .id = snd_soc_dapm_switch, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .kcontrol_news = 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, \ +{ .id = snd_soc_dapm_mux, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .kcontrol_news = 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, \ +{ .id = snd_soc_dapm_virt_mux, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .kcontrol_news = 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, \ +{ .id = snd_soc_dapm_pga, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, 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} +{ .id = snd_soc_dapm_supply, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, 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), \ +{ .id = snd_soc_dapm_pga, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .kcontrol_news = 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), \ +{ .id = snd_soc_dapm_mixer, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .kcontrol_news = 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, \ - .num_kcontrols = ARRAY_SIZE(wcontrols), .event = wevent, .event_flags = wflags} +{ .id = snd_soc_dapm_mixer, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .kcontrol_news = 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) \ @@ -199,35 +222,36 @@ struct device; /* stream domain */ #define SND_SOC_DAPM_AIF_IN(wname, stname, wslot, wreg, wshift, winvert) \ { .id = snd_soc_dapm_aif_in, .name = wname, .sname = stname, \ - .reg = wreg, .shift = wshift, .invert = winvert } + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), } #define SND_SOC_DAPM_AIF_IN_E(wname, stname, wslot, wreg, wshift, winvert, \ wevent, wflags) \ { .id = snd_soc_dapm_aif_in, .name = wname, .sname = stname, \ - .reg = wreg, .shift = wshift, .invert = winvert, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ .event = wevent, .event_flags = wflags } #define SND_SOC_DAPM_AIF_OUT(wname, stname, wslot, wreg, wshift, winvert) \ { .id = snd_soc_dapm_aif_out, .name = wname, .sname = stname, \ - .reg = wreg, .shift = wshift, .invert = winvert } + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), } #define SND_SOC_DAPM_AIF_OUT_E(wname, stname, wslot, wreg, wshift, winvert, \ wevent, wflags) \ { .id = snd_soc_dapm_aif_out, .name = wname, .sname = stname, \ - .reg = wreg, .shift = wshift, .invert = winvert, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ .event = wevent, .event_flags = wflags } #define SND_SOC_DAPM_DAC(wname, stname, wreg, wshift, winvert) \ -{ .id = snd_soc_dapm_dac, .name = wname, .sname = stname, .reg = wreg, \ - .shift = wshift, .invert = winvert} +{ .id = snd_soc_dapm_dac, .name = wname, .sname = stname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert) } #define SND_SOC_DAPM_DAC_E(wname, stname, wreg, wshift, winvert, \ wevent, wflags) \ -{ .id = snd_soc_dapm_dac, .name = wname, .sname = stname, .reg = wreg, \ - .shift = wshift, .invert = winvert, \ +{ .id = snd_soc_dapm_dac, .name = wname, .sname = stname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ .event = wevent, .event_flags = wflags} + #define SND_SOC_DAPM_ADC(wname, stname, wreg, wshift, winvert) \ -{ .id = snd_soc_dapm_adc, .name = wname, .sname = stname, .reg = wreg, \ - .shift = wshift, .invert = winvert} +{ .id = snd_soc_dapm_adc, .name = wname, .sname = stname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), } #define SND_SOC_DAPM_ADC_E(wname, stname, wreg, wshift, winvert, \ wevent, wflags) \ -{ .id = snd_soc_dapm_adc, .name = wname, .sname = stname, .reg = wreg, \ - .shift = wshift, .invert = winvert, \ +{ .id = snd_soc_dapm_adc, .name = wname, .sname = stname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ .event = wevent, .event_flags = wflags} #define SND_SOC_DAPM_CLOCK_SUPPLY(wname) \ { .id = snd_soc_dapm_clock_supply, .name = wname, \ @@ -241,14 +265,14 @@ struct device; .on_val = won_val, .off_val = woff_val, .event = dapm_reg_event, \ .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD} #define SND_SOC_DAPM_SUPPLY(wname, wreg, wshift, winvert, wevent, wflags) \ -{ .id = snd_soc_dapm_supply, .name = wname, .reg = wreg, \ - .shift = wshift, .invert = winvert, .event = wevent, \ - .event_flags = wflags} +{ .id = snd_soc_dapm_supply, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .event = wevent, .event_flags = wflags} #define SND_SOC_DAPM_REGULATOR_SUPPLY(wname, wdelay, wflags) \ { .id = snd_soc_dapm_regulator_supply, .name = wname, \ .reg = SND_SOC_NOPM, .shift = wdelay, .event = dapm_regulator_event, \ .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD, \ - .invert = wflags} + .on_val = wflags} /* dapm kcontrol types */ @@ -527,7 +551,6 @@ struct snd_soc_dapm_widget { unsigned int on_val; /* on state value */ unsigned int off_val; /* off state value */ unsigned char power:1; /* block power status */ - unsigned char invert:1; /* invert the power bit */ unsigned char active:1; /* active stream on DAC, ADC's */ unsigned char connected:1; /* connected codec pin */ unsigned char new:1; /* cnew complete */ diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index b779d36..59bcc66 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -1122,7 +1122,7 @@ int dapm_regulator_event(struct snd_soc_dapm_widget *w, int ret; if (SND_SOC_DAPM_EVENT_ON(event)) { - if (w->invert & SND_SOC_DAPM_REGULATOR_BYPASS) { + if (w->on_val & SND_SOC_DAPM_REGULATOR_BYPASS) { ret = regulator_allow_bypass(w->regulator, false); if (ret != 0) dev_warn(w->dapm->dev, @@ -1132,7 +1132,7 @@ int dapm_regulator_event(struct snd_soc_dapm_widget *w, return regulator_enable(w->regulator); } else { - if (w->invert & SND_SOC_DAPM_REGULATOR_BYPASS) { + if (w->on_val & SND_SOC_DAPM_REGULATOR_BYPASS) { ret = regulator_allow_bypass(w->regulator, true); if (ret != 0) dev_warn(w->dapm->dev, @@ -1360,26 +1360,21 @@ static void dapm_seq_run_coalesced(struct snd_soc_card *card, struct list_head *pending) { struct snd_soc_dapm_widget *w; - int reg, power; + int reg; unsigned int value = 0; unsigned int mask = 0; - unsigned int cur_mask; reg = list_first_entry(pending, struct snd_soc_dapm_widget, power_list)->reg; list_for_each_entry(w, pending, power_list) { - cur_mask = 1 << w->shift; BUG_ON(reg != w->reg); - if (w->invert) - power = !w->power; + mask |= w->mask << w->shift; + if (w->power) + value |= w->on_val << w->shift; else - power = w->power; - - mask |= cur_mask; - if (power) - value |= cur_mask; + value |= w->off_val << w->shift; pop_dbg(w->dapm->dev, card->pop_time, "pop test : Queue %s: reg=0x%x, 0x%x/0x%x\n", @@ -1867,8 +1862,8 @@ static ssize_t dapm_widget_power_read_file(struct file *file, if (w->reg >= 0) ret += snprintf(buf + ret, PAGE_SIZE - ret, - " - R%d(0x%x) bit %d", - w->reg, w->reg, w->shift); + " - R%d(0x%x) mask 0x%x", + w->reg, w->reg, w->mask << w->shift); ret += snprintf(buf + ret, PAGE_SIZE - ret, "\n"); @@ -2669,12 +2664,9 @@ int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm) /* Read the initial power state from the device */ if (w->reg >= 0) { - val = soc_widget_read(w, w->reg); - val &= 1 << w->shift; - if (w->invert) - val = !val; - - if (val) + val = soc_widget_read(w, w->reg) >> w->shift; + val &= w->mask; + if (val == w->on_val) w->power = 1; } @@ -3093,7 +3085,7 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm, return NULL; } - if (w->invert & SND_SOC_DAPM_REGULATOR_BYPASS) { + if (w->on_val & SND_SOC_DAPM_REGULATOR_BYPASS) { ret = regulator_allow_bypass(w->regulator, true); if (ret != 0) dev_warn(w->dapm->dev, -- cgit v0.10.2 From 2553628e1973709bf378320ecffd3e4fb34458db Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 29 Jul 2013 17:14:02 +0200 Subject: ASoC: dapm: Add snd_soc_dapm_add_path() helper function snd_soc_dapm_add_path() is similar to snd_soc_dapm_add_route() except that it expects the pointer to the source and sink widgets instead of their names. This allows us to simplify the case where we already have a pointer to widgets. (E.g. as we have in snd_soc_dapm_link_dai_widgets()). snd_soc_dapm_add_route() will be updated to just look up the widget and then use snd_soc_dapm_add_path() to handle everything else. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 59bcc66..b811a27 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -2263,64 +2263,14 @@ int snd_soc_dapm_sync(struct snd_soc_dapm_context *dapm) } EXPORT_SYMBOL_GPL(snd_soc_dapm_sync); -static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm, - const struct snd_soc_dapm_route *route) +static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm, + struct snd_soc_dapm_widget *wsource, struct snd_soc_dapm_widget *wsink, + const char *control, + int (*connected)(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink)) { struct snd_soc_dapm_path *path; - struct snd_soc_dapm_widget *wsource = NULL, *wsink = NULL, *w; - struct snd_soc_dapm_widget *wtsource = NULL, *wtsink = NULL; - const char *sink; - const char *control = route->control; - const char *source; - char prefixed_sink[80]; - char prefixed_source[80]; - int ret = 0; - - if (dapm->codec && dapm->codec->name_prefix) { - snprintf(prefixed_sink, sizeof(prefixed_sink), "%s %s", - dapm->codec->name_prefix, route->sink); - sink = prefixed_sink; - snprintf(prefixed_source, sizeof(prefixed_source), "%s %s", - dapm->codec->name_prefix, route->source); - source = prefixed_source; - } else { - sink = route->sink; - source = route->source; - } - - /* - * find src and dest widgets over all widgets but favor a widget from - * current DAPM context - */ - list_for_each_entry(w, &dapm->card->widgets, list) { - if (!wsink && !(strcmp(w->name, sink))) { - wtsink = w; - if (w->dapm == dapm) - wsink = w; - continue; - } - if (!wsource && !(strcmp(w->name, source))) { - wtsource = w; - if (w->dapm == dapm) - wsource = w; - } - } - /* use widget from another DAPM context if not found from this */ - if (!wsink) - wsink = wtsink; - if (!wsource) - wsource = wtsource; - - if (wsource == NULL) { - dev_err(dapm->dev, "ASoC: no source widget found for %s\n", - route->source); - return -ENODEV; - } - if (wsink == NULL) { - dev_err(dapm->dev, "ASoC: no sink widget found for %s\n", - route->sink); - return -ENODEV; - } + int ret; path = kzalloc(sizeof(struct snd_soc_dapm_path), GFP_KERNEL); if (!path) @@ -2328,7 +2278,7 @@ static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm, path->source = wsource; path->sink = wsink; - path->connected = route->connected; + path->connected = connected; INIT_LIST_HEAD(&path->list); INIT_LIST_HEAD(&path->list_source); INIT_LIST_HEAD(&path->list_sink); @@ -2414,11 +2364,77 @@ static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm, dapm_mark_dirty(wsink, "Route added"); return 0; +err: + kfree(path); + return ret; +} +static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm, + const struct snd_soc_dapm_route *route) +{ + struct snd_soc_dapm_widget *wsource = NULL, *wsink = NULL, *w; + struct snd_soc_dapm_widget *wtsource = NULL, *wtsink = NULL; + const char *sink; + const char *source; + char prefixed_sink[80]; + char prefixed_source[80]; + int ret; + + if (dapm->codec && dapm->codec->name_prefix) { + snprintf(prefixed_sink, sizeof(prefixed_sink), "%s %s", + dapm->codec->name_prefix, route->sink); + sink = prefixed_sink; + snprintf(prefixed_source, sizeof(prefixed_source), "%s %s", + dapm->codec->name_prefix, route->source); + source = prefixed_source; + } else { + sink = route->sink; + source = route->source; + } + + /* + * find src and dest widgets over all widgets but favor a widget from + * current DAPM context + */ + list_for_each_entry(w, &dapm->card->widgets, list) { + if (!wsink && !(strcmp(w->name, sink))) { + wtsink = w; + if (w->dapm == dapm) + wsink = w; + continue; + } + if (!wsource && !(strcmp(w->name, source))) { + wtsource = w; + if (w->dapm == dapm) + wsource = w; + } + } + /* use widget from another DAPM context if not found from this */ + if (!wsink) + wsink = wtsink; + if (!wsource) + wsource = wtsource; + + if (wsource == NULL) { + dev_err(dapm->dev, "ASoC: no source widget found for %s\n", + route->source); + return -ENODEV; + } + if (wsink == NULL) { + dev_err(dapm->dev, "ASoC: no sink widget found for %s\n", + route->sink); + return -ENODEV; + } + + ret = snd_soc_dapm_add_path(dapm, wsource, wsink, route->control, + route->connected); + if (ret) + goto err; + + return 0; err: dev_warn(dapm->dev, "ASoC: no dapm match for %s --> %s --> %s\n", - source, control, sink); - kfree(path); + source, route->control, sink); return ret; } @@ -3421,9 +3437,6 @@ int snd_soc_dapm_link_dai_widgets(struct snd_soc_card *card) { struct snd_soc_dapm_widget *dai_w, *w; struct snd_soc_dai *dai; - struct snd_soc_dapm_route r; - - memset(&r, 0, sizeof(r)); /* For each DAI widget... */ list_for_each_entry(dai_w, &card->widgets, list) { @@ -3456,23 +3469,21 @@ int snd_soc_dapm_link_dai_widgets(struct snd_soc_card *card) if (dai->driver->playback.stream_name && strstr(w->sname, dai->driver->playback.stream_name)) { - r.source = dai->playback_widget->name; - r.sink = w->name; dev_dbg(dai->dev, "%s -> %s\n", - r.source, r.sink); + dai->playback_widget->name, w->name); - snd_soc_dapm_add_route(w->dapm, &r); + snd_soc_dapm_add_path(w->dapm, + dai->playback_widget, w, NULL, NULL); } if (dai->driver->capture.stream_name && strstr(w->sname, dai->driver->capture.stream_name)) { - r.source = w->name; - r.sink = dai->capture_widget->name; dev_dbg(dai->dev, "%s -> %s\n", - r.source, r.sink); + w->name, dai->capture_widget->name); - snd_soc_dapm_add_route(w->dapm, &r); + snd_soc_dapm_add_path(w->dapm, w, + dai->capture_widget, NULL, NULL); } } } -- cgit v0.10.2 From 39eb5fd13dff8d3d04489fe3f59e0d22bf89041e Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 29 Jul 2013 17:14:03 +0200 Subject: ASoC: dapm: Delay w->power update until the changes are written Wait with updating the widgets power field until the changes are actually written to the hardware in dapm_seq_run_coalesced(). This will allow us to query the current hardware state between calling dapm_power_one_widget() and actually writing the new power state to hardware. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index b811a27..9abb3b2 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -293,6 +293,7 @@ static void dapm_reset(struct snd_soc_card *card) memset(&card->dapm_stats, 0, sizeof(card->dapm_stats)); list_for_each_entry(w, &card->widgets, list) { + w->new_power = w->power; w->power_checked = false; w->inputs = -1; w->outputs = -1; @@ -1340,7 +1341,7 @@ static void dapm_seq_check_event(struct snd_soc_card *card, return; } - if (w->power != power) + if (w->new_power != power) return; if (w->event && (w->event_flags & event)) { @@ -1369,6 +1370,7 @@ static void dapm_seq_run_coalesced(struct snd_soc_card *card, list_for_each_entry(w, pending, power_list) { BUG_ON(reg != w->reg); + w->power = w->new_power; mask |= w->mask << w->shift; if (w->power) @@ -1676,8 +1678,6 @@ static void dapm_widget_set_power(struct snd_soc_dapm_widget *w, bool power, dapm_seq_insert(w, up_list, true); else dapm_seq_insert(w, down_list, false); - - w->power = power; } static void dapm_power_one_widget(struct snd_soc_dapm_widget *w, @@ -1752,7 +1752,7 @@ static int dapm_power_widgets(struct snd_soc_card *card, int event) break; } - if (w->power) { + if (w->new_power) { d = w->dapm; /* Supplies and micbiases only bring the -- cgit v0.10.2 From 113591e477acb6b6dbc186ad2ee29a2502e68c33 Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 30 Jul 2013 11:18:52 +0100 Subject: ASoC: uda134x: fix codec driver by converting to DAPM For some reason, the DAC/ADCs are not being powered up when I try and use the UDA1341 driver; this used to work. Looking back in the git history, I don't see anything obvious which would cause this regression. However, from dumping the register writes, it seems that the codec is powered down, and nothing calls set_bias_level to wake the codec up. Moreover, this driver hasn't had DAPM support added to it, which prevents platform drivers from taking advantage of DAPMs facilities. So, let's add DAPM support to the driver. As we move the power control for the DAC/ADC into DAPM, we no longer need it in set_bias_level() - this function just becomes a way to manipulate the power control and sync the register cache with the hardware at the appropriate point. Signed-off-by: Russell King Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/uda134x.c b/sound/soc/codecs/uda134x.c index 6d0aa44..c94d4c1 100644 --- a/sound/soc/codecs/uda134x.c +++ b/sound/soc/codecs/uda134x.c @@ -325,7 +325,6 @@ static int uda134x_set_dai_fmt(struct snd_soc_dai *codec_dai, static int uda134x_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { - u8 reg; struct uda134x_platform_data *pd = codec->control_data; int i; u8 *cache = codec->reg_cache; @@ -334,23 +333,6 @@ static int uda134x_set_bias_level(struct snd_soc_codec *codec, switch (level) { case SND_SOC_BIAS_ON: - /* ADC, DAC on */ - switch (pd->model) { - case UDA134X_UDA1340: - case UDA134X_UDA1344: - case UDA134X_UDA1345: - reg = uda134x_read_reg_cache(codec, UDA134X_DATA011); - uda134x_write(codec, UDA134X_DATA011, reg | 0x03); - break; - case UDA134X_UDA1341: - reg = uda134x_read_reg_cache(codec, UDA134X_STATUS1); - uda134x_write(codec, UDA134X_STATUS1, reg | 0x03); - break; - default: - printk(KERN_ERR "UDA134X SoC codec: " - "unsupported model %d\n", pd->model); - return -EINVAL; - } break; case SND_SOC_BIAS_PREPARE: /* power on */ @@ -362,23 +344,6 @@ static int uda134x_set_bias_level(struct snd_soc_codec *codec, } break; case SND_SOC_BIAS_STANDBY: - /* ADC, DAC power off */ - switch (pd->model) { - case UDA134X_UDA1340: - case UDA134X_UDA1344: - case UDA134X_UDA1345: - reg = uda134x_read_reg_cache(codec, UDA134X_DATA011); - uda134x_write(codec, UDA134X_DATA011, reg & ~(0x03)); - break; - case UDA134X_UDA1341: - reg = uda134x_read_reg_cache(codec, UDA134X_STATUS1); - uda134x_write(codec, UDA134X_STATUS1, reg & ~(0x03)); - break; - default: - printk(KERN_ERR "UDA134X SoC codec: " - "unsupported model %d\n", pd->model); - return -EINVAL; - } break; case SND_SOC_BIAS_OFF: /* power off */ @@ -450,6 +415,37 @@ SOC_ENUM("PCM Playback De-emphasis", uda134x_mixer_enum[1]), SOC_SINGLE("DC Filter Enable Switch", UDA134X_STATUS0, 0, 1, 0), }; +/* UDA1341 has the DAC/ADC power down in STATUS1 */ +static const struct snd_soc_dapm_widget uda1341_dapm_widgets[] = { + SND_SOC_DAPM_DAC("DAC", "Playback", UDA134X_STATUS1, 0, 0), + SND_SOC_DAPM_ADC("ADC", "Capture", UDA134X_STATUS1, 1, 0), +}; + +/* UDA1340/4/5 has the DAC/ADC pwoer down in DATA0 11 */ +static const struct snd_soc_dapm_widget uda1340_dapm_widgets[] = { + SND_SOC_DAPM_DAC("DAC", "Playback", UDA134X_DATA011, 0, 0), + SND_SOC_DAPM_ADC("ADC", "Capture", UDA134X_DATA011, 1, 0), +}; + +/* Common DAPM widgets */ +static const struct snd_soc_dapm_widget uda134x_dapm_widgets[] = { + SND_SOC_DAPM_INPUT("VINL1"), + SND_SOC_DAPM_INPUT("VINR1"), + SND_SOC_DAPM_INPUT("VINL2"), + SND_SOC_DAPM_INPUT("VINR2"), + SND_SOC_DAPM_OUTPUT("VOUTL"), + SND_SOC_DAPM_OUTPUT("VOUTR"), +}; + +static const struct snd_soc_dapm_route uda134x_dapm_routes[] = { + { "ADC", NULL, "VINL1" }, + { "ADC", NULL, "VINR1" }, + { "ADC", NULL, "VINL2" }, + { "ADC", NULL, "VINR2" }, + { "VOUTL", NULL, "DAC" }, + { "VOUTR", NULL, "DAC" }, +}; + static const struct snd_soc_dai_ops uda134x_dai_ops = { .startup = uda134x_startup, .shutdown = uda134x_shutdown, @@ -485,6 +481,8 @@ static int uda134x_soc_probe(struct snd_soc_codec *codec) { struct uda134x_priv *uda134x; struct uda134x_platform_data *pd = codec->card->dev->platform_data; + const struct snd_soc_dapm_widget *widgets; + unsigned num_widgets; int ret; @@ -526,6 +524,22 @@ static int uda134x_soc_probe(struct snd_soc_codec *codec) else uda134x_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + if (pd->model == UDA134X_UDA1341) { + widgets = uda1341_dapm_widgets; + num_widgets = ARRAY_SIZE(uda1341_dapm_widgets); + } else { + widgets = uda1340_dapm_widgets; + num_widgets = ARRAY_SIZE(uda1340_dapm_widgets); + } + + ret = snd_soc_dapm_new_controls(&codec->dapm, widgets, num_widgets); + if (ret) { + printk(KERN_ERR "%s failed to register dapm controls: %d", + __func__, ret); + kfree(uda134x); + return ret; + } + switch (pd->model) { case UDA134X_UDA1340: case UDA134X_UDA1344: @@ -599,6 +613,10 @@ static struct snd_soc_codec_driver soc_codec_dev_uda134x = { .read = uda134x_read_reg_cache, .write = uda134x_write, .set_bias_level = uda134x_set_bias_level, + .dapm_widgets = uda134x_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(uda134x_dapm_widgets), + .dapm_routes = uda134x_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(uda134x_dapm_routes), }; static int uda134x_codec_probe(struct platform_device *pdev) -- cgit v0.10.2 From 0890c2b7be08b0928f7f507e371918205a0312f7 Mon Sep 17 00:00:00 2001 From: Richard Genoud Date: Tue, 30 Jul 2013 11:59:45 +0200 Subject: ASoC: wm8731: add rates constraints Depending on the mclk (or crystal) selected, the wm8731 codec have some constraints on its data sampling rates: e.g. with a 12.288MHz or 18.432MHz crystal, the authorized rates are 8KHz, 32KHz, 48KHz and 96KHz. Signed-off-by: Richard Genoud Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c index 5276062..456bb8c 100644 --- a/sound/soc/codecs/wm8731.c +++ b/sound/soc/codecs/wm8731.c @@ -45,6 +45,7 @@ static const char *wm8731_supply_names[WM8731_NUM_SUPPLIES] = { struct wm8731_priv { struct regmap *regmap; struct regulator_bulk_data supplies[WM8731_NUM_SUPPLIES]; + const struct snd_pcm_hw_constraint_list *constraints; unsigned int sysclk; int sysclk_type; int playback_fs; @@ -290,6 +291,36 @@ static const struct _coeff_div coeff_div[] = { {12000000, 88200, 136, 0xf, 0x1, 0x1}, }; +/* rates constraints */ +static const unsigned int wm8731_rates_12000000[] = { + 8000, 32000, 44100, 48000, 96000, 88200, +}; + +static const unsigned int wm8731_rates_12288000_18432000[] = { + 8000, 32000, 48000, 96000, +}; + +static const unsigned int wm8731_rates_11289600_16934400[] = { + 8000, 44100, 88200, +}; + +static const struct snd_pcm_hw_constraint_list wm8731_constraints_12000000 = { + .list = wm8731_rates_12000000, + .count = ARRAY_SIZE(wm8731_rates_12000000), +}; + +static const +struct snd_pcm_hw_constraint_list wm8731_constraints_12288000_18432000 = { + .list = wm8731_rates_12288000_18432000, + .count = ARRAY_SIZE(wm8731_rates_12288000_18432000), +}; + +static const +struct snd_pcm_hw_constraint_list wm8731_constraints_11289600_16934400 = { + .list = wm8731_rates_11289600_16934400, + .count = ARRAY_SIZE(wm8731_rates_11289600_16934400), +}; + static inline int get_coeff(int mclk, int rate) { int i; @@ -362,17 +393,26 @@ static int wm8731_set_dai_sysclk(struct snd_soc_dai *codec_dai, } switch (freq) { - case 11289600: + case 0: + wm8731->constraints = NULL; + break; case 12000000: + wm8731->constraints = &wm8731_constraints_12000000; + break; case 12288000: - case 16934400: case 18432000: - wm8731->sysclk = freq; + wm8731->constraints = &wm8731_constraints_12288000_18432000; + break; + case 16934400: + case 11289600: + wm8731->constraints = &wm8731_constraints_11289600_16934400; break; default: return -EINVAL; } + wm8731->sysclk = freq; + snd_soc_dapm_sync(&codec->dapm); return 0; @@ -475,12 +515,26 @@ static int wm8731_set_bias_level(struct snd_soc_codec *codec, return 0; } +static int wm8731_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(dai->codec); + + if (wm8731->constraints) + snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + wm8731->constraints); + + return 0; +} + #define WM8731_RATES SNDRV_PCM_RATE_8000_96000 #define WM8731_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ SNDRV_PCM_FMTBIT_S24_LE) static const struct snd_soc_dai_ops wm8731_dai_ops = { + .startup = wm8731_startup, .hw_params = wm8731_hw_params, .digital_mute = wm8731_mute, .set_sysclk = wm8731_set_dai_sysclk, -- cgit v0.10.2 From 50b4dc690a5c6ffed4c528829cf18f77e5af98bd Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 30 Jul 2013 13:34:10 +0200 Subject: ASoC: bf5xx-ac97: Remove unused extern declaration The blackfin ac97 driver never defines nor uses a global ac97 struct. So remove the extern declaration for it. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/blackfin/bf5xx-ac97.h b/sound/soc/blackfin/bf5xx-ac97.h index 0c3e22d..a680fdc 100644 --- a/sound/soc/blackfin/bf5xx-ac97.h +++ b/sound/soc/blackfin/bf5xx-ac97.h @@ -9,7 +9,6 @@ #ifndef _BF5XX_AC97_H #define _BF5XX_AC97_H -extern struct snd_ac97 *ac97; /* Frame format in memory, only support stereo currently */ struct ac97_frame { u16 ac97_tag; /* slot 0 */ -- cgit v0.10.2 From 46a02c978fbc79de856d0fe7a8c1d4fc620796e0 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 31 Jul 2013 11:52:44 +0300 Subject: ASoC: dapm: using freed pointer in dapm_kcontrol_add_widget() There is a typo here so we end up using the old freed pointer instead of the newly allocated one. (If the "n" is zero then the code works, obviously). Signed-off-by: Dan Carpenter Acked-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 9abb3b2..d74c356 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -225,13 +225,13 @@ static int dapm_kcontrol_add_widget(struct snd_kcontrol *kcontrol, new_data = krealloc(data, sizeof(*data) + sizeof(widget) * n, GFP_KERNEL); - if (!data) + if (!new_data) return -ENOMEM; - data->wlist.widgets[n - 1] = widget; - data->wlist.num_widgets = n; + new_data->wlist.widgets[n - 1] = widget; + new_data->wlist.num_widgets = n; - kcontrol->private_data = data; + kcontrol->private_data = new_data; return 0; } -- cgit v0.10.2 From db5ff9541b61ef8394bad0fb05508921b8c5b17b Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Wed, 31 Jul 2013 20:07:05 +0800 Subject: ASoC: spdif: Add S20_3LE and S24_LE support for dummy codec drivers Generally, S/PDIF supports 20bit and optional 24bit samples. Thus add these two formats for the dummy codec drivers. If one S/PDIF controller has its own limitation, its CPU DAI driver should set the supported format by its own circumstance, since the soc-pcm driver will use the intersection of cpu_dai's formats and codec_dai's formats. Signed-off-by: Nicolin Chen Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/spdif_receiver.c b/sound/soc/codecs/spdif_receiver.c index e9d7881..26d3474 100644 --- a/sound/soc/codecs/spdif_receiver.c +++ b/sound/soc/codecs/spdif_receiver.c @@ -25,6 +25,8 @@ #define STUB_RATES SNDRV_PCM_RATE_8000_192000 #define STUB_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE) static struct snd_soc_codec_driver soc_codec_spdif_dir; diff --git a/sound/soc/codecs/spdif_transmitter.c b/sound/soc/codecs/spdif_transmitter.c index 1828049..efc3d88 100644 --- a/sound/soc/codecs/spdif_transmitter.c +++ b/sound/soc/codecs/spdif_transmitter.c @@ -25,8 +25,9 @@ #define DRV_NAME "spdif-dit" #define STUB_RATES SNDRV_PCM_RATE_8000_96000 -#define STUB_FORMATS SNDRV_PCM_FMTBIT_S16_LE - +#define STUB_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE) static struct snd_soc_codec_driver soc_codec_spdif_dit; -- cgit v0.10.2 From 3f1a91aa25579ba5e7268a47a73d2a83e4802c62 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 29 Jul 2013 18:37:32 -0300 Subject: ASoC: fsl: Fix module build Building imx_v6_v7_defconfig with all audio drivers as modules results in the folowing build error: ERROR: "imx_pcm_fiq_init" [sound/soc/fsl/snd-soc-imx-ssi.ko] undefined! ERROR: "imx_pcm_dma_init" [sound/soc/fsl/snd-soc-imx-ssi.ko] undefined! ERROR: "imx_pcm_fiq_exit" [sound/soc/fsl/snd-soc-imx-ssi.ko] undefined! ERROR: "imx_pcm_dma_exit" [sound/soc/fsl/snd-soc-imx-ssi.ko] undefined! ERROR: "imx_pcm_dma_init" [sound/soc/fsl/snd-soc-fsl-ssi.ko] undefined! ERROR: "imx_pcm_dma_exit" [sound/soc/fsl/snd-soc-fsl-ssi.ko] undefined! Fix this by allowing SND_SOC_IMX_PCM_FIQ and SND_SOC_IMX_PCM_DMA to be also built as modules and by using 'IS_ENABLED' to cover the module case. Reported-by: Guennadi Liakhovetski Signed-off-by: Fabio Estevam Acked-by: Shawn Guo Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index 3a79d01..c26449b 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig @@ -109,11 +109,11 @@ config SND_SOC_IMX_SSI tristate config SND_SOC_IMX_PCM_FIQ - bool + tristate select FIQ config SND_SOC_IMX_PCM_DMA - bool + tristate select SND_SOC_GENERIC_DMAENGINE_PCM config SND_SOC_IMX_AUDMUX diff --git a/sound/soc/fsl/imx-pcm.h b/sound/soc/fsl/imx-pcm.h index 9136625..5d5b733 100644 --- a/sound/soc/fsl/imx-pcm.h +++ b/sound/soc/fsl/imx-pcm.h @@ -38,7 +38,7 @@ struct imx_pcm_fiq_params { struct snd_dmaengine_dai_dma_data *dma_params_tx; }; -#ifdef CONFIG_SND_SOC_IMX_PCM_DMA +#if IS_ENABLED(CONFIG_SND_SOC_IMX_PCM_DMA) int imx_pcm_dma_init(struct platform_device *pdev); void imx_pcm_dma_exit(struct platform_device *pdev); #else @@ -52,7 +52,7 @@ static inline void imx_pcm_dma_exit(struct platform_device *pdev) } #endif -#ifdef CONFIG_SND_SOC_IMX_PCM_FIQ +#if IS_ENABLED(CONFIG_SND_SOC_IMX_PCM_FIQ) int imx_pcm_fiq_init(struct platform_device *pdev, struct imx_pcm_fiq_params *params); void imx_pcm_fiq_exit(struct platform_device *pdev); -- cgit v0.10.2 From 70263cb474853c116f80713d468f3c17d805921c Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Tue, 30 Jul 2013 07:51:37 +0800 Subject: ASoC: rcar: fix return value check in rsnd_gen1_probe() In case of error, the function devm_ioremap_resource() returns ERR_PTR() and never returns NULL. The NULL test in the return value check should be replaced with IS_ERR(), and also remove the dev_err call to avoid redundant error message. Signed-off-by: Wei Yongjun Acked-by: Kuninori Morimoto Signed-off-by: Mark Brown diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c index 5e4ae0d..61232cd 100644 --- a/sound/soc/sh/rcar/gen.c +++ b/sound/soc/sh/rcar/gen.c @@ -150,25 +150,16 @@ static int rsnd_gen1_probe(struct platform_device *pdev, sru_res = platform_get_resource(pdev, IORESOURCE_MEM, RSND_GEN1_SRU); adg_res = platform_get_resource(pdev, IORESOURCE_MEM, RSND_GEN1_ADG); ssi_res = platform_get_resource(pdev, IORESOURCE_MEM, RSND_GEN1_SSI); - if (!sru_res || - !adg_res || - !ssi_res) { - dev_err(dev, "Not enough SRU/SSI/ADG platform resources.\n"); - return -ENODEV; - } - - gen->ops = &rsnd_gen1_ops; gen->base[RSND_GEN1_SRU] = devm_ioremap_resource(dev, sru_res); gen->base[RSND_GEN1_ADG] = devm_ioremap_resource(dev, adg_res); gen->base[RSND_GEN1_SSI] = devm_ioremap_resource(dev, ssi_res); - if (!gen->base[RSND_GEN1_SRU] || - !gen->base[RSND_GEN1_ADG] || - !gen->base[RSND_GEN1_SSI]) { - dev_err(dev, "SRU/SSI/ADG ioremap failed\n"); + if (IS_ERR(gen->base[RSND_GEN1_SRU]) || + IS_ERR(gen->base[RSND_GEN1_ADG]) || + IS_ERR(gen->base[RSND_GEN1_SSI])) return -ENODEV; - } + gen->ops = &rsnd_gen1_ops; rsnd_gen1_reg_map_init(gen); dev_dbg(dev, "Gen1 device probed\n"); -- cgit v0.10.2 From 8fe120b5a665fc869c23f86e4964b801f6e53486 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 31 Jul 2013 19:00:32 +0200 Subject: ASoC: omap-abe-twl6040: Remove support for pdata (legacy boot) Just recently OMAP4 legacy boot support has been removed. No reason to keep the code used by the legacy boot (pdata based) since neither OMAP4 or OMAP5 can boot in this mode. Signed-off-by: Peter Ujfalusi Signed-off-by: Mark Brown diff --git a/include/linux/platform_data/omap-abe-twl6040.h b/include/linux/platform_data/omap-abe-twl6040.h deleted file mode 100644 index 5d298ac..0000000 --- a/include/linux/platform_data/omap-abe-twl6040.h +++ /dev/null @@ -1,49 +0,0 @@ -/** - * omap-abe-twl6040.h - ASoC machine driver OMAP4+ devices, header. - * - * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com - * All rights reserved. - * - * Author: Peter Ujfalusi - * - * 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 - */ - -#ifndef _OMAP_ABE_TWL6040_H_ -#define _OMAP_ABE_TWL6040_H_ - -/* To select if only one channel is connected in a stereo port */ -#define ABE_TWL6040_LEFT (1 << 0) -#define ABE_TWL6040_RIGHT (1 << 1) - -struct omap_abe_twl6040_data { - char *card_name; - /* Feature flags for connected audio pins */ - u8 has_hs; - u8 has_hf; - bool has_ep; - u8 has_aux; - u8 has_vibra; - bool has_dmic; - bool has_hsmic; - bool has_mainmic; - bool has_submic; - u8 has_afm; - /* Other features */ - bool jack_detection; /* board can detect jack events */ - int mclk_freq; /* MCLK frequency speed for twl6040 */ -}; - -#endif /* _OMAP_ABE_TWL6040_H_ */ diff --git a/sound/soc/omap/omap-abe-twl6040.c b/sound/soc/omap/omap-abe-twl6040.c index 70cd5c7..ebb1390 100644 --- a/sound/soc/omap/omap-abe-twl6040.c +++ b/sound/soc/omap/omap-abe-twl6040.c @@ -23,7 +23,6 @@ #include #include #include -#include #include #include @@ -166,19 +165,10 @@ static const struct snd_soc_dapm_route audio_map[] = { {"AFMR", NULL, "Line In"}, }; -static inline void twl6040_disconnect_pin(struct snd_soc_dapm_context *dapm, - int connected, char *pin) -{ - if (!connected) - snd_soc_dapm_disable_pin(dapm, pin); -} - static int omap_abe_twl6040_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_codec *codec = rtd->codec; struct snd_soc_card *card = codec->card; - struct snd_soc_dapm_context *dapm = &codec->dapm; - struct omap_abe_twl6040_data *pdata = dev_get_platdata(card->dev); struct abe_twl6040 *priv = snd_soc_card_get_drvdata(card); int hs_trim; int ret = 0; @@ -203,24 +193,6 @@ static int omap_abe_twl6040_init(struct snd_soc_pcm_runtime *rtd) twl6040_hs_jack_detect(codec, &hs_jack, SND_JACK_HEADSET); } - /* - * NULL pdata means we booted with DT. In this case the routing is - * provided and the card is fully routed, no need to mark pins. - */ - if (!pdata) - return ret; - - /* Disable not connected paths if not used */ - twl6040_disconnect_pin(dapm, pdata->has_hs, "Headset Stereophone"); - twl6040_disconnect_pin(dapm, pdata->has_hf, "Ext Spk"); - twl6040_disconnect_pin(dapm, pdata->has_ep, "Earphone Spk"); - twl6040_disconnect_pin(dapm, pdata->has_aux, "Line Out"); - twl6040_disconnect_pin(dapm, pdata->has_vibra, "Vibrator"); - twl6040_disconnect_pin(dapm, pdata->has_hsmic, "Headset Mic"); - twl6040_disconnect_pin(dapm, pdata->has_mainmic, "Main Handset Mic"); - twl6040_disconnect_pin(dapm, pdata->has_submic, "Sub Handset Mic"); - twl6040_disconnect_pin(dapm, pdata->has_afm, "Line In"); - return ret; } @@ -274,13 +246,18 @@ static struct snd_soc_card omap_abe_card = { static int omap_abe_probe(struct platform_device *pdev) { - struct omap_abe_twl6040_data *pdata = dev_get_platdata(&pdev->dev); struct device_node *node = pdev->dev.of_node; struct snd_soc_card *card = &omap_abe_card; + struct device_node *dai_node; struct abe_twl6040 *priv; int num_links = 0; int ret = 0; + if (!node) { + dev_err(&pdev->dev, "of node is missing.\n"); + return -ENODEV; + } + card->dev = &pdev->dev; priv = devm_kzalloc(&pdev->dev, sizeof(struct abe_twl6040), GFP_KERNEL); @@ -289,78 +266,50 @@ static int omap_abe_probe(struct platform_device *pdev) priv->dmic_codec_dev = ERR_PTR(-EINVAL); - if (node) { - struct device_node *dai_node; - - if (snd_soc_of_parse_card_name(card, "ti,model")) { - dev_err(&pdev->dev, "Card name is not provided\n"); - return -ENODEV; - } + if (snd_soc_of_parse_card_name(card, "ti,model")) { + dev_err(&pdev->dev, "Card name is not provided\n"); + return -ENODEV; + } - ret = snd_soc_of_parse_audio_routing(card, - "ti,audio-routing"); - if (ret) { - dev_err(&pdev->dev, - "Error while parsing DAPM routing\n"); - return ret; - } + ret = snd_soc_of_parse_audio_routing(card, "ti,audio-routing"); + if (ret) { + dev_err(&pdev->dev, "Error while parsing DAPM routing\n"); + return ret; + } - dai_node = of_parse_phandle(node, "ti,mcpdm", 0); - if (!dai_node) { - dev_err(&pdev->dev, "McPDM node is not provided\n"); - return -EINVAL; - } - abe_twl6040_dai_links[0].cpu_dai_name = NULL; - abe_twl6040_dai_links[0].cpu_of_node = dai_node; + dai_node = of_parse_phandle(node, "ti,mcpdm", 0); + if (!dai_node) { + dev_err(&pdev->dev, "McPDM node is not provided\n"); + return -EINVAL; + } + abe_twl6040_dai_links[0].cpu_dai_name = NULL; + abe_twl6040_dai_links[0].cpu_of_node = dai_node; - dai_node = of_parse_phandle(node, "ti,dmic", 0); - if (dai_node) { - num_links = 2; - abe_twl6040_dai_links[1].cpu_dai_name = NULL; - abe_twl6040_dai_links[1].cpu_of_node = dai_node; + dai_node = of_parse_phandle(node, "ti,dmic", 0); + if (dai_node) { + num_links = 2; + abe_twl6040_dai_links[1].cpu_dai_name = NULL; + abe_twl6040_dai_links[1].cpu_of_node = dai_node; - priv->dmic_codec_dev = platform_device_register_simple( + priv->dmic_codec_dev = platform_device_register_simple( "dmic-codec", -1, NULL, 0); - if (IS_ERR(priv->dmic_codec_dev)) { - dev_err(&pdev->dev, - "Can't instantiate dmic-codec\n"); - return PTR_ERR(priv->dmic_codec_dev); - } - } else { - num_links = 1; - } - - priv->jack_detection = of_property_read_bool(node, - "ti,jack-detection"); - of_property_read_u32(node, "ti,mclk-freq", - &priv->mclk_freq); - if (!priv->mclk_freq) { - dev_err(&pdev->dev, "MCLK frequency not provided\n"); - ret = -EINVAL; - goto err_unregister; + if (IS_ERR(priv->dmic_codec_dev)) { + dev_err(&pdev->dev, "Can't instantiate dmic-codec\n"); + return PTR_ERR(priv->dmic_codec_dev); } - - omap_abe_card.fully_routed = 1; - } else if (pdata) { - if (pdata->card_name) { - card->name = pdata->card_name; - } else { - dev_err(&pdev->dev, "Card name is not provided\n"); - return -ENODEV; - } - - if (pdata->has_dmic) - num_links = 2; - else - num_links = 1; - - priv->jack_detection = pdata->jack_detection; - priv->mclk_freq = pdata->mclk_freq; } else { - dev_err(&pdev->dev, "Missing pdata\n"); - return -ENODEV; + num_links = 1; + } + + priv->jack_detection = of_property_read_bool(node, "ti,jack-detection"); + of_property_read_u32(node, "ti,mclk-freq", &priv->mclk_freq); + if (!priv->mclk_freq) { + dev_err(&pdev->dev, "MCLK frequency not provided\n"); + ret = -EINVAL; + goto err_unregister; } + card->fully_routed = 1; if (!priv->mclk_freq) { dev_err(&pdev->dev, "MCLK frequency missing\n"); -- cgit v0.10.2 From d4780eec779c4e6d2fe5963dd2aee0a85d956122 Mon Sep 17 00:00:00 2001 From: Dimitris Papastamos Date: Thu, 1 Aug 2013 09:53:45 +0100 Subject: ASoC: wm0010: Use DMA-safe memory for SPI transfers We should be allocating our buffers for the SPI transfers from the DMA zone. Signed-off-by: Dimitris Papastamos Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm0010.c b/sound/soc/codecs/wm0010.c index 10adc41..d5ebcb0 100644 --- a/sound/soc/codecs/wm0010.c +++ b/sound/soc/codecs/wm0010.c @@ -420,7 +420,7 @@ static int wm0010_firmware_load(const char *name, struct snd_soc_codec *codec) xfer->codec = codec; list_add_tail(&xfer->list, &xfer_list); - out = kzalloc(len, GFP_KERNEL); + out = kzalloc(len, GFP_KERNEL | GFP_DMA); if (!out) { dev_err(codec->dev, "Failed to allocate RX buffer\n"); @@ -429,7 +429,7 @@ static int wm0010_firmware_load(const char *name, struct snd_soc_codec *codec) } xfer->t.rx_buf = out; - img = kzalloc(len, GFP_KERNEL); + img = kzalloc(len, GFP_KERNEL | GFP_DMA); if (!img) { dev_err(codec->dev, "Failed to allocate image buffer\n"); @@ -523,14 +523,14 @@ static int wm0010_stage2_load(struct snd_soc_codec *codec) dev_dbg(codec->dev, "Downloading %zu byte stage 2 loader\n", fw->size); /* Copy to local buffer first as vmalloc causes problems for dma */ - img = kzalloc(fw->size, GFP_KERNEL); + img = kzalloc(fw->size, GFP_KERNEL | GFP_DMA); if (!img) { dev_err(codec->dev, "Failed to allocate image buffer\n"); ret = -ENOMEM; goto abort2; } - out = kzalloc(fw->size, GFP_KERNEL); + out = kzalloc(fw->size, GFP_KERNEL | GFP_DMA); if (!out) { dev_err(codec->dev, "Failed to allocate output buffer\n"); ret = -ENOMEM; @@ -670,14 +670,14 @@ static int wm0010_boot(struct snd_soc_codec *codec) ret = -ENOMEM; len = pll_rec.length + 8; - out = kzalloc(len, GFP_KERNEL); + out = kzalloc(len, GFP_KERNEL | GFP_DMA); if (!out) { dev_err(codec->dev, "Failed to allocate RX buffer\n"); goto abort; } - img_swap = kzalloc(len, GFP_KERNEL); + img_swap = kzalloc(len, GFP_KERNEL | GFP_DMA); if (!img_swap) { dev_err(codec->dev, "Failed to allocate image buffer\n"); -- cgit v0.10.2 From 95169d080fcaad6c990ce3602d9b3d38753b1fa4 Mon Sep 17 00:00:00 2001 From: Marek Belisko Date: Thu, 1 Aug 2013 11:14:58 +0200 Subject: ASoC: Add PCM1681 codec driver. PCM1681 can be controlled via I2C, SPI or in bootstrap mode (no control mode). This code add support only for I2C mode. Signed-off-by: Marek Belisko Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/sound/ti,pcm1681.txt b/Documentation/devicetree/bindings/sound/ti,pcm1681.txt new file mode 100644 index 0000000..4df1718 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/ti,pcm1681.txt @@ -0,0 +1,15 @@ +Texas Instruments PCM1681 8-channel PWM Processor + +Required properties: + + - compatible: Should contain "ti,pcm1681". + - reg: The i2c address. Should contain <0x4c>. + +Examples: + + i2c_bus { + pcm1681@4c { + compatible = "ti,pcm1681"; + reg = <0x4c>; + }; + }; diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index badb6fb..e2daf82 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -54,6 +54,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_MC13783 if MFD_MC13XXX select SND_SOC_ML26124 if I2C select SND_SOC_HDMI_CODEC + select SND_SOC_PCM1681 if I2C select SND_SOC_PCM3008 select SND_SOC_RT5631 if I2C select SND_SOC_RT5640 if I2C @@ -292,6 +293,9 @@ config SND_SOC_MAX9850 config SND_SOC_HDMI_CODEC tristate +config SND_SOC_PCM1681 + tristate + config SND_SOC_PCM3008 tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 70fd806..4a068d2 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -42,6 +42,7 @@ snd-soc-max9850-objs := max9850.o snd-soc-mc13783-objs := mc13783.o snd-soc-ml26124-objs := ml26124.o snd-soc-hdmi-codec-objs := hdmi.o +snd-soc-pcm1681-objs := pcm1681.o snd-soc-pcm3008-objs := pcm3008.o snd-soc-rt5631-objs := rt5631.o snd-soc-rt5640-objs := rt5640.o @@ -171,6 +172,7 @@ obj-$(CONFIG_SND_SOC_MAX9850) += snd-soc-max9850.o obj-$(CONFIG_SND_SOC_MC13783) += snd-soc-mc13783.o obj-$(CONFIG_SND_SOC_ML26124) += snd-soc-ml26124.o obj-$(CONFIG_SND_SOC_HDMI_CODEC) += snd-soc-hdmi-codec.o +obj-$(CONFIG_SND_SOC_PCM1681) += snd-soc-pcm1681.o obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o obj-$(CONFIG_SND_SOC_RT5631) += snd-soc-rt5631.o obj-$(CONFIG_SND_SOC_RT5640) += snd-soc-rt5640.o diff --git a/sound/soc/codecs/pcm1681.c b/sound/soc/codecs/pcm1681.c new file mode 100644 index 0000000..27da41b --- /dev/null +++ b/sound/soc/codecs/pcm1681.c @@ -0,0 +1,313 @@ +/* + * PCM1681 ASoC codec driver + * + * Copyright (c) StreamUnlimited GmbH 2013 + * Marek Belisko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PCM1681_PCM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE) + +#define PCM1681_PCM_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | \ + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | \ + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000) + +#define PCM1681_SOFT_MUTE_ALL 0xff +#define PCM1681_DEEMPH_RATE_MASK 0x18 +#define PCM1681_DEEMPH_MASK 0x01 + +#define PCM1681_ATT_CONTROL(X) (X <= 6 ? X : X + 9) /* Attenuation level */ +#define PCM1681_SOFT_MUTE 0x07 /* Soft mute control register */ +#define PCM1681_DAC_CONTROL 0x08 /* DAC operation control */ +#define PCM1681_FMT_CONTROL 0x09 /* Audio interface data format */ +#define PCM1681_DEEMPH_CONTROL 0x0a /* De-emphasis control */ +#define PCM1681_ZERO_DETECT_STATUS 0x0e /* Zero detect status reg */ + +static const struct reg_default pcm1681_reg_defaults[] = { + { 0x01, 0xff }, + { 0x02, 0xff }, + { 0x03, 0xff }, + { 0x04, 0xff }, + { 0x05, 0xff }, + { 0x06, 0xff }, + { 0x07, 0x00 }, + { 0x08, 0x00 }, + { 0x09, 0x06 }, + { 0x0A, 0x00 }, + { 0x0B, 0xff }, + { 0x0C, 0x0f }, + { 0x0D, 0x00 }, + { 0x10, 0xff }, + { 0x11, 0xff }, + { 0x12, 0x00 }, + { 0x13, 0x00 }, +}; + +static bool pcm1681_accessible_reg(struct device *dev, unsigned int reg) +{ + return !((reg == 0x00) || (reg == 0x0f)); +} + +static bool pcm1681_writeable_reg(struct device *dev, unsigned register reg) +{ + return pcm1681_accessible_reg(dev, reg) && + (reg != PCM1681_ZERO_DETECT_STATUS); +} + +struct pcm1681_private { + struct regmap *regmap; + unsigned int format; + /* Current deemphasis status */ + unsigned int deemph; + /* Current rate for deemphasis control */ + unsigned int rate; +}; + +static const int pcm1681_deemph[] = { 44100, 48000, 32000 }; + +static int pcm1681_set_deemph(struct snd_soc_codec *codec) +{ + struct pcm1681_private *priv = snd_soc_codec_get_drvdata(codec); + int i = 0, val = -1, enable = 0; + + if (priv->deemph) + for (i = 0; i < ARRAY_SIZE(pcm1681_deemph); i++) + if (pcm1681_deemph[i] == priv->rate) + val = i; + + if (val != -1) { + regmap_update_bits(priv->regmap, PCM1681_DEEMPH_CONTROL, + PCM1681_DEEMPH_RATE_MASK, val); + enable = 1; + } else + enable = 0; + + /* enable/disable deemphasis functionality */ + return regmap_update_bits(priv->regmap, PCM1681_DEEMPH_CONTROL, + PCM1681_DEEMPH_MASK, enable); +} + +static int pcm1681_get_deemph(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct pcm1681_private *priv = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.enumerated.item[0] = priv->deemph; + + return 0; +} + +static int pcm1681_put_deemph(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct pcm1681_private *priv = snd_soc_codec_get_drvdata(codec); + + priv->deemph = ucontrol->value.enumerated.item[0]; + + return pcm1681_set_deemph(codec); +} + +static int pcm1681_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int format) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct pcm1681_private *priv = snd_soc_codec_get_drvdata(codec); + + /* The PCM1681 can only be slave to all clocks */ + if ((format & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS) { + dev_err(codec->dev, "Invalid clocking mode\n"); + return -EINVAL; + } + + priv->format = format; + + return 0; +} + +static int pcm1681_digital_mute(struct snd_soc_dai *dai, int mute) +{ + struct snd_soc_codec *codec = dai->codec; + struct pcm1681_private *priv = snd_soc_codec_get_drvdata(codec); + int val; + + if (mute) + val = PCM1681_SOFT_MUTE_ALL; + else + val = 0; + + return regmap_write(priv->regmap, PCM1681_SOFT_MUTE, val); +} + +static int pcm1681_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct pcm1681_private *priv = snd_soc_codec_get_drvdata(codec); + int val = 0, ret; + int pcm_format = params_format(params); + + priv->rate = params_rate(params); + + switch (priv->format & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_RIGHT_J: + if (pcm_format == SNDRV_PCM_FORMAT_S24_LE) + val = 0x00; + else if (pcm_format == SNDRV_PCM_FORMAT_S16_LE) + val = 0x03; + break; + case SND_SOC_DAIFMT_I2S: + val = 0x04; + break; + case SND_SOC_DAIFMT_LEFT_J: + val = 0x05; + break; + default: + dev_err(codec->dev, "Invalid DAI format\n"); + return -EINVAL; + } + + ret = regmap_update_bits(priv->regmap, PCM1681_FMT_CONTROL, 0x0f, val); + if (ret < 0) + return ret; + + return pcm1681_set_deemph(codec); +} + +static const struct snd_soc_dai_ops pcm1681_dai_ops = { + .set_fmt = pcm1681_set_dai_fmt, + .hw_params = pcm1681_hw_params, + .digital_mute = pcm1681_digital_mute, +}; + +static const DECLARE_TLV_DB_SCALE(pcm1681_dac_tlv, -6350, 50, 1); + +static const struct snd_kcontrol_new pcm1681_controls[] = { + SOC_DOUBLE_R_TLV("Channel 1/2 Playback Volume", + PCM1681_ATT_CONTROL(1), PCM1681_ATT_CONTROL(2), 0, + 0x7f, 0, pcm1681_dac_tlv), + SOC_DOUBLE_R_TLV("Channel 3/4 Playback Volume", + PCM1681_ATT_CONTROL(3), PCM1681_ATT_CONTROL(4), 0, + 0x7f, 0, pcm1681_dac_tlv), + SOC_DOUBLE_R_TLV("Channel 5/6 Playback Volume", + PCM1681_ATT_CONTROL(5), PCM1681_ATT_CONTROL(6), 0, + 0x7f, 0, pcm1681_dac_tlv), + SOC_DOUBLE_R_TLV("Channel 7/8 Playback Volume", + PCM1681_ATT_CONTROL(7), PCM1681_ATT_CONTROL(8), 0, + 0x7f, 0, pcm1681_dac_tlv), + SOC_SINGLE_BOOL_EXT("De-emphasis Switch", 0, + pcm1681_get_deemph, pcm1681_put_deemph), +}; + +struct snd_soc_dai_driver pcm1681_dai = { + .name = "pcm1681-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 8, + .rates = PCM1681_PCM_RATES, + .formats = PCM1681_PCM_FORMATS, + }, + .ops = &pcm1681_dai_ops, +}; + +#ifdef CONFIG_OF +static const struct of_device_id pcm1681_dt_ids[] = { + { .compatible = "ti,pcm1681", }, + { } +}; +MODULE_DEVICE_TABLE(of, pcm1681_dt_ids); +#endif + +static const struct regmap_config pcm1681_regmap = { + .reg_bits = 8, + .val_bits = 8, + .max_register = ARRAY_SIZE(pcm1681_reg_defaults) + 1, + .reg_defaults = pcm1681_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(pcm1681_reg_defaults), + .writeable_reg = pcm1681_writeable_reg, + .readable_reg = pcm1681_accessible_reg, +}; + +static struct snd_soc_codec_driver soc_codec_dev_pcm1681 = { + .controls = pcm1681_controls, + .num_controls = ARRAY_SIZE(pcm1681_controls), +}; + +static const struct i2c_device_id pcm1681_i2c_id[] = { + {"pcm1681", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, pcm1681_i2c_id); + +static int pcm1681_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret; + struct pcm1681_private *priv; + + priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->regmap = devm_regmap_init_i2c(client, &pcm1681_regmap); + if (IS_ERR(priv->regmap)) { + ret = PTR_ERR(priv->regmap); + dev_err(&client->dev, "Failed to create regmap: %d\n", ret); + return ret; + } + + i2c_set_clientdata(client, priv); + + return snd_soc_register_codec(&client->dev, &soc_codec_dev_pcm1681, + &pcm1681_dai, 1); +} + +static int pcm1681_i2c_remove(struct i2c_client *client) +{ + snd_soc_unregister_codec(&client->dev); + return 0; +} + +static struct i2c_driver pcm1681_i2c_driver = { + .driver = { + .name = "pcm1681", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(pcm1681_dt_ids), + }, + .id_table = pcm1681_i2c_id, + .probe = pcm1681_i2c_probe, + .remove = pcm1681_i2c_remove, +}; + +module_i2c_driver(pcm1681_i2c_driver); + +MODULE_DESCRIPTION("Texas Instruments PCM1681 ALSA SoC Codec Driver"); +MODULE_AUTHOR("Marek Belisko "); +MODULE_LICENSE("GPL"); -- cgit v0.10.2 From 92bb4c32708ee3e1d6eb0e185d678dab35152daf Mon Sep 17 00:00:00 2001 From: Dimitris Papastamos Date: Thu, 1 Aug 2013 11:11:28 +0100 Subject: ASoC: wm_adsp: Sanitize parameter passing No need to hold on to the `codec' pointer. We can use the `dsp' pointer and grab all the information we need from there. This makes the parameters for the functions a bit more sane and idiomatic. Signed-off-by: Dimitris Papastamos Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 3168224..b38f350 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -227,7 +227,6 @@ struct wm_coeff_ctl_ops { struct wm_coeff_ctl { const char *name; - struct snd_soc_card *card; struct wm_adsp_alg_region region; struct wm_coeff_ctl_ops ops; struct wm_adsp *adsp; @@ -484,7 +483,7 @@ static int wmfw_add_ctl(struct wm_adsp *adsp, struct wm_coeff_ctl *ctl) struct snd_kcontrol_new *kcontrol; int ret; - if (!ctl || !ctl->name || !ctl->card) + if (!ctl || !ctl->name) return -EINVAL; kcontrol = kzalloc(sizeof(*kcontrol), GFP_KERNEL); @@ -498,14 +497,14 @@ static int wmfw_add_ctl(struct wm_adsp *adsp, struct wm_coeff_ctl *ctl) kcontrol->put = wm_coeff_put; kcontrol->private_value = (unsigned long)ctl; - ret = snd_soc_add_card_controls(ctl->card, + ret = snd_soc_add_card_controls(adsp->card, kcontrol, 1); if (ret < 0) goto err_kcontrol; kfree(kcontrol); - ctl->kcontrol = snd_soc_card_get_kcontrol(ctl->card, + ctl->kcontrol = snd_soc_card_get_kcontrol(adsp->card, ctl->name); list_add(&ctl->list, &adsp->ctl_list); @@ -777,11 +776,10 @@ static void wm_adsp_ctl_work(struct work_struct *work) kfree(ctl_work); } -static int wm_adsp_create_control(struct snd_soc_codec *codec, +static int wm_adsp_create_control(struct wm_adsp *dsp, const struct wm_adsp_alg_region *region) { - struct wm_adsp *dsp = snd_soc_codec_get_drvdata(codec); struct wm_coeff_ctl *ctl; struct wmfw_ctl_work *ctl_work; char *name; @@ -840,7 +838,6 @@ static int wm_adsp_create_control(struct snd_soc_codec *codec, ctl->set = 0; ctl->ops.xget = wm_coeff_get; ctl->ops.xput = wm_coeff_put; - ctl->card = codec->card; ctl->adsp = dsp; ctl->len = region->len; @@ -877,7 +874,7 @@ err_name: return ret; } -static int wm_adsp_setup_algs(struct wm_adsp *dsp, struct snd_soc_codec *codec) +static int wm_adsp_setup_algs(struct wm_adsp *dsp) { struct regmap *regmap = dsp->regmap; struct wmfw_adsp1_id_hdr adsp1_id; @@ -1065,7 +1062,7 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp, struct snd_soc_codec *codec) if (i + 1 < algs) { region->len = be32_to_cpu(adsp1_alg[i + 1].dm); region->len -= be32_to_cpu(adsp1_alg[i].dm); - wm_adsp_create_control(codec, region); + wm_adsp_create_control(dsp, region); } else { adsp_warn(dsp, "Missing length info for region DM with ID %x\n", be32_to_cpu(adsp1_alg[i].alg.id)); @@ -1082,7 +1079,7 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp, struct snd_soc_codec *codec) if (i + 1 < algs) { region->len = be32_to_cpu(adsp1_alg[i + 1].zm); region->len -= be32_to_cpu(adsp1_alg[i].zm); - wm_adsp_create_control(codec, region); + wm_adsp_create_control(dsp, region); } else { adsp_warn(dsp, "Missing length info for region ZM with ID %x\n", be32_to_cpu(adsp1_alg[i].alg.id)); @@ -1111,7 +1108,7 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp, struct snd_soc_codec *codec) if (i + 1 < algs) { region->len = be32_to_cpu(adsp2_alg[i + 1].xm); region->len -= be32_to_cpu(adsp2_alg[i].xm); - wm_adsp_create_control(codec, region); + wm_adsp_create_control(dsp, region); } else { adsp_warn(dsp, "Missing length info for region XM with ID %x\n", be32_to_cpu(adsp2_alg[i].alg.id)); @@ -1128,7 +1125,7 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp, struct snd_soc_codec *codec) if (i + 1 < algs) { region->len = be32_to_cpu(adsp2_alg[i + 1].ym); region->len -= be32_to_cpu(adsp2_alg[i].ym); - wm_adsp_create_control(codec, region); + wm_adsp_create_control(dsp, region); } else { adsp_warn(dsp, "Missing length info for region YM with ID %x\n", be32_to_cpu(adsp2_alg[i].alg.id)); @@ -1145,7 +1142,7 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp, struct snd_soc_codec *codec) if (i + 1 < algs) { region->len = be32_to_cpu(adsp2_alg[i + 1].zm); region->len -= be32_to_cpu(adsp2_alg[i].zm); - wm_adsp_create_control(codec, region); + wm_adsp_create_control(dsp, region); } else { adsp_warn(dsp, "Missing length info for region ZM with ID %x\n", be32_to_cpu(adsp2_alg[i].alg.id)); @@ -1365,6 +1362,8 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w, int ret; int val; + dsp->card = codec->card; + switch (event) { case SND_SOC_DAPM_POST_PMU: regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, @@ -1399,7 +1398,7 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w, if (ret != 0) goto err; - ret = wm_adsp_setup_algs(dsp, codec); + ret = wm_adsp_setup_algs(dsp); if (ret != 0) goto err; @@ -1492,6 +1491,8 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w, unsigned int val; int ret; + dsp->card = codec->card; + switch (event) { case SND_SOC_DAPM_POST_PMU: /* @@ -1554,7 +1555,7 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w, if (ret != 0) goto err; - ret = wm_adsp_setup_algs(dsp, codec); + ret = wm_adsp_setup_algs(dsp); if (ret != 0) goto err; diff --git a/sound/soc/codecs/wm_adsp.h b/sound/soc/codecs/wm_adsp.h index 64087fb..d018dea 100644 --- a/sound/soc/codecs/wm_adsp.h +++ b/sound/soc/codecs/wm_adsp.h @@ -39,6 +39,7 @@ struct wm_adsp { int type; struct device *dev; struct regmap *regmap; + struct snd_soc_card *card; int base; int sysclk_reg; -- cgit v0.10.2 From 2f6f0ffb2b073a0a5a9ffe5705b8e8cc43558d3a Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 1 Aug 2013 11:02:47 +0100 Subject: ASoC: samsung: Make secondary I2S DAI device a child of primary More for neatness than for any great utility. Really we shouldn't be creating the child device at all, refactoring will follow. Signed-off-by: Mark Brown diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index 9737358..849ac0e 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -1019,6 +1019,8 @@ static struct i2s_dai *i2s_alloc_dai(struct platform_device *pdev, bool sec) if (IS_ERR(i2s->pdev)) return NULL; + i2s->pdev->dev.parent = &pdev->dev; + platform_set_drvdata(i2s->pdev, i2s); ret = platform_device_add(i2s->pdev); if (ret < 0) -- cgit v0.10.2 From 9356e9d51c80114fce2d7d8be99bce1d7e19d063 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 1 Aug 2013 14:08:06 +0200 Subject: ASoC: dapm: Check return value of snd_soc_cnew() snd_soc_cnew() can return NULL, so we should check the result before trying to use it. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index d74c356..b4fae87 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -671,8 +671,10 @@ static int dapm_create_or_share_mixmux_kcontrol(struct snd_soc_dapm_widget *w, kcontrol = snd_soc_cnew(&w->kcontrol_news[kci], NULL, name, prefix); - kcontrol->private_free = dapm_kcontrol_free; kfree(long_name); + if (!kcontrol) + return -ENOMEM; + kcontrol->private_free = dapm_kcontrol_free; ret = dapm_kcontrol_data_alloc(w, kcontrol); if (ret) { -- cgit v0.10.2 From 868ead653e7f65a9ac05777d0736a181a3c1c150 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Thu, 1 Aug 2013 19:29:22 +0800 Subject: ASoC: rt5640: remove unused mux Remove unused "INL Mux" and "INR Mux". Signed-off-by: Bard Liao Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c index ce585e3..4db7314 100644 --- a/sound/soc/codecs/rt5640.c +++ b/sound/soc/codecs/rt5640.c @@ -737,29 +737,6 @@ static const struct snd_kcontrol_new rt5640_mono_mix[] = { RT5640_M_BST1_MM_SFT, 1, 1), }; -/* INL/R source */ -static const char * const rt5640_inl_src[] = { - "IN2P", "MONOP" -}; - -static const SOC_ENUM_SINGLE_DECL( - rt5640_inl_enum, RT5640_INL_INR_VOL, - RT5640_INL_SEL_SFT, rt5640_inl_src); - -static const struct snd_kcontrol_new rt5640_inl_mux = - SOC_DAPM_ENUM("INL source", rt5640_inl_enum); - -static const char * const rt5640_inr_src[] = { - "IN2N", "MONON" -}; - -static const SOC_ENUM_SINGLE_DECL( - rt5640_inr_enum, RT5640_INL_INR_VOL, - RT5640_INR_SEL_SFT, rt5640_inr_src); - -static const struct snd_kcontrol_new rt5640_inr_mux = - SOC_DAPM_ENUM("INR source", rt5640_inr_enum); - /* Stereo ADC source */ static const char * const rt5640_stereo_adc1_src[] = { "DIG MIX", "ADC" @@ -1005,9 +982,6 @@ static const struct snd_soc_dapm_widget rt5640_dapm_widgets[] = { RT5640_PWR_IN_L_BIT, 0, NULL, 0), SND_SOC_DAPM_PGA("INR VOL", RT5640_PWR_VOL, RT5640_PWR_IN_R_BIT, 0, NULL, 0), - /* IN Mux */ - SND_SOC_DAPM_MUX("INL Mux", SND_SOC_NOPM, 0, 0, &rt5640_inl_mux), - SND_SOC_DAPM_MUX("INR Mux", SND_SOC_NOPM, 0, 0, &rt5640_inr_mux), /* REC Mixer */ SND_SOC_DAPM_MIXER("RECMIXL", RT5640_PWR_MIXER, RT5640_PWR_RM_L_BIT, 0, rt5640_rec_l_mix, ARRAY_SIZE(rt5640_rec_l_mix)), -- cgit v0.10.2 From 2c75bdf3fd935119cf8681ac0df2b4a5edd5167d Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 1 Aug 2013 14:08:07 +0200 Subject: ASoC: dapm: Fix kcontrol path list corruption When calling krealloc for the kcontrol data the items in the path list that point back to the head of the list will now point to freed memory, which causes the list to become corrupted. To fix this, instead of resizing the whole data struct, only resize the widget list. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index b4fae87..5f64c16 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -177,7 +177,7 @@ static inline struct snd_soc_dapm_widget *dapm_cnew_widget( struct dapm_kcontrol_data { unsigned int value; struct list_head paths; - struct snd_soc_dapm_widget_list wlist; + struct snd_soc_dapm_widget_list *wlist; }; static int dapm_kcontrol_data_alloc(struct snd_soc_dapm_widget *widget, @@ -185,7 +185,7 @@ static int dapm_kcontrol_data_alloc(struct snd_soc_dapm_widget *widget, { struct dapm_kcontrol_data *data; - data = kzalloc(sizeof(*data) + sizeof(widget), GFP_KERNEL); + data = kzalloc(sizeof(*data), GFP_KERNEL); if (!data) { dev_err(widget->dapm->dev, "ASoC: can't allocate kcontrol data for %s\n", @@ -193,8 +193,6 @@ static int dapm_kcontrol_data_alloc(struct snd_soc_dapm_widget *widget, return -ENOMEM; } - data->wlist.widgets[0] = widget; - data->wlist.num_widgets = 1; INIT_LIST_HEAD(&data->paths); kcontrol->private_data = data; @@ -205,6 +203,7 @@ static int dapm_kcontrol_data_alloc(struct snd_soc_dapm_widget *widget, static void dapm_kcontrol_free(struct snd_kcontrol *kctl) { struct dapm_kcontrol_data *data = snd_kcontrol_chip(kctl); + kfree(data->wlist); kfree(data); } @@ -213,25 +212,30 @@ static struct snd_soc_dapm_widget_list *dapm_kcontrol_get_wlist( { struct dapm_kcontrol_data *data = snd_kcontrol_chip(kcontrol); - return &data->wlist; + return data->wlist; } static int dapm_kcontrol_add_widget(struct snd_kcontrol *kcontrol, struct snd_soc_dapm_widget *widget) { struct dapm_kcontrol_data *data = snd_kcontrol_chip(kcontrol); - struct dapm_kcontrol_data *new_data; - unsigned int n = data->wlist.num_widgets + 1; + struct snd_soc_dapm_widget_list *new_wlist; + unsigned int n; + + if (data->wlist) + n = data->wlist->num_widgets + 1; + else + n = 1; - new_data = krealloc(data, sizeof(*data) + sizeof(widget) * n, - GFP_KERNEL); - if (!new_data) + new_wlist = krealloc(data->wlist, + sizeof(*new_wlist) + sizeof(widget) * n, GFP_KERNEL); + if (!new_wlist) return -ENOMEM; - new_data->wlist.widgets[n - 1] = widget; - new_data->wlist.num_widgets = n; + new_wlist->widgets[n - 1] = widget; + new_wlist->num_widgets = n; - kcontrol->private_data = new_data; + data->wlist = new_wlist; return 0; } @@ -689,12 +693,12 @@ static int dapm_create_or_share_mixmux_kcontrol(struct snd_soc_dapm_widget *w, w->name, name, ret); return ret; } - } else { - ret = dapm_kcontrol_add_widget(kcontrol, w); - if (ret) - return ret; } + ret = dapm_kcontrol_add_widget(kcontrol, w); + if (ret) + return ret; + w->kcontrols[kci] = kcontrol; dapm_kcontrol_add_path(kcontrol, path); -- cgit v0.10.2 From bde7bc6014a0a6f63cff42211ccd9b7129ce2df9 Mon Sep 17 00:00:00 2001 From: Chih-Chung Chang Date: Mon, 5 Aug 2013 16:38:42 +0800 Subject: ALSA: hda - Fix jack gating when auto_{mute,mic} is suppressed. The snd_hda_jack_set_gating_jack() call didn't work when auto_{mute,mic} is suppressed because (1) am_entry is not filled with nid of the mic pin. (2) The jacks are not created (by snd_hda_jack_detect_enable_callback) before the snd_hda_jack_set_gating_jack call. Now we use the first input pin nid directly, and create the jack if it doesn't exist yet. Signed-off-by: Chih-Chung Chang Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/hda_jack.c b/sound/pci/hda/hda_jack.c index dc93761..05b3e3e 100644 --- a/sound/pci/hda/hda_jack.c +++ b/sound/pci/hda/hda_jack.c @@ -253,8 +253,8 @@ EXPORT_SYMBOL_HDA(snd_hda_jack_detect_enable); int snd_hda_jack_set_gating_jack(struct hda_codec *codec, hda_nid_t gated_nid, hda_nid_t gating_nid) { - struct hda_jack_tbl *gated = snd_hda_jack_tbl_get(codec, gated_nid); - struct hda_jack_tbl *gating = snd_hda_jack_tbl_get(codec, gating_nid); + struct hda_jack_tbl *gated = snd_hda_jack_tbl_new(codec, gated_nid); + struct hda_jack_tbl *gating = snd_hda_jack_tbl_new(codec, gating_nid); if (!gated || !gating) return -EINVAL; diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index ad7a098..6ac4810 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -3260,6 +3260,28 @@ static void alc_fixup_headset_mode_alc668(struct hda_codec *codec, alc_fixup_headset_mode(codec, fix, action); } +/* Returns the nid of the external mic input pin, or 0 if it cannot be found. */ +static int find_ext_mic_pin(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->gen.autocfg; + hda_nid_t nid; + unsigned int defcfg; + int i; + + for (i = 0; i < cfg->num_inputs; i++) { + if (cfg->inputs[i].type != AUTO_PIN_MIC) + continue; + nid = cfg->inputs[i].pin; + defcfg = snd_hda_codec_get_pincfg(codec, nid); + if (snd_hda_get_input_pin_attr(defcfg) == INPUT_PIN_ATTR_INT) + continue; + return nid; + } + + return 0; +} + static void alc271_hp_gate_mic_jack(struct hda_codec *codec, const struct hda_fixup *fix, int action) @@ -3267,11 +3289,12 @@ static void alc271_hp_gate_mic_jack(struct hda_codec *codec, struct alc_spec *spec = codec->spec; if (action == HDA_FIXUP_ACT_PROBE) { - if (snd_BUG_ON(!spec->gen.am_entry[1].pin || - !spec->gen.autocfg.hp_pins[0])) + int mic_pin = find_ext_mic_pin(codec); + int hp_pin = spec->gen.autocfg.hp_pins[0]; + + if (snd_BUG_ON(!mic_pin || !hp_pin)) return; - snd_hda_jack_set_gating_jack(codec, spec->gen.am_entry[1].pin, - spec->gen.autocfg.hp_pins[0]); + snd_hda_jack_set_gating_jack(codec, mic_pin, hp_pin); } } -- cgit v0.10.2 From 6ad709482e151068b7197f4572edeeae5eeaff93 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 4 Aug 2013 20:32:04 +0100 Subject: ASoC: spdif_transceiver: add output pin widget CODECs without any outputs now remain powered down, which means any paths to these codecs also remain powered down. Add an always-enabled output pin widget to the spdif transceiver codec. This enables DAPM to correctly identify that the spdif transceiver is in use when playback is enabled, which will then allow DAPM to power up any links from the CPU DAI to the S/PDIF transceiver. Signed-off-by: Russell King Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/spdif_transmitter.c b/sound/soc/codecs/spdif_transmitter.c index efc3d88..4e96d10 100644 --- a/sound/soc/codecs/spdif_transmitter.c +++ b/sound/soc/codecs/spdif_transmitter.c @@ -29,7 +29,20 @@ SNDRV_PCM_FMTBIT_S20_3LE | \ SNDRV_PCM_FMTBIT_S24_LE) -static struct snd_soc_codec_driver soc_codec_spdif_dit; +static const struct snd_soc_dapm_widget dit_widgets[] = { + SND_SOC_DAPM_OUTPUT("spdif-out"), +}; + +static const const struct snd_soc_dapm_route dit_routes[] = { + { "spdif-out", NULL, "Playback" }, +}; + +static struct snd_soc_codec_driver soc_codec_spdif_dit = { + .dapm_widgets = dit_widgets, + .num_dapm_widgets = ARRAY_SIZE(dit_widgets), + .dapm_routes = dit_routes, + .num_dapm_routes = ARRAY_SIZE(dit_routes), +}; static struct snd_soc_dai_driver dit_stub_dai = { .name = "dit-hifi", -- cgit v0.10.2 From a7d094297946e32da9bdf03cd5be1f6954d17ed3 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 4 Aug 2013 20:22:03 +0100 Subject: ASoC: kirkwood: merge struct kirkwood_dma_priv with struct kirkwood_dma_data Merge these two structures together; nothing other than the I2S and DMA driver makes use of struct kirkwood_dma_data, and it's not like struct kirkwood_dma_data is really just used to convey DMA specific data to the backend; it's more a general shared structure between the two halves. This will later allow kirkwood-dma.c and kirkwood-i2s.c to be merged together. Signed-off-by: Russell King Signed-off-by: Mark Brown diff --git a/sound/soc/kirkwood/kirkwood-dma.c b/sound/soc/kirkwood/kirkwood-dma.c index a9f1453..ba50dd1 100644 --- a/sound/soc/kirkwood/kirkwood-dma.c +++ b/sound/soc/kirkwood/kirkwood-dma.c @@ -33,11 +33,11 @@ SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE | \ SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_BE) -struct kirkwood_dma_priv { - struct snd_pcm_substream *play_stream; - struct snd_pcm_substream *rec_stream; - struct kirkwood_dma_data *data; -}; +static struct kirkwood_dma_data *kirkwood_priv(struct snd_pcm_substream *subs) +{ + struct snd_soc_pcm_runtime *soc_runtime = subs->private_data; + return snd_soc_dai_get_drvdata(soc_runtime->cpu_dai); +} static struct snd_pcm_hardware kirkwood_dma_snd_hw = { .info = (SNDRV_PCM_INFO_INTERLEAVED | @@ -63,8 +63,7 @@ static u64 kirkwood_dma_dmamask = DMA_BIT_MASK(32); static irqreturn_t kirkwood_dma_irq(int irq, void *dev_id) { - struct kirkwood_dma_priv *prdata = dev_id; - struct kirkwood_dma_data *priv = prdata->data; + struct kirkwood_dma_data *priv = dev_id; unsigned long mask, status, cause; mask = readl(priv->io + KIRKWOOD_INT_MASK); @@ -89,10 +88,10 @@ static irqreturn_t kirkwood_dma_irq(int irq, void *dev_id) writel(status, priv->io + KIRKWOOD_INT_CAUSE); if (status & KIRKWOOD_INT_CAUSE_PLAY_BYTES) - snd_pcm_period_elapsed(prdata->play_stream); + snd_pcm_period_elapsed(priv->substream_play); if (status & KIRKWOOD_INT_CAUSE_REC_BYTES) - snd_pcm_period_elapsed(prdata->rec_stream); + snd_pcm_period_elapsed(priv->substream_rec); return IRQ_HANDLED; } @@ -126,15 +125,10 @@ static int kirkwood_dma_open(struct snd_pcm_substream *substream) { int err; struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; - struct snd_soc_platform *platform = soc_runtime->platform; - struct snd_soc_dai *cpu_dai = soc_runtime->cpu_dai; - struct kirkwood_dma_data *priv; - struct kirkwood_dma_priv *prdata = snd_soc_platform_get_drvdata(platform); + struct kirkwood_dma_data *priv = kirkwood_priv(substream); const struct mbus_dram_target_info *dram; unsigned long addr; - priv = snd_soc_dai_get_dma_data(cpu_dai, substream); snd_soc_set_runtime_hwparams(substream, &kirkwood_dma_snd_hw); /* Ensure that all constraints linked to dma burst are fulfilled */ @@ -157,21 +151,11 @@ static int kirkwood_dma_open(struct snd_pcm_substream *substream) if (err < 0) return err; - if (prdata == NULL) { - prdata = kzalloc(sizeof(struct kirkwood_dma_priv), GFP_KERNEL); - if (prdata == NULL) - return -ENOMEM; - - prdata->data = priv; - + if (!priv->substream_play && !priv->substream_rec) { err = request_irq(priv->irq, kirkwood_dma_irq, IRQF_SHARED, - "kirkwood-i2s", prdata); - if (err) { - kfree(prdata); + "kirkwood-i2s", priv); + if (err) return -EBUSY; - } - - snd_soc_platform_set_drvdata(platform, prdata); /* * Enable Error interrupts. We're only ack'ing them but @@ -183,11 +167,11 @@ static int kirkwood_dma_open(struct snd_pcm_substream *substream) dram = mv_mbus_dram_info(); addr = substream->dma_buffer.addr; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - prdata->play_stream = substream; + priv->substream_play = substream; kirkwood_dma_conf_mbus_windows(priv->io, KIRKWOOD_PLAYBACK_WIN, addr, dram); } else { - prdata->rec_stream = substream; + priv->substream_rec = substream; kirkwood_dma_conf_mbus_windows(priv->io, KIRKWOOD_RECORD_WIN, addr, dram); } @@ -197,27 +181,19 @@ static int kirkwood_dma_open(struct snd_pcm_substream *substream) static int kirkwood_dma_close(struct snd_pcm_substream *substream) { - struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; - struct snd_soc_dai *cpu_dai = soc_runtime->cpu_dai; - struct snd_soc_platform *platform = soc_runtime->platform; - struct kirkwood_dma_priv *prdata = snd_soc_platform_get_drvdata(platform); - struct kirkwood_dma_data *priv; - - priv = snd_soc_dai_get_dma_data(cpu_dai, substream); + struct kirkwood_dma_data *priv = kirkwood_priv(substream); - if (!prdata || !priv) + if (!priv) return 0; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - prdata->play_stream = NULL; + priv->substream_play = NULL; else - prdata->rec_stream = NULL; + priv->substream_rec = NULL; - if (!prdata->play_stream && !prdata->rec_stream) { + if (!priv->substream_play && !priv->substream_rec) { writel(0, priv->io + KIRKWOOD_ERR_MASK); - free_irq(priv->irq, prdata); - kfree(prdata); - snd_soc_platform_set_drvdata(platform, NULL); + free_irq(priv->irq, priv); } return 0; @@ -243,13 +219,9 @@ static int kirkwood_dma_hw_free(struct snd_pcm_substream *substream) static int kirkwood_dma_prepare(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; - struct snd_soc_dai *cpu_dai = soc_runtime->cpu_dai; - struct kirkwood_dma_data *priv; + struct kirkwood_dma_data *priv = kirkwood_priv(substream); unsigned long size, count; - priv = snd_soc_dai_get_dma_data(cpu_dai, substream); - /* compute buffer size in term of "words" as requested in specs */ size = frames_to_bytes(runtime, runtime->buffer_size); size = (size>>2)-1; @@ -272,13 +244,9 @@ static int kirkwood_dma_prepare(struct snd_pcm_substream *substream) static snd_pcm_uframes_t kirkwood_dma_pointer(struct snd_pcm_substream *substream) { - struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; - struct snd_soc_dai *cpu_dai = soc_runtime->cpu_dai; - struct kirkwood_dma_data *priv; + struct kirkwood_dma_data *priv = kirkwood_priv(substream); snd_pcm_uframes_t count; - priv = snd_soc_dai_get_dma_data(cpu_dai, substream); - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) count = bytes_to_frames(substream->runtime, readl(priv->io + KIRKWOOD_PLAY_BYTE_COUNT)); diff --git a/sound/soc/kirkwood/kirkwood.h b/sound/soc/kirkwood/kirkwood.h index 4d92637..10a3aaa 100644 --- a/sound/soc/kirkwood/kirkwood.h +++ b/sound/soc/kirkwood/kirkwood.h @@ -129,6 +129,8 @@ struct kirkwood_dma_data { struct clk *extclk; uint32_t ctl_play; uint32_t ctl_rec; + struct snd_pcm_substream *substream_play; + struct snd_pcm_substream *substream_rec; int irq; int burst; }; -- cgit v0.10.2 From 57295073b6acfdfaf9319d3caf92a5c433fdf109 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 5 Aug 2013 11:27:31 +0200 Subject: ASoC: dapm: Implement mixer input auto-disable Some devices have the problem that if a internal audio signal source is disabled the output of the source becomes undefined or goes to a undesired state (E.g. DAC output goes to ground instead of VMID). In this case it is necessary, in order to avoid unwanted clicks and pops, to disable any mixer input the signal feeds into or to active a mute control along the path to the output. Often it is still desirable to expose the same mixer input control to userspace, so cerain paths can sill be disabled manually. This means we can not use conventional DAPM to manage the mixer input control. This patch implements a method for letting DAPM overwrite the state of a userspace visible control. I.e. DAPM will disable the control if the path on which the control sits becomes inactive. Userspace will then only see a cached copy of the controls state. Once DAPM powers the path up again it will sync the userspace setting with the hardware and give control back to userspace. To implement this a new widget type is introduced. One widget of this type will be created for each DAPM kcontrol which has the auto-disable feature enabled. For each path that is controlled by the kcontrol the widget will be connected to the source of that path. The new widget type behaves like a supply widget, which means it will power up if one of its sinks are powered up and will only power down if all of its sinks are powered down. In order to only have the mixer input enabled when the source signal is valid the new widget type will be disabled before all other widget types and only be enabled after all other widget types. E.g. consider the following simplified example. A DAC is connected to a mixer and the mixer has a control to enable or disable the signal from the DAC. +-------+ +-----+ | | | DAC |-----[Ctrl]-| Mixer | +-----+ : | | | : +-------+ | : +-------------+ | Ctrl widget | +-------------+ If the control has the auto-disable feature enabled we'll create a widget for the control. This widget is connected to the DAC as it is the source for the mixer input. If the DAC powers up the control widget powers up and if the DAC powers down the control widget is powered down. As long as the control widget is powered down the hardware input control is kept disabled and if it is enabled userspace can freely change the control's state. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 3575721..c728d28 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -280,14 +280,26 @@ struct device; { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ .info = snd_soc_info_volsw, \ .get = snd_soc_dapm_get_volsw, .put = snd_soc_dapm_put_volsw, \ - .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert) } + .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert, 0) } +#define SOC_DAPM_SINGLE_AUTODISABLE(xname, reg, shift, max, invert) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = snd_soc_info_volsw, \ + .get = snd_soc_dapm_get_volsw, .put = snd_soc_dapm_put_volsw, \ + .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert, 1) } #define SOC_DAPM_SINGLE_TLV(xname, reg, shift, max, invert, tlv_array) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ .info = snd_soc_info_volsw, \ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | SNDRV_CTL_ELEM_ACCESS_READWRITE,\ .tlv.p = (tlv_array), \ .get = snd_soc_dapm_get_volsw, .put = snd_soc_dapm_put_volsw, \ - .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert) } + .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert, 0) } +#define SOC_DAPM_SINGLE_TLV_AUTODISABLE(xname, reg, shift, max, invert, tlv_array) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = snd_soc_info_volsw, \ + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | SNDRV_CTL_ELEM_ACCESS_READWRITE,\ + .tlv.p = (tlv_array), \ + .get = snd_soc_dapm_get_volsw, .put = snd_soc_dapm_put_volsw, \ + .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert, 0) } #define SOC_DAPM_ENUM(xname, xenum) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ .info = snd_soc_info_enum_double, \ @@ -484,6 +496,7 @@ enum snd_soc_dapm_type { snd_soc_dapm_dai_in, /* link to DAI structure */ snd_soc_dapm_dai_out, snd_soc_dapm_dai_link, /* link between two DAI structures */ + snd_soc_dapm_kcontrol, /* Auto-disabled kcontrol */ }; enum snd_soc_dapm_subclass { diff --git a/include/sound/soc.h b/include/sound/soc.h index b1e1f96..6201c6e 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -30,13 +30,13 @@ /* * Convenience kcontrol builders */ -#define SOC_DOUBLE_VALUE(xreg, shift_left, shift_right, xmax, xinvert) \ +#define SOC_DOUBLE_VALUE(xreg, shift_left, shift_right, xmax, xinvert, xautodisable) \ ((unsigned long)&(struct soc_mixer_control) \ {.reg = xreg, .rreg = xreg, .shift = shift_left, \ .rshift = shift_right, .max = xmax, .platform_max = xmax, \ - .invert = xinvert}) -#define SOC_SINGLE_VALUE(xreg, xshift, xmax, xinvert) \ - SOC_DOUBLE_VALUE(xreg, xshift, xshift, xmax, xinvert) + .invert = xinvert, .autodisable = xautodisable}) +#define SOC_SINGLE_VALUE(xreg, xshift, xmax, xinvert, xautodisable) \ + SOC_DOUBLE_VALUE(xreg, xshift, xshift, xmax, xinvert, xautodisable) #define SOC_SINGLE_VALUE_EXT(xreg, xmax, xinvert) \ ((unsigned long)&(struct soc_mixer_control) \ {.reg = xreg, .max = xmax, .platform_max = xmax, .invert = xinvert}) @@ -52,7 +52,7 @@ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ .info = snd_soc_info_volsw, .get = snd_soc_get_volsw,\ .put = snd_soc_put_volsw, \ - .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert) } + .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert, 0) } #define SOC_SINGLE_RANGE(xname, xreg, xshift, xmin, xmax, xinvert) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\ .info = snd_soc_info_volsw_range, .get = snd_soc_get_volsw_range, \ @@ -68,7 +68,7 @@ .tlv.p = (tlv_array), \ .info = snd_soc_info_volsw, .get = snd_soc_get_volsw,\ .put = snd_soc_put_volsw, \ - .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert) } + .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert, 0) } #define SOC_SINGLE_SX_TLV(xname, xreg, xshift, xmin, xmax, tlv_array) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ @@ -97,7 +97,7 @@ .info = snd_soc_info_volsw, .get = snd_soc_get_volsw, \ .put = snd_soc_put_volsw, \ .private_value = SOC_DOUBLE_VALUE(reg, shift_left, shift_right, \ - max, invert) } + max, invert, 0) } #define SOC_DOUBLE_R(xname, reg_left, reg_right, xshift, xmax, xinvert) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ .info = snd_soc_info_volsw, \ @@ -119,7 +119,7 @@ .info = snd_soc_info_volsw, .get = snd_soc_get_volsw, \ .put = snd_soc_put_volsw, \ .private_value = SOC_DOUBLE_VALUE(reg, shift_left, shift_right, \ - max, invert) } + max, invert, 0) } #define SOC_DOUBLE_R_TLV(xname, reg_left, reg_right, xshift, xmax, xinvert, tlv_array) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\ @@ -190,14 +190,14 @@ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ .info = snd_soc_info_volsw, \ .get = xhandler_get, .put = xhandler_put, \ - .private_value = SOC_SINGLE_VALUE(xreg, xshift, xmax, xinvert) } + .private_value = SOC_SINGLE_VALUE(xreg, xshift, xmax, xinvert, 0) } #define SOC_DOUBLE_EXT(xname, reg, shift_left, shift_right, max, invert,\ xhandler_get, xhandler_put) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\ .info = snd_soc_info_volsw, \ .get = xhandler_get, .put = xhandler_put, \ .private_value = \ - SOC_DOUBLE_VALUE(reg, shift_left, shift_right, max, invert) } + SOC_DOUBLE_VALUE(reg, shift_left, shift_right, max, invert, 0) } #define SOC_SINGLE_EXT_TLV(xname, xreg, xshift, xmax, xinvert,\ xhandler_get, xhandler_put, tlv_array) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ @@ -206,7 +206,7 @@ .tlv.p = (tlv_array), \ .info = snd_soc_info_volsw, \ .get = xhandler_get, .put = xhandler_put, \ - .private_value = SOC_SINGLE_VALUE(xreg, xshift, xmax, xinvert) } + .private_value = SOC_SINGLE_VALUE(xreg, xshift, xmax, xinvert, 0) } #define SOC_DOUBLE_EXT_TLV(xname, xreg, shift_left, shift_right, xmax, xinvert,\ xhandler_get, xhandler_put, tlv_array) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ @@ -216,7 +216,7 @@ .info = snd_soc_info_volsw, \ .get = xhandler_get, .put = xhandler_put, \ .private_value = SOC_DOUBLE_VALUE(xreg, shift_left, shift_right, \ - xmax, xinvert) } + xmax, xinvert, 0) } #define SOC_DOUBLE_R_EXT_TLV(xname, reg_left, reg_right, xshift, xmax, xinvert,\ xhandler_get, xhandler_put, tlv_array) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ @@ -1088,7 +1088,9 @@ struct snd_soc_pcm_runtime { /* mixer control */ struct soc_mixer_control { int min, max, platform_max; - unsigned int reg, rreg, shift, rshift, invert; + unsigned int reg, rreg, shift, rshift; + unsigned int invert:1; + unsigned int autodisable:1; }; struct soc_bytes { diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 5f64c16..0944bc4 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -47,6 +47,15 @@ #define DAPM_UPDATE_STAT(widget, val) widget->dapm->card->dapm_stats.val++; +static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm, + struct snd_soc_dapm_widget *wsource, struct snd_soc_dapm_widget *wsink, + const char *control, + int (*connected)(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink)); +static struct snd_soc_dapm_widget * +snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm, + const struct snd_soc_dapm_widget *widget); + /* dapm power sequences - make this per codec in the future */ static int dapm_up_seq[] = { [snd_soc_dapm_pre] = 0, @@ -73,16 +82,18 @@ static int dapm_up_seq[] = { [snd_soc_dapm_hp] = 10, [snd_soc_dapm_spk] = 10, [snd_soc_dapm_line] = 10, - [snd_soc_dapm_post] = 11, + [snd_soc_dapm_kcontrol] = 11, + [snd_soc_dapm_post] = 12, }; static int dapm_down_seq[] = { [snd_soc_dapm_pre] = 0, - [snd_soc_dapm_adc] = 1, - [snd_soc_dapm_hp] = 2, - [snd_soc_dapm_spk] = 2, - [snd_soc_dapm_line] = 2, - [snd_soc_dapm_out_drv] = 2, + [snd_soc_dapm_kcontrol] = 1, + [snd_soc_dapm_adc] = 2, + [snd_soc_dapm_hp] = 3, + [snd_soc_dapm_spk] = 3, + [snd_soc_dapm_line] = 3, + [snd_soc_dapm_out_drv] = 3, [snd_soc_dapm_pga] = 4, [snd_soc_dapm_switch] = 5, [snd_soc_dapm_mixer_named_ctl] = 5, @@ -176,6 +187,7 @@ static inline struct snd_soc_dapm_widget *dapm_cnew_widget( struct dapm_kcontrol_data { unsigned int value; + struct snd_soc_dapm_widget *widget; struct list_head paths; struct snd_soc_dapm_widget_list *wlist; }; @@ -184,6 +196,7 @@ static int dapm_kcontrol_data_alloc(struct snd_soc_dapm_widget *widget, struct snd_kcontrol *kcontrol) { struct dapm_kcontrol_data *data; + struct soc_mixer_control *mc; data = kzalloc(sizeof(*data), GFP_KERNEL); if (!data) { @@ -195,6 +208,39 @@ static int dapm_kcontrol_data_alloc(struct snd_soc_dapm_widget *widget, INIT_LIST_HEAD(&data->paths); + switch (widget->id) { + case snd_soc_dapm_switch: + case snd_soc_dapm_mixer: + case snd_soc_dapm_mixer_named_ctl: + mc = (struct soc_mixer_control *)kcontrol->private_value; + + if (mc->autodisable) { + struct snd_soc_dapm_widget template; + + memset(&template, 0, sizeof(template)); + template.reg = mc->reg; + template.mask = (1 << fls(mc->max)) - 1; + template.shift = mc->shift; + if (mc->invert) + template.off_val = mc->max; + else + template.off_val = 0; + template.on_val = template.off_val; + template.id = snd_soc_dapm_kcontrol; + template.name = kcontrol->id.name; + + data->widget = snd_soc_dapm_new_control(widget->dapm, + &template); + if (!data->widget) { + kfree(data); + return -ENOMEM; + } + } + break; + default: + break; + } + kcontrol->private_data = data; return 0; @@ -203,6 +249,7 @@ static int dapm_kcontrol_data_alloc(struct snd_soc_dapm_widget *widget, static void dapm_kcontrol_free(struct snd_kcontrol *kctl) { struct dapm_kcontrol_data *data = snd_kcontrol_chip(kctl); + kfree(data->widget); kfree(data->wlist); kfree(data); } @@ -246,6 +293,21 @@ static void dapm_kcontrol_add_path(const struct snd_kcontrol *kcontrol, struct dapm_kcontrol_data *data = snd_kcontrol_chip(kcontrol); list_add_tail(&path->list_kcontrol, &data->paths); + + if (data->widget) { + snd_soc_dapm_add_path(data->widget->dapm, data->widget, + path->source, NULL, NULL); + } +} + +static bool dapm_kcontrol_is_powered(const struct snd_kcontrol *kcontrol) +{ + struct dapm_kcontrol_data *data = snd_kcontrol_chip(kcontrol); + + if (!data->widget) + return true; + + return data->widget->power; } static struct list_head *dapm_kcontrol_get_path_list( @@ -275,6 +337,9 @@ static bool dapm_kcontrol_set_value(const struct snd_kcontrol *kcontrol, if (data->value == value) return false; + if (data->widget) + data->widget->on_val = value; + data->value = value; return true; @@ -515,6 +580,7 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w, case snd_soc_dapm_spk: case snd_soc_dapm_line: case snd_soc_dapm_dai_link: + case snd_soc_dapm_kcontrol: p->connect = 1; break; /* does affect routing - dynamically connected */ @@ -880,6 +946,7 @@ static int is_connected_output_ep(struct snd_soc_dapm_widget *widget, case snd_soc_dapm_supply: case snd_soc_dapm_regulator_supply: case snd_soc_dapm_clock_supply: + case snd_soc_dapm_kcontrol: return 0; default: break; @@ -975,6 +1042,7 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget, case snd_soc_dapm_supply: case snd_soc_dapm_regulator_supply: case snd_soc_dapm_clock_supply: + case snd_soc_dapm_kcontrol: return 0; default: break; @@ -1523,7 +1591,7 @@ static void dapm_widget_update(struct snd_soc_card *card) unsigned int wi; int ret; - if (!update) + if (!update || !dapm_kcontrol_is_powered(update->kcontrol)) return; wlist = dapm_kcontrol_get_wlist(update->kcontrol); @@ -1668,6 +1736,7 @@ static void dapm_widget_set_power(struct snd_soc_dapm_widget *w, bool power, case snd_soc_dapm_supply: case snd_soc_dapm_regulator_supply: case snd_soc_dapm_clock_supply: + case snd_soc_dapm_kcontrol: /* Supplies can't affect their outputs, only their inputs */ break; default: @@ -2335,6 +2404,7 @@ static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm, case snd_soc_dapm_dai_in: case snd_soc_dapm_dai_out: case snd_soc_dapm_dai_link: + case snd_soc_dapm_kcontrol: list_add(&path->list, &dapm->card->paths); list_add(&path->list_sink, &wsink->sources); list_add(&path->list_source, &wsource->sinks); @@ -2717,6 +2787,7 @@ int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); + struct snd_soc_card *card = codec->card; struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; unsigned int reg = mc->reg; @@ -2724,17 +2795,24 @@ int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol, int max = mc->max; unsigned int mask = (1 << fls(max)) - 1; unsigned int invert = mc->invert; + unsigned int val; if (snd_soc_volsw_is_stereo(mc)) dev_warn(codec->dapm.dev, "ASoC: Control '%s' is stereo, which is not supported\n", kcontrol->id.name); - ucontrol->value.integer.value[0] = - (snd_soc_read(codec, reg) >> shift) & mask; + mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); + if (dapm_kcontrol_is_powered(kcontrol)) + val = (snd_soc_read(codec, reg) >> shift) & mask; + else + val = dapm_kcontrol_get_value(kcontrol); + mutex_unlock(&card->dapm_mutex); + if (invert) - ucontrol->value.integer.value[0] = - max - ucontrol->value.integer.value[0]; + ucontrol->value.integer.value[0] = max - val; + else + ucontrol->value.integer.value[0] = val; return 0; } @@ -2775,11 +2853,14 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, if (invert) val = max - val; - mask = mask << shift; - val = val << shift; mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); + dapm_kcontrol_set_value(kcontrol, val); + + mask = mask << shift; + val = val << shift; + change = snd_soc_test_bits(codec, reg, mask, val); if (change) { update.kcontrol = kcontrol; @@ -3179,6 +3260,7 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm, case snd_soc_dapm_supply: case snd_soc_dapm_regulator_supply: case snd_soc_dapm_clock_supply: + case snd_soc_dapm_kcontrol: w->power_check = dapm_supply_check_power; break; default: -- cgit v0.10.2 From e06e4c2d530fd4995c41083009647263ccd77d3b Mon Sep 17 00:00:00 2001 From: Oskar Schirmer Date: Mon, 5 Aug 2013 07:36:02 +0000 Subject: ASoC: sgtl5000: fix codec clock source transition to avoid clockless moment Powering down PLL before switching to a mode that does not use it is a bad idea. It would cause the SGTL5000 be without internal clock supply, especially on the I2C interface, which would make subsequent access to it fail. Thus, in case of not using PLL any longer, first set the mode control, then power down PLL. Signed-off-by: Oskar Schirmer Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c index 7c99f3c..54ca169 100644 --- a/sound/soc/codecs/sgtl5000.c +++ b/sound/soc/codecs/sgtl5000.c @@ -644,16 +644,19 @@ static int sgtl5000_set_clock(struct snd_soc_codec *codec, int frame_rate) snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER, SGTL5000_PLL_POWERUP | SGTL5000_VCOAMP_POWERUP, SGTL5000_PLL_POWERUP | SGTL5000_VCOAMP_POWERUP); + + /* if using pll, clk_ctrl must be set after pll power up */ + snd_soc_write(codec, SGTL5000_CHIP_CLK_CTRL, clk_ctl); } else { + /* otherwise, clk_ctrl must be set before pll power down */ + snd_soc_write(codec, SGTL5000_CHIP_CLK_CTRL, clk_ctl); + /* power down pll */ snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER, SGTL5000_PLL_POWERUP | SGTL5000_VCOAMP_POWERUP, 0); } - /* if using pll, clk_ctrl must be set after pll power up */ - snd_soc_write(codec, SGTL5000_CHIP_CLK_CTRL, clk_ctl); - return 0; } -- cgit v0.10.2 From 9d58a077465ff23b935042bf1cbdac64cdb78a2c Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Mon, 5 Aug 2013 13:17:28 +0100 Subject: ASoC: core: init delayed_work for codec-codec links We must init the delayed_work for codec-codec links otherwise shutting down the DAI chain will fault when calling flush_delayed_work_sync() on the linked DAI. Signed-off-by: Richard Fitzgerald Signed-off-by: Mark Brown diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 0ec070c..2940e2c 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -530,6 +530,15 @@ static int soc_ac97_dev_register(struct snd_soc_codec *codec) } #endif +static void codec2codec_close_delayed_work(struct work_struct *work) +{ + /* Currently nothing to do for c2c links + * Since c2c links are internal nodes in the DAPM graph and + * don't interface with the outside world or application layer + * we don't have to do any special handling on close. + */ +} + #ifdef CONFIG_PM_SLEEP /* powers down audio subsystem for suspend */ int snd_soc_suspend(struct device *dev) @@ -1428,6 +1437,9 @@ static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order) return ret; } } else { + INIT_DELAYED_WORK(&rtd->delayed_work, + codec2codec_close_delayed_work); + /* link the DAI widgets */ play_w = codec_dai->playback_widget; capture_w = cpu_dai->capture_widget; -- cgit v0.10.2 From af64d7341ab51335eeb03453180cf200b120ec43 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 4 Aug 2013 20:23:03 +0100 Subject: ASoC: kirkwood: Free external clock if it is a duplicate of internal [Remaining patch from "ASoC: kirkwood: use devm_clk_get() for the external clock" -- broonie] Signed-off-by: Russell King Signed-off-by: Mark Brown diff --git a/sound/soc/kirkwood/kirkwood-i2s.c b/sound/soc/kirkwood/kirkwood-i2s.c index ba72039..0109b1e 100644 --- a/sound/soc/kirkwood/kirkwood-i2s.c +++ b/sound/soc/kirkwood/kirkwood-i2s.c @@ -495,6 +495,7 @@ static int kirkwood_i2s_dev_probe(struct platform_device *pdev) priv->extclk = devm_clk_get(&pdev->dev, "extclk"); if (!IS_ERR(priv->extclk)) { if (priv->extclk == priv->clk) { + devm_clk_put(&pdev->dev, priv->extclk); priv->extclk = ERR_PTR(-EINVAL); } else { dev_info(&pdev->dev, "found external clock\n"); -- cgit v0.10.2 From 19c2c5f55e31ac8da87bb8efe0cf86aa933e6a2f Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 4 Aug 2013 20:24:03 +0100 Subject: ASoC: avoid duplicated DAI routes ASoC automatically creates snd_soc_dapm_dai_in and snd_soc_dapm_dai_out widgets for DAI drivers, and adds them to the list. Later on, ASoC creates automatic routes between these widgets and a widget with a stream name. We look for a snd_soc_dapm_dai_in or snd_soc_dapm_dai_out widget, and use this to obtain the DAI structure. We then scan all widgets for any with a stream name refering to either the capture or the playback stream, and create routes. If you have both a snd_soc_dapm_dai_in and a snd_soc_dapm_dai_out referring to the same DAI structure, this ends up creating one set of routes for the DAI for the snd_soc_dapm_dai_in widget, and a duplicated set of routes for the snd_soc_dapm_dai_out widget. Fix this by checking that the stream name for the widget matches the DAI widget name. Signed-off-by: Russell King Signed-off-by: Mark Brown diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 0944bc4..7f53d86 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -3551,7 +3551,7 @@ int snd_soc_dapm_link_dai_widgets(struct snd_soc_card *card) break; } - if (!w->sname) + if (!w->sname || !strstr(w->sname, dai_w->name)) continue; if (dai->driver->playback.stream_name && -- cgit v0.10.2 From 13b02fa0dbb1311d08dfacd897a6ff41232d7cfb Mon Sep 17 00:00:00 2001 From: Michael Trimarchi Date: Sat, 3 Aug 2013 16:20:43 +0200 Subject: ASoC: Add PCM1792A spi mode codec support Add PCM1792A spi mode codec support. This version implements only a subset of functionalities. Tested connect to a pandaboard ES device and based on recently pcm1681 codec. Signed-off-by: Michael Trimarchi Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/sound/pcm1792a.txt b/Documentation/devicetree/bindings/sound/pcm1792a.txt new file mode 100644 index 0000000..970ba1e --- /dev/null +++ b/Documentation/devicetree/bindings/sound/pcm1792a.txt @@ -0,0 +1,18 @@ +Texas Instruments pcm1792a DT bindings + +This driver supports the SPI bus. + +Required properties: + + - compatible: "ti,pcm1792a" + +For required properties on SPI, please consult +Documentation/devicetree/bindings/spi/spi-bus.txt + +Examples: + + codec_spi: 1792a@0 { + compatible = "ti,pcm1792a"; + spi-max-frequency = <600000>; + }; + diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index badb6fb..4afe943 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -54,6 +54,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_MC13783 if MFD_MC13XXX select SND_SOC_ML26124 if I2C select SND_SOC_HDMI_CODEC + select SND_SOC_PCM1792A if SPI_MASTER select SND_SOC_PCM3008 select SND_SOC_RT5631 if I2C select SND_SOC_RT5640 if I2C @@ -292,6 +293,9 @@ config SND_SOC_MAX9850 config SND_SOC_HDMI_CODEC tristate +config SND_SOC_PCM1792A + tristate + config SND_SOC_PCM3008 tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 70fd806..811ca12 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -42,6 +42,7 @@ snd-soc-max9850-objs := max9850.o snd-soc-mc13783-objs := mc13783.o snd-soc-ml26124-objs := ml26124.o snd-soc-hdmi-codec-objs := hdmi.o +snd-soc-pcm1792a-codec-objs := pcm1792a.o snd-soc-pcm3008-objs := pcm3008.o snd-soc-rt5631-objs := rt5631.o snd-soc-rt5640-objs := rt5640.o @@ -171,6 +172,7 @@ obj-$(CONFIG_SND_SOC_MAX9850) += snd-soc-max9850.o obj-$(CONFIG_SND_SOC_MC13783) += snd-soc-mc13783.o obj-$(CONFIG_SND_SOC_ML26124) += snd-soc-ml26124.o obj-$(CONFIG_SND_SOC_HDMI_CODEC) += snd-soc-hdmi-codec.o +obj-$(CONFIG_SND_SOC_PCM1792A) += snd-soc-pcm1792a-codec.o obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o obj-$(CONFIG_SND_SOC_RT5631) += snd-soc-rt5631.o obj-$(CONFIG_SND_SOC_RT5640) += snd-soc-rt5640.o diff --git a/sound/soc/codecs/pcm1792a.c b/sound/soc/codecs/pcm1792a.c new file mode 100644 index 0000000..3f83bf9 --- /dev/null +++ b/sound/soc/codecs/pcm1792a.c @@ -0,0 +1,245 @@ +/* + * PCM1792A ASoC codec driver + * + * Copyright (c) Amarula Solutions B.V. 2013 + * + * Michael Trimarchi + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "pcm1792a.h" + +#define PCM1792A_DAC_VOL_LEFT 0x10 +#define PCM1792A_DAC_VOL_RIGHT 0x11 +#define PCM1792A_FMT_CONTROL 0x12 +#define PCM1792A_SOFT_MUTE PCM1792A_FMT_CONTROL + +#define PCM1792A_FMT_MASK 0x70 +#define PCM1792A_FMT_SHIFT 4 +#define PCM1792A_MUTE_MASK 0x01 +#define PCM1792A_MUTE_SHIFT 0 +#define PCM1792A_ATLD_ENABLE (1 << 7) + +static const struct reg_default pcm1792a_reg_defaults[] = { + { 0x10, 0xff }, + { 0x11, 0xff }, + { 0x12, 0x50 }, + { 0x13, 0x00 }, + { 0x14, 0x00 }, + { 0x15, 0x01 }, + { 0x16, 0x00 }, + { 0x17, 0x00 }, +}; + +static bool pcm1792a_accessible_reg(struct device *dev, unsigned int reg) +{ + return reg >= 0x10 && reg <= 0x17; +} + +static bool pcm1792a_writeable_reg(struct device *dev, unsigned register reg) +{ + bool accessible; + + accessible = pcm1792a_accessible_reg(dev, reg); + + return accessible && reg != 0x16 && reg != 0x17; +} + +struct pcm1792a_private { + struct regmap *regmap; + unsigned int format; + unsigned int rate; +}; + +static int pcm1792a_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int format) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct pcm1792a_private *priv = snd_soc_codec_get_drvdata(codec); + + priv->format = format; + + return 0; +} + +static int pcm1792a_digital_mute(struct snd_soc_dai *dai, int mute) +{ + struct snd_soc_codec *codec = dai->codec; + struct pcm1792a_private *priv = snd_soc_codec_get_drvdata(codec); + int ret; + + ret = regmap_update_bits(priv->regmap, PCM1792A_SOFT_MUTE, + PCM1792A_MUTE_MASK, !!mute); + if (ret < 0) + return ret; + + return 0; +} + +static int pcm1792a_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct pcm1792a_private *priv = snd_soc_codec_get_drvdata(codec); + int val = 0, ret; + int pcm_format = params_format(params); + + priv->rate = params_rate(params); + + switch (priv->format & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_RIGHT_J: + if (pcm_format == SNDRV_PCM_FORMAT_S24_LE || + pcm_format == SNDRV_PCM_FORMAT_S32_LE) + val = 0x02; + else if (pcm_format == SNDRV_PCM_FORMAT_S16_LE) + val = 0x00; + break; + case SND_SOC_DAIFMT_I2S: + if (pcm_format == SNDRV_PCM_FORMAT_S24_LE || + pcm_format == SNDRV_PCM_FORMAT_S32_LE) + val = 0x05; + else if (pcm_format == SNDRV_PCM_FORMAT_S16_LE) + val = 0x04; + break; + default: + dev_err(codec->dev, "Invalid DAI format\n"); + return -EINVAL; + } + + val = val << PCM1792A_FMT_SHIFT | PCM1792A_ATLD_ENABLE; + + ret = regmap_update_bits(priv->regmap, PCM1792A_FMT_CONTROL, + PCM1792A_FMT_MASK | PCM1792A_ATLD_ENABLE, val); + if (ret < 0) + return ret; + + return 0; +} + +static const struct snd_soc_dai_ops pcm1792a_dai_ops = { + .set_fmt = pcm1792a_set_dai_fmt, + .hw_params = pcm1792a_hw_params, + .digital_mute = pcm1792a_digital_mute, +}; + +static const DECLARE_TLV_DB_SCALE(pcm1792a_dac_tlv, -12000, 50, 1); + +static const struct snd_kcontrol_new pcm1792a_controls[] = { + SOC_DOUBLE_R_RANGE_TLV("DAC Playback Volume", PCM1792A_DAC_VOL_LEFT, + PCM1792A_DAC_VOL_RIGHT, 0, 0xf, 0xff, 0, + pcm1792a_dac_tlv), +}; + +static struct snd_soc_dai_driver pcm1792a_dai = { + .name = "pcm1792a-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = PCM1792A_RATES, + .formats = PCM1792A_FORMATS, }, + .capture = { + .channels_min = 0, + .channels_max = 0, + }, + .ops = &pcm1792a_dai_ops, +}; + +#ifdef CONFIG_OF +static const struct of_device_id pcm1792a_of_match[] = { + { .compatible = "ti,pcm1792a", }, + { } +}; +MODULE_DEVICE_TABLE(of, pcm1792a_of_match); +#endif + +static const struct regmap_config pcm1792a_regmap = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 24, + .reg_defaults = pcm1792a_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(pcm1792a_reg_defaults), + .writeable_reg = pcm1792a_writeable_reg, + .readable_reg = pcm1792a_accessible_reg, +}; + +static struct snd_soc_codec_driver soc_codec_dev_pcm1792a = { + .controls = pcm1792a_controls, + .num_controls = ARRAY_SIZE(pcm1792a_controls), +}; + +static int pcm1792a_spi_probe(struct spi_device *spi) +{ + struct pcm1792a_private *pcm1792a; + int ret; + + pcm1792a = devm_kzalloc(&spi->dev, sizeof(struct pcm1792a_private), + GFP_KERNEL); + if (!pcm1792a) + return -ENOMEM; + + spi_set_drvdata(spi, pcm1792a); + + pcm1792a->regmap = devm_regmap_init_spi(spi, &pcm1792a_regmap); + if (IS_ERR(pcm1792a->regmap)) { + ret = PTR_ERR(pcm1792a->regmap); + dev_err(&spi->dev, "Failed to register regmap: %d\n", ret); + return ret; + } + + return snd_soc_register_codec(&spi->dev, + &soc_codec_dev_pcm1792a, &pcm1792a_dai, 1); +} + +static int pcm1792a_spi_remove(struct spi_device *spi) +{ + snd_soc_unregister_codec(&spi->dev); + return 0; +} + +static const struct spi_device_id pcm1792a_spi_ids[] = { + { "pcm1792a", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(spi, pcm1792a_spi_ids); + +static struct spi_driver pcm1792a_codec_driver = { + .driver = { + .name = "pcm1792a", + .owner = THIS_MODULE, + .of_match_table = pcm1792a_of_match, + }, + .id_table = pcm1792a_spi_ids, + .probe = pcm1792a_spi_probe, + .remove = pcm1792a_spi_remove, +}; + +module_spi_driver(pcm1792a_codec_driver); + +MODULE_DESCRIPTION("ASoC PCM1792A driver"); +MODULE_AUTHOR("Michael Trimarchi "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/pcm1792a.h b/sound/soc/codecs/pcm1792a.h new file mode 100644 index 0000000..7a83d1f --- /dev/null +++ b/sound/soc/codecs/pcm1792a.h @@ -0,0 +1,26 @@ +/* + * definitions for PCM1792A + * + * Copyright 2013 Amarula Solutions + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __PCM1792A_H__ +#define __PCM1792A_H__ + +#define PCM1792A_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_8000_48000 | \ + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000) + +#define PCM1792A_FORMATS (SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S16_LE) + +#endif -- cgit v0.10.2 From db43b16fa0e913582b63c971848e08151d50d952 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 4 Aug 2013 20:26:03 +0100 Subject: ASoC: kirkwood: provide KIRKWOOD_PLAYCTL_ENABLE_MASK Provide a helper macro which includes the sum of all enable bits in the playback control register. This simplifies the code a little. Signed-off-by: Russell King Signed-off-by: Mark Brown diff --git a/sound/soc/kirkwood/kirkwood-i2s.c b/sound/soc/kirkwood/kirkwood-i2s.c index 0109b1e..ad1c789 100644 --- a/sound/soc/kirkwood/kirkwood-i2s.c +++ b/sound/soc/kirkwood/kirkwood-i2s.c @@ -198,8 +198,7 @@ static int kirkwood_i2s_hw_params(struct snd_pcm_substream *substream, ctl_play |= KIRKWOOD_PLAYCTL_MONO_OFF; priv->ctl_play &= ~(KIRKWOOD_PLAYCTL_MONO_MASK | - KIRKWOOD_PLAYCTL_I2S_EN | - KIRKWOOD_PLAYCTL_SPDIF_EN | + KIRKWOOD_PLAYCTL_ENABLE_MASK | KIRKWOOD_PLAYCTL_SIZE_MASK); priv->ctl_play |= ctl_play; } else { @@ -243,8 +242,7 @@ static int kirkwood_i2s_play_trigger(struct snd_pcm_substream *substream, case SNDRV_PCM_TRIGGER_START: /* configure */ ctl = priv->ctl_play; - value = ctl & ~(KIRKWOOD_PLAYCTL_I2S_EN | - KIRKWOOD_PLAYCTL_SPDIF_EN); + value = ctl & ~KIRKWOOD_PLAYCTL_ENABLE_MASK; writel(value, priv->io + KIRKWOOD_PLAYCTL); /* enable interrupts */ @@ -266,7 +264,7 @@ static int kirkwood_i2s_play_trigger(struct snd_pcm_substream *substream, writel(value, priv->io + KIRKWOOD_INT_MASK); /* disable all playbacks */ - ctl &= ~(KIRKWOOD_PLAYCTL_I2S_EN | KIRKWOOD_PLAYCTL_SPDIF_EN); + ctl &= ~KIRKWOOD_PLAYCTL_ENABLE_MASK; writel(ctl, priv->io + KIRKWOOD_PLAYCTL); break; @@ -386,7 +384,7 @@ static int kirkwood_i2s_probe(struct snd_soc_dai *dai) /* disable playback/record */ value = readl(priv->io + KIRKWOOD_PLAYCTL); - value &= ~(KIRKWOOD_PLAYCTL_I2S_EN|KIRKWOOD_PLAYCTL_SPDIF_EN); + value &= ~KIRKWOOD_PLAYCTL_ENABLE_MASK; writel(value, priv->io + KIRKWOOD_PLAYCTL); value = readl(priv->io + KIRKWOOD_RECCTL); diff --git a/sound/soc/kirkwood/kirkwood.h b/sound/soc/kirkwood/kirkwood.h index 10a3aaa..9a50607 100644 --- a/sound/soc/kirkwood/kirkwood.h +++ b/sound/soc/kirkwood/kirkwood.h @@ -54,7 +54,7 @@ #define KIRKWOOD_PLAYCTL_MONO_OFF (0<<5) #define KIRKWOOD_PLAYCTL_I2S_MUTE (1<<7) #define KIRKWOOD_PLAYCTL_SPDIF_EN (1<<4) -#define KIRKWOOD_PLAYCTL_I2S_EN (1<<3) +#define KIRKWOOD_PLAYCTL_I2S_EN (1<<3) #define KIRKWOOD_PLAYCTL_SIZE_MASK (7<<0) #define KIRKWOOD_PLAYCTL_SIZE_16 (7<<0) #define KIRKWOOD_PLAYCTL_SIZE_16_C (3<<0) @@ -62,6 +62,9 @@ #define KIRKWOOD_PLAYCTL_SIZE_24 (1<<0) #define KIRKWOOD_PLAYCTL_SIZE_32 (0<<0) +#define KIRKWOOD_PLAYCTL_ENABLE_MASK (KIRKWOOD_PLAYCTL_SPDIF_EN | \ + KIRKWOOD_PLAYCTL_I2S_EN) + #define KIRKWOOD_PLAY_BUF_ADDR 0x1104 #define KIRKWOOD_PLAY_BUF_SIZE 0x1108 #define KIRKWOOD_PLAY_BYTE_COUNT 0x110C -- cgit v0.10.2 From 64ddf1f89cd7a483e1204320395023774234b49a Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 4 Aug 2013 20:27:03 +0100 Subject: ASoC: kirkwood: combine kirkwood-i2s and kirkwood-dma drivers These really should be a single driver because they're fully integrated in hardware. Make them so. Signed-off-by: Russell King Signed-off-by: Mark Brown diff --git a/arch/arm/mach-dove/common.c b/arch/arm/mach-dove/common.c index 00247c7..304f069 100644 --- a/arch/arm/mach-dove/common.c +++ b/arch/arm/mach-dove/common.c @@ -108,8 +108,8 @@ static void __init dove_clk_init(void) orion_clkdev_add(NULL, "sdhci-dove.1", sdio1); orion_clkdev_add(NULL, "orion_nand", nand); orion_clkdev_add(NULL, "cafe1000-ccic.0", camera); - orion_clkdev_add(NULL, "kirkwood-i2s.0", i2s0); - orion_clkdev_add(NULL, "kirkwood-i2s.1", i2s1); + orion_clkdev_add(NULL, "mvebu-audio.0", i2s0); + orion_clkdev_add(NULL, "mvebu-audio.1", i2s1); orion_clkdev_add(NULL, "mv_crypto", crypto); orion_clkdev_add(NULL, "dove-ac97", ac97); orion_clkdev_add(NULL, "dove-pdma", pdma); diff --git a/arch/arm/mach-kirkwood/common.c b/arch/arm/mach-kirkwood/common.c index e9238b5..1663de0 100644 --- a/arch/arm/mach-kirkwood/common.c +++ b/arch/arm/mach-kirkwood/common.c @@ -264,7 +264,7 @@ void __init kirkwood_clk_init(void) orion_clkdev_add(NULL, MV_XOR_NAME ".1", xor1); orion_clkdev_add("0", "pcie", pex0); orion_clkdev_add("1", "pcie", pex1); - orion_clkdev_add(NULL, "kirkwood-i2s", audio); + orion_clkdev_add(NULL, "mvebu-audio", audio); orion_clkdev_add(NULL, MV64XXX_I2C_CTLR_NAME ".0", runit); orion_clkdev_add(NULL, MV64XXX_I2C_CTLR_NAME ".1", runit); @@ -560,7 +560,7 @@ void __init kirkwood_timer_init(void) /***************************************************************************** * Audio ****************************************************************************/ -static struct resource kirkwood_i2s_resources[] = { +static struct resource kirkwood_audio_resources[] = { [0] = { .start = AUDIO_PHYS_BASE, .end = AUDIO_PHYS_BASE + SZ_16K - 1, @@ -573,29 +573,23 @@ static struct resource kirkwood_i2s_resources[] = { }, }; -static struct kirkwood_asoc_platform_data kirkwood_i2s_data = { +static struct kirkwood_asoc_platform_data kirkwood_audio_data = { .burst = 128, }; -static struct platform_device kirkwood_i2s_device = { - .name = "kirkwood-i2s", +static struct platform_device kirkwood_audio_device = { + .name = "mvebu-audio", .id = -1, - .num_resources = ARRAY_SIZE(kirkwood_i2s_resources), - .resource = kirkwood_i2s_resources, + .num_resources = ARRAY_SIZE(kirkwood_audio_resources), + .resource = kirkwood_audio_resources, .dev = { - .platform_data = &kirkwood_i2s_data, + .platform_data = &kirkwood_audio_data, }, }; -static struct platform_device kirkwood_pcm_device = { - .name = "kirkwood-pcm-audio", - .id = -1, -}; - void __init kirkwood_audio_init(void) { - platform_device_register(&kirkwood_i2s_device); - platform_device_register(&kirkwood_pcm_device); + platform_device_register(&kirkwood_audio_device); } /***************************************************************************** diff --git a/sound/soc/kirkwood/Kconfig b/sound/soc/kirkwood/Kconfig index 59085ad..9e1970c 100644 --- a/sound/soc/kirkwood/Kconfig +++ b/sound/soc/kirkwood/Kconfig @@ -6,14 +6,10 @@ config SND_KIRKWOOD_SOC the Kirkwood I2S interface. You will also need to select the audio interfaces to support below. -config SND_KIRKWOOD_SOC_I2S - tristate - config SND_KIRKWOOD_SOC_OPENRD tristate "SoC Audio support for Kirkwood Openrd Client" depends on SND_KIRKWOOD_SOC && (MACH_OPENRD_CLIENT || MACH_OPENRD_ULTIMATE || COMPILE_TEST) depends on I2C - select SND_KIRKWOOD_SOC_I2S select SND_SOC_CS42L51 help Say Y if you want to add support for SoC audio on @@ -22,7 +18,6 @@ config SND_KIRKWOOD_SOC_OPENRD config SND_KIRKWOOD_SOC_T5325 tristate "SoC Audio support for HP t5325" depends on SND_KIRKWOOD_SOC && (MACH_T5325 || COMPILE_TEST) && I2C - select SND_KIRKWOOD_SOC_I2S select SND_SOC_ALC5623 help Say Y if you want to add support for SoC audio on diff --git a/sound/soc/kirkwood/Makefile b/sound/soc/kirkwood/Makefile index 3e62ae9..9e78138 100644 --- a/sound/soc/kirkwood/Makefile +++ b/sound/soc/kirkwood/Makefile @@ -1,8 +1,6 @@ -snd-soc-kirkwood-objs := kirkwood-dma.o -snd-soc-kirkwood-i2s-objs := kirkwood-i2s.o +snd-soc-kirkwood-objs := kirkwood-dma.o kirkwood-i2s.o obj-$(CONFIG_SND_KIRKWOOD_SOC) += snd-soc-kirkwood.o -obj-$(CONFIG_SND_KIRKWOOD_SOC_I2S) += snd-soc-kirkwood-i2s.o snd-soc-openrd-objs := kirkwood-openrd.o snd-soc-t5325-objs := kirkwood-t5325.o diff --git a/sound/soc/kirkwood/kirkwood-dma.c b/sound/soc/kirkwood/kirkwood-dma.c index ba50dd1..01622f6 100644 --- a/sound/soc/kirkwood/kirkwood-dma.c +++ b/sound/soc/kirkwood/kirkwood-dma.c @@ -334,36 +334,8 @@ static void kirkwood_dma_free_dma_buffers(struct snd_pcm *pcm) } } -static struct snd_soc_platform_driver kirkwood_soc_platform = { +struct snd_soc_platform_driver kirkwood_soc_platform = { .ops = &kirkwood_dma_ops, .pcm_new = kirkwood_dma_new, .pcm_free = kirkwood_dma_free_dma_buffers, }; - -static int kirkwood_soc_platform_probe(struct platform_device *pdev) -{ - return snd_soc_register_platform(&pdev->dev, &kirkwood_soc_platform); -} - -static int kirkwood_soc_platform_remove(struct platform_device *pdev) -{ - snd_soc_unregister_platform(&pdev->dev); - return 0; -} - -static struct platform_driver kirkwood_pcm_driver = { - .driver = { - .name = "kirkwood-pcm-audio", - .owner = THIS_MODULE, - }, - - .probe = kirkwood_soc_platform_probe, - .remove = kirkwood_soc_platform_remove, -}; - -module_platform_driver(kirkwood_pcm_driver); - -MODULE_AUTHOR("Arnaud Patard "); -MODULE_DESCRIPTION("Marvell Kirkwood Audio DMA module"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:kirkwood-pcm-audio"); diff --git a/sound/soc/kirkwood/kirkwood-i2s.c b/sound/soc/kirkwood/kirkwood-i2s.c index ad1c789..e5f3f7a 100644 --- a/sound/soc/kirkwood/kirkwood-i2s.c +++ b/sound/soc/kirkwood/kirkwood-i2s.c @@ -24,7 +24,7 @@ #include #include "kirkwood.h" -#define DRV_NAME "kirkwood-i2s" +#define DRV_NAME "mvebu-audio" #define KIRKWOOD_I2S_FORMATS \ (SNDRV_PCM_FMTBIT_S16_LE | \ @@ -517,10 +517,20 @@ static int kirkwood_i2s_dev_probe(struct platform_device *pdev) err = snd_soc_register_component(&pdev->dev, &kirkwood_i2s_component, soc_dai, 1); - if (!err) - return 0; - dev_err(&pdev->dev, "snd_soc_register_component failed\n"); + if (err) { + dev_err(&pdev->dev, "snd_soc_register_component failed\n"); + goto err_component; + } + err = snd_soc_register_platform(&pdev->dev, &kirkwood_soc_platform); + if (err) { + dev_err(&pdev->dev, "snd_soc_register_platform failed\n"); + goto err_platform; + } + return 0; + err_platform: + snd_soc_unregister_component(&pdev->dev); + err_component: if (!IS_ERR(priv->extclk)) clk_disable_unprepare(priv->extclk); clk_disable_unprepare(priv->clk); @@ -532,6 +542,7 @@ static int kirkwood_i2s_dev_remove(struct platform_device *pdev) { struct kirkwood_dma_data *priv = dev_get_drvdata(&pdev->dev); + snd_soc_unregister_platform(&pdev->dev); snd_soc_unregister_component(&pdev->dev); if (!IS_ERR(priv->extclk)) @@ -556,4 +567,4 @@ module_platform_driver(kirkwood_i2s_driver); MODULE_AUTHOR("Arnaud Patard, "); MODULE_DESCRIPTION("Kirkwood I2S SoC Interface"); MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:kirkwood-i2s"); +MODULE_ALIAS("platform:mvebu-audio"); diff --git a/sound/soc/kirkwood/kirkwood-openrd.c b/sound/soc/kirkwood/kirkwood-openrd.c index addbebc..025be0e 100644 --- a/sound/soc/kirkwood/kirkwood-openrd.c +++ b/sound/soc/kirkwood/kirkwood-openrd.c @@ -52,8 +52,8 @@ static struct snd_soc_dai_link openrd_client_dai[] = { { .name = "CS42L51", .stream_name = "CS42L51 HiFi", - .cpu_dai_name = "kirkwood-i2s", - .platform_name = "kirkwood-pcm-audio", + .cpu_dai_name = "mvebu-audio", + .platform_name = "mvebu-audio", .codec_dai_name = "cs42l51-hifi", .codec_name = "cs42l51-codec.0-004a", .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS, diff --git a/sound/soc/kirkwood/kirkwood-t5325.c b/sound/soc/kirkwood/kirkwood-t5325.c index 4f4cb56..27545b0 100644 --- a/sound/soc/kirkwood/kirkwood-t5325.c +++ b/sound/soc/kirkwood/kirkwood-t5325.c @@ -68,8 +68,8 @@ static struct snd_soc_dai_link t5325_dai[] = { { .name = "ALC5621", .stream_name = "ALC5621 HiFi", - .cpu_dai_name = "kirkwood-i2s", - .platform_name = "kirkwood-pcm-audio", + .cpu_dai_name = "mvebu-audio", + .platform_name = "mvebu-audio", .codec_dai_name = "alc5621-hifi", .codec_name = "alc562x-codec.0-001a", .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS, diff --git a/sound/soc/kirkwood/kirkwood.h b/sound/soc/kirkwood/kirkwood.h index 9a50607..1d13dee 100644 --- a/sound/soc/kirkwood/kirkwood.h +++ b/sound/soc/kirkwood/kirkwood.h @@ -138,4 +138,6 @@ struct kirkwood_dma_data { int burst; }; +extern struct snd_soc_platform_driver kirkwood_soc_platform; + #endif -- cgit v0.10.2 From e4065f3ff122e35cfc760d9a712564f3d9ef3a49 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 4 Aug 2013 20:28:04 +0100 Subject: ASoC: kirkwood: move calculation of max buffer size to kirkwood.h Signed-off-by: Russell King Signed-off-by: Mark Brown diff --git a/sound/soc/kirkwood/kirkwood-dma.c b/sound/soc/kirkwood/kirkwood-dma.c index 01622f6..b238434 100644 --- a/sound/soc/kirkwood/kirkwood-dma.c +++ b/sound/soc/kirkwood/kirkwood-dma.c @@ -51,7 +51,7 @@ static struct snd_pcm_hardware kirkwood_dma_snd_hw = { .rate_max = 384000, .channels_min = 1, .channels_max = 8, - .buffer_bytes_max = KIRKWOOD_SND_MAX_PERIOD_BYTES * KIRKWOOD_SND_MAX_PERIODS, + .buffer_bytes_max = KIRKWOOD_SND_MAX_BUFFER_BYTES, .period_bytes_min = KIRKWOOD_SND_MIN_PERIOD_BYTES, .period_bytes_max = KIRKWOOD_SND_MAX_PERIOD_BYTES, .periods_min = KIRKWOOD_SND_MIN_PERIODS, diff --git a/sound/soc/kirkwood/kirkwood.h b/sound/soc/kirkwood/kirkwood.h index 1d13dee..f8e1ccc 100644 --- a/sound/soc/kirkwood/kirkwood.h +++ b/sound/soc/kirkwood/kirkwood.h @@ -125,6 +125,8 @@ #define KIRKWOOD_SND_MAX_PERIODS 16 #define KIRKWOOD_SND_MIN_PERIOD_BYTES 0x4000 #define KIRKWOOD_SND_MAX_PERIOD_BYTES 0x4000 +#define KIRKWOOD_SND_MAX_BUFFER_BYTES (KIRKWOOD_SND_MAX_PERIOD_BYTES \ + * KIRKWOOD_SND_MAX_PERIODS) struct kirkwood_dma_data { void __iomem *io; -- cgit v0.10.2 From 55af2d23c6c9caf7da6c9a55bbea83dccbc1af2b Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 5 Aug 2013 18:20:53 +0100 Subject: ASoC: pcm1792a: Fix build with !OF Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/pcm1792a.c b/sound/soc/codecs/pcm1792a.c index 3f83bf9..72cf835 100644 --- a/sound/soc/codecs/pcm1792a.c +++ b/sound/soc/codecs/pcm1792a.c @@ -169,13 +169,11 @@ static struct snd_soc_dai_driver pcm1792a_dai = { .ops = &pcm1792a_dai_ops, }; -#ifdef CONFIG_OF static const struct of_device_id pcm1792a_of_match[] = { { .compatible = "ti,pcm1792a", }, { } }; MODULE_DEVICE_TABLE(of, pcm1792a_of_match); -#endif static const struct regmap_config pcm1792a_regmap = { .reg_bits = 8, @@ -231,7 +229,7 @@ static struct spi_driver pcm1792a_codec_driver = { .driver = { .name = "pcm1792a", .owner = THIS_MODULE, - .of_match_table = pcm1792a_of_match, + .of_match_table = of_match_ptr(pcm1792a_of_match), }, .id_table = pcm1792a_spi_ids, .probe = pcm1792a_spi_probe, -- cgit v0.10.2 From d66a5b9c82f2a2a6d424a7ccad51c52f150fa181 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lothar=20Wa=C3=9Fmann?= Date: Fri, 2 Aug 2013 10:30:15 +0200 Subject: ASoC: mxs: add some error messages to help identifying problems MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lothar Waßmann Signed-off-by: Mark Brown diff --git a/sound/soc/mxs/mxs-sgtl5000.c b/sound/soc/mxs/mxs-sgtl5000.c index b2e372d..ce084eb 100644 --- a/sound/soc/mxs/mxs-sgtl5000.c +++ b/sound/soc/mxs/mxs-sgtl5000.c @@ -50,18 +50,27 @@ static int mxs_sgtl5000_hw_params(struct snd_pcm_substream *substream, } /* Sgtl5000 sysclk should be >= 8MHz and <= 27M */ - if (mclk < 8000000 || mclk > 27000000) + if (mclk < 8000000 || mclk > 27000000) { + dev_err(codec_dai->dev, "Invalid mclk frequency: %u.%03uMHz\n", + mclk / 1000000, mclk / 1000 % 1000); return -EINVAL; + } /* Set SGTL5000's SYSCLK (provided by SAIF MCLK) */ ret = snd_soc_dai_set_sysclk(codec_dai, SGTL5000_SYSCLK, mclk, 0); - if (ret) + if (ret) { + dev_err(codec_dai->dev, "Failed to set sysclk to %u.%03uMHz\n", + mclk / 1000000, mclk / 1000 % 1000); return ret; + } /* The SAIF MCLK should be the same as SGTL5000_SYSCLK */ ret = snd_soc_dai_set_sysclk(cpu_dai, MXS_SAIF_MCLK, mclk, 0); - if (ret) + if (ret) { + dev_err(cpu_dai->dev, "Failed to set sysclk to %u.%03uMHz\n", + mclk / 1000000, mclk / 1000 % 1000); return ret; + } /* set codec to slave mode */ dai_format = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | @@ -69,13 +78,19 @@ static int mxs_sgtl5000_hw_params(struct snd_pcm_substream *substream, /* set codec DAI configuration */ ret = snd_soc_dai_set_fmt(codec_dai, dai_format); - if (ret) + if (ret) { + dev_err(codec_dai->dev, "Failed to set dai format to %08x\n", + dai_format); return ret; + } /* set cpu DAI configuration */ ret = snd_soc_dai_set_fmt(cpu_dai, dai_format); - if (ret) + if (ret) { + dev_err(cpu_dai->dev, "Failed to set dai format to %08x\n", + dai_format); return ret; + } return 0; } @@ -153,8 +168,10 @@ static int mxs_sgtl5000_probe(struct platform_device *pdev) * should be >= 8MHz and <= 27M. */ ret = mxs_saif_get_mclk(0, 44100 * 256, 44100); - if (ret) + if (ret) { + dev_err(&pdev->dev, "failed to get mclk\n"); return ret; + } card->dev = &pdev->dev; platform_set_drvdata(pdev, card); -- cgit v0.10.2 From d833cdb10cb689ffcbebbf4bae5227072c53f88a Mon Sep 17 00:00:00 2001 From: Eldad Zack Date: Sat, 3 Aug 2013 10:50:15 +0200 Subject: ALSA: usb-audio: remove disabled debug code in set_format Code block does not compile when enabled. Signed-off-by: Eldad Zack Signed-off-by: Takashi Iwai diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index 15b151e..3d3e8d1 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -486,15 +486,6 @@ add_sync_ep: snd_usb_set_format_quirk(subs, fmt); -#if 0 - printk(KERN_DEBUG - "setting done: format = %d, rate = %d..%d, channels = %d\n", - fmt->format, fmt->rate_min, fmt->rate_max, fmt->channels); - printk(KERN_DEBUG - " datapipe = 0x%0x, syncpipe = 0x%0x\n", - subs->datapipe, subs->syncpipe); -#endif - return 0; } -- cgit v0.10.2 From d133f2c22e9cb7b6afd170437cf0ef1e8a1571b6 Mon Sep 17 00:00:00 2001 From: Eldad Zack Date: Sat, 3 Aug 2013 10:50:16 +0200 Subject: ALSA: usb-audio: remove assignment from if condition Following general kernel style. Signed-off-by: Eldad Zack Signed-off-by: Takashi Iwai diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index 3d3e8d1..be5c7c2 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -479,7 +479,8 @@ add_sync_ep: subs->data_endpoint->sync_master = subs->sync_endpoint; } - if ((err = snd_usb_init_pitch(subs->stream->chip, fmt->iface, alts, fmt)) < 0) + err = snd_usb_init_pitch(subs->stream->chip, fmt->iface, alts, fmt); + if (err < 0) return err; subs->cur_audiofmt = fmt; -- cgit v0.10.2 From 71bb64c56d787a221752b1de034fe8c07c737f5c Mon Sep 17 00:00:00 2001 From: Eldad Zack Date: Sat, 3 Aug 2013 10:50:17 +0200 Subject: ALSA: usb-audio: separate sync endpoint setting from set_format Setting the sync endpoint currently takes up about half of set_format(). Move it to a dedicated function. No functional change. Signed-off-by: Eldad Zack Signed-off-by: Takashi Iwai diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index be5c7c2..e24ce7d 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -327,64 +327,17 @@ static int search_roland_implicit_fb(struct usb_device *dev, int ifnum, return 0; } -/* - * find a matching format and set up the interface - */ -static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt) + +static int set_sync_endpoint(struct snd_usb_substream *subs, + struct audioformat *fmt, + struct usb_device *dev, + struct usb_host_interface *alts, + struct usb_interface_descriptor *altsd) { - struct usb_device *dev = subs->dev; - struct usb_host_interface *alts; - struct usb_interface_descriptor *altsd; struct usb_interface *iface; - unsigned int ep, attr; int is_playback = subs->direction == SNDRV_PCM_STREAM_PLAYBACK; - int err, implicit_fb = 0; - - iface = usb_ifnum_to_if(dev, fmt->iface); - if (WARN_ON(!iface)) - return -EINVAL; - alts = &iface->altsetting[fmt->altset_idx]; - altsd = get_iface_desc(alts); - if (WARN_ON(altsd->bAlternateSetting != fmt->altsetting)) - return -EINVAL; - - if (fmt == subs->cur_audiofmt) - return 0; - - /* close the old interface */ - if (subs->interface >= 0 && subs->interface != fmt->iface) { - err = usb_set_interface(subs->dev, subs->interface, 0); - if (err < 0) { - snd_printk(KERN_ERR "%d:%d:%d: return to setting 0 failed (%d)\n", - dev->devnum, fmt->iface, fmt->altsetting, err); - return -EIO; - } - subs->interface = -1; - subs->altset_idx = 0; - } - - /* set interface */ - if (subs->interface != fmt->iface || - subs->altset_idx != fmt->altset_idx) { - err = usb_set_interface(dev, fmt->iface, fmt->altsetting); - if (err < 0) { - snd_printk(KERN_ERR "%d:%d:%d: usb_set_interface failed (%d)\n", - dev->devnum, fmt->iface, fmt->altsetting, err); - return -EIO; - } - snd_printdd(KERN_INFO "setting usb interface %d:%d\n", - fmt->iface, fmt->altsetting); - subs->interface = fmt->iface; - subs->altset_idx = fmt->altset_idx; - - snd_usb_set_interface_quirk(dev); - } - - subs->data_endpoint = snd_usb_add_endpoint(subs->stream->chip, - alts, fmt->endpoint, subs->direction, - SND_USB_ENDPOINT_TYPE_DATA); - if (!subs->data_endpoint) - return -EINVAL; + unsigned int ep, attr; + int implicit_fb = 0; /* we need a sync pipe in async OUT or adaptive IN mode */ /* check the number of EP, since some devices have broken @@ -479,6 +432,71 @@ add_sync_ep: subs->data_endpoint->sync_master = subs->sync_endpoint; } + return 0; +} + +/* + * find a matching format and set up the interface + */ +static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt) +{ + struct usb_device *dev = subs->dev; + struct usb_host_interface *alts; + struct usb_interface_descriptor *altsd; + struct usb_interface *iface; + int err; + + iface = usb_ifnum_to_if(dev, fmt->iface); + if (WARN_ON(!iface)) + return -EINVAL; + alts = &iface->altsetting[fmt->altset_idx]; + altsd = get_iface_desc(alts); + if (WARN_ON(altsd->bAlternateSetting != fmt->altsetting)) + return -EINVAL; + + if (fmt == subs->cur_audiofmt) + return 0; + + /* close the old interface */ + if (subs->interface >= 0 && subs->interface != fmt->iface) { + err = usb_set_interface(subs->dev, subs->interface, 0); + if (err < 0) { + snd_printk(KERN_ERR "%d:%d:%d: return to setting 0 failed (%d)\n", + dev->devnum, fmt->iface, fmt->altsetting, err); + return -EIO; + } + subs->interface = -1; + subs->altset_idx = 0; + } + + /* set interface */ + if (subs->interface != fmt->iface || + subs->altset_idx != fmt->altset_idx) { + err = usb_set_interface(dev, fmt->iface, fmt->altsetting); + if (err < 0) { + snd_printk(KERN_ERR "%d:%d:%d: usb_set_interface failed (%d)\n", + dev->devnum, fmt->iface, fmt->altsetting, err); + return -EIO; + } + snd_printdd(KERN_INFO "setting usb interface %d:%d\n", + fmt->iface, fmt->altsetting); + subs->interface = fmt->iface; + subs->altset_idx = fmt->altset_idx; + + snd_usb_set_interface_quirk(dev); + } + + subs->data_endpoint = snd_usb_add_endpoint(subs->stream->chip, + alts, fmt->endpoint, subs->direction, + SND_USB_ENDPOINT_TYPE_DATA); + + if (!subs->data_endpoint) + return -EINVAL; + + err = set_sync_endpoint(subs, fmt, dev, alts, altsd); + if (err < 0) + return err; + err = snd_usb_init_pitch(subs->stream->chip, fmt->iface, alts, fmt); if (err < 0) return err; -- cgit v0.10.2 From a60945fd08e45fceca9e3525d70e080f7ad60a4e Mon Sep 17 00:00:00 2001 From: Eldad Zack Date: Sat, 3 Aug 2013 10:50:18 +0200 Subject: ALSA: usb-audio: move implicit fb quirks to separate function Separate setting implicit feedback quirks from setting a sync endpoint (which may also be explicit feedback or async). Signed-off-by: Eldad Zack Signed-off-by: Takashi Iwai diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index e24ce7d..0016f28 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -327,24 +327,16 @@ static int search_roland_implicit_fb(struct usb_device *dev, int ifnum, return 0; } - -static int set_sync_endpoint(struct snd_usb_substream *subs, - struct audioformat *fmt, - struct usb_device *dev, - struct usb_host_interface *alts, - struct usb_interface_descriptor *altsd) +static int set_sync_ep_implicit_fb_quirk(struct snd_usb_substream *subs, + struct usb_device *dev, + struct usb_interface_descriptor *altsd, + unsigned int attr) { + struct usb_host_interface *alts; struct usb_interface *iface; int is_playback = subs->direction == SNDRV_PCM_STREAM_PLAYBACK; - unsigned int ep, attr; int implicit_fb = 0; - - /* we need a sync pipe in async OUT or adaptive IN mode */ - /* check the number of EP, since some devices have broken - * descriptors which fool us. if it has only one EP, - * assume it as adaptive-out or sync-in. - */ - attr = fmt->ep_attr & USB_ENDPOINT_SYNCTYPE; + unsigned int ep; switch (subs->stream->chip->usb_id) { case USB_ID(0x0763, 0x2030): /* M-Audio Fast Track C400 */ @@ -388,6 +380,45 @@ static int set_sync_endpoint(struct snd_usb_substream *subs, goto add_sync_ep; } + /* No quirk */ + return 0; + +add_sync_ep: + subs->sync_endpoint = snd_usb_add_endpoint(subs->stream->chip, + alts, ep, !subs->direction, + implicit_fb ? + SND_USB_ENDPOINT_TYPE_DATA : + SND_USB_ENDPOINT_TYPE_SYNC); + if (!subs->sync_endpoint) + return -EINVAL; + + subs->data_endpoint->sync_master = subs->sync_endpoint; + + return 0; +} + +static int set_sync_endpoint(struct snd_usb_substream *subs, + struct audioformat *fmt, + struct usb_device *dev, + struct usb_host_interface *alts, + struct usb_interface_descriptor *altsd) +{ + int is_playback = subs->direction == SNDRV_PCM_STREAM_PLAYBACK; + unsigned int ep, attr; + int implicit_fb = 0; + int err; + + /* we need a sync pipe in async OUT or adaptive IN mode */ + /* check the number of EP, since some devices have broken + * descriptors which fool us. if it has only one EP, + * assume it as adaptive-out or sync-in. + */ + attr = fmt->ep_attr & USB_ENDPOINT_SYNCTYPE; + + err = set_sync_ep_implicit_fb_quirk(subs, dev, altsd, attr); + if (err < 0) + return err; + if (((is_playback && attr == USB_ENDPOINT_SYNC_ASYNC) || (!is_playback && attr == USB_ENDPOINT_SYNC_ADAPTIVE)) && altsd->bNumEndpoints >= 2) { @@ -420,7 +451,6 @@ static int set_sync_endpoint(struct snd_usb_substream *subs, implicit_fb = (get_endpoint(alts, 1)->bmAttributes & USB_ENDPOINT_USAGE_MASK) == USB_ENDPOINT_USAGE_IMPLICIT_FB; -add_sync_ep: subs->sync_endpoint = snd_usb_add_endpoint(subs->stream->chip, alts, ep, !subs->direction, implicit_fb ? -- cgit v0.10.2 From f34d0650133389c76e22e9f27e57b74ed9e2c042 Mon Sep 17 00:00:00 2001 From: Eldad Zack Date: Sat, 3 Aug 2013 10:50:19 +0200 Subject: ALSA: usb-audio: reverse condition logic in set_sync_endpoint Reverse logic on the conditions required to qualify for a sync endpoint and remove one level of indendation. Signed-off-by: Eldad Zack Signed-off-by: Takashi Iwai diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index 0016f28..c31dbdc 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -419,49 +419,52 @@ static int set_sync_endpoint(struct snd_usb_substream *subs, if (err < 0) return err; - if (((is_playback && attr == USB_ENDPOINT_SYNC_ASYNC) || - (!is_playback && attr == USB_ENDPOINT_SYNC_ADAPTIVE)) && - altsd->bNumEndpoints >= 2) { - /* check sync-pipe endpoint */ - /* ... and check descriptor size before accessing bSynchAddress - because there is a version of the SB Audigy 2 NX firmware lacking - the audio fields in the endpoint descriptors */ - if ((get_endpoint(alts, 1)->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_ISOC || - (get_endpoint(alts, 1)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE && - get_endpoint(alts, 1)->bSynchAddress != 0 && - !implicit_fb)) { - snd_printk(KERN_ERR "%d:%d:%d : invalid sync pipe. bmAttributes %02x, bLength %d, bSynchAddress %02x\n", - dev->devnum, fmt->iface, fmt->altsetting, - get_endpoint(alts, 1)->bmAttributes, - get_endpoint(alts, 1)->bLength, - get_endpoint(alts, 1)->bSynchAddress); - return -EINVAL; - } - ep = get_endpoint(alts, 1)->bEndpointAddress; - if (!implicit_fb && - get_endpoint(alts, 0)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE && - (( is_playback && ep != (unsigned int)(get_endpoint(alts, 0)->bSynchAddress | USB_DIR_IN)) || - (!is_playback && ep != (unsigned int)(get_endpoint(alts, 0)->bSynchAddress & ~USB_DIR_IN)))) { - snd_printk(KERN_ERR "%d:%d:%d : invalid sync pipe. is_playback %d, ep %02x, bSynchAddress %02x\n", - dev->devnum, fmt->iface, fmt->altsetting, - is_playback, ep, get_endpoint(alts, 0)->bSynchAddress); - return -EINVAL; - } - - implicit_fb = (get_endpoint(alts, 1)->bmAttributes & USB_ENDPOINT_USAGE_MASK) - == USB_ENDPOINT_USAGE_IMPLICIT_FB; + if (altsd->bNumEndpoints < 2) + return 0; - subs->sync_endpoint = snd_usb_add_endpoint(subs->stream->chip, - alts, ep, !subs->direction, - implicit_fb ? - SND_USB_ENDPOINT_TYPE_DATA : - SND_USB_ENDPOINT_TYPE_SYNC); - if (!subs->sync_endpoint) - return -EINVAL; + if ((is_playback && attr != USB_ENDPOINT_SYNC_ASYNC) || + (!is_playback && attr != USB_ENDPOINT_SYNC_ADAPTIVE)) + return 0; - subs->data_endpoint->sync_master = subs->sync_endpoint; + /* check sync-pipe endpoint */ + /* ... and check descriptor size before accessing bSynchAddress + because there is a version of the SB Audigy 2 NX firmware lacking + the audio fields in the endpoint descriptors */ + if ((get_endpoint(alts, 1)->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_ISOC || + (get_endpoint(alts, 1)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE && + get_endpoint(alts, 1)->bSynchAddress != 0 && + !implicit_fb)) { + snd_printk(KERN_ERR "%d:%d:%d : invalid sync pipe. bmAttributes %02x, bLength %d, bSynchAddress %02x\n", + dev->devnum, fmt->iface, fmt->altsetting, + get_endpoint(alts, 1)->bmAttributes, + get_endpoint(alts, 1)->bLength, + get_endpoint(alts, 1)->bSynchAddress); + return -EINVAL; + } + ep = get_endpoint(alts, 1)->bEndpointAddress; + if (!implicit_fb && + get_endpoint(alts, 0)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE && + ((is_playback && ep != (unsigned int)(get_endpoint(alts, 0)->bSynchAddress | USB_DIR_IN)) || + (!is_playback && ep != (unsigned int)(get_endpoint(alts, 0)->bSynchAddress & ~USB_DIR_IN)))) { + snd_printk(KERN_ERR "%d:%d:%d : invalid sync pipe. is_playback %d, ep %02x, bSynchAddress %02x\n", + dev->devnum, fmt->iface, fmt->altsetting, + is_playback, ep, get_endpoint(alts, 0)->bSynchAddress); + return -EINVAL; } + implicit_fb = (get_endpoint(alts, 1)->bmAttributes & USB_ENDPOINT_USAGE_MASK) + == USB_ENDPOINT_USAGE_IMPLICIT_FB; + + subs->sync_endpoint = snd_usb_add_endpoint(subs->stream->chip, + alts, ep, !subs->direction, + implicit_fb ? + SND_USB_ENDPOINT_TYPE_DATA : + SND_USB_ENDPOINT_TYPE_SYNC); + if (!subs->sync_endpoint) + return -EINVAL; + + subs->data_endpoint->sync_master = subs->sync_endpoint; + return 0; } -- cgit v0.10.2 From 95fec88332dbbe4344ffc1b564480402a89ee805 Mon Sep 17 00:00:00 2001 From: Eldad Zack Date: Sat, 3 Aug 2013 10:50:20 +0200 Subject: ALSA: usb-audio: do not initialize and check implicit_fb Since implicit_fb is not changed, !implicit_fb will always be true - it is set only after these checks. Similarly, there's also no need to set it at the top of the function. Change the type of implicit_fb to bool (more appropriate). Signed-off-by: Eldad Zack Signed-off-by: Takashi Iwai diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index c31dbdc..bb2e0f5 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -405,7 +405,7 @@ static int set_sync_endpoint(struct snd_usb_substream *subs, { int is_playback = subs->direction == SNDRV_PCM_STREAM_PLAYBACK; unsigned int ep, attr; - int implicit_fb = 0; + bool implicit_fb; int err; /* we need a sync pipe in async OUT or adaptive IN mode */ @@ -432,8 +432,7 @@ static int set_sync_endpoint(struct snd_usb_substream *subs, the audio fields in the endpoint descriptors */ if ((get_endpoint(alts, 1)->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_ISOC || (get_endpoint(alts, 1)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE && - get_endpoint(alts, 1)->bSynchAddress != 0 && - !implicit_fb)) { + get_endpoint(alts, 1)->bSynchAddress != 0)) { snd_printk(KERN_ERR "%d:%d:%d : invalid sync pipe. bmAttributes %02x, bLength %d, bSynchAddress %02x\n", dev->devnum, fmt->iface, fmt->altsetting, get_endpoint(alts, 1)->bmAttributes, @@ -442,8 +441,7 @@ static int set_sync_endpoint(struct snd_usb_substream *subs, return -EINVAL; } ep = get_endpoint(alts, 1)->bEndpointAddress; - if (!implicit_fb && - get_endpoint(alts, 0)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE && + if (get_endpoint(alts, 0)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE && ((is_playback && ep != (unsigned int)(get_endpoint(alts, 0)->bSynchAddress | USB_DIR_IN)) || (!is_playback && ep != (unsigned int)(get_endpoint(alts, 0)->bSynchAddress & ~USB_DIR_IN)))) { snd_printk(KERN_ERR "%d:%d:%d : invalid sync pipe. is_playback %d, ep %02x, bSynchAddress %02x\n", -- cgit v0.10.2 From 914273c714845e2f3363e962f6dff59626a79fa3 Mon Sep 17 00:00:00 2001 From: Eldad Zack Date: Sat, 3 Aug 2013 10:50:21 +0200 Subject: ALSA: usb-audio: remove is_playback from implicit feedback quirks An implicit feedback endpoint can only be a capture source. The consumer (sink) of the implicit feedback endpoint is therefore limited to playback EPs. Check if the target endpoint is a playback first and remove redundant checks. Signed-off-by: Eldad Zack Signed-off-by: Takashi Iwai diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index bb2e0f5..af30e08 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -334,41 +334,39 @@ static int set_sync_ep_implicit_fb_quirk(struct snd_usb_substream *subs, { struct usb_host_interface *alts; struct usb_interface *iface; - int is_playback = subs->direction == SNDRV_PCM_STREAM_PLAYBACK; int implicit_fb = 0; unsigned int ep; + /* Implicit feedback sync EPs consumers are always playback EPs */ + if (subs->direction != SNDRV_PCM_STREAM_PLAYBACK) + return 0; + switch (subs->stream->chip->usb_id) { case USB_ID(0x0763, 0x2030): /* M-Audio Fast Track C400 */ case USB_ID(0x0763, 0x2031): /* M-Audio Fast Track C600 */ - if (is_playback) { - implicit_fb = 1; - ep = 0x81; - iface = usb_ifnum_to_if(dev, 3); + implicit_fb = 1; + ep = 0x81; + iface = usb_ifnum_to_if(dev, 3); - if (!iface || iface->num_altsetting == 0) - return -EINVAL; + if (!iface || iface->num_altsetting == 0) + return -EINVAL; - alts = &iface->altsetting[1]; - goto add_sync_ep; - } + alts = &iface->altsetting[1]; + goto add_sync_ep; break; case USB_ID(0x0763, 0x2080): /* M-Audio FastTrack Ultra */ case USB_ID(0x0763, 0x2081): - if (is_playback) { - implicit_fb = 1; - ep = 0x81; - iface = usb_ifnum_to_if(dev, 2); + implicit_fb = 1; + ep = 0x81; + iface = usb_ifnum_to_if(dev, 2); - if (!iface || iface->num_altsetting == 0) - return -EINVAL; + if (!iface || iface->num_altsetting == 0) + return -EINVAL; - alts = &iface->altsetting[1]; - goto add_sync_ep; - } + alts = &iface->altsetting[1]; + goto add_sync_ep; } - if (is_playback && - attr == USB_ENDPOINT_SYNC_ASYNC && + if (attr == USB_ENDPOINT_SYNC_ASYNC && altsd->bInterfaceClass == USB_CLASS_VENDOR_SPEC && altsd->bInterfaceProtocol == 2 && altsd->bNumEndpoints == 1 && -- cgit v0.10.2 From 88abb8eff494d0be7819e744e74d62d5bc852905 Mon Sep 17 00:00:00 2001 From: Eldad Zack Date: Sat, 3 Aug 2013 10:51:14 +0200 Subject: ALSA: usb-audio: remove implicit_fb from quirk Since the quirks all apply to implicit feedback (the source endpoint is always a data endpoint), there's no need to set and check a flag for it. Signed-off-by: Eldad Zack Signed-off-by: Takashi Iwai diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index af30e08..b375d58 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -334,7 +334,6 @@ static int set_sync_ep_implicit_fb_quirk(struct snd_usb_substream *subs, { struct usb_host_interface *alts; struct usb_interface *iface; - int implicit_fb = 0; unsigned int ep; /* Implicit feedback sync EPs consumers are always playback EPs */ @@ -344,7 +343,6 @@ static int set_sync_ep_implicit_fb_quirk(struct snd_usb_substream *subs, switch (subs->stream->chip->usb_id) { case USB_ID(0x0763, 0x2030): /* M-Audio Fast Track C400 */ case USB_ID(0x0763, 0x2031): /* M-Audio Fast Track C600 */ - implicit_fb = 1; ep = 0x81; iface = usb_ifnum_to_if(dev, 3); @@ -356,7 +354,6 @@ static int set_sync_ep_implicit_fb_quirk(struct snd_usb_substream *subs, break; case USB_ID(0x0763, 0x2080): /* M-Audio FastTrack Ultra */ case USB_ID(0x0763, 0x2081): - implicit_fb = 1; ep = 0x81; iface = usb_ifnum_to_if(dev, 2); @@ -374,7 +371,6 @@ static int set_sync_ep_implicit_fb_quirk(struct snd_usb_substream *subs, search_roland_implicit_fb(dev, altsd->bInterfaceNumber + 1, altsd->bAlternateSetting, &alts, &ep) >= 0) { - implicit_fb = 1; goto add_sync_ep; } @@ -384,9 +380,7 @@ static int set_sync_ep_implicit_fb_quirk(struct snd_usb_substream *subs, add_sync_ep: subs->sync_endpoint = snd_usb_add_endpoint(subs->stream->chip, alts, ep, !subs->direction, - implicit_fb ? - SND_USB_ENDPOINT_TYPE_DATA : - SND_USB_ENDPOINT_TYPE_SYNC); + SND_USB_ENDPOINT_TYPE_DATA); if (!subs->sync_endpoint) return -EINVAL; -- cgit v0.10.2 From e7e58df8ef3c9edb09a240084b4e0523c12bcb71 Mon Sep 17 00:00:00 2001 From: Eldad Zack Date: Sat, 3 Aug 2013 10:51:15 +0200 Subject: ALSA: usb-audio: WARN_ON when alts is passed as NULL Prevent NULL dereference in snd_usb_add_endpoints(), when alts is passed as NULL. In this case, WARN (since this is a non-fatal bug) and return NULL ep. Call sites treat a NULL return value as an error. Signed-off-by: Eldad Zack Signed-off-by: Takashi Iwai diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index 7a444b5..92ea945 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -418,6 +418,9 @@ struct snd_usb_endpoint *snd_usb_add_endpoint(struct snd_usb_audio *chip, struct snd_usb_endpoint *ep; int is_playback = direction == SNDRV_PCM_STREAM_PLAYBACK; + if (WARN_ON(!alts)) + return NULL; + mutex_lock(&chip->mutex); list_for_each_entry(ep, &chip->ep_list, list) { -- cgit v0.10.2 From ed6a27723979cfffab62c450baba4f75ebcbda78 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 5 Aug 2013 23:34:17 +0100 Subject: ASoC: wm8994: Fix class W controls Commit 6e0650 (ASoC: wm8994: Use SOC_SINGLE_EXT() instead of open-coding it) went too far and converted a DAPM control to use SOC_SINGLE_EXT() which crashes. Revert that portion of the patch. Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index 24131a7..c99b6da 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -1433,7 +1433,7 @@ SOC_DAPM_SINGLE("AIF1.1 Switch", WM8994_DAC2_RIGHT_MIXER_ROUTING, #define WM8994_CLASS_W_SWITCH(xname, reg, shift, max, invert) \ SOC_SINGLE_EXT(xname, reg, shift, max, invert, \ - snd_soc_get_volsw, wm8994_put_class_w) + snd_soc_dapm_get_volsw, wm8994_put_class_w) static int wm8994_put_class_w(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) -- cgit v0.10.2 From f8f11795b96a3632edb25a8924c61bfb74581cb0 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 6 Aug 2013 13:39:29 +0200 Subject: ASoC: tlv320aic26: Fix keyclick feature The tlv320aic26 contains a embedded snd_soc_codec struct which is referenced in the keyclick code. That struct is never initialized though, replace the embedded struct with a pointer and use that in the keyclick code. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/tlv320aic26.c b/sound/soc/codecs/tlv320aic26.c index b1f6982..b192cd4 100644 --- a/sound/soc/codecs/tlv320aic26.c +++ b/sound/soc/codecs/tlv320aic26.c @@ -29,7 +29,7 @@ MODULE_LICENSE("GPL"); /* AIC26 driver private data */ struct aic26 { struct spi_device *spi; - struct snd_soc_codec codec; + struct snd_soc_codec *codec; int master; int datfm; int mclk; @@ -330,7 +330,7 @@ static ssize_t aic26_keyclick_show(struct device *dev, struct aic26 *aic26 = dev_get_drvdata(dev); int val, amp, freq, len; - val = aic26_reg_read_cache(&aic26->codec, AIC26_REG_AUDIO_CTRL2); + val = aic26_reg_read_cache(aic26->codec, AIC26_REG_AUDIO_CTRL2); amp = (val >> 12) & 0x7; freq = (125 << ((val >> 8) & 0x7)) >> 1; len = 2 * (1 + ((val >> 4) & 0xf)); @@ -346,9 +346,9 @@ static ssize_t aic26_keyclick_set(struct device *dev, struct aic26 *aic26 = dev_get_drvdata(dev); int val; - val = aic26_reg_read_cache(&aic26->codec, AIC26_REG_AUDIO_CTRL2); + val = aic26_reg_read_cache(aic26->codec, AIC26_REG_AUDIO_CTRL2); val |= 0x8000; - aic26_reg_write(&aic26->codec, AIC26_REG_AUDIO_CTRL2, val); + aic26_reg_write(aic26->codec, AIC26_REG_AUDIO_CTRL2, val); return count; } @@ -360,8 +360,11 @@ static DEVICE_ATTR(keyclick, 0644, aic26_keyclick_show, aic26_keyclick_set); */ static int aic26_probe(struct snd_soc_codec *codec) { + struct aic26 *aic26 = dev_get_drvdata(codec->dev); int ret, err, i, reg; + aic26->codec = codec; + dev_info(codec->dev, "Probing AIC26 SoC CODEC driver\n"); /* Reset the codec to power on defaults */ -- cgit v0.10.2 From 95ad868289a24dbc072412ce2fb0d40cb34c5794 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 6 Aug 2013 13:39:31 +0200 Subject: ASoC: mc13783: Remove embedded snd_soc_codec structs from private data structs It is unused and a leftover of the pre multi-component era. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/mc13783.c b/sound/soc/codecs/mc13783.c index 5402dfb..4d3c8fd 100644 --- a/sound/soc/codecs/mc13783.c +++ b/sound/soc/codecs/mc13783.c @@ -94,7 +94,6 @@ #define AUDIO_DAC_CFS_DLY_B (1 << 10) struct mc13783_priv { - struct snd_soc_codec codec; struct mc13xxx *mc13xxx; enum mc13783_ssi_port adc_ssi_port; -- cgit v0.10.2 From 0d59ff3a24ad099f741da5efd9e3e02bfd64496e Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 6 Aug 2013 13:39:30 +0200 Subject: ASoC: twl4030: Remove embedded snd_soc_codec structs from private data structs It is unused and a leftover of the pre multi-component era. Signed-off-by: Lars-Peter Clausen Acked-by: Peter Ujfalusi Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index 8e6e5b0..1e3884d 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c @@ -137,8 +137,6 @@ static const u8 twl4030_reg[TWL4030_CACHEREGNUM] = { /* codec private data */ struct twl4030_priv { - struct snd_soc_codec codec; - unsigned int codec_powered; /* reference counts of AIF/APLL users */ -- cgit v0.10.2 From c7f3843575eac1eea1fbda2f6b61d36627fa8f7c Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Tue, 6 Aug 2013 17:03:55 +0100 Subject: ASoC: wm5110: Correct input OSR bits for wm5110 The input OSR bits are specified differently for wm5110 than for current revs of wm5102. This patch corrects support for this on wm5110. Signed-off-by: Charles Keepax Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c index 8dc6881..779a0ee 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c @@ -553,6 +553,26 @@ const struct soc_enum arizona_ng_hold = 4, arizona_ng_hold_text); EXPORT_SYMBOL_GPL(arizona_ng_hold); +static const char * const arizona_in_dmic_osr_text[] = { + "1.536MHz", "3.072MHz", "6.144MHz", +}; + +const struct soc_enum arizona_in_dmic_osr[] = { + SOC_ENUM_SINGLE(ARIZONA_IN1L_CONTROL, ARIZONA_IN1_OSR_SHIFT, + ARRAY_SIZE(arizona_in_dmic_osr_text), + arizona_in_dmic_osr_text), + SOC_ENUM_SINGLE(ARIZONA_IN2L_CONTROL, ARIZONA_IN2_OSR_SHIFT, + ARRAY_SIZE(arizona_in_dmic_osr_text), + arizona_in_dmic_osr_text), + SOC_ENUM_SINGLE(ARIZONA_IN3L_CONTROL, ARIZONA_IN3_OSR_SHIFT, + ARRAY_SIZE(arizona_in_dmic_osr_text), + arizona_in_dmic_osr_text), + SOC_ENUM_SINGLE(ARIZONA_IN4L_CONTROL, ARIZONA_IN4_OSR_SHIFT, + ARRAY_SIZE(arizona_in_dmic_osr_text), + arizona_in_dmic_osr_text), +}; +EXPORT_SYMBOL_GPL(arizona_in_dmic_osr); + static void arizona_in_set_vu(struct snd_soc_codec *codec, int ena) { struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec); diff --git a/sound/soc/codecs/arizona.h b/sound/soc/codecs/arizona.h index fe1b794..b6b6d70 100644 --- a/sound/soc/codecs/arizona.h +++ b/sound/soc/codecs/arizona.h @@ -198,6 +198,7 @@ extern const struct soc_enum arizona_lhpf3_mode; extern const struct soc_enum arizona_lhpf4_mode; extern const struct soc_enum arizona_ng_hold; +extern const struct soc_enum arizona_in_dmic_osr[]; extern int arizona_in_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, diff --git a/sound/soc/codecs/wm5110.c b/sound/soc/codecs/wm5110.c index fc41037..77fd531 100644 --- a/sound/soc/codecs/wm5110.c +++ b/sound/soc/codecs/wm5110.c @@ -58,14 +58,10 @@ static DECLARE_TLV_DB_SCALE(ng_tlv, -10200, 600, 0); SOC_SINGLE(name " NG SPKDAT2R Switch", base, 11, 1, 0) static const struct snd_kcontrol_new wm5110_snd_controls[] = { -SOC_SINGLE("IN1 High Performance Switch", ARIZONA_IN1L_CONTROL, - ARIZONA_IN1_OSR_SHIFT, 1, 0), -SOC_SINGLE("IN2 High Performance Switch", ARIZONA_IN2L_CONTROL, - ARIZONA_IN2_OSR_SHIFT, 1, 0), -SOC_SINGLE("IN3 High Performance Switch", ARIZONA_IN3L_CONTROL, - ARIZONA_IN3_OSR_SHIFT, 1, 0), -SOC_SINGLE("IN4 High Performance Switch", ARIZONA_IN4L_CONTROL, - ARIZONA_IN4_OSR_SHIFT, 1, 0), +SOC_ENUM("IN1 OSR", arizona_in_dmic_osr[0]), +SOC_ENUM("IN2 OSR", arizona_in_dmic_osr[1]), +SOC_ENUM("IN3 OSR", arizona_in_dmic_osr[2]), +SOC_ENUM("IN4 OSR", arizona_in_dmic_osr[3]), SOC_SINGLE_RANGE_TLV("IN1L Volume", ARIZONA_IN1L_CONTROL, ARIZONA_IN1L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), -- cgit v0.10.2 From 4b4dab82340d969521f4f86108441cb597c8595d Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Sun, 28 Jul 2013 18:58:29 -0700 Subject: ASoC: rsnd: remove platform dai and add dai_id on platform setting Current rsnd driver is using struct rsnd_dai_platform_info so that indicate sound DAI information (playback/capture SSI ID). But, SSI settings were also required separately. Thus, platform settings was very un-understandable. This patch adds dai_id to SSI settings, and removed rsnd_dai_platform_info. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown diff --git a/include/sound/rcar_snd.h b/include/sound/rcar_snd.h index 99d8dd0..33233ed 100644 --- a/include/sound/rcar_snd.h +++ b/include/sound/rcar_snd.h @@ -28,15 +28,24 @@ /* * flags * - * 0xA0000000 + * 0xAB000000 * * A : clock sharing settings + * B : SSI direction */ #define RSND_SSI_CLK_PIN_SHARE (1 << 31) #define RSND_SSI_CLK_FROM_ADG (1 << 30) /* clock parent is master */ #define RSND_SSI_SYNC (1 << 29) /* SSI34_sync etc */ +#define RSND_SSI_PLAY (1 << 24) + +#define RSND_SSI_SET(_dai_id, _pio_irq, _flags) \ +{ .dai_id = _dai_id, .pio_irq = _pio_irq, .flags = _flags } +#define RSND_SSI_UNUSED \ +{ .dai_id = -1, .pio_irq = -1, .flags = 0 } + struct rsnd_ssi_platform_info { + int dai_id; int pio_irq; u32 flags; }; @@ -45,11 +54,6 @@ struct rsnd_scu_platform_info { u32 flags; }; -struct rsnd_dai_platform_info { - int ssi_id_playback; - int ssi_id_capture; -}; - /* * flags * @@ -66,8 +70,6 @@ struct rcar_snd_info { int ssi_info_nr; struct rsnd_scu_platform_info *scu_info; int scu_info_nr; - struct rsnd_dai_platform_info *dai_info; - int dai_info_nr; int (*start)(int id); int (*stop)(int id); }; diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index 9a5469d..420d6df 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -219,6 +219,16 @@ int rsnd_dai_disconnect(struct rsnd_mod *mod) return 0; } +int rsnd_dai_id(struct rsnd_priv *priv, struct rsnd_dai *rdai) +{ + int id = rdai - priv->rdai; + + if ((id < 0) || (id >= rsnd_dai_nr(priv))) + return -EINVAL; + + return id; +} + struct rsnd_dai *rsnd_dai_get(struct rsnd_priv *priv, int id) { return priv->rdai + id; @@ -315,9 +325,10 @@ static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd, struct rsnd_priv *priv = snd_soc_dai_get_drvdata(dai); struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai); struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream); - struct rsnd_dai_platform_info *info = rsnd_dai_get_platform_info(rdai); - int ssi_id = rsnd_dai_is_play(rdai, io) ? info->ssi_id_playback : - info->ssi_id_capture; + struct rsnd_mod *mod = rsnd_ssi_mod_get_frm_dai(priv, + rsnd_dai_id(priv, rdai), + rsnd_dai_is_play(rdai, io)); + int ssi_id = rsnd_mod_id(mod); int ret; unsigned long flags; @@ -439,10 +450,24 @@ static int rsnd_dai_probe(struct platform_device *pdev, { struct snd_soc_dai_driver *drv; struct rsnd_dai *rdai; + struct rsnd_mod *pmod, *cmod; struct device *dev = rsnd_priv_to_dev(priv); - struct rsnd_dai_platform_info *dai_info; - int dai_nr = info->dai_info_nr; - int i, pid, cid; + int dai_nr; + int i; + + /* get max dai nr */ + for (dai_nr = 0; dai_nr < 32; dai_nr++) { + pmod = rsnd_ssi_mod_get_frm_dai(priv, dai_nr, 1); + cmod = rsnd_ssi_mod_get_frm_dai(priv, dai_nr, 0); + + if (!pmod && !cmod) + break; + } + + if (!dai_nr) { + dev_err(dev, "no dai\n"); + return -EIO; + } drv = devm_kzalloc(dev, sizeof(*drv) * dai_nr, GFP_KERNEL); rdai = devm_kzalloc(dev, sizeof(*rdai) * dai_nr, GFP_KERNEL); @@ -452,10 +477,9 @@ static int rsnd_dai_probe(struct platform_device *pdev, } for (i = 0; i < dai_nr; i++) { - dai_info = &info->dai_info[i]; - pid = dai_info->ssi_id_playback; - cid = dai_info->ssi_id_capture; + pmod = rsnd_ssi_mod_get_frm_dai(priv, i, 1); + cmod = rsnd_ssi_mod_get_frm_dai(priv, i, 0); /* * init rsnd_dai @@ -463,8 +487,6 @@ static int rsnd_dai_probe(struct platform_device *pdev, INIT_LIST_HEAD(&rdai[i].playback.head); INIT_LIST_HEAD(&rdai[i].capture.head); - rdai[i].info = dai_info; - snprintf(rdai[i].name, RSND_DAI_NAME_SIZE, "rsnd-dai.%d", i); /* @@ -472,20 +494,22 @@ static int rsnd_dai_probe(struct platform_device *pdev, */ drv[i].name = rdai[i].name; drv[i].ops = &rsnd_soc_dai_ops; - if (pid >= 0) { + if (pmod) { drv[i].playback.rates = RSND_RATES; drv[i].playback.formats = RSND_FMTS; drv[i].playback.channels_min = 2; drv[i].playback.channels_max = 2; } - if (cid >= 0) { + if (cmod) { drv[i].capture.rates = RSND_RATES; drv[i].capture.formats = RSND_FMTS; drv[i].capture.channels_min = 2; drv[i].capture.channels_max = 2; } - dev_dbg(dev, "%s (%d, %d) probed", rdai[i].name, pid, cid); + dev_dbg(dev, "%s (%s/%s)\n", rdai[i].name, + pmod ? "play" : " -- ", + cmod ? "capture" : " -- "); } priv->dai_nr = dai_nr; @@ -627,10 +651,6 @@ static int rsnd_probe(struct platform_device *pdev) if (ret < 0) return ret; - ret = rsnd_dai_probe(pdev, info, priv); - if (ret < 0) - return ret; - ret = rsnd_scu_probe(pdev, info, priv); if (ret < 0) return ret; @@ -643,6 +663,10 @@ static int rsnd_probe(struct platform_device *pdev) if (ret < 0) return ret; + ret = rsnd_dai_probe(pdev, info, priv); + if (ret < 0) + return ret; + /* * asoc register */ diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c index 61232cd..460c57e 100644 --- a/sound/soc/sh/rcar/gen.c +++ b/sound/soc/sh/rcar/gen.c @@ -49,7 +49,6 @@ static int rsnd_gen1_path_init(struct rsnd_priv *priv, struct rsnd_dai *rdai, struct rsnd_dai_stream *io) { - struct rsnd_dai_platform_info *info = rsnd_dai_get_platform_info(rdai); struct rsnd_mod *mod; int ret; int id; @@ -67,10 +66,11 @@ static int rsnd_gen1_path_init(struct rsnd_priv *priv, * Then, SSI id = SCU id here */ - if (rsnd_dai_is_play(rdai, io)) - id = info->ssi_id_playback; - else - id = info->ssi_id_capture; + /* get SSI's ID */ + mod = rsnd_ssi_mod_get_frm_dai(priv, + rsnd_dai_id(priv, rdai), + rsnd_dai_is_play(rdai, io)); + id = rsnd_mod_id(mod); /* SSI */ mod = rsnd_ssi_mod_get(priv, id); diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 0e7727c..9243e38 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -157,6 +157,7 @@ int rsnd_dai_disconnect(struct rsnd_mod *mod); int rsnd_dai_connect(struct rsnd_dai *rdai, struct rsnd_mod *mod, struct rsnd_dai_stream *io); int rsnd_dai_is_play(struct rsnd_dai *rdai, struct rsnd_dai_stream *io); +int rsnd_dai_id(struct rsnd_priv *priv, struct rsnd_dai *rdai); #define rsnd_dai_get_platform_info(rdai) ((rdai)->info) #define rsnd_io_to_runtime(io) ((io)->substream->runtime) @@ -254,5 +255,7 @@ int rsnd_ssi_probe(struct platform_device *pdev, void rsnd_ssi_remove(struct platform_device *pdev, struct rsnd_priv *priv); struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id); +struct rsnd_mod *rsnd_ssi_mod_get_frm_dai(struct rsnd_priv *priv, + int dai_id, int is_play); #endif diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index 061ac7e..c48a6c7 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -87,6 +87,7 @@ struct rsnd_ssiu { #define rsnd_ssi_clk_from_parent(ssi) ((ssi)->parent) #define rsnd_rdai_is_clk_master(rdai) ((rdai)->clk_master) #define rsnd_ssi_mode_flags(p) ((p)->info->flags) +#define rsnd_ssi_dai_id(ssi) ((ssi)->info->dai_id) #define rsnd_ssi_to_ssiu(ssi)\ (((struct rsnd_ssiu *)((ssi) - rsnd_mod_id(&(ssi)->mod))) - 1) @@ -502,6 +503,27 @@ static struct rsnd_mod_ops rsnd_ssi_non_ops = { /* * ssi mod function */ +struct rsnd_mod *rsnd_ssi_mod_get_frm_dai(struct rsnd_priv *priv, + int dai_id, int is_play) +{ + struct rsnd_ssi *ssi; + int i, has_play; + + is_play = !!is_play; + + for_each_rsnd_ssi(ssi, priv, i) { + if (rsnd_ssi_dai_id(ssi) != dai_id) + continue; + + has_play = !!(rsnd_ssi_mode_flags(ssi) & RSND_SSI_PLAY); + + if (is_play == has_play) + return &ssi->mod; + } + + return NULL; +} + struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id) { BUG_ON(id < 0 || id >= rsnd_ssi_nr(priv)); -- cgit v0.10.2 From 0a4d94c07ce782e645a8c0484d52221758b4c398 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Sun, 28 Jul 2013 18:58:50 -0700 Subject: ASoC: rsnd: add common DMAEngine method R-Car Sound driver will support DMA transfer in the future, then, SSI/SRU/SRC will use it. Current R-Car can't use soc-dmaengine-pcm.c since its DMAEngine doesn't support dmaengine_prep_dma_cyclic(), and SSI needs double plane transfer (which needs special submit) on DMAC. This patch adds common DMAEngine method for it Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index 420d6df..a357060 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -174,6 +174,138 @@ void rsnd_mod_init(struct rsnd_priv *priv, } /* + * rsnd_dma functions + */ +static void rsnd_dma_continue(struct rsnd_dma *dma) +{ + /* push next A or B plane */ + dma->submit_loop = 1; + schedule_work(&dma->work); +} + +void rsnd_dma_start(struct rsnd_dma *dma) +{ + /* push both A and B plane*/ + dma->submit_loop = 2; + schedule_work(&dma->work); +} + +void rsnd_dma_stop(struct rsnd_dma *dma) +{ + dma->submit_loop = 0; + cancel_work_sync(&dma->work); + dmaengine_terminate_all(dma->chan); +} + +static void rsnd_dma_complete(void *data) +{ + struct rsnd_dma *dma = (struct rsnd_dma *)data; + struct rsnd_priv *priv = dma->priv; + unsigned long flags; + + rsnd_lock(priv, flags); + + dma->complete(dma); + + if (dma->submit_loop) + rsnd_dma_continue(dma); + + rsnd_unlock(priv, flags); +} + +static void rsnd_dma_do_work(struct work_struct *work) +{ + struct rsnd_dma *dma = container_of(work, struct rsnd_dma, work); + struct rsnd_priv *priv = dma->priv; + struct device *dev = rsnd_priv_to_dev(priv); + struct dma_async_tx_descriptor *desc; + dma_addr_t buf; + size_t len; + int i; + + for (i = 0; i < dma->submit_loop; i++) { + + if (dma->inquiry(dma, &buf, &len) < 0) + return; + + desc = dmaengine_prep_slave_single( + dma->chan, buf, len, dma->dir, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!desc) { + dev_err(dev, "dmaengine_prep_slave_sg() fail\n"); + return; + } + + desc->callback = rsnd_dma_complete; + desc->callback_param = dma; + + if (dmaengine_submit(desc) < 0) { + dev_err(dev, "dmaengine_submit() fail\n"); + return; + } + + } + + dma_async_issue_pending(dma->chan); +} + +int rsnd_dma_available(struct rsnd_dma *dma) +{ + return !!dma->chan; +} + +static bool rsnd_dma_filter(struct dma_chan *chan, void *param) +{ + chan->private = param; + + return true; +} + +int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma, + int is_play, int id, + int (*inquiry)(struct rsnd_dma *dma, + dma_addr_t *buf, int *len), + int (*complete)(struct rsnd_dma *dma)) +{ + struct device *dev = rsnd_priv_to_dev(priv); + dma_cap_mask_t mask; + + if (dma->chan) { + dev_err(dev, "it already has dma channel\n"); + return -EIO; + } + + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + + dma->slave.shdma_slave.slave_id = id; + + dma->chan = dma_request_channel(mask, rsnd_dma_filter, + &dma->slave.shdma_slave); + if (!dma->chan) { + dev_err(dev, "can't get dma channel\n"); + return -EIO; + } + + dma->dir = is_play ? DMA_TO_DEVICE : DMA_FROM_DEVICE; + dma->priv = priv; + dma->inquiry = inquiry; + dma->complete = complete; + INIT_WORK(&dma->work, rsnd_dma_do_work); + + return 0; +} + +void rsnd_dma_quit(struct rsnd_priv *priv, + struct rsnd_dma *dma) +{ + if (dma->chan) + dma_release_channel(dma->chan); + + dma->chan = NULL; +} + +/* * rsnd_dai functions */ #define rsnd_dai_call(rdai, io, fn) \ diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 9243e38..15dccd5 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -13,9 +13,12 @@ #include #include +#include #include #include #include +#include +#include #include #include #include @@ -79,6 +82,32 @@ void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod, enum rsnd_reg reg, u32 mask, u32 data); /* + * R-Car DMA + */ +struct rsnd_dma { + struct rsnd_priv *priv; + struct sh_dmae_slave slave; + struct work_struct work; + struct dma_chan *chan; + enum dma_data_direction dir; + int (*inquiry)(struct rsnd_dma *dma, dma_addr_t *buf, int *len); + int (*complete)(struct rsnd_dma *dma); + + int submit_loop; +}; + +void rsnd_dma_start(struct rsnd_dma *dma); +void rsnd_dma_stop(struct rsnd_dma *dma); +int rsnd_dma_available(struct rsnd_dma *dma); +int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma, + int is_play, int id, + int (*inquiry)(struct rsnd_dma *dma, dma_addr_t *buf, int *len), + int (*complete)(struct rsnd_dma *dma)); +void rsnd_dma_quit(struct rsnd_priv *priv, + struct rsnd_dma *dma); + + +/* * R-Car sound mod */ @@ -103,9 +132,12 @@ struct rsnd_mod { struct rsnd_priv *priv; struct rsnd_mod_ops *ops; struct list_head list; /* connect to rsnd_dai playback/capture */ + struct rsnd_dma dma; }; #define rsnd_mod_to_priv(mod) ((mod)->priv) +#define rsnd_mod_to_dma(mod) (&(mod)->dma) +#define rsnd_dma_to_mod(_dma) container_of((_dma), struct rsnd_mod, dma) #define rsnd_mod_id(mod) ((mod)->id) #define for_each_rsnd_mod(pos, n, io) \ list_for_each_entry_safe(pos, n, &(io)->head, list) -- cgit v0.10.2 From 849fc82a6f4f32b4c8c502bb7c4a68df51170232 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Sun, 28 Jul 2013 18:59:02 -0700 Subject: ASoC: rsnd: SSI supports DMA transfer This patch adds DMAEngine transfer on SSI. But, it transfers sound data from memory to SSI directly without using HPBIF at this time. It will be updated soon Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown diff --git a/include/sound/rcar_snd.h b/include/sound/rcar_snd.h index 33233ed..a72687d 100644 --- a/include/sound/rcar_snd.h +++ b/include/sound/rcar_snd.h @@ -39,13 +39,14 @@ #define RSND_SSI_PLAY (1 << 24) -#define RSND_SSI_SET(_dai_id, _pio_irq, _flags) \ -{ .dai_id = _dai_id, .pio_irq = _pio_irq, .flags = _flags } +#define RSND_SSI_SET(_dai_id, _dma_id, _pio_irq, _flags) \ +{ .dai_id = _dai_id, .dma_id = _dma_id, .pio_irq = _pio_irq, .flags = _flags } #define RSND_SSI_UNUSED \ -{ .dai_id = -1, .pio_irq = -1, .flags = 0 } +{ .dai_id = -1, .dma_id = -1, .pio_irq = -1, .flags = 0 } struct rsnd_ssi_platform_info { int dai_id; + int dma_id; int pio_irq; u32 flags; }; diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index c48a6c7..2079ccf 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -19,6 +19,7 @@ * SSICR */ #define FORCE (1 << 31) /* Fixed */ +#define DMEN (1 << 28) /* DMA Enable */ #define UIEN (1 << 27) /* Underflow Interrupt Enable */ #define OIEN (1 << 26) /* Overflow Interrupt Enable */ #define IIEN (1 << 25) /* Idle Mode Interrupt Enable */ @@ -51,6 +52,11 @@ #define IIRQ (1 << 25) /* Idle Mode Interrupt Status */ #define DIRQ (1 << 24) /* Data Interrupt Status Flag */ +/* + * SSIWSR + */ +#define CONT (1 << 8) /* WS Continue Function */ + struct rsnd_ssi { struct clk *clk; struct rsnd_ssi_platform_info *info; /* rcar_snd.h */ @@ -63,6 +69,7 @@ struct rsnd_ssi { u32 cr_clk; u32 cr_etc; int err; + int dma_offset; unsigned int usrcnt; unsigned int rate; }; @@ -83,7 +90,10 @@ struct rsnd_ssiu { #define rsnd_ssi_nr(priv) (((struct rsnd_ssiu *)((priv)->ssiu))->ssi_nr) #define rsnd_mod_to_ssi(_mod) container_of((_mod), struct rsnd_ssi, mod) -#define rsnd_ssi_is_pio(ssi) ((ssi)->info->pio_irq > 0) +#define rsnd_dma_to_ssi(dma) rsnd_mod_to_ssi(rsnd_dma_to_mod(dma)) +#define rsnd_ssi_pio_available(ssi) ((ssi)->info->pio_irq > 0) +#define rsnd_ssi_dma_available(ssi) \ + rsnd_dma_available(rsnd_mod_to_dma(&(ssi)->mod)) #define rsnd_ssi_clk_from_parent(ssi) ((ssi)->parent) #define rsnd_rdai_is_clk_master(rdai) ((rdai)->clk_master) #define rsnd_ssi_mode_flags(p) ((p)->info->flags) @@ -477,6 +487,79 @@ static struct rsnd_mod_ops rsnd_ssi_pio_ops = { .stop = rsnd_ssi_pio_stop, }; +static int rsnd_ssi_dma_inquiry(struct rsnd_dma *dma, dma_addr_t *buf, int *len) +{ + struct rsnd_ssi *ssi = rsnd_dma_to_ssi(dma); + struct rsnd_dai_stream *io = ssi->io; + struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); + + *len = io->byte_per_period; + *buf = runtime->dma_addr + + rsnd_dai_pointer_offset(io, ssi->dma_offset + *len); + ssi->dma_offset = *len; /* it cares A/B plane */ + + return 0; +} + +static int rsnd_ssi_dma_complete(struct rsnd_dma *dma) +{ + struct rsnd_ssi *ssi = rsnd_dma_to_ssi(dma); + struct rsnd_dai_stream *io = ssi->io; + u32 status = rsnd_mod_read(&ssi->mod, SSISR); + + rsnd_ssi_record_error(ssi, status); + + rsnd_dai_pointer_update(ssi->io, io->byte_per_period); + + return 0; +} + +static int rsnd_ssi_dma_start(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); + struct rsnd_dma *dma = rsnd_mod_to_dma(&ssi->mod); + + /* enable DMA transfer */ + ssi->cr_etc = DMEN; + ssi->dma_offset = 0; + + rsnd_dma_start(dma); + + rsnd_ssi_hw_start(ssi, ssi->rdai, io); + + /* enable WS continue */ + if (rsnd_rdai_is_clk_master(rdai)) + rsnd_mod_write(&ssi->mod, SSIWSR, CONT); + + return 0; +} + +static int rsnd_ssi_dma_stop(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); + struct rsnd_dma *dma = rsnd_mod_to_dma(&ssi->mod); + + ssi->cr_etc = 0; + + rsnd_ssi_hw_stop(ssi, rdai); + + rsnd_dma_stop(dma); + + return 0; +} + +static struct rsnd_mod_ops rsnd_ssi_dma_ops = { + .name = "ssi (dma)", + .init = rsnd_ssi_init, + .quit = rsnd_ssi_quit, + .start = rsnd_ssi_dma_start, + .stop = rsnd_ssi_dma_stop, +}; + /* * Non SSI */ @@ -574,9 +657,26 @@ int rsnd_ssi_probe(struct platform_device *pdev, ops = &rsnd_ssi_non_ops; /* + * SSI DMA case + */ + if (pinfo->dma_id > 0) { + ret = rsnd_dma_init( + priv, rsnd_mod_to_dma(&ssi->mod), + (rsnd_ssi_mode_flags(ssi) & RSND_SSI_PLAY), + pinfo->dma_id, + rsnd_ssi_dma_inquiry, + rsnd_ssi_dma_complete); + if (ret < 0) + dev_info(dev, "SSI DMA failed. try PIO transter\n"); + else + ops = &rsnd_ssi_dma_ops; + } + + /* * SSI PIO case */ - if (rsnd_ssi_is_pio(ssi)) { + if (!rsnd_ssi_dma_available(ssi) && + rsnd_ssi_pio_available(ssi)) { ret = devm_request_irq(dev, pinfo->pio_irq, &rsnd_ssi_pio_interrupt, IRQF_SHARED, @@ -605,6 +705,10 @@ void rsnd_ssi_remove(struct platform_device *pdev, struct rsnd_ssi *ssi; int i; - for_each_rsnd_ssi(ssi, priv, i) + for_each_rsnd_ssi(ssi, priv, i) { clk_put(ssi->clk); + if (rsnd_ssi_dma_available(ssi)) + rsnd_dma_quit(priv, rsnd_mod_to_dma(&ssi->mod)); + } + } -- cgit v0.10.2 From 374a528111fa07878090bd9694a3e153814de39c Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Sun, 28 Jul 2013 18:59:12 -0700 Subject: ASoC: rsnd: SSI supports DMA transfer via BUSIF This patch adds BUSIF support for R-Car sound DMAEngine transfer. The sound data will be transferred via FIFO which can cover blank time which will happen when DMA channel is switching. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown diff --git a/include/sound/rcar_snd.h b/include/sound/rcar_snd.h index a72687d..d35412a 100644 --- a/include/sound/rcar_snd.h +++ b/include/sound/rcar_snd.h @@ -36,6 +36,7 @@ #define RSND_SSI_CLK_PIN_SHARE (1 << 31) #define RSND_SSI_CLK_FROM_ADG (1 << 30) /* clock parent is master */ #define RSND_SSI_SYNC (1 << 29) /* SSI34_sync etc */ +#define RSND_SSI_DEPENDENT (1 << 28) /* SSI needs SRU/SCU */ #define RSND_SSI_PLAY (1 << 24) @@ -51,6 +52,11 @@ struct rsnd_ssi_platform_info { u32 flags; }; +/* + * flags + */ +#define RSND_SCU_USB_HPBIF (1 << 31) /* it needs RSND_SSI_DEPENDENT */ + struct rsnd_scu_platform_info { u32 flags; }; diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c index 460c57e..babb203 100644 --- a/sound/soc/sh/rcar/gen.c +++ b/sound/soc/sh/rcar/gen.c @@ -34,9 +34,6 @@ struct rsnd_gen { #define rsnd_priv_to_gen(p) ((struct rsnd_gen *)(p)->gen) -#define rsnd_is_gen1(s) ((s)->info->flags & RSND_GEN1) -#define rsnd_is_gen2(s) ((s)->info->flags & RSND_GEN2) - /* * Gen2 * will be filled in the future @@ -115,8 +112,15 @@ static struct rsnd_gen_ops rsnd_gen1_ops = { static void rsnd_gen1_reg_map_init(struct rsnd_gen *gen) { + RSND_GEN1_REG_MAP(gen, SRU, SRC_ROUTE_SEL, 0x0, 0x00); + RSND_GEN1_REG_MAP(gen, SRU, SRC_TMG_SEL0, 0x0, 0x08); + RSND_GEN1_REG_MAP(gen, SRU, SRC_TMG_SEL1, 0x0, 0x0c); + RSND_GEN1_REG_MAP(gen, SRU, SRC_TMG_SEL2, 0x0, 0x10); + RSND_GEN1_REG_MAP(gen, SRU, SRC_CTRL, 0x0, 0xc0); RSND_GEN1_REG_MAP(gen, SRU, SSI_MODE0, 0x0, 0xD0); RSND_GEN1_REG_MAP(gen, SRU, SSI_MODE1, 0x0, 0xD4); + RSND_GEN1_REG_MAP(gen, SRU, BUSIF_MODE, 0x4, 0x20); + RSND_GEN1_REG_MAP(gen, SRU, BUSIF_ADINR, 0x40, 0x214); RSND_GEN1_REG_MAP(gen, ADG, BRRA, 0x0, 0x00); RSND_GEN1_REG_MAP(gen, ADG, BRRB, 0x0, 0x04); diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 15dccd5..9cc6986 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -32,8 +32,15 @@ */ enum rsnd_reg { /* SRU/SCU */ + RSND_REG_SRC_ROUTE_SEL, + RSND_REG_SRC_TMG_SEL0, + RSND_REG_SRC_TMG_SEL1, + RSND_REG_SRC_TMG_SEL2, + RSND_REG_SRC_CTRL, RSND_REG_SSI_MODE0, RSND_REG_SSI_MODE1, + RSND_REG_BUSIF_MODE, + RSND_REG_BUSIF_ADINR, /* ADG */ RSND_REG_BRRA, @@ -213,6 +220,8 @@ int rsnd_gen_path_exit(struct rsnd_priv *priv, void __iomem *rsnd_gen_reg_get(struct rsnd_priv *priv, struct rsnd_mod *mod, enum rsnd_reg reg); +#define rsnd_is_gen1(s) ((s)->info->flags & RSND_GEN1) +#define rsnd_is_gen2(s) ((s)->info->flags & RSND_GEN2) /* * R-Car ADG diff --git a/sound/soc/sh/rcar/scu.c b/sound/soc/sh/rcar/scu.c index c12e65f..29837e3 100644 --- a/sound/soc/sh/rcar/scu.c +++ b/sound/soc/sh/rcar/scu.c @@ -15,6 +15,18 @@ struct rsnd_scu { struct rsnd_mod mod; }; +#define rsnd_scu_mode_flags(p) ((p)->info->flags) + +/* + * ADINR + */ +#define OTBL_24 (0 << 16) +#define OTBL_22 (2 << 16) +#define OTBL_20 (4 << 16) +#define OTBL_18 (6 << 16) +#define OTBL_16 (8 << 16) + + #define rsnd_mod_to_scu(_mod) \ container_of((_mod), struct rsnd_scu, mod) @@ -24,6 +36,116 @@ struct rsnd_scu { ((pos) = (struct rsnd_scu *)(priv)->scu + i); \ i++) +static int rsnd_scu_set_route(struct rsnd_priv *priv, + struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct scu_route_config { + u32 mask; + int shift; + } routes[] = { + { 0xF, 0, }, /* 0 */ + { 0xF, 4, }, /* 1 */ + { 0xF, 8, }, /* 2 */ + { 0x7, 12, }, /* 3 */ + { 0x7, 16, }, /* 4 */ + { 0x7, 20, }, /* 5 */ + { 0x7, 24, }, /* 6 */ + { 0x3, 28, }, /* 7 */ + { 0x3, 30, }, /* 8 */ + }; + + u32 mask; + u32 val; + int shift; + int id; + + /* + * Gen1 only + */ + if (!rsnd_is_gen1(priv)) + return 0; + + id = rsnd_mod_id(mod); + if (id < 0 || id > ARRAY_SIZE(routes)) + return -EIO; + + /* + * SRC_ROUTE_SELECT + */ + val = rsnd_dai_is_play(rdai, io) ? 0x1 : 0x2; + val = val << routes[id].shift; + mask = routes[id].mask << routes[id].shift; + + rsnd_mod_bset(mod, SRC_ROUTE_SEL, mask, val); + + /* + * SRC_TIMING_SELECT + */ + shift = (id % 4) * 8; + mask = 0x1F << shift; + if (8 == id) /* SRU8 is very special */ + val = id << shift; + else + val = (id + 1) << shift; + + switch (id / 4) { + case 0: + rsnd_mod_bset(mod, SRC_TMG_SEL0, mask, val); + break; + case 1: + rsnd_mod_bset(mod, SRC_TMG_SEL1, mask, val); + break; + case 2: + rsnd_mod_bset(mod, SRC_TMG_SEL2, mask, val); + break; + } + + return 0; +} + +static int rsnd_scu_set_mode(struct rsnd_priv *priv, + struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + int id = rsnd_mod_id(mod); + u32 val; + + if (rsnd_is_gen1(priv)) { + val = (1 << id); + rsnd_mod_bset(mod, SRC_CTRL, val, val); + } + + return 0; +} + +static int rsnd_scu_set_hpbif(struct rsnd_priv *priv, + struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); + u32 adinr = runtime->channels; + + switch (runtime->sample_bits) { + case 16: + adinr |= OTBL_16; + break; + case 32: + adinr |= OTBL_24; + break; + default: + return -EIO; + } + + rsnd_mod_write(mod, BUSIF_MODE, 1); + rsnd_mod_write(mod, BUSIF_ADINR, adinr); + + return 0; +} + static int rsnd_scu_init(struct rsnd_mod *mod, struct rsnd_dai *rdai, struct rsnd_dai_stream *io) @@ -53,9 +175,36 @@ static int rsnd_scu_start(struct rsnd_mod *mod, struct rsnd_dai_stream *io) { struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct rsnd_scu *scu = rsnd_mod_to_scu(mod); struct device *dev = rsnd_priv_to_dev(priv); + u32 flags = rsnd_scu_mode_flags(scu); + int ret; + + /* + * SCU will be used if it has RSND_SCU_USB_HPBIF flags + */ + if (!(flags & RSND_SCU_USB_HPBIF)) { + /* it use PIO transter */ + dev_dbg(dev, "%s%d is not used\n", + rsnd_mod_name(mod), rsnd_mod_id(mod)); + + return 0; + } + + /* it use DMA transter */ + ret = rsnd_scu_set_route(priv, mod, rdai, io); + if (ret < 0) + return ret; + + ret = rsnd_scu_set_mode(priv, mod, rdai, io); + if (ret < 0) + return ret; - dev_dbg(dev, "%s.%d start\n", rsnd_mod_name(mod), rsnd_mod_id(mod)); + ret = rsnd_scu_set_hpbif(priv, mod, rdai, io); + if (ret < 0) + return ret; + + dev_dbg(dev, "%s%d start\n", rsnd_mod_name(mod), rsnd_mod_id(mod)); return 0; } @@ -112,8 +261,9 @@ int rsnd_scu_probe(struct platform_device *pdev, rsnd_mod_init(priv, &scu->mod, &rsnd_scu_ops, i); scu->info = &info->scu_info[i]; - } + dev_dbg(dev, "SCU%d probed\n", i); + } dev_dbg(dev, "scu probed\n"); return 0; diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index 2079ccf..fae26d3 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -104,6 +104,7 @@ struct rsnd_ssiu { static void rsnd_ssi_mode_init(struct rsnd_priv *priv, struct rsnd_ssiu *ssiu) { + struct device *dev = rsnd_priv_to_dev(priv); struct rsnd_ssi *ssi; u32 flags; u32 val; @@ -113,8 +114,17 @@ static void rsnd_ssi_mode_init(struct rsnd_priv *priv, * SSI_MODE0 */ ssiu->ssi_mode0 = 0; - for_each_rsnd_ssi(ssi, priv, i) - ssiu->ssi_mode0 |= (1 << i); + for_each_rsnd_ssi(ssi, priv, i) { + flags = rsnd_ssi_mode_flags(ssi); + + /* see also BUSIF_MODE */ + if (!(flags & RSND_SSI_DEPENDENT)) { + ssiu->ssi_mode0 |= (1 << i); + dev_dbg(dev, "SSI%d uses INDEPENDENT mode\n", i); + } else { + dev_dbg(dev, "SSI%d uses DEPENDENT mode\n", i); + } + } /* * SSI_MODE1 @@ -670,6 +680,8 @@ int rsnd_ssi_probe(struct platform_device *pdev, dev_info(dev, "SSI DMA failed. try PIO transter\n"); else ops = &rsnd_ssi_dma_ops; + + dev_dbg(dev, "SSI%d use DMA transfer\n", i); } /* @@ -687,6 +699,8 @@ int rsnd_ssi_probe(struct platform_device *pdev, } ops = &rsnd_ssi_pio_ops; + + dev_dbg(dev, "SSI%d use PIO transfer\n", i); } rsnd_mod_init(priv, &ssi->mod, ops, i); -- cgit v0.10.2 From 2460719c79854a3bebe569cbfbfa0b1caa1dc434 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Sun, 28 Jul 2013 18:59:25 -0700 Subject: ASoC: rsnd: scu: cleanup empty functions This patch cleanups empty functions on scu Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown diff --git a/sound/soc/sh/rcar/scu.c b/sound/soc/sh/rcar/scu.c index 29837e3..184d9008 100644 --- a/sound/soc/sh/rcar/scu.c +++ b/sound/soc/sh/rcar/scu.c @@ -146,30 +146,6 @@ static int rsnd_scu_set_hpbif(struct rsnd_priv *priv, return 0; } -static int rsnd_scu_init(struct rsnd_mod *mod, - struct rsnd_dai *rdai, - struct rsnd_dai_stream *io) -{ - struct rsnd_priv *priv = rsnd_mod_to_priv(mod); - struct device *dev = rsnd_priv_to_dev(priv); - - dev_dbg(dev, "%s.%d init\n", rsnd_mod_name(mod), rsnd_mod_id(mod)); - - return 0; -} - -static int rsnd_scu_quit(struct rsnd_mod *mod, - struct rsnd_dai *rdai, - struct rsnd_dai_stream *io) -{ - struct rsnd_priv *priv = rsnd_mod_to_priv(mod); - struct device *dev = rsnd_priv_to_dev(priv); - - dev_dbg(dev, "%s.%d quit\n", rsnd_mod_name(mod), rsnd_mod_id(mod)); - - return 0; -} - static int rsnd_scu_start(struct rsnd_mod *mod, struct rsnd_dai *rdai, struct rsnd_dai_stream *io) @@ -209,24 +185,9 @@ static int rsnd_scu_start(struct rsnd_mod *mod, return 0; } -static int rsnd_scu_stop(struct rsnd_mod *mod, - struct rsnd_dai *rdai, - struct rsnd_dai_stream *io) -{ - struct rsnd_priv *priv = rsnd_mod_to_priv(mod); - struct device *dev = rsnd_priv_to_dev(priv); - - dev_dbg(dev, "%s.%d stop\n", rsnd_mod_name(mod), rsnd_mod_id(mod)); - - return 0; -} - static struct rsnd_mod_ops rsnd_scu_ops = { .name = "scu", - .init = rsnd_scu_init, - .quit = rsnd_scu_quit, .start = rsnd_scu_start, - .stop = rsnd_scu_stop, }; struct rsnd_mod *rsnd_scu_mod_get(struct rsnd_priv *priv, int id) -- cgit v0.10.2 From 8548a464b942a97324d0e3e340ce95356cff32c4 Mon Sep 17 00:00:00 2001 From: Markus Pargmann Date: Sat, 27 Jul 2013 13:31:52 +0200 Subject: ASoC: imx-audmux: Read default configuration from devicetree Adds a function to parse a default port configuration from devicetree. Signed-off-by: Markus Pargmann Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/sound/imx-audmux.txt b/Documentation/devicetree/bindings/sound/imx-audmux.txt index 215aa98..f88a00e 100644 --- a/Documentation/devicetree/bindings/sound/imx-audmux.txt +++ b/Documentation/devicetree/bindings/sound/imx-audmux.txt @@ -5,6 +5,15 @@ Required properties: or "fsl,imx31-audmux" for the version firstly used on i.MX31. - reg : Should contain AUDMUX registers location and length +An initial configuration can be setup using child nodes. + +Required properties of optional child nodes: +- fsl,audmux-port : Integer of the audmux port that is configured by this + child node. +- fsl,port-config : List of configuration options for the specific port. For + imx31-audmux and above, it is a list of tuples . For + imx21-audmux it is a list of pcr values. + Example: audmux@021d8000 { diff --git a/sound/soc/fsl/imx-audmux.c b/sound/soc/fsl/imx-audmux.c index 1a5da1e..103d1b0 100644 --- a/sound/soc/fsl/imx-audmux.c +++ b/sound/soc/fsl/imx-audmux.c @@ -251,6 +251,66 @@ int imx_audmux_v2_configure_port(unsigned int port, unsigned int ptcr, } EXPORT_SYMBOL_GPL(imx_audmux_v2_configure_port); +static int imx_audmux_parse_dt_defaults(struct platform_device *pdev, + struct device_node *of_node) +{ + struct device_node *child; + + for_each_available_child_of_node(of_node, child) { + unsigned int port; + unsigned int ptcr = 0; + unsigned int pdcr = 0; + unsigned int pcr = 0; + unsigned int val; + int ret; + int i = 0; + + ret = of_property_read_u32(child, "fsl,audmux-port", &port); + if (ret) { + dev_warn(&pdev->dev, "Failed to get fsl,audmux-port of child node \"%s\"\n", + child->full_name); + continue; + } + if (!of_property_read_bool(child, "fsl,port-config")) { + dev_warn(&pdev->dev, "child node \"%s\" does not have property fsl,port-config\n", + child->full_name); + continue; + } + + for (i = 0; (ret = of_property_read_u32_index(child, + "fsl,port-config\n", i, &val)) == 0; + ++i) { + if (audmux_type == IMX31_AUDMUX) { + if (i % 2) + pdcr |= val; + else + ptcr |= val; + } else { + pcr |= val; + } + } + + if (ret != -ENODATA) { + dev_err(&pdev->dev, "Failed to read u32 at index %d of child %s\n", + i, child->full_name); + continue; + } + + if (audmux_type == IMX31_AUDMUX) { + if (i % 2) { + dev_err(&pdev->dev, "One pdcr value is missing in child node %s\n", + child->full_name); + continue; + } + imx_audmux_v2_configure_port(port, ptcr, pdcr); + } else { + imx_audmux_v1_configure_port(port, pcr); + } + } + + return 0; +} + static int imx_audmux_probe(struct platform_device *pdev) { struct resource *res; @@ -275,6 +335,8 @@ static int imx_audmux_probe(struct platform_device *pdev) if (audmux_type == IMX31_AUDMUX) audmux_debugfs_init(); + imx_audmux_parse_dt_defaults(pdev, pdev->dev.of_node); + return 0; } -- cgit v0.10.2 From de623ece5be03e4447dbe08eaca30c92202a34a2 Mon Sep 17 00:00:00 2001 From: Markus Pargmann Date: Sat, 27 Jul 2013 13:31:53 +0200 Subject: ASoC: fsl-ssi: Add support for imx-pcm-fiq Add support for non-dma pcm for imx platforms with imx-pcm-fiq support. Instead of imx-pcm-audio, in this case imx-pcm-fiq-audio device is added and the SIER flags are set differently. We need imx-pcm-fiq for some boards that use an incompatible codec. imx-pcm-fiq handles those codecs differently and allows to operate with them. DMA is not possible because some data sent by the codecs, e.g. wm9712, is not in the datastream. Also some data is mixed up in the fifos, so that we need to sort them out manually. Signed-off-by: Markus Pargmann Tested-by: Shawn Guo Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/sound/fsl,ssi.txt b/Documentation/devicetree/bindings/sound/fsl,ssi.txt index 5ff76c9..e45cbce 100644 --- a/Documentation/devicetree/bindings/sound/fsl,ssi.txt +++ b/Documentation/devicetree/bindings/sound/fsl,ssi.txt @@ -47,6 +47,10 @@ Optional properties: - codec-handle: Phandle to a 'codec' node that defines an audio codec connected to this SSI. This node is typically a child of an I2C or other control node. +- fsl,fiq-stream-filter: Bool property. Disabled DMA and use FIQ instead to + filter the codec stream. This is necessary for some boards + where an incompatible codec is connected to this SSI, e.g. + on pca100 and pcm043. Child 'codec' node required properties: - compatible: Compatible list, contains the name of the codec diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 4d78df7..8b075ef 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -8,6 +8,26 @@ * This file is licensed under the terms of the GNU General Public License * version 2. This program is licensed "as is" without any warranty of any * kind, whether express or implied. + * + * + * Some notes why imx-pcm-fiq is used instead of DMA on some boards: + * + * The i.MX SSI core has some nasty limitations in AC97 mode. While most + * sane processor vendors have a FIFO per AC97 slot, the i.MX has only + * one FIFO which combines all valid receive slots. We cannot even select + * which slots we want to receive. The WM9712 with which this driver + * was developed with always sends GPIO status data in slot 12 which + * we receive in our (PCM-) data stream. The only chance we have is to + * manually skip this data in the FIQ handler. With sampling rates different + * from 48000Hz not every frame has valid receive data, so the ratio + * between pcm data and GPIO status data changes. Our FIQ handler is not + * able to handle this, hence this driver only works with 48000Hz sampling + * rate. + * Reading and writing AC97 registers is another challenge. The core + * provides us status bits when the read register is updated with *another* + * value. When we read the same register two times (and the register still + * contains the same value) these status bits are not set. We work + * around this by not polling these bits but only wait a fixed delay. */ #include @@ -121,11 +141,13 @@ struct fsl_ssi_private { bool new_binding; bool ssi_on_imx; + bool use_dma; struct clk *clk; struct snd_dmaengine_dai_dma_data dma_params_tx; struct snd_dmaengine_dai_dma_data dma_params_rx; struct imx_dma_data filter_data_tx; struct imx_dma_data filter_data_rx; + struct imx_pcm_fiq_params fiq_params; struct { unsigned int rfrc; @@ -355,7 +377,12 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream, */ /* Enable the interrupts and DMA requests */ - write_ssi(SIER_FLAGS, &ssi->sier); + if (ssi_private->use_dma) + write_ssi(SIER_FLAGS, &ssi->sier); + else + write_ssi(CCSR_SSI_SIER_TIE | CCSR_SSI_SIER_TFE0_EN | + CCSR_SSI_SIER_RIE | + CCSR_SSI_SIER_RFF0_EN, &ssi->sier); /* * Set the watermark for transmit FIFI 0 and receive FIFO 0. We @@ -543,7 +570,7 @@ static int fsl_ssi_dai_probe(struct snd_soc_dai *dai) { struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(dai); - if (ssi_private->ssi_on_imx) { + if (ssi_private->ssi_on_imx && ssi_private->use_dma) { dai->playback_dma_data = &ssi_private->dma_params_tx; dai->capture_dma_data = &ssi_private->dma_params_rx; } @@ -683,6 +710,9 @@ static int fsl_ssi_probe(struct platform_device *pdev) strcpy(ssi_private->name, p); + ssi_private->use_dma = !of_property_read_bool(np, + "fsl,fiq-stream-filter"); + /* Initialize this copy of the CPU DAI driver structure */ memcpy(&ssi_private->cpu_dai_drv, &fsl_ssi_dai_template, sizeof(fsl_ssi_dai_template)); @@ -707,12 +737,16 @@ static int fsl_ssi_probe(struct platform_device *pdev) return -ENXIO; } - /* The 'name' should not have any slashes in it. */ - ret = devm_request_irq(&pdev->dev, ssi_private->irq, fsl_ssi_isr, 0, - ssi_private->name, ssi_private); - if (ret < 0) { - dev_err(&pdev->dev, "could not claim irq %u\n", ssi_private->irq); - goto error_irqmap; + if (ssi_private->use_dma) { + /* The 'name' should not have any slashes in it. */ + ret = devm_request_irq(&pdev->dev, ssi_private->irq, + fsl_ssi_isr, 0, ssi_private->name, + ssi_private); + if (ret < 0) { + dev_err(&pdev->dev, "could not claim irq %u\n", + ssi_private->irq); + goto error_irqmap; + } } /* Are the RX and the TX clocks locked? */ @@ -766,7 +800,7 @@ static int fsl_ssi_probe(struct platform_device *pdev) */ ret = of_property_read_u32_array(pdev->dev.of_node, "fsl,ssi-dma-events", dma_events, 2); - if (ret) { + if (ret && !ssi_private->use_dma) { dev_err(&pdev->dev, "could not get dma events\n"); goto error_clk; } @@ -805,9 +839,30 @@ static int fsl_ssi_probe(struct platform_device *pdev) } if (ssi_private->ssi_on_imx) { - ret = imx_pcm_dma_init(pdev); - if (ret) - goto error_dev; + if (!ssi_private->use_dma) { + + /* + * Some boards use an incompatible codec. To get it + * working, we are using imx-fiq-pcm-audio, that + * can handle those codecs. DMA is not possible in this + * situation. + */ + + ssi_private->fiq_params.irq = ssi_private->irq; + ssi_private->fiq_params.base = ssi_private->ssi; + ssi_private->fiq_params.dma_params_rx = + &ssi_private->dma_params_rx; + ssi_private->fiq_params.dma_params_tx = + &ssi_private->dma_params_tx; + + ret = imx_pcm_fiq_init(pdev, &ssi_private->fiq_params); + if (ret) + goto error_dev; + } else { + ret = imx_pcm_dma_init(pdev); + if (ret) + goto error_dev; + } } /* -- cgit v0.10.2 From 3a5e517bb2e9856fd836e90caa415f116d34bd04 Mon Sep 17 00:00:00 2001 From: Markus Pargmann Date: Sat, 27 Jul 2013 13:31:54 +0200 Subject: ASoC: fsl-ssi: Use generic DMA bindings if possible There may be some platforms using fsl-ssi that do not have a DMA driver with generic DMA bindings. So this patch adds support for the generic DMA bindings, while still accepting the old "fsl,dma-events" property if "dmas" is not found. Signed-off-by: Markus Pargmann Tested-by: Shawn Guo Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/sound/fsl,ssi.txt b/Documentation/devicetree/bindings/sound/fsl,ssi.txt index e45cbce..088a2c0 100644 --- a/Documentation/devicetree/bindings/sound/fsl,ssi.txt +++ b/Documentation/devicetree/bindings/sound/fsl,ssi.txt @@ -51,6 +51,10 @@ Optional properties: filter the codec stream. This is necessary for some boards where an incompatible codec is connected to this SSI, e.g. on pca100 and pcm043. +- dmas: Generic dma devicetree binding as described in + Documentation/devicetree/bindings/dma/dma.txt. +- dma-names: Two dmas have to be defined, "tx" and "rx", if fsl,imx-fiq + is not defined. Child 'codec' node required properties: - compatible: Compatible list, contains the name of the codec diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 8b075ef..0c072ff 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -794,15 +794,19 @@ static int fsl_ssi_probe(struct platform_device *pdev) &ssi_private->filter_data_tx; ssi_private->dma_params_rx.filter_data = &ssi_private->filter_data_rx; - /* - * TODO: This is a temporary solution and should be changed - * to use generic DMA binding later when the helplers get in. - */ - ret = of_property_read_u32_array(pdev->dev.of_node, + if (!of_property_read_bool(pdev->dev.of_node, "dmas") && + ssi_private->use_dma) { + /* + * FIXME: This is a temporary solution until all + * necessary dma drivers support the generic dma + * bindings. + */ + ret = of_property_read_u32_array(pdev->dev.of_node, "fsl,ssi-dma-events", dma_events, 2); - if (ret && !ssi_private->use_dma) { - dev_err(&pdev->dev, "could not get dma events\n"); - goto error_clk; + if (ret && ssi_private->use_dma) { + dev_err(&pdev->dev, "could not get dma events but fsl-ssi is configured to use DMA\n"); + goto error_clk; + } } shared = of_device_is_compatible(of_get_parent(np), -- cgit v0.10.2 From fdbcb3cba54b29a37dfe42acdc0e72c543e0807d Mon Sep 17 00:00:00 2001 From: Nicolas Ferre Date: Tue, 30 Jul 2013 12:32:03 +0200 Subject: ASoC: atmel: machine driver for at91sam9x5-wm8731 boards Description of the Asoc machine driver for an at91sam9x5 based board with a wm8731 audio DAC. Wm8731 is clocked by a crystal and used as a master on the SSC/I2S interface. Its connections are a headphone jack and an Line input jack. [Richard: this is based on an old patch from Nicolas that I forward ported and reworked to use only device tree] Signed-off-by: Nicolas Ferre Signed-off-by: Richard Genoud Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/sound/atmel-sam9x5-wm8731-audio.txt b/Documentation/devicetree/bindings/sound/atmel-sam9x5-wm8731-audio.txt new file mode 100644 index 0000000..0720857 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/atmel-sam9x5-wm8731-audio.txt @@ -0,0 +1,35 @@ +* Atmel at91sam9x5ek wm8731 audio complex + +Required properties: + - compatible: "atmel,sam9x5-wm8731-audio" + - atmel,model: The user-visible name of this sound complex. + - atmel,ssc-controller: The phandle of the SSC controller + - atmel,audio-codec: The phandle of the WM8731 audio codec + - atmel,audio-routing: A list of the connections between audio components. + Each entry is a pair of strings, the first being the connection's sink, + the second being the connection's source. + +Available audio endpoints for the audio-routing table: + +Board connectors: + * Headphone Jack + * Line In Jack + +wm8731 pins: +cf Documentation/devicetree/bindings/sound/wm8731.txt + +Example: +sound { + compatible = "atmel,sam9x5-wm8731-audio"; + + atmel,model = "wm8731 @ AT91SAM9X5EK"; + + atmel,audio-routing = + "Headphone Jack", "RHPOUT", + "Headphone Jack", "LHPOUT", + "LLINEIN", "Line In Jack", + "RLINEIN", "Line In Jack"; + + atmel,ssc-controller = <&ssc0>; + atmel,audio-codec = <&wm8731>; +}; diff --git a/Documentation/devicetree/bindings/sound/wm8731.txt b/Documentation/devicetree/bindings/sound/wm8731.txt index 15f7004..236690e 100644 --- a/Documentation/devicetree/bindings/sound/wm8731.txt +++ b/Documentation/devicetree/bindings/sound/wm8731.txt @@ -16,3 +16,12 @@ codec: wm8731@1a { compatible = "wlf,wm8731"; reg = <0x1a>; }; + +Available audio endpoints for an audio-routing table: + * LOUT: Left Channel Line Output + * ROUT: Right Channel Line Output + * LHPOUT: Left Channel Headphone Output + * RHPOUT: Right Channel Headphone Output + * LLINEIN: Left Channel Line Input + * RLINEIN: Right Channel Line Input + * MICIN: Microphone Input diff --git a/sound/soc/atmel/Kconfig b/sound/soc/atmel/Kconfig index 986323b..e48d38a 100644 --- a/sound/soc/atmel/Kconfig +++ b/sound/soc/atmel/Kconfig @@ -43,6 +43,16 @@ config SND_ATMEL_SOC_WM8904 Say Y if you want to add support for Atmel ASoC driver for boards using WM8904 codec. +config SND_AT91_SOC_SAM9X5_WM8731 + tristate "SoC Audio support for WM8731-based at91sam9x5 board" + depends on ATMEL_SSC && SND_ATMEL_SOC && SOC_AT91SAM9X5 + select SND_ATMEL_SOC_SSC + select SND_ATMEL_SOC_DMA + select SND_SOC_WM8731 + help + Say Y if you want to add support for audio SoC on an + at91sam9x5 based board that is using WM8731 codec. + config SND_AT91_SOC_AFEB9260 tristate "SoC Audio support for AFEB9260 board" depends on ARCH_AT91 && ATMEL_SSC && ARCH_AT91 && MACH_AFEB9260 && SND_ATMEL_SOC diff --git a/sound/soc/atmel/Makefile b/sound/soc/atmel/Makefile index 922d4da..5baabc8 100644 --- a/sound/soc/atmel/Makefile +++ b/sound/soc/atmel/Makefile @@ -12,7 +12,9 @@ obj-$(CONFIG_SND_ATMEL_SOC_SSC) += snd-soc-atmel_ssc_dai.o # AT91 Machine Support snd-soc-sam9g20-wm8731-objs := sam9g20_wm8731.o snd-atmel-soc-wm8904-objs := atmel_wm8904.o +snd-soc-sam9x5-wm8731-objs := sam9x5_wm8731.o obj-$(CONFIG_SND_AT91_SOC_SAM9G20_WM8731) += snd-soc-sam9g20-wm8731.o obj-$(CONFIG_SND_ATMEL_SOC_WM8904) += snd-atmel-soc-wm8904.o +obj-$(CONFIG_SND_AT91_SOC_SAM9X5_WM8731) += snd-soc-sam9x5-wm8731.o obj-$(CONFIG_SND_AT91_SOC_AFEB9260) += snd-soc-afeb9260.o diff --git a/sound/soc/atmel/sam9x5_wm8731.c b/sound/soc/atmel/sam9x5_wm8731.c new file mode 100644 index 0000000..992ae38 --- /dev/null +++ b/sound/soc/atmel/sam9x5_wm8731.c @@ -0,0 +1,208 @@ +/* + * sam9x5_wm8731 -- SoC audio for AT91SAM9X5-based boards + * that are using WM8731 as codec. + * + * Copyright (C) 2011 Atmel, + * Nicolas Ferre + * + * Copyright (C) 2013 Paratronic, + * Richard Genoud + * + * Based on sam9g20_wm8731.c by: + * Sedji Gaouaou + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "../codecs/wm8731.h" +#include "atmel_ssc_dai.h" + + +#define MCLK_RATE 12288000 + +#define DRV_NAME "sam9x5-snd-wm8731" + +struct sam9x5_drvdata { + int ssc_id; +}; + +/* + * Logic for a wm8731 as connected on a at91sam9x5ek based board. + */ +static int sam9x5_wm8731_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct device *dev = rtd->dev; + int ret; + + dev_dbg(dev, "ASoC: %s called\n", __func__); + + /* set the codec system clock for DAC and ADC */ + ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK_XTAL, + MCLK_RATE, SND_SOC_CLOCK_IN); + if (ret < 0) { + dev_err(dev, "ASoC: Failed to set WM8731 SYSCLK: %d\n", ret); + return ret; + } + + return 0; +} + +/* + * Audio paths on at91sam9x5ek board: + * + * |A| ------------> | | ---R----> Headphone Jack + * |T| <----\ | WM | ---L--/ + * |9| ---> CLK <--> | 8731 | <--R----- Line In Jack + * |1| <------------ | | <--L--/ + */ +static const struct snd_soc_dapm_widget sam9x5_dapm_widgets[] = { + SND_SOC_DAPM_HP("Headphone Jack", NULL), + SND_SOC_DAPM_LINE("Line In Jack", NULL), +}; + +static int sam9x5_wm8731_driver_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct device_node *codec_np, *cpu_np; + struct snd_soc_card *card; + struct snd_soc_dai_link *dai; + struct sam9x5_drvdata *priv; + int ret; + + if (!np) { + dev_err(&pdev->dev, "No device node supplied\n"); + return -EINVAL; + } + + card = devm_kzalloc(&pdev->dev, sizeof(*card), GFP_KERNEL); + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + dai = devm_kzalloc(&pdev->dev, sizeof(*dai), GFP_KERNEL); + if (!dai || !card || !priv) { + ret = -ENOMEM; + goto out; + } + + card->dev = &pdev->dev; + card->owner = THIS_MODULE; + card->dai_link = dai; + card->num_links = 1; + card->dapm_widgets = sam9x5_dapm_widgets; + card->num_dapm_widgets = ARRAY_SIZE(sam9x5_dapm_widgets); + dai->name = "WM8731"; + dai->stream_name = "WM8731 PCM"; + dai->codec_dai_name = "wm8731-hifi"; + dai->init = sam9x5_wm8731_init; + dai->dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF + | SND_SOC_DAIFMT_CBM_CFM; + + ret = snd_soc_of_parse_card_name(card, "atmel,model"); + if (ret) { + dev_err(&pdev->dev, "atmel,model node missing\n"); + goto out; + } + + ret = snd_soc_of_parse_audio_routing(card, "atmel,audio-routing"); + if (ret) { + dev_err(&pdev->dev, "atmel,audio-routing node missing\n"); + goto out; + } + + codec_np = of_parse_phandle(np, "atmel,audio-codec", 0); + if (!codec_np) { + dev_err(&pdev->dev, "atmel,audio-codec node missing\n"); + ret = -EINVAL; + goto out; + } + + dai->codec_of_node = codec_np; + + cpu_np = of_parse_phandle(np, "atmel,ssc-controller", 0); + if (!cpu_np) { + dev_err(&pdev->dev, "atmel,ssc-controller node missing\n"); + ret = -EINVAL; + goto out; + } + dai->cpu_of_node = cpu_np; + dai->platform_of_node = cpu_np; + + priv->ssc_id = of_alias_get_id(cpu_np, "ssc"); + + ret = atmel_ssc_set_audio(priv->ssc_id); + if (ret != 0) { + dev_err(&pdev->dev, + "ASoC: Failed to set SSC %d for audio: %d\n", + ret, priv->ssc_id); + goto out; + } + + of_node_put(codec_np); + of_node_put(cpu_np); + + platform_set_drvdata(pdev, card); + + ret = snd_soc_register_card(card); + if (ret) { + dev_err(&pdev->dev, + "ASoC: Platform device allocation failed\n"); + goto out_put_audio; + } + + dev_dbg(&pdev->dev, "ASoC: %s ok\n", __func__); + + return ret; + +out_put_audio: + atmel_ssc_put_audio(priv->ssc_id); +out: + return ret; +} + +static int sam9x5_wm8731_driver_remove(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + struct sam9x5_drvdata *priv = card->drvdata; + + snd_soc_unregister_card(card); + atmel_ssc_put_audio(priv->ssc_id); + + return 0; +} + +static const struct of_device_id sam9x5_wm8731_of_match[] = { + { .compatible = "atmel,sam9x5-wm8731-audio", }, + {}, +}; +MODULE_DEVICE_TABLE(of, sam9x5_wm8731_of_match); + +static struct platform_driver sam9x5_wm8731_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(sam9x5_wm8731_of_match), + }, + .probe = sam9x5_wm8731_driver_probe, + .remove = sam9x5_wm8731_driver_remove, +}; +module_platform_driver(sam9x5_wm8731_driver); + +/* Module information */ +MODULE_AUTHOR("Nicolas Ferre "); +MODULE_AUTHOR("Richard Genoud "); +MODULE_DESCRIPTION("ALSA SoC machine driver for AT91SAM9x5 - WM8731"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_NAME); -- cgit v0.10.2 From f813175aa54367f455d8bb96f8f5ac2cec924528 Mon Sep 17 00:00:00 2001 From: Richard Genoud Date: Tue, 30 Jul 2013 12:32:04 +0200 Subject: ASoC: atmel: update atmel SSC with DMA binding As atmel-ssc can be used with DMA, the documentation should be updated. Also, a configuration DMA example is given. Signed-off-by: Richard Genoud Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/misc/atmel-ssc.txt b/Documentation/devicetree/bindings/misc/atmel-ssc.txt index 38e51ad..a45ae08 100644 --- a/Documentation/devicetree/bindings/misc/atmel-ssc.txt +++ b/Documentation/devicetree/bindings/misc/atmel-ssc.txt @@ -7,9 +7,30 @@ Required properties: - reg: Should contain SSC registers location and length - interrupts: Should contain SSC interrupt -Example: + +Required properties for devices compatible with "atmel,at91sam9g45-ssc": +- dmas: DMA specifier, consisting of a phandle to DMA controller node, + the memory interface and SSC DMA channel ID (for tx and rx). + See Documentation/devicetree/bindings/dma/atmel-dma.txt for details. +- dma-names: Must be "tx", "rx". + +Examples: +- PDC transfer: ssc0: ssc@fffbc000 { compatible = "atmel,at91rm9200-ssc"; reg = <0xfffbc000 0x4000>; interrupts = <14 4 5>; }; + +- DMA transfer: +ssc0: ssc@f0010000 { + compatible = "atmel,at91sam9g45-ssc"; + reg = <0xf0010000 0x4000>; + interrupts = <28 4 5>; + dmas = <&dma0 1 13>, + <&dma0 1 14>; + dma-names = "tx", "rx"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_ssc0_tx &pinctrl_ssc0_rx>; + status = "disabled"; +}; -- cgit v0.10.2 From 9f19de649f70c3bd32da09fc08643d4fca1d06fe Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Tue, 6 Aug 2013 18:03:07 -0300 Subject: ASoC: imx-mc13783: Make SND_SOC_IMX_MC13783 visible again Commit 02502da45 (ASoC: imx-mc13783: Depend on ARCH_ARM) introduced 'ARCH_ARM' as a dependency for SND_SOC_IMX_MC13783, but this is a non-existent symbol. This makes the selection of SND_SOC_IMX_MC13783 to be impossible. Use the correct 'ARM' symbol instead. Signed-off-by: Fabio Estevam Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index c26449b..e15f771 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig @@ -194,7 +194,7 @@ config SND_SOC_IMX_SGTL5000 config SND_SOC_IMX_MC13783 tristate "SoC Audio support for I.MX boards with mc13783" - depends on MFD_MC13783 && ARCH_ARM + depends on MFD_MC13783 && ARM select SND_SOC_IMX_SSI select SND_SOC_IMX_AUDMUX select SND_SOC_MC13783 -- cgit v0.10.2 From 9e7e474c0963dfd1f60b200160ff9e7cefb32b06 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 6 Aug 2013 23:51:12 +0100 Subject: ASoC: ad1980: Provide stub DAPM support Since non-DAPM devices are not going to be supported provide DAPM input and output widgets and hook them up to the DAIs. Signed-off-by: Mark Brown Acked-by: Lars-Peter Clausen diff --git a/sound/soc/codecs/ad1980.c b/sound/soc/codecs/ad1980.c index 89fcf7d..7257a88 100644 --- a/sound/soc/codecs/ad1980.c +++ b/sound/soc/codecs/ad1980.c @@ -96,6 +96,44 @@ SOC_ENUM("Capture Source", ad1980_cap_src), SOC_SINGLE("Mic Boost Switch", AC97_MIC, 6, 1, 0), }; +static const struct snd_soc_dapm_widget ad1980_dapm_widgets[] = { +SND_SOC_DAPM_INPUT("MIC1"), +SND_SOC_DAPM_INPUT("MIC2"), +SND_SOC_DAPM_INPUT("CD_L"), +SND_SOC_DAPM_INPUT("CD_R"), +SND_SOC_DAPM_INPUT("AUX_L"), +SND_SOC_DAPM_INPUT("AUX_R"), +SND_SOC_DAPM_INPUT("LINE_IN_L"), +SND_SOC_DAPM_INPUT("LINE_IN_R"), + +SND_SOC_DAPM_OUTPUT("LFE_OUT"), +SND_SOC_DAPM_OUTPUT("CENTER_OUT"), +SND_SOC_DAPM_OUTPUT("LINE_OUT_L"), +SND_SOC_DAPM_OUTPUT("LINE_OUT_R"), +SND_SOC_DAPM_OUTPUT("MONO_OUT"), +SND_SOC_DAPM_OUTPUT("HP_OUT_L"), +SND_SOC_DAPM_OUTPUT("HP_OUT_R"), +}; + +static const struct snd_soc_dapm_route ad1980_dapm_routes[] = { + { "Capture", NULL, "MIC1" }, + { "Capture", NULL, "MIC2" }, + { "Capture", NULL, "CD_L" }, + { "Capture", NULL, "CD_R" }, + { "Capture", NULL, "AUX_L" }, + { "Capture", NULL, "AUX_R" }, + { "Capture", NULL, "LINE_IN_L" }, + { "Capture", NULL, "LINE_IN_R" }, + + { "LFE_OUT", NULL, "Playback" }, + { "CENTER_OUT", NULL, "Playback" }, + { "LINE_OUT_L", NULL, "Playback" }, + { "LINE_OUT_R", NULL, "Playback" }, + { "MONO_OUT", NULL, "Playback" }, + { "HP_OUT_L", NULL, "Playback" }, + { "HP_OUT_R", NULL, "Playback" }, +}; + static unsigned int ac97_read(struct snd_soc_codec *codec, unsigned int reg) { @@ -253,6 +291,11 @@ static struct snd_soc_codec_driver soc_codec_dev_ad1980 = { .reg_cache_step = 2, .write = ac97_write, .read = ac97_read, + + .dapm_widgets = ad1980_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(ad1980_dapm_widgets), + .dapm_routes = ad1980_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(ad1980_dapm_routes), }; static int ad1980_probe(struct platform_device *pdev) -- cgit v0.10.2 From 45a14a8b50465a6ce61005f7fe9f3fd5c06823d5 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 7 Aug 2013 18:24:09 +0100 Subject: ASoC: ads711x: Add DAPM support This makes it easier to hook into boards and ensures the driver continues to work with support for non-DAPM CODECs removed. Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/ads117x.c b/sound/soc/codecs/ads117x.c index 506d474..8f388ed 100644 --- a/sound/soc/codecs/ads117x.c +++ b/sound/soc/codecs/ads117x.c @@ -23,6 +23,28 @@ #define ADS117X_RATES (SNDRV_PCM_RATE_8000_48000) #define ADS117X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE) +static const struct snd_soc_dapm_widget ads117x_dapm_widgets[] = { +SND_SOC_DAPM_INPUT("Input1"), +SND_SOC_DAPM_INPUT("Input2"), +SND_SOC_DAPM_INPUT("Input3"), +SND_SOC_DAPM_INPUT("Input4"), +SND_SOC_DAPM_INPUT("Input5"), +SND_SOC_DAPM_INPUT("Input6"), +SND_SOC_DAPM_INPUT("Input7"), +SND_SOC_DAPM_INPUT("Input8"), +}; + +static const struct snd_soc_dapm_route ads117x_dapm_routes[] = { + { "Capture", NULL, "Input1" }, + { "Capture", NULL, "Input2" }, + { "Capture", NULL, "Input3" }, + { "Capture", NULL, "Input4" }, + { "Capture", NULL, "Input5" }, + { "Capture", NULL, "Input6" }, + { "Capture", NULL, "Input7" }, + { "Capture", NULL, "Input8" }, +}; + static struct snd_soc_dai_driver ads117x_dai = { /* ADC */ .name = "ads117x-hifi", @@ -34,7 +56,12 @@ static struct snd_soc_dai_driver ads117x_dai = { .formats = ADS117X_FORMATS,}, }; -static struct snd_soc_codec_driver soc_codec_dev_ads117x; +static struct snd_soc_codec_driver soc_codec_dev_ads117x = { + .dapm_widgets = ads117x_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(ads117x_dapm_widgets), + .dapm_routes = ads117x_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(ads117x_dapm_routes), +}; static int ads117x_probe(struct platform_device *pdev) { -- cgit v0.10.2 From d33183584f1239ba70b6483b2ae8d78c38ab9a8d Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 31 Jul 2013 12:37:58 +0100 Subject: ASoC: dt: Move WM8903 pin list from Tegra board binding to CODEC binding The pin list is the same for any board using the CODEC. Signed-off-by: Mark Brown Acked-by: Stephen Warren Acked-by: Mark Rutland diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm8903.txt b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm8903.txt index 3bf722d..4b44dfb 100644 --- a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm8903.txt +++ b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm8903.txt @@ -11,28 +11,8 @@ Required properties: - nvidia,audio-routing : A list of the connections between audio components. Each entry is a pair of strings, the first being the connection's sink, the second being the connection's source. Valid names for sources and - sinks are the WM8903's pins, and the jacks on the board: - - WM8903 pins: - - * IN1L - * IN1R - * IN2L - * IN2R - * IN3L - * IN3R - * DMICDAT - * HPOUTL - * HPOUTR - * LINEOUTL - * LINEOUTR - * LOP - * LON - * ROP - * RON - * MICBIAS - - Board connectors: + sinks are the WM8903's pins (documented in the WM8903 binding document), + and the jacks on the board: * Headphone Jack * Int Spk diff --git a/Documentation/devicetree/bindings/sound/wm8903.txt b/Documentation/devicetree/bindings/sound/wm8903.txt index f102cbc..94ec32c 100644 --- a/Documentation/devicetree/bindings/sound/wm8903.txt +++ b/Documentation/devicetree/bindings/sound/wm8903.txt @@ -28,6 +28,25 @@ Optional properties: performed. If any entry has the value 0xffffffff, that GPIO's configuration will not be modified. +Pins on the device (for linking into audio routes): + + * IN1L + * IN1R + * IN2L + * IN2R + * IN3L + * IN3R + * DMICDAT + * HPOUTL + * HPOUTR + * LINEOUTL + * LINEOUTR + * LOP + * LON + * ROP + * RON + * MICBIAS + Example: codec: wm8903@1a { -- cgit v0.10.2 From 663819fb7d7e21f45431db1a2c0180bf2388ed2f Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 7 Aug 2013 17:55:14 +0300 Subject: ALSA: don't push static constants on stack for %*ph There is no need to pass constants via stack. The width may be explicitly specified in the format. Signed-off-by: Andy Shevchenko Signed-off-by: Takashi Iwai diff --git a/sound/isa/gus/interwave.c b/sound/isa/gus/interwave.c index 9942691..afef0d7 100644 --- a/sound/isa/gus/interwave.c +++ b/sound/isa/gus/interwave.c @@ -443,8 +443,7 @@ static void snd_interwave_detect_memory(struct snd_gus_card *gus) for (i = 0; i < 8; ++i) iwave[i] = snd_gf1_peek(gus, bank_pos + i); #ifdef CONFIG_SND_DEBUG_ROM - printk(KERN_DEBUG "ROM at 0x%06x = %*phC\n", bank_pos, - 8, iwave); + printk(KERN_DEBUG "ROM at 0x%06x = %8phC\n", bank_pos, iwave); #endif if (strncmp(iwave, "INTRWAVE", 8)) continue; /* first check */ diff --git a/sound/usb/6fire/firmware.c b/sound/usb/6fire/firmware.c index b9defcd..780bf3f 100644 --- a/sound/usb/6fire/firmware.c +++ b/sound/usb/6fire/firmware.c @@ -346,10 +346,10 @@ static int usb6fire_fw_check(u8 *version) if (!memcmp(version, known_fw_versions + i, 2)) return 0; - snd_printk(KERN_ERR PREFIX "invalid fimware version in device: %*ph. " + snd_printk(KERN_ERR PREFIX "invalid fimware version in device: %4ph. " "please reconnect to power. if this failure " "still happens, check your firmware installation.", - 4, version); + version); return -EINVAL; } -- cgit v0.10.2 From 16695971bec3b8b2398f7ab8dfa4c5a22bfcf95d Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 8 Aug 2013 12:25:57 +0100 Subject: ASoC: pcm1681: Staticise DAI driver It is not exported so doesn't need to be in the global namespace and sparse warns on this. Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/pcm1681.c b/sound/soc/codecs/pcm1681.c index 27da41b..51b1866 100644 --- a/sound/soc/codecs/pcm1681.c +++ b/sound/soc/codecs/pcm1681.c @@ -225,7 +225,7 @@ static const struct snd_kcontrol_new pcm1681_controls[] = { pcm1681_get_deemph, pcm1681_put_deemph), }; -struct snd_soc_dai_driver pcm1681_dai = { +static struct snd_soc_dai_driver pcm1681_dai = { .name = "pcm1681-hifi", .playback = { .stream_name = "Playback", -- cgit v0.10.2 From 827d22f13618557bd35f938b020c954d83a82977 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 7 Aug 2013 17:59:51 +0100 Subject: ASoC: ad73311: Add DAPM support This makes it possible to hook up other devices in boards and is required by removal of support for non-DAPM CODECs in the core. Signed-off-by: Mark Brown Acked-by: Lars-Peter Clausen diff --git a/sound/soc/codecs/ad73311.c b/sound/soc/codecs/ad73311.c index b1f2baf..5fac8ad 100644 --- a/sound/soc/codecs/ad73311.c +++ b/sound/soc/codecs/ad73311.c @@ -23,6 +23,21 @@ #include "ad73311.h" +static const struct snd_soc_dapm_widget ad73311_dapm_widgets[] = { +SND_SOC_DAPM_INPUT("VINP"), +SND_SOC_DAPM_INPUT("VINN"), +SND_SOC_DAPM_OUTPUT("VOUTN"), +SND_SOC_DAPM_OUTPUT("VOUTP"), +}; + +static const struct snd_soc_dapm_route ad73311_dapm_routes[] = { + { "Capture", NULL, "VINP" }, + { "Capture", NULL, "VINN" }, + + { "VOUTN", NULL, "Playback" }, + { "VOUTP", NULL, "Playback" }, +}; + static struct snd_soc_dai_driver ad73311_dai = { .name = "ad73311-hifi", .playback = { @@ -39,7 +54,12 @@ static struct snd_soc_dai_driver ad73311_dai = { .formats = SNDRV_PCM_FMTBIT_S16_LE, }, }; -static struct snd_soc_codec_driver soc_codec_dev_ad73311; +static struct snd_soc_codec_driver soc_codec_dev_ad73311 = { + .dapm_widgets = ad73311_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(ad73311_dapm_widgets), + .dapm_routes = ad73311_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(ad73311_dapm_routes), +}; static int ad73311_probe(struct platform_device *pdev) { -- cgit v0.10.2 From ae48f5efb8b33af3d8b4201250ae8c3cab551557 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 7 Aug 2013 18:01:08 +0100 Subject: ASoC: ad73311: Add to list of of Analog Devices supported CODECs Signed-off-by: Mark Brown Acked-by: Lars-Peter Clausen diff --git a/MAINTAINERS b/MAINTAINERS index defc053..6c398bb 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -595,6 +595,7 @@ S: Supported F: sound/soc/codecs/adau* F: sound/soc/codecs/adav* F: sound/soc/codecs/ad1* +F: sound/soc/codecs/ad7* F: sound/soc/codecs/ssm* F: sound/soc/codecs/sigmadsp.* -- cgit v0.10.2 From 74b45231b23d47b041b137737241d482481a76a9 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Thu, 8 Aug 2013 16:52:18 +0530 Subject: ASoC: s6105-ipcam: Fix incorrect placement of __initdata __initdata should be placed between the variable name and equal sign for the variable to be placed in the intended section. Signed-off-by: Sachin Kamat Signed-off-by: Mark Brown diff --git a/sound/soc/s6000/s6105-ipcam.c b/sound/soc/s6000/s6105-ipcam.c index 58cfb1e..945e8ab 100644 --- a/sound/soc/s6000/s6105-ipcam.c +++ b/sound/soc/s6000/s6105-ipcam.c @@ -192,7 +192,7 @@ static struct snd_soc_card snd_soc_card_s6105 = { .num_links = 1, }; -static struct s6000_snd_platform_data __initdata s6105_snd_data = { +static struct s6000_snd_platform_data s6105_snd_data __initdata = { .wide = 0, .channel_in = 0, .channel_out = 1, -- cgit v0.10.2 From f5cb0be917ff30ef1df0aab5eb4cb269713c05e3 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 9 Aug 2013 12:57:06 +0300 Subject: sound: oss/dmabuf: remove an unneeded temporary variable We don't actually use the "go" variable so it can be removed. Signed-off-by: Dan Carpenter Signed-off-by: Takashi Iwai diff --git a/sound/oss/dmabuf.c b/sound/oss/dmabuf.c index a59c888..461d94c 100644 --- a/sound/oss/dmabuf.c +++ b/sound/oss/dmabuf.c @@ -557,7 +557,6 @@ int DMAbuf_getrdbuffer(int dev, char **buf, int *len, int dontblock) unsigned long flags; int err = 0, n = 0; struct dma_buffparms *dmap = adev->dmap_in; - int go; if (!(adev->open_mode & OPEN_READ)) return -EIO; @@ -584,7 +583,7 @@ int DMAbuf_getrdbuffer(int dev, char **buf, int *len, int dontblock) spin_unlock_irqrestore(&dmap->lock,flags); return -EAGAIN; } - if ((go = adev->go)) + if (adev->go) timeout = dmabuf_timeout(dmap); spin_unlock_irqrestore(&dmap->lock,flags); -- cgit v0.10.2 From 34d2f1b6feac3bc7e6022d30d624e9f3687717d3 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sat, 10 Aug 2013 09:53:14 +0200 Subject: ASoC: Remove unused soc_pm_waitq The soc_pm_waitq waitqueue has been around as long as the ASoC framework existed, but has never been used so far, so remove it. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 2940e2c..c7d16df 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -47,8 +47,6 @@ #define NAME_SIZE 32 -static DECLARE_WAIT_QUEUE_HEAD(soc_pm_waitq); - #ifdef CONFIG_DEBUG_FS struct dentry *snd_soc_debugfs_root; EXPORT_SYMBOL_GPL(snd_soc_debugfs_root); -- cgit v0.10.2 From 33bbe1499cb3eebb4d5f66d56ed8026d0bc56d63 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 9 Aug 2013 18:10:50 +0100 Subject: ALSA: Add MAINTAINERS entry for compressed audio API Help ensure that Vinod gets included in review of compressed audio patches by adding a MAINTAINERS entry for it. Signed-off-by: Mark Brown Signed-off-by: Takashi Iwai diff --git a/MAINTAINERS b/MAINTAINERS index bf61e04..b4d1de9 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7655,6 +7655,15 @@ F: include/sound/ F: include/uapi/sound/ F: sound/ +SOUND - COMPRESSED AUDIO +M: Vinod Koul +L: alsa-devel@alsa-project.org (moderated for non-subscribers) +T: git git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound.git +S: Supported +F: include/sound/compress_driver.h +F: sound/core/compress_offload.c +F: sound/soc/soc-compress.c + SOUND - SOC LAYER / DYNAMIC AUDIO POWER MANAGEMENT (ASoC) M: Liam Girdwood M: Mark Brown -- cgit v0.10.2 From 9c0aeaa3849150acaaf016202c6741d542b3c1df Mon Sep 17 00:00:00 2001 From: Markus Pargmann Date: Sat, 10 Aug 2013 14:55:57 +0200 Subject: ASoC: imx-audmux: default configuration parser fixups Signed-off-by: Markus Pargmann Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/imx-audmux.c b/sound/soc/fsl/imx-audmux.c index 103d1b0..ab17381 100644 --- a/sound/soc/fsl/imx-audmux.c +++ b/sound/soc/fsl/imx-audmux.c @@ -278,7 +278,7 @@ static int imx_audmux_parse_dt_defaults(struct platform_device *pdev, } for (i = 0; (ret = of_property_read_u32_index(child, - "fsl,port-config\n", i, &val)) == 0; + "fsl,port-config", i, &val)) == 0; ++i) { if (audmux_type == IMX31_AUDMUX) { if (i % 2) @@ -290,7 +290,7 @@ static int imx_audmux_parse_dt_defaults(struct platform_device *pdev, } } - if (ret != -ENODATA) { + if (ret != -EOVERFLOW) { dev_err(&pdev->dev, "Failed to read u32 at index %d of child %s\n", i, child->full_name); continue; -- cgit v0.10.2 From c77f872e663e3f6ea18f774bf4399884884b4b22 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sat, 10 Aug 2013 21:33:09 +0200 Subject: ASoC: Remove unused snd_soc_info_volsw_ext() The SOC_SINGLE_EXT control has been using snd_soc_info_volsw() for its info callback since commit 1c433fb ("[ALSA] soc - 0.13 ASoC headers"). The snd_soc_info_volsw_ext() function has been unused ever since then, so remove it. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/include/sound/soc.h b/include/sound/soc.h index 6eabee7..724a42a 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -497,8 +497,6 @@ int snd_soc_put_value_enum_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); int snd_soc_info_volsw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo); -int snd_soc_info_volsw_ext(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo); #define snd_soc_info_bool_ext snd_ctl_boolean_mono_info int snd_soc_get_volsw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index c7d16df..6ba5f7c 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -2578,32 +2578,6 @@ int snd_soc_info_enum_ext(struct snd_kcontrol *kcontrol, EXPORT_SYMBOL_GPL(snd_soc_info_enum_ext); /** - * snd_soc_info_volsw_ext - external single mixer info callback - * @kcontrol: mixer control - * @uinfo: control element information - * - * Callback to provide information about a single external mixer control. - * - * Returns 0 for success. - */ -int snd_soc_info_volsw_ext(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - int max = kcontrol->private_value; - - if (max == 1 && !strstr(kcontrol->id.name, " Volume")) - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - else - uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = max; - return 0; -} -EXPORT_SYMBOL_GPL(snd_soc_info_volsw_ext); - -/** * snd_soc_info_volsw - single mixer info callback * @kcontrol: mixer control * @uinfo: control element information -- cgit v0.10.2 From 9a953e6f27fd280a2af5719b77394fbb228c5b46 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sat, 10 Aug 2013 21:33:10 +0200 Subject: ASoC: Use snd_soc_info_enum_double() for SOC_ENUM_EXT controls snd_soc_info_enum_ext() and snd_soc_info_enum_double() are almost identical. The only difference is that snd_soc_info_enum_double() is also able to handle stereo controls. Using snd_soc_info_enum double() instead of snd_soc_info_enum_ext() for the SOC_ENUM_EXT control's info callback allows us to remove snd_soc_info_enum_ext(). Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/include/sound/soc.h b/include/sound/soc.h index 724a42a..6f86a41 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -234,7 +234,7 @@ .private_value = xdata } #define SOC_ENUM_EXT(xname, xenum, xhandler_get, xhandler_put) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ - .info = snd_soc_info_enum_ext, \ + .info = snd_soc_info_enum_double, \ .get = xhandler_get, .put = xhandler_put, \ .private_value = (unsigned long)&xenum } @@ -485,8 +485,6 @@ int snd_soc_add_dai_controls(struct snd_soc_dai *dai, const struct snd_kcontrol_new *controls, int num_controls); int snd_soc_info_enum_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo); -int snd_soc_info_enum_ext(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo); int snd_soc_get_enum_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); int snd_soc_put_enum_double(struct snd_kcontrol *kcontrol, diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 6ba5f7c..f46472d 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -2551,33 +2551,6 @@ int snd_soc_put_value_enum_double(struct snd_kcontrol *kcontrol, EXPORT_SYMBOL_GPL(snd_soc_put_value_enum_double); /** - * snd_soc_info_enum_ext - external enumerated single mixer info callback - * @kcontrol: mixer control - * @uinfo: control element information - * - * Callback to provide information about an external enumerated - * single mixer. - * - * Returns 0 for success. - */ -int snd_soc_info_enum_ext(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; - - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = e->max; - - if (uinfo->value.enumerated.item > e->max - 1) - uinfo->value.enumerated.item = e->max - 1; - strcpy(uinfo->value.enumerated.name, - e->texts[uinfo->value.enumerated.item]); - return 0; -} -EXPORT_SYMBOL_GPL(snd_soc_info_enum_ext); - -/** * snd_soc_info_volsw - single mixer info callback * @kcontrol: mixer control * @uinfo: control element information -- cgit v0.10.2 From 439fe8a7bb07f8394fef03d7aa4f207166a32b88 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sun, 11 Aug 2013 13:26:43 +0100 Subject: ASoC: max9768: Add DAPM support This makes it possible to hook the device into a more complex board and ensures it will continue to work with non-DAPM support removed from the core. Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/max9768.c b/sound/soc/codecs/max9768.c index a6ac231..31f9156 100644 --- a/sound/soc/codecs/max9768.c +++ b/sound/soc/codecs/max9768.c @@ -118,6 +118,18 @@ static const struct snd_kcontrol_new max9768_mute[] = { SOC_SINGLE_BOOL_EXT("Playback Switch", 0, max9768_get_gpio, max9768_set_gpio), }; +static const struct snd_soc_dapm_widget max9768_dapm_widgets[] = { +SND_SOC_DAPM_INPUT("IN"), + +SND_SOC_DAPM_OUTPUT("OUT+"), +SND_SOC_DAPM_OUTPUT("OUT-"), +}; + +static const struct snd_soc_dapm_route max9768_dapm_routes[] = { + { "OUT+", NULL, "IN" }, + { "OUT-", NULL, "IN" }, +}; + static int max9768_probe(struct snd_soc_codec *codec) { struct max9768 *max9768 = snd_soc_codec_get_drvdata(codec); @@ -148,6 +160,10 @@ static struct snd_soc_codec_driver max9768_codec_driver = { .probe = max9768_probe, .controls = max9768_volume, .num_controls = ARRAY_SIZE(max9768_volume), + .dapm_widgets = max9768_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(max9768_dapm_widgets), + .dapm_routes = max9768_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(max9768_dapm_routes), }; static const struct regmap_config max9768_i2c_regmap_config = { -- cgit v0.10.2 From bad268f3504e2a58e406c3f0e282c1de629bd42f Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sun, 11 Aug 2013 13:12:13 +0100 Subject: ASoC: cs4271: Convert to table based control init Signed-off-by: Mark Brown Acked-by: Alexander Sverdlin diff --git a/sound/soc/codecs/cs4271.c b/sound/soc/codecs/cs4271.c index 03036b3..65ad56c 100644 --- a/sound/soc/codecs/cs4271.c +++ b/sound/soc/codecs/cs4271.c @@ -576,8 +576,7 @@ static int cs4271_probe(struct snd_soc_codec *codec) CS4271_MODE2_MUTECAEQUB, CS4271_MODE2_MUTECAEQUB); - return snd_soc_add_codec_controls(codec, cs4271_snd_controls, - ARRAY_SIZE(cs4271_snd_controls)); + return 0; } static int cs4271_remove(struct snd_soc_codec *codec) @@ -596,6 +595,9 @@ static struct snd_soc_codec_driver soc_codec_dev_cs4271 = { .remove = cs4271_remove, .suspend = cs4271_soc_suspend, .resume = cs4271_soc_resume, + + .controls = cs4271_snd_controls, + .num_controls = ARRAY_SIZE(cs4271_snd_controls), }; #if defined(CONFIG_SPI_MASTER) -- cgit v0.10.2 From 2e7fb942a30563125d6aac497fa0dcddbb7d731d Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sun, 11 Aug 2013 13:15:10 +0100 Subject: ASoC: cs4271: Add DAPM support This makes it possible to hook the device into a more complex board and ensures it will continue to work with non-DAPM support removed from the core. Signed-off-by: Mark Brown Acked-by: Alexander Sverdlin diff --git a/sound/soc/codecs/cs4271.c b/sound/soc/codecs/cs4271.c index 65ad56c..a20f1bb 100644 --- a/sound/soc/codecs/cs4271.c +++ b/sound/soc/codecs/cs4271.c @@ -173,6 +173,26 @@ struct cs4271_private { bool enable_soft_reset; }; +static const struct snd_soc_dapm_widget cs4271_dapm_widgets[] = { +SND_SOC_DAPM_INPUT("AINA"), +SND_SOC_DAPM_INPUT("AINB"), + +SND_SOC_DAPM_OUTPUT("AOUTA+"), +SND_SOC_DAPM_OUTPUT("AOUTA-"), +SND_SOC_DAPM_OUTPUT("AOUTB+"), +SND_SOC_DAPM_OUTPUT("AOUTB-"), +}; + +static const struct snd_soc_dapm_route cs4271_dapm_routes[] = { + { "Capture", NULL, "AINA" }, + { "Capture", NULL, "AINB" }, + + { "AOUTA+", NULL, "Playback" }, + { "AOUTA-", NULL, "Playback" }, + { "AOUTB+", NULL, "Playback" }, + { "AOUTB-", NULL, "Playback" }, +}; + /* * @freq is the desired MCLK rate * MCLK rate should (c) be the sample rate, multiplied by one of the @@ -598,6 +618,10 @@ static struct snd_soc_codec_driver soc_codec_dev_cs4271 = { .controls = cs4271_snd_controls, .num_controls = ARRAY_SIZE(cs4271_snd_controls), + .dapm_widgets = cs4271_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(cs4271_dapm_widgets), + .dapm_routes = cs4271_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(cs4271_dapm_routes), }; #if defined(CONFIG_SPI_MASTER) -- cgit v0.10.2 From 4edec9eaf40877535b9b05cb0bf699f353c53418 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sun, 11 Aug 2013 18:51:42 +0200 Subject: sound/soc/pxa/mioa701_wm9713.c: Avoid using ARRAY_AND_SIZE(e) as a function argument Replace ARRAY_AND_SIZE(e) in function argument position to avoid hiding the arity of the called function. The semantic match that makes this change is as follows: (http://coccinelle.lip6.fr/) // @@ expression e,f; @@ f(..., - ARRAY_AND_SIZE(e) + e,ARRAY_SIZE(e) ,...) // Signed-off-by: Julia Lawall Signed-off-by: Mark Brown diff --git a/sound/soc/pxa/mioa701_wm9713.c b/sound/soc/pxa/mioa701_wm9713.c index 97b711e..20fdce6 100644 --- a/sound/soc/pxa/mioa701_wm9713.c +++ b/sound/soc/pxa/mioa701_wm9713.c @@ -133,10 +133,11 @@ static int mioa701_wm9713_init(struct snd_soc_pcm_runtime *rtd) unsigned short reg; /* Add mioa701 specific widgets */ - snd_soc_dapm_new_controls(dapm, ARRAY_AND_SIZE(mioa701_dapm_widgets)); + snd_soc_dapm_new_controls(dapm, mioa701_dapm_widgets, + ARRAY_SIZE(mioa701_dapm_widgets)); /* Set up mioa701 specific audio path audio_mapnects */ - snd_soc_dapm_add_routes(dapm, ARRAY_AND_SIZE(audio_map)); + snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); /* Prepare GPIO8 for rear speaker amplifier */ reg = codec->driver->read(codec, AC97_GPIO_CFG); -- cgit v0.10.2 From 2820f6158faec223cb426c8603fe589582e6903d Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sun, 11 Aug 2013 19:35:11 +0100 Subject: ASoC: doc: Add documentation to MAINTAINERS patterns Signed-off-by: Mark Brown diff --git a/MAINTAINERS b/MAINTAINERS index bf61e04..8ca83d9 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7662,6 +7662,7 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git L: alsa-devel@alsa-project.org (moderated for non-subscribers) W: http://alsa-project.org/main/index.php/ASoC S: Supported +F: Documentation/sound/alsa/soc/ F: sound/soc/ F: include/sound/soc* -- cgit v0.10.2 From f672f31ab58c1e7e96acb4ea54eebb8bb59a2667 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Mon, 12 Aug 2013 11:15:27 +0530 Subject: ALSA: compress: update the MAINTAINER entry add missing uapi/ headers and Documentation files Signed-off-by: Vinod Koul Signed-off-by: Takashi Iwai diff --git a/MAINTAINERS b/MAINTAINERS index b4d1de9..1c2bba3 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7660,7 +7660,9 @@ M: Vinod Koul L: alsa-devel@alsa-project.org (moderated for non-subscribers) T: git git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound.git S: Supported +F: Documentation/sound/alsa/compress_offload.txt F: include/sound/compress_driver.h +F: include/uapi/sound/compress_* F: sound/core/compress_offload.c F: sound/soc/soc-compress.c -- cgit v0.10.2 From 3d24cfe485e2750cc209a77dd62fa1fe004fc6c7 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 9 Aug 2013 18:12:29 +0100 Subject: ASoC: compress: Use power efficient workqueue There is no need for the power down work to be done on a per CPU workqueue especially considering the fairly long delay before powerdown. Signed-off-by: Mark Brown Acked-by: Vinod Koul diff --git a/sound/soc/soc-compress.c b/sound/soc/soc-compress.c index d220150..53c9ecd 100644 --- a/sound/soc/soc-compress.c +++ b/sound/soc/soc-compress.c @@ -149,8 +149,9 @@ static int soc_compr_free(struct snd_compr_stream *cstream) SND_SOC_DAPM_STREAM_STOP); } else { rtd->pop_wait = 1; - schedule_delayed_work(&rtd->delayed_work, - msecs_to_jiffies(rtd->pmdown_time)); + queue_delayed_work(system_power_efficient_wq, + &rtd->delayed_work, + msecs_to_jiffies(rtd->pmdown_time)); } } else { /* capture streams can be powered down now */ -- cgit v0.10.2 From 9190aeb4ecbdcab7d66d186c207f76d09b41d082 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sun, 11 Aug 2013 15:07:36 +0100 Subject: ASoC: adau1701: Use gpio_set_value_cansleep() The GPIO manipulation done by this driver is never in atomic context so we can use gpio_set_value_cansleep() and support GPIOs that can't be set from atomic context. Signed-off-by: Mark Brown Acked-by: Lars-Peter Clausen diff --git a/sound/soc/codecs/adau1701.c b/sound/soc/codecs/adau1701.c index 2c10252..ebff1128b 100644 --- a/sound/soc/codecs/adau1701.c +++ b/sound/soc/codecs/adau1701.c @@ -247,21 +247,21 @@ static int adau1701_reset(struct snd_soc_codec *codec, unsigned int clkdiv) gpio_is_valid(adau1701->gpio_pll_mode[1])) { switch (clkdiv) { case 64: - gpio_set_value(adau1701->gpio_pll_mode[0], 0); - gpio_set_value(adau1701->gpio_pll_mode[1], 0); + gpio_set_value_cansleep(adau1701->gpio_pll_mode[0], 0); + gpio_set_value_cansleep(adau1701->gpio_pll_mode[1], 0); break; case 256: - gpio_set_value(adau1701->gpio_pll_mode[0], 0); - gpio_set_value(adau1701->gpio_pll_mode[1], 1); + gpio_set_value_cansleep(adau1701->gpio_pll_mode[0], 0); + gpio_set_value_cansleep(adau1701->gpio_pll_mode[1], 1); break; case 384: - gpio_set_value(adau1701->gpio_pll_mode[0], 1); - gpio_set_value(adau1701->gpio_pll_mode[1], 0); + gpio_set_value_cansleep(adau1701->gpio_pll_mode[0], 1); + gpio_set_value_cansleep(adau1701->gpio_pll_mode[1], 0); break; case 0: /* fallback */ case 512: - gpio_set_value(adau1701->gpio_pll_mode[0], 1); - gpio_set_value(adau1701->gpio_pll_mode[1], 1); + gpio_set_value_cansleep(adau1701->gpio_pll_mode[0], 1); + gpio_set_value_cansleep(adau1701->gpio_pll_mode[1], 1); break; } } @@ -269,10 +269,10 @@ static int adau1701_reset(struct snd_soc_codec *codec, unsigned int clkdiv) adau1701->pll_clkdiv = clkdiv; if (gpio_is_valid(adau1701->gpio_nreset)) { - gpio_set_value(adau1701->gpio_nreset, 0); + gpio_set_value_cansleep(adau1701->gpio_nreset, 0); /* minimum reset time is 20ns */ udelay(1); - gpio_set_value(adau1701->gpio_nreset, 1); + gpio_set_value_cansleep(adau1701->gpio_nreset, 1); /* power-up time may be as long as 85ms */ mdelay(85); } -- cgit v0.10.2 From 2e61926cb4a42d79a406aa64f04869d1227ca42c Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 7 Aug 2013 19:01:40 +0100 Subject: ASoC: ak4104: Add stub DAPM support This makes it easer to integrate the device with other on-board components and ensures correct operation following removal of support for non-DAPM CODECs. Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/ak4104.c b/sound/soc/codecs/ak4104.c index c7cfdf9..9a7c89b 100644 --- a/sound/soc/codecs/ak4104.c +++ b/sound/soc/codecs/ak4104.c @@ -51,6 +51,14 @@ struct ak4104_private { struct regmap *regmap; }; +static const struct snd_soc_dapm_widget ak4104_dapm_widgets[] = { +SND_SOC_DAPM_OUTPUT("TX"), +}; + +static const struct snd_soc_dapm_route ak4104_dapm_routes[] = { + { "TX", NULL, "Playback" }, +}; + static int ak4104_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int format) { @@ -214,6 +222,11 @@ static int ak4104_remove(struct snd_soc_codec *codec) static struct snd_soc_codec_driver soc_codec_device_ak4104 = { .probe = ak4104_probe, .remove = ak4104_remove, + + .dapm_widgets = ak4104_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(ak4104_dapm_widgets), + .dapm_routes = ak4104_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(ak4104_dapm_routes), }; static const struct regmap_config ak4104_regmap = { -- cgit v0.10.2 From a5db4d50fa578936275c1e26d5d2fda25c0d2bf6 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 7 Aug 2013 19:05:47 +0100 Subject: ASoC: ak4104: Manage TXE using DAPM Saves some code. We should also be able to manage the power up and reset registers using DAPM but it's probably more trouble than it's worth in mains powered systems. Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/ak4104.c b/sound/soc/codecs/ak4104.c index 9a7c89b..71059c0 100644 --- a/sound/soc/codecs/ak4104.c +++ b/sound/soc/codecs/ak4104.c @@ -52,11 +52,14 @@ struct ak4104_private { }; static const struct snd_soc_dapm_widget ak4104_dapm_widgets[] = { +SND_SOC_DAPM_PGA("TXE", AK4104_REG_TX, AK4104_TX_TXE, 0, NULL, 0), + SND_SOC_DAPM_OUTPUT("TX"), }; static const struct snd_soc_dapm_route ak4104_dapm_routes[] = { - { "TX", NULL, "Playback" }, + { "TXE", NULL, "Playback" }, + { "TX", NULL, "TXE" }, }; static int ak4104_set_dai_fmt(struct snd_soc_dai *codec_dai, @@ -146,29 +149,11 @@ static int ak4104_hw_params(struct snd_pcm_substream *substream, if (ret < 0) return ret; - /* enable transmitter */ - ret = regmap_update_bits(ak4104->regmap, AK4104_REG_TX, - AK4104_TX_TXE, AK4104_TX_TXE); - if (ret < 0) - return ret; - return 0; } -static int ak4104_hw_free(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - struct snd_soc_codec *codec = dai->codec; - struct ak4104_private *ak4104 = snd_soc_codec_get_drvdata(codec); - - /* disable transmitter */ - return regmap_update_bits(ak4104->regmap, AK4104_REG_TX, - AK4104_TX_TXE, 0); -} - static const struct snd_soc_dai_ops ak4101_dai_ops = { .hw_params = ak4104_hw_params, - .hw_free = ak4104_hw_free, .set_fmt = ak4104_set_dai_fmt, }; -- cgit v0.10.2 From dcf1439a493f75336f7e9d272d01b04bc1c4ca8e Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sun, 11 Aug 2013 12:28:56 +0100 Subject: ASoC: ak5386: Add DAPM support This makes it possible to hook the device into a more complex board and ensures it will continue to work with non-DAPM support removed from the core. Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/ak5386.c b/sound/soc/codecs/ak5386.c index 1f30398..72e953b 100644 --- a/sound/soc/codecs/ak5386.c +++ b/sound/soc/codecs/ak5386.c @@ -22,7 +22,22 @@ struct ak5386_priv { int reset_gpio; }; -static struct snd_soc_codec_driver soc_codec_ak5386; +static const struct snd_soc_dapm_widget ak5386_dapm_widgets[] = { +SND_SOC_DAPM_INPUT("AINL"), +SND_SOC_DAPM_INPUT("AINR"), +}; + +static const struct snd_soc_dapm_route ak5386_dapm_routes[] = { + { "Capture", NULL, "AINL" }, + { "Capture", NULL, "AINR" }, +}; + +static struct snd_soc_codec_driver soc_codec_ak5386 = { + .dapm_widgets = ak5386_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(ak5386_dapm_widgets), + .dapm_routes = ak5386_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(ak5386_dapm_routes), +}; static int ak5386_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int format) -- cgit v0.10.2 From e7edb2731bf8e00aaeb7d20800ae108068618f63 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Mon, 12 Aug 2013 11:33:32 +0100 Subject: ASoC: arizona: Add widget<->mux route into mux route macro The routes linking the widget and the input mux were being added manually, rather than by the ARIZONA_MUX_ROUTES macro. This patchs adds the routes to the macro. Signed-off-by: Charles Keepax Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/arizona.h b/sound/soc/codecs/arizona.h index b6b6d70..9e81b63 100644 --- a/sound/soc/codecs/arizona.h +++ b/sound/soc/codecs/arizona.h @@ -150,7 +150,8 @@ extern int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS]; ARIZONA_MUX(name_str " Aux 5", &name##_aux5_mux), \ ARIZONA_MUX(name_str " Aux 6", &name##_aux6_mux) -#define ARIZONA_MUX_ROUTES(name) \ +#define ARIZONA_MUX_ROUTES(widget, name) \ + { widget, NULL, name " Input" }, \ ARIZONA_MIXER_INPUT_ROUTES(name " Input") #define ARIZONA_MIXER_ROUTES(widget, name) \ diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c index a6cbdb4..f38c52d 100644 --- a/sound/soc/codecs/wm5102.c +++ b/sound/soc/codecs/wm5102.c @@ -1501,23 +1501,6 @@ static const struct snd_soc_dapm_route wm5102_dapm_routes[] = { { "IN3L PGA", NULL, "IN3L" }, { "IN3R PGA", NULL, "IN3R" }, - { "ASRC1L", NULL, "ASRC1L Input" }, - { "ASRC1R", NULL, "ASRC1R Input" }, - { "ASRC2L", NULL, "ASRC2L Input" }, - { "ASRC2R", NULL, "ASRC2R Input" }, - - { "ISRC1DEC1", NULL, "ISRC1DEC1 Input" }, - { "ISRC1DEC2", NULL, "ISRC1DEC2 Input" }, - - { "ISRC1INT1", NULL, "ISRC1INT1 Input" }, - { "ISRC1INT2", NULL, "ISRC1INT2 Input" }, - - { "ISRC2DEC1", NULL, "ISRC2DEC1 Input" }, - { "ISRC2DEC2", NULL, "ISRC2DEC2 Input" }, - - { "ISRC2INT1", NULL, "ISRC2INT1 Input" }, - { "ISRC2INT2", NULL, "ISRC2INT2 Input" }, - ARIZONA_MIXER_ROUTES("OUT1L", "HPOUT1L"), ARIZONA_MIXER_ROUTES("OUT1R", "HPOUT1R"), ARIZONA_MIXER_ROUTES("OUT2L", "HPOUT2L"), @@ -1569,22 +1552,22 @@ static const struct snd_soc_dapm_route wm5102_dapm_routes[] = { ARIZONA_MIXER_ROUTES("LHPF3", "LHPF3"), ARIZONA_MIXER_ROUTES("LHPF4", "LHPF4"), - ARIZONA_MUX_ROUTES("ASRC1L"), - ARIZONA_MUX_ROUTES("ASRC1R"), - ARIZONA_MUX_ROUTES("ASRC2L"), - ARIZONA_MUX_ROUTES("ASRC2R"), + ARIZONA_MUX_ROUTES("ASRC1L", "ASRC1L"), + ARIZONA_MUX_ROUTES("ASRC1R", "ASRC1R"), + ARIZONA_MUX_ROUTES("ASRC2L", "ASRC2L"), + ARIZONA_MUX_ROUTES("ASRC2R", "ASRC2R"), - ARIZONA_MUX_ROUTES("ISRC1INT1"), - ARIZONA_MUX_ROUTES("ISRC1INT2"), + ARIZONA_MUX_ROUTES("ISRC1INT1", "ISRC1INT1"), + ARIZONA_MUX_ROUTES("ISRC1INT2", "ISRC1INT2"), - ARIZONA_MUX_ROUTES("ISRC1DEC1"), - ARIZONA_MUX_ROUTES("ISRC1DEC2"), + ARIZONA_MUX_ROUTES("ISRC1DEC1", "ISRC1DEC1"), + ARIZONA_MUX_ROUTES("ISRC1DEC2", "ISRC1DEC2"), - ARIZONA_MUX_ROUTES("ISRC2INT1"), - ARIZONA_MUX_ROUTES("ISRC2INT2"), + ARIZONA_MUX_ROUTES("ISRC2INT1", "ISRC2INT1"), + ARIZONA_MUX_ROUTES("ISRC2INT2", "ISRC2INT2"), - ARIZONA_MUX_ROUTES("ISRC2DEC1"), - ARIZONA_MUX_ROUTES("ISRC2DEC2"), + ARIZONA_MUX_ROUTES("ISRC2DEC1", "ISRC2DEC1"), + ARIZONA_MUX_ROUTES("ISRC2DEC2", "ISRC2DEC2"), ARIZONA_DSP_ROUTES("DSP1"), diff --git a/sound/soc/codecs/wm5110.c b/sound/soc/codecs/wm5110.c index 77fd531..38e50c8 100644 --- a/sound/soc/codecs/wm5110.c +++ b/sound/soc/codecs/wm5110.c @@ -978,10 +978,10 @@ static const struct snd_soc_dapm_route wm5110_dapm_routes[] = { ARIZONA_MIXER_ROUTES("LHPF3", "LHPF3"), ARIZONA_MIXER_ROUTES("LHPF4", "LHPF4"), - ARIZONA_MUX_ROUTES("ASRC1L"), - ARIZONA_MUX_ROUTES("ASRC1R"), - ARIZONA_MUX_ROUTES("ASRC2L"), - ARIZONA_MUX_ROUTES("ASRC2R"), + ARIZONA_MUX_ROUTES("ASRC1L", "ASRC1L"), + ARIZONA_MUX_ROUTES("ASRC1R", "ASRC1R"), + ARIZONA_MUX_ROUTES("ASRC2L", "ASRC2L"), + ARIZONA_MUX_ROUTES("ASRC2R", "ASRC2R"), { "HPOUT1L", NULL, "OUT1L" }, { "HPOUT1R", NULL, "OUT1R" }, -- cgit v0.10.2 From bc2eee29fc8224ffad495d0c68ead0ce603309e3 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 9 Aug 2013 15:05:03 +0200 Subject: ALSA: hda - Allow auto_mute_via_amp on bind mute controls The auto-mute using the amp currently works only for a single amp on a pin. Make it working also with HDA_CTL_BIND_MUTE type, too. Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index f6c0344..6ed2209 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -816,6 +816,8 @@ static void resume_path_from_idx(struct hda_codec *codec, int path_idx) static int hda_gen_mixer_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); +static int hda_gen_bind_mute_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); enum { HDA_CTL_WIDGET_VOL, @@ -833,7 +835,13 @@ static const struct snd_kcontrol_new control_templates[] = { .put = hda_gen_mixer_mute_put, /* replaced */ .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0), }, - HDA_BIND_MUTE(NULL, 0, 0, 0), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .info = snd_hda_mixer_amp_switch_info, + .get = snd_hda_mixer_bind_switch_get, + .put = hda_gen_bind_mute_put, /* replaced */ + .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0), + }, }; /* add dynamic controls from template */ @@ -940,8 +948,8 @@ static int add_stereo_sw(struct hda_codec *codec, const char *pfx, } /* playback mute control with the software mute bit check */ -static int hda_gen_mixer_mute_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +static void sync_auto_mute_bits(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct hda_gen_spec *spec = codec->spec; @@ -952,10 +960,22 @@ static int hda_gen_mixer_mute_put(struct snd_kcontrol *kcontrol, ucontrol->value.integer.value[0] &= enabled; ucontrol->value.integer.value[1] &= enabled; } +} +static int hda_gen_mixer_mute_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + sync_auto_mute_bits(kcontrol, ucontrol); return snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); } +static int hda_gen_bind_mute_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + sync_auto_mute_bits(kcontrol, ucontrol); + return snd_hda_mixer_bind_switch_put(kcontrol, ucontrol); +} + /* any ctl assigned to the path with the given index? */ static bool path_has_mixer(struct hda_codec *codec, int path_idx, int ctl_type) { -- cgit v0.10.2 From e80c60f3cbe76fa95029abc53b1a29172b51b96a Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 12 Aug 2013 14:44:59 +0200 Subject: ALSA: hda - Mute the right widget in auto_mute_via_amp mode The current generic parser code assumes that always a pin widget controls the mute for an output blindly although it might be a different widget in the middle. Instead of the fixed assumption, check each parsed path and just pick up the right widget that has been already defined as a mute control. Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 6ed2209..fd1965c 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -3768,7 +3768,7 @@ static bool detect_jacks(struct hda_codec *codec, int num_pins, hda_nid_t *pins) /* standard HP/line-out auto-mute helper */ static void do_automute(struct hda_codec *codec, int num_pins, hda_nid_t *pins, - bool mute) + int *paths, bool mute) { struct hda_gen_spec *spec = codec->spec; int i; @@ -3780,10 +3780,19 @@ static void do_automute(struct hda_codec *codec, int num_pins, hda_nid_t *pins, break; if (spec->auto_mute_via_amp) { + struct nid_path *path; + hda_nid_t mute_nid; + + path = snd_hda_get_path_from_idx(codec, paths[i]); + if (!path) + continue; + mute_nid = get_amp_nid_(path->ctls[NID_PATH_MUTE_CTL]); + if (!mute_nid) + continue; if (mute) - spec->mute_bits |= (1ULL << nid); + spec->mute_bits |= (1ULL << mute_nid); else - spec->mute_bits &= ~(1ULL << nid); + spec->mute_bits &= ~(1ULL << mute_nid); set_pin_eapd(codec, nid, !mute); continue; } @@ -3814,14 +3823,19 @@ static void do_automute(struct hda_codec *codec, int num_pins, hda_nid_t *pins, void snd_hda_gen_update_outputs(struct hda_codec *codec) { struct hda_gen_spec *spec = codec->spec; + int *paths; int on; /* Control HP pins/amps depending on master_mute state; * in general, HP pins/amps control should be enabled in all cases, * but currently set only for master_mute, just to be safe */ + if (spec->autocfg.line_out_type == AUTO_PIN_HP_OUT) + paths = spec->out_paths; + else + paths = spec->hp_paths; do_automute(codec, ARRAY_SIZE(spec->autocfg.hp_pins), - spec->autocfg.hp_pins, spec->master_mute); + spec->autocfg.hp_pins, paths, spec->master_mute); if (!spec->automute_speaker) on = 0; @@ -3829,8 +3843,12 @@ void snd_hda_gen_update_outputs(struct hda_codec *codec) on = spec->hp_jack_present | spec->line_jack_present; on |= spec->master_mute; spec->speaker_muted = on; + if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT) + paths = spec->out_paths; + else + paths = spec->speaker_paths; do_automute(codec, ARRAY_SIZE(spec->autocfg.speaker_pins), - spec->autocfg.speaker_pins, on); + spec->autocfg.speaker_pins, paths, on); /* toggle line-out mutes if needed, too */ /* if LO is a copy of either HP or Speaker, don't need to handle it */ @@ -3843,8 +3861,9 @@ void snd_hda_gen_update_outputs(struct hda_codec *codec) on = spec->hp_jack_present; on |= spec->master_mute; spec->line_out_muted = on; + paths = spec->out_paths; do_automute(codec, ARRAY_SIZE(spec->autocfg.line_out_pins), - spec->autocfg.line_out_pins, on); + spec->autocfg.line_out_pins, paths, on); } EXPORT_SYMBOL_HDA(snd_hda_gen_update_outputs); -- cgit v0.10.2 From 946d92a100f6c36b1c53922d5105b3c19a59173d Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 12 Aug 2013 23:28:42 +0100 Subject: ASoC: dapm: Don't create routes when creating kcontrols Attempting to create the route as part of adding a mux control causes us to attempt to add the same route twice since we loop over all sources for the mux after creating the control. Instead do the addition in the callers. Signed-off-by: Mark Brown Acked-by: Lars-Peter Clausen diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 662a904..b885a9b 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -665,7 +665,7 @@ static int dapm_is_shared_kcontrol(struct snd_soc_dapm_context *dapm, * create it. Either way, add the widget into the control's widget list */ static int dapm_create_or_share_mixmux_kcontrol(struct snd_soc_dapm_widget *w, - int kci, struct snd_soc_dapm_path *path) + int kci) { struct snd_soc_dapm_context *dapm = w->dapm; struct snd_card *card = dapm->card->snd_card; @@ -766,7 +766,6 @@ static int dapm_create_or_share_mixmux_kcontrol(struct snd_soc_dapm_widget *w, return ret; w->kcontrols[kci] = kcontrol; - dapm_kcontrol_add_path(kcontrol, path); return 0; } @@ -790,9 +789,11 @@ static int dapm_new_mixer(struct snd_soc_dapm_widget *w) continue; } - ret = dapm_create_or_share_mixmux_kcontrol(w, i, path); + ret = dapm_create_or_share_mixmux_kcontrol(w, i); if (ret < 0) return ret; + + dapm_kcontrol_add_path(w->kcontrols[i], path); } } @@ -818,10 +819,7 @@ static int dapm_new_mux(struct snd_soc_dapm_widget *w) return -EINVAL; } - path = list_first_entry(&w->sources, struct snd_soc_dapm_path, - list_sink); - - ret = dapm_create_or_share_mixmux_kcontrol(w, 0, path); + ret = dapm_create_or_share_mixmux_kcontrol(w, 0); if (ret < 0) return ret; -- cgit v0.10.2 From 69c2d346e8fa8dbed122e82f727332f35718ab86 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 13 Aug 2013 00:20:36 +0100 Subject: ASoC: dapm: Ensure kcontrol list is initialised Ensure that the recently added path kcontrol list is initialised otherwise we may crash trying to delete routes that don't have kcontrols. Signed-off-by: Mark Brown Acked-by: Lars-Peter Clausen diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index b885a9b..d84bd0f 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -2354,6 +2354,7 @@ static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm, path->sink = wsink; path->connected = connected; INIT_LIST_HEAD(&path->list); + INIT_LIST_HEAD(&path->list_kcontrol); INIT_LIST_HEAD(&path->list_source); INIT_LIST_HEAD(&path->list_sink); -- cgit v0.10.2 From 1d61210cfc3b5e663d5cf2b003c0faec64712481 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sun, 11 Aug 2013 18:59:35 +0100 Subject: ASoC: dt: Move WM8753 pin list from Tegra board binding to CODEC binding The pin list is the same for any board using the CODEC. Signed-off-by: Mark Brown Acked-by: Stephen Warren diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm8753.txt b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm8753.txt index d145106..aab6ce0 100644 --- a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm8753.txt +++ b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm8753.txt @@ -11,31 +11,8 @@ Required properties: - nvidia,audio-routing : A list of the connections between audio components. Each entry is a pair of strings, the first being the connection's sink, the second being the connection's source. Valid names for sources and - sinks are the WM8753's pins, and the jacks on the board: - - WM8753 pins: - - * LOUT1 - * LOUT2 - * ROUT1 - * ROUT2 - * MONO1 - * MONO2 - * OUT3 - * OUT4 - * LINE1 - * LINE2 - * RXP - * RXN - * ACIN - * ACOP - * MIC1N - * MIC1 - * MIC2N - * MIC2 - * Mic Bias - - Board connectors: + sinks are the WM8753's pins as documented in the binding for the WM8753, + and the jacks on the board: * Headphone Jack * Mic Jack diff --git a/Documentation/devicetree/bindings/sound/wm8753.txt b/Documentation/devicetree/bindings/sound/wm8753.txt index e65277a..18fc706 100644 --- a/Documentation/devicetree/bindings/sound/wm8753.txt +++ b/Documentation/devicetree/bindings/sound/wm8753.txt @@ -10,6 +10,28 @@ Required properties: - reg : the I2C address of the device for I2C, the chip select number for SPI. +Pins on the device (for linking into audio routes): + + * LOUT1 + * LOUT2 + * ROUT1 + * ROUT2 + * MONO1 + * MONO2 + * OUT3 + * OUT4 + * LINE1 + * LINE2 + * RXP + * RXN + * ACIN + * ACOP + * MIC1N + * MIC1 + * MIC2N + * MIC2 + * Mic Bias + Example: codec: wm8737@1a { -- cgit v0.10.2 From b33d1f0803959b1f44b70dadbe0a16cf7c3d4d62 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sun, 11 Aug 2013 18:59:20 +0100 Subject: ASoC: dt: Move RT5640 pin list from Tegra board binding to CODEC binding The pin list is the same for any board using the CODEC. Signed-off-by: Mark Brown Acked-by: Stephen Warren diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-rt5640.txt b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-rt5640.txt index d130818..cba4f88 100644 --- a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-rt5640.txt +++ b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-rt5640.txt @@ -11,29 +11,8 @@ Required properties: - nvidia,audio-routing : A list of the connections between audio components. Each entry is a pair of strings, the first being the connection's sink, the second being the connection's source. Valid names for sources and - sinks are the RT5640's pins, and the jacks on the board: - - RT5640 pins: - - * DMIC1 - * DMIC2 - * MICBIAS1 - * IN1P - * IN1R - * IN2P - * IN2R - * HPOL - * HPOR - * LOUTL - * LOUTR - * MONOP - * MONON - * SPOLP - * SPOLN - * SPORP - * SPORN - - Board connectors: + sinks are the RT5640's pins (as documented in its binding), and the jacks + on the board: * Headphones * Speakers diff --git a/Documentation/devicetree/bindings/sound/rt5640.txt b/Documentation/devicetree/bindings/sound/rt5640.txt index 005bcb2..068a114 100644 --- a/Documentation/devicetree/bindings/sound/rt5640.txt +++ b/Documentation/devicetree/bindings/sound/rt5640.txt @@ -18,6 +18,26 @@ Optional properties: - realtek,ldo1-en-gpios : The GPIO that controls the CODEC's LDO1_EN pin. +Pins on the device (for linking into audio routes): + + * DMIC1 + * DMIC2 + * MICBIAS1 + * IN1P + * IN1R + * IN2P + * IN2R + * HPOL + * HPOR + * LOUTL + * LOUTR + * MONOP + * MONON + * SPOLP + * SPOLN + * SPORP + * SPORN + Example: rt5640 { -- cgit v0.10.2 From 81164e61c12d597a8aeea3313188da97223466aa Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sun, 11 Aug 2013 18:59:06 +0100 Subject: ASoC: dt: Move ALC5632 pin list from Tegra board binding to CODEC binding The pin list is the same for any board using the CODEC. Signed-off-by: Mark Brown Acked-by: Stephen Warren diff --git a/Documentation/devicetree/bindings/sound/alc5632.txt b/Documentation/devicetree/bindings/sound/alc5632.txt index 8608f74..ffd886d 100644 --- a/Documentation/devicetree/bindings/sound/alc5632.txt +++ b/Documentation/devicetree/bindings/sound/alc5632.txt @@ -13,6 +13,25 @@ Required properties: - #gpio-cells : Should be two. The first cell is the pin number and the second cell is used to specify optional parameters (currently unused). +Pins on the device (for linking into audio routes): + + * SPK_OUTP + * SPK_OUTN + * HP_OUT_L + * HP_OUT_R + * AUX_OUT_P + * AUX_OUT_N + * LINE_IN_L + * LINE_IN_R + * PHONE_P + * PHONE_N + * MIC1_P + * MIC1_N + * MIC2_P + * MIC2_N + * MICBIAS1 + * DMICDAT + Example: alc5632: alc5632@1e { diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-alc5632.txt b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-alc5632.txt index 05ffecb..8b8903e 100644 --- a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-alc5632.txt +++ b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-alc5632.txt @@ -11,28 +11,8 @@ Required properties: - nvidia,audio-routing : A list of the connections between audio components. Each entry is a pair of strings, the first being the connection's sink, the second being the connection's source. Valid names for sources and - sinks are the ALC5632's pins: - - ALC5632 pins: - - * SPK_OUTP - * SPK_OUTN - * HP_OUT_L - * HP_OUT_R - * AUX_OUT_P - * AUX_OUT_N - * LINE_IN_L - * LINE_IN_R - * PHONE_P - * PHONE_N - * MIC1_P - * MIC1_N - * MIC2_P - * MIC2_N - * MICBIAS1 - * DMICDAT - - Board connectors: + sinks are the ALC5632's pins as documented in the binding for the device + and: * Headset Stereophone * Int Spk -- cgit v0.10.2 From 3ce1fb524a777bd9c2b02a298bbc96f0df46a78e Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sun, 11 Aug 2013 15:37:12 +0100 Subject: ASoC: wm8753: Fix typo in DT binding example Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/sound/wm8753.txt b/Documentation/devicetree/bindings/sound/wm8753.txt index 18fc706..8eee612 100644 --- a/Documentation/devicetree/bindings/sound/wm8753.txt +++ b/Documentation/devicetree/bindings/sound/wm8753.txt @@ -34,7 +34,7 @@ Pins on the device (for linking into audio routes): Example: -codec: wm8737@1a { +codec: wm8753@1a { compatible = "wlf,wm8753"; reg = <0x1a>; }; -- cgit v0.10.2 From 40843aea5a9bd2c3d7917d086e6d23cb02cc4b39 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Mon, 12 Aug 2013 23:46:55 +0100 Subject: ASoC: wm8997: Initial CODEC driver The wm8997 is a compact, high-performance audio hub CODEC with SLIMbus interfacing, for smartphones, tablets and other portable audio devices based on the Arizona platform. This patch adds the wm8997 CODEC driver. [Fixed some interface churn from bitrot due to the patch not going via the MFD tree as expected -- broonie] Signed-off-by: Charles Keepax Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index badb6fb..bb34c8a 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -122,6 +122,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_WM8994 if MFD_WM8994 select SND_SOC_WM8995 if SND_SOC_I2C_AND_SPI select SND_SOC_WM8996 if I2C + select SND_SOC_WM8997 if MFD_WM8997 select SND_SOC_WM9081 if I2C select SND_SOC_WM9090 if I2C select SND_SOC_WM9705 if SND_SOC_AC97_BUS @@ -145,8 +146,10 @@ config SND_SOC_ARIZONA tristate default y if SND_SOC_WM5102=y default y if SND_SOC_WM5110=y + default y if SND_SOC_WM8997=y default m if SND_SOC_WM5102=m default m if SND_SOC_WM5110=m + default m if SND_SOC_WM8997=m config SND_SOC_WM_HUBS tristate @@ -500,6 +503,9 @@ config SND_SOC_WM8995 config SND_SOC_WM8996 tristate +config SND_SOC_WM8997 + tristate + config SND_SOC_WM9081 tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 70fd806..68ea0a2 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -114,6 +114,7 @@ snd-soc-wm8991-objs := wm8991.o snd-soc-wm8993-objs := wm8993.o snd-soc-wm8994-objs := wm8994.o wm8958-dsp2.o snd-soc-wm8995-objs := wm8995.o +snd-soc-wm8997-objs := wm8997.o snd-soc-wm9081-objs := wm9081.o snd-soc-wm9090-objs := wm9090.o snd-soc-wm9705-objs := wm9705.o @@ -239,6 +240,7 @@ obj-$(CONFIG_SND_SOC_WM8991) += snd-soc-wm8991.o obj-$(CONFIG_SND_SOC_WM8993) += snd-soc-wm8993.o obj-$(CONFIG_SND_SOC_WM8994) += snd-soc-wm8994.o obj-$(CONFIG_SND_SOC_WM8995) += snd-soc-wm8995.o +obj-$(CONFIG_SND_SOC_WM8997) += snd-soc-wm8997.o obj-$(CONFIG_SND_SOC_WM9081) += snd-soc-wm9081.o obj-$(CONFIG_SND_SOC_WM9090) += snd-soc-wm9090.o obj-$(CONFIG_SND_SOC_WM9705) += snd-soc-wm9705.o diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c index 779a0ee..657808b 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c @@ -200,9 +200,16 @@ int arizona_init_spk(struct snd_soc_codec *codec) if (ret != 0) return ret; - ret = snd_soc_dapm_new_controls(&codec->dapm, &arizona_spkr, 1); - if (ret != 0) - return ret; + switch (arizona->type) { + case WM8997: + break; + default: + ret = snd_soc_dapm_new_controls(&codec->dapm, + &arizona_spkr, 1); + if (ret != 0) + return ret; + break; + } ret = arizona_request_irq(arizona, ARIZONA_IRQ_SPK_SHUTDOWN_WARN, "Thermal warning", arizona_thermal_warn, diff --git a/sound/soc/codecs/wm8997.c b/sound/soc/codecs/wm8997.c new file mode 100644 index 0000000..0a43bac --- /dev/null +++ b/sound/soc/codecs/wm8997.c @@ -0,0 +1,1175 @@ +/* + * wm8997.c -- WM8997 ALSA SoC Audio driver + * + * Copyright 2012 Wolfson Microelectronics plc + * + * Author: Charles Keepax + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "arizona.h" +#include "wm8997.h" + +struct wm8997_priv { + struct arizona_priv core; + struct arizona_fll fll[2]; +}; + +static DECLARE_TLV_DB_SCALE(ana_tlv, 0, 100, 0); +static DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0); +static DECLARE_TLV_DB_SCALE(digital_tlv, -6400, 50, 0); +static DECLARE_TLV_DB_SCALE(noise_tlv, 0, 600, 0); +static DECLARE_TLV_DB_SCALE(ng_tlv, -10200, 600, 0); + +static const struct reg_default wm8997_sysclk_reva_patch[] = { + { 0x301D, 0x7B15 }, + { 0x301B, 0x0050 }, + { 0x305D, 0x7B17 }, + { 0x305B, 0x0050 }, + { 0x3001, 0x08FE }, + { 0x3003, 0x00F4 }, + { 0x3041, 0x08FF }, + { 0x3043, 0x0005 }, + { 0x3020, 0x0225 }, + { 0x3021, 0x0A00 }, + { 0x3022, 0xE24D }, + { 0x3023, 0x0800 }, + { 0x3024, 0xE24D }, + { 0x3025, 0xF000 }, + { 0x3060, 0x0226 }, + { 0x3061, 0x0A00 }, + { 0x3062, 0xE252 }, + { 0x3063, 0x0800 }, + { 0x3064, 0xE252 }, + { 0x3065, 0xF000 }, + { 0x3116, 0x022B }, + { 0x3117, 0xFA00 }, + { 0x3110, 0x246C }, + { 0x3111, 0x0A03 }, + { 0x3112, 0x246E }, + { 0x3113, 0x0A03 }, + { 0x3114, 0x2470 }, + { 0x3115, 0x0A03 }, + { 0x3126, 0x246C }, + { 0x3127, 0x0A02 }, + { 0x3128, 0x246E }, + { 0x3129, 0x0A02 }, + { 0x312A, 0x2470 }, + { 0x312B, 0xFA02 }, + { 0x3125, 0x0800 }, +}; + +static int wm8997_sysclk_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = w->codec; + struct arizona *arizona = dev_get_drvdata(codec->dev->parent); + struct regmap *regmap = codec->control_data; + const struct reg_default *patch = NULL; + int i, patch_size; + + switch (arizona->rev) { + case 0: + patch = wm8997_sysclk_reva_patch; + patch_size = ARRAY_SIZE(wm8997_sysclk_reva_patch); + break; + default: + break; + } + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + if (patch) + for (i = 0; i < patch_size; i++) + regmap_write(regmap, patch[i].reg, + patch[i].def); + break; + default: + break; + } + + return 0; +} + +static const char *wm8997_osr_text[] = { + "Low power", "Normal", "High performance", +}; + +static const unsigned int wm8997_osr_val[] = { + 0x0, 0x3, 0x5, +}; + +static const struct soc_enum wm8997_hpout_osr[] = { + SOC_VALUE_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_1L, + ARIZONA_OUT1_OSR_SHIFT, 0x7, 3, + wm8997_osr_text, wm8997_osr_val), + SOC_VALUE_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_3L, + ARIZONA_OUT3_OSR_SHIFT, 0x7, 3, + wm8997_osr_text, wm8997_osr_val), +}; + +#define WM8997_NG_SRC(name, base) \ + SOC_SINGLE(name " NG HPOUT1L Switch", base, 0, 1, 0), \ + SOC_SINGLE(name " NG HPOUT1R Switch", base, 1, 1, 0), \ + SOC_SINGLE(name " NG EPOUT Switch", base, 4, 1, 0), \ + SOC_SINGLE(name " NG SPKOUT Switch", base, 6, 1, 0), \ + SOC_SINGLE(name " NG SPKDAT1L Switch", base, 8, 1, 0), \ + SOC_SINGLE(name " NG SPKDAT1R Switch", base, 9, 1, 0) + +static const struct snd_kcontrol_new wm8997_snd_controls[] = { +SOC_SINGLE("IN1 High Performance Switch", ARIZONA_IN1L_CONTROL, + ARIZONA_IN1_OSR_SHIFT, 1, 0), +SOC_SINGLE("IN2 High Performance Switch", ARIZONA_IN2L_CONTROL, + ARIZONA_IN2_OSR_SHIFT, 1, 0), + +SOC_SINGLE_RANGE_TLV("IN1L Volume", ARIZONA_IN1L_CONTROL, + ARIZONA_IN1L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), +SOC_SINGLE_RANGE_TLV("IN1R Volume", ARIZONA_IN1R_CONTROL, + ARIZONA_IN1R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), +SOC_SINGLE_RANGE_TLV("IN2L Volume", ARIZONA_IN2L_CONTROL, + ARIZONA_IN2L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), +SOC_SINGLE_RANGE_TLV("IN2R Volume", ARIZONA_IN2R_CONTROL, + ARIZONA_IN2R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), + +SOC_SINGLE_TLV("IN1L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1L, + ARIZONA_IN1L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("IN1R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1R, + ARIZONA_IN1R_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("IN2L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_2L, + ARIZONA_IN2L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("IN2R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_2R, + ARIZONA_IN2R_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), + +SOC_ENUM("Input Ramp Up", arizona_in_vi_ramp), +SOC_ENUM("Input Ramp Down", arizona_in_vd_ramp), + +ARIZONA_MIXER_CONTROLS("EQ1", ARIZONA_EQ1MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("EQ2", ARIZONA_EQ2MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("EQ3", ARIZONA_EQ3MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("EQ4", ARIZONA_EQ4MIX_INPUT_1_SOURCE), + +SND_SOC_BYTES_MASK("EQ1 Coefficeints", ARIZONA_EQ1_1, 21, + ARIZONA_EQ1_ENA_MASK), +SND_SOC_BYTES_MASK("EQ2 Coefficeints", ARIZONA_EQ2_1, 21, + ARIZONA_EQ2_ENA_MASK), +SND_SOC_BYTES_MASK("EQ3 Coefficeints", ARIZONA_EQ3_1, 21, + ARIZONA_EQ3_ENA_MASK), +SND_SOC_BYTES_MASK("EQ4 Coefficeints", ARIZONA_EQ4_1, 21, + ARIZONA_EQ4_ENA_MASK), + +SOC_SINGLE_TLV("EQ1 B1 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B1_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ1 B2 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B2_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ1 B3 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B3_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ1 B4 Volume", ARIZONA_EQ1_2, ARIZONA_EQ1_B4_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ1 B5 Volume", ARIZONA_EQ1_2, ARIZONA_EQ1_B5_GAIN_SHIFT, + 24, 0, eq_tlv), + +SOC_SINGLE_TLV("EQ2 B1 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B1_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ2 B2 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B2_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ2 B3 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B3_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ2 B4 Volume", ARIZONA_EQ2_2, ARIZONA_EQ2_B4_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ2 B5 Volume", ARIZONA_EQ2_2, ARIZONA_EQ2_B5_GAIN_SHIFT, + 24, 0, eq_tlv), + +SOC_SINGLE_TLV("EQ3 B1 Volume", ARIZONA_EQ3_1, ARIZONA_EQ3_B1_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ3 B2 Volume", ARIZONA_EQ3_1, ARIZONA_EQ3_B2_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ3 B3 Volume", ARIZONA_EQ3_1, ARIZONA_EQ3_B3_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ3 B4 Volume", ARIZONA_EQ3_2, ARIZONA_EQ3_B4_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ3 B5 Volume", ARIZONA_EQ3_2, ARIZONA_EQ3_B5_GAIN_SHIFT, + 24, 0, eq_tlv), + +SOC_SINGLE_TLV("EQ4 B1 Volume", ARIZONA_EQ4_1, ARIZONA_EQ4_B1_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ4 B2 Volume", ARIZONA_EQ4_1, ARIZONA_EQ4_B2_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ4 B3 Volume", ARIZONA_EQ4_1, ARIZONA_EQ4_B3_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ4 B4 Volume", ARIZONA_EQ4_2, ARIZONA_EQ4_B4_GAIN_SHIFT, + 24, 0, eq_tlv), +SOC_SINGLE_TLV("EQ4 B5 Volume", ARIZONA_EQ4_2, ARIZONA_EQ4_B5_GAIN_SHIFT, + 24, 0, eq_tlv), + +ARIZONA_MIXER_CONTROLS("DRC1L", ARIZONA_DRC1LMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("DRC1R", ARIZONA_DRC1RMIX_INPUT_1_SOURCE), + +SND_SOC_BYTES_MASK("DRC1", ARIZONA_DRC1_CTRL1, 5, + ARIZONA_DRC1R_ENA | ARIZONA_DRC1L_ENA), + +ARIZONA_MIXER_CONTROLS("LHPF1", ARIZONA_HPLP1MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("LHPF2", ARIZONA_HPLP2MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("LHPF3", ARIZONA_HPLP3MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("LHPF4", ARIZONA_HPLP4MIX_INPUT_1_SOURCE), + +SOC_ENUM("LHPF1 Mode", arizona_lhpf1_mode), +SOC_ENUM("LHPF2 Mode", arizona_lhpf2_mode), +SOC_ENUM("LHPF3 Mode", arizona_lhpf3_mode), +SOC_ENUM("LHPF4 Mode", arizona_lhpf4_mode), + +SND_SOC_BYTES("LHPF1 Coefficients", ARIZONA_HPLPF1_2, 1), +SND_SOC_BYTES("LHPF2 Coefficients", ARIZONA_HPLPF2_2, 1), +SND_SOC_BYTES("LHPF3 Coefficients", ARIZONA_HPLPF3_2, 1), +SND_SOC_BYTES("LHPF4 Coefficients", ARIZONA_HPLPF4_2, 1), + +SOC_VALUE_ENUM("ISRC1 FSL", arizona_isrc_fsl[0]), +SOC_VALUE_ENUM("ISRC2 FSL", arizona_isrc_fsl[1]), + +ARIZONA_MIXER_CONTROLS("Mic", ARIZONA_MICMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("Noise", ARIZONA_NOISEMIX_INPUT_1_SOURCE), + +SOC_SINGLE_TLV("Noise Generator Volume", ARIZONA_COMFORT_NOISE_GENERATOR, + ARIZONA_NOISE_GEN_GAIN_SHIFT, 0x16, 0, noise_tlv), + +ARIZONA_MIXER_CONTROLS("HPOUT1L", ARIZONA_OUT1LMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("HPOUT1R", ARIZONA_OUT1RMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("EPOUT", ARIZONA_OUT3LMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SPKOUT", ARIZONA_OUT4LMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SPKDAT1L", ARIZONA_OUT5LMIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SPKDAT1R", ARIZONA_OUT5RMIX_INPUT_1_SOURCE), + +SOC_SINGLE("Speaker High Performance Switch", ARIZONA_OUTPUT_PATH_CONFIG_4L, + ARIZONA_OUT4_OSR_SHIFT, 1, 0), +SOC_SINGLE("SPKDAT1 High Performance Switch", ARIZONA_OUTPUT_PATH_CONFIG_5L, + ARIZONA_OUT5_OSR_SHIFT, 1, 0), + +SOC_DOUBLE_R("HPOUT1 Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_1L, + ARIZONA_DAC_DIGITAL_VOLUME_1R, ARIZONA_OUT1L_MUTE_SHIFT, 1, 1), +SOC_SINGLE("EPOUT Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_3L, + ARIZONA_OUT3L_MUTE_SHIFT, 1, 1), +SOC_SINGLE("Speaker Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_4L, + ARIZONA_OUT4L_MUTE_SHIFT, 1, 1), +SOC_DOUBLE_R("SPKDAT1 Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_5L, + ARIZONA_DAC_DIGITAL_VOLUME_5R, ARIZONA_OUT5L_MUTE_SHIFT, 1, 1), + +SOC_DOUBLE_R_TLV("HPOUT1 Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_1L, + ARIZONA_DAC_DIGITAL_VOLUME_1R, ARIZONA_OUT1L_VOL_SHIFT, + 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("EPOUT Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_3L, + ARIZONA_OUT3L_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("Speaker Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_4L, + ARIZONA_OUT4L_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_DOUBLE_R_TLV("SPKDAT1 Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_5L, + ARIZONA_DAC_DIGITAL_VOLUME_5R, ARIZONA_OUT5L_VOL_SHIFT, + 0xbf, 0, digital_tlv), + +SOC_VALUE_ENUM("HPOUT1 OSR", wm8997_hpout_osr[0]), +SOC_VALUE_ENUM("EPOUT OSR", wm8997_hpout_osr[1]), + +SOC_ENUM("Output Ramp Up", arizona_out_vi_ramp), +SOC_ENUM("Output Ramp Down", arizona_out_vd_ramp), + +SOC_DOUBLE("SPKDAT1 Switch", ARIZONA_PDM_SPK1_CTRL_1, ARIZONA_SPK1L_MUTE_SHIFT, + ARIZONA_SPK1R_MUTE_SHIFT, 1, 1), + +SOC_SINGLE("Noise Gate Switch", ARIZONA_NOISE_GATE_CONTROL, + ARIZONA_NGATE_ENA_SHIFT, 1, 0), +SOC_SINGLE_TLV("Noise Gate Threshold Volume", ARIZONA_NOISE_GATE_CONTROL, + ARIZONA_NGATE_THR_SHIFT, 7, 1, ng_tlv), +SOC_ENUM("Noise Gate Hold", arizona_ng_hold), + +WM8997_NG_SRC("HPOUT1L", ARIZONA_NOISE_GATE_SELECT_1L), +WM8997_NG_SRC("HPOUT1R", ARIZONA_NOISE_GATE_SELECT_1R), +WM8997_NG_SRC("EPOUT", ARIZONA_NOISE_GATE_SELECT_3L), +WM8997_NG_SRC("SPKOUT", ARIZONA_NOISE_GATE_SELECT_4L), +WM8997_NG_SRC("SPKDAT1L", ARIZONA_NOISE_GATE_SELECT_5L), +WM8997_NG_SRC("SPKDAT1R", ARIZONA_NOISE_GATE_SELECT_5R), + +ARIZONA_MIXER_CONTROLS("AIF1TX1", ARIZONA_AIF1TX1MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF1TX2", ARIZONA_AIF1TX2MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF1TX3", ARIZONA_AIF1TX3MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF1TX4", ARIZONA_AIF1TX4MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF1TX5", ARIZONA_AIF1TX5MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF1TX6", ARIZONA_AIF1TX6MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF1TX7", ARIZONA_AIF1TX7MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF1TX8", ARIZONA_AIF1TX8MIX_INPUT_1_SOURCE), + +ARIZONA_MIXER_CONTROLS("AIF2TX1", ARIZONA_AIF2TX1MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("AIF2TX2", ARIZONA_AIF2TX2MIX_INPUT_1_SOURCE), + +ARIZONA_MIXER_CONTROLS("SLIMTX1", ARIZONA_SLIMTX1MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SLIMTX2", ARIZONA_SLIMTX2MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SLIMTX3", ARIZONA_SLIMTX3MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SLIMTX4", ARIZONA_SLIMTX4MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SLIMTX5", ARIZONA_SLIMTX5MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SLIMTX6", ARIZONA_SLIMTX6MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SLIMTX7", ARIZONA_SLIMTX7MIX_INPUT_1_SOURCE), +ARIZONA_MIXER_CONTROLS("SLIMTX8", ARIZONA_SLIMTX8MIX_INPUT_1_SOURCE), +}; + +ARIZONA_MIXER_ENUMS(EQ1, ARIZONA_EQ1MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(EQ2, ARIZONA_EQ2MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(EQ3, ARIZONA_EQ3MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(EQ4, ARIZONA_EQ4MIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(DRC1L, ARIZONA_DRC1LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(DRC1R, ARIZONA_DRC1RMIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(LHPF1, ARIZONA_HPLP1MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(LHPF2, ARIZONA_HPLP2MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(LHPF3, ARIZONA_HPLP3MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(LHPF4, ARIZONA_HPLP4MIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(Mic, ARIZONA_MICMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(Noise, ARIZONA_NOISEMIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(PWM1, ARIZONA_PWM1MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(PWM2, ARIZONA_PWM2MIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(OUT1L, ARIZONA_OUT1LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(OUT1R, ARIZONA_OUT1RMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(OUT3, ARIZONA_OUT3LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SPKOUT, ARIZONA_OUT4LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SPKDAT1L, ARIZONA_OUT5LMIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SPKDAT1R, ARIZONA_OUT5RMIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(AIF1TX1, ARIZONA_AIF1TX1MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF1TX2, ARIZONA_AIF1TX2MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF1TX3, ARIZONA_AIF1TX3MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF1TX4, ARIZONA_AIF1TX4MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF1TX5, ARIZONA_AIF1TX5MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF1TX6, ARIZONA_AIF1TX6MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF1TX7, ARIZONA_AIF1TX7MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF1TX8, ARIZONA_AIF1TX8MIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(AIF2TX1, ARIZONA_AIF2TX1MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(AIF2TX2, ARIZONA_AIF2TX2MIX_INPUT_1_SOURCE); + +ARIZONA_MIXER_ENUMS(SLIMTX1, ARIZONA_SLIMTX1MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SLIMTX2, ARIZONA_SLIMTX2MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SLIMTX3, ARIZONA_SLIMTX3MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SLIMTX4, ARIZONA_SLIMTX4MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SLIMTX5, ARIZONA_SLIMTX5MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SLIMTX6, ARIZONA_SLIMTX6MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SLIMTX7, ARIZONA_SLIMTX7MIX_INPUT_1_SOURCE); +ARIZONA_MIXER_ENUMS(SLIMTX8, ARIZONA_SLIMTX8MIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(ISRC1INT1, ARIZONA_ISRC1INT1MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC1INT2, ARIZONA_ISRC1INT2MIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(ISRC1DEC1, ARIZONA_ISRC1DEC1MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC1DEC2, ARIZONA_ISRC1DEC2MIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(ISRC2INT1, ARIZONA_ISRC2INT1MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC2INT2, ARIZONA_ISRC2INT2MIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(ISRC2DEC1, ARIZONA_ISRC2DEC1MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC2DEC2, ARIZONA_ISRC2DEC2MIX_INPUT_1_SOURCE); + +static const char *wm8997_aec_loopback_texts[] = { + "HPOUT1L", "HPOUT1R", "EPOUT", "SPKOUT", "SPKDAT1L", "SPKDAT1R", +}; + +static const unsigned int wm8997_aec_loopback_values[] = { + 0, 1, 4, 6, 8, 9, +}; + +static const struct soc_enum wm8997_aec_loopback = + SOC_VALUE_ENUM_SINGLE(ARIZONA_DAC_AEC_CONTROL_1, + ARIZONA_AEC_LOOPBACK_SRC_SHIFT, 0xf, + ARRAY_SIZE(wm8997_aec_loopback_texts), + wm8997_aec_loopback_texts, + wm8997_aec_loopback_values); + +static const struct snd_kcontrol_new wm8997_aec_loopback_mux = + SOC_DAPM_VALUE_ENUM("AEC Loopback", wm8997_aec_loopback); + +static const struct snd_soc_dapm_widget wm8997_dapm_widgets[] = { +SND_SOC_DAPM_SUPPLY("SYSCLK", ARIZONA_SYSTEM_CLOCK_1, ARIZONA_SYSCLK_ENA_SHIFT, + 0, wm8997_sysclk_ev, SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_SUPPLY("ASYNCCLK", ARIZONA_ASYNC_CLOCK_1, + ARIZONA_ASYNC_CLK_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("OPCLK", ARIZONA_OUTPUT_SYSTEM_CLOCK, + ARIZONA_OPCLK_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("ASYNCOPCLK", ARIZONA_OUTPUT_ASYNC_CLOCK, + ARIZONA_OPCLK_ASYNC_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_REGULATOR_SUPPLY("DBVDD2", 0, 0), +SND_SOC_DAPM_REGULATOR_SUPPLY("CPVDD", 20, 0), +SND_SOC_DAPM_REGULATOR_SUPPLY("MICVDD", 0, SND_SOC_DAPM_REGULATOR_BYPASS), +SND_SOC_DAPM_REGULATOR_SUPPLY("SPKVDD", 0, 0), + +SND_SOC_DAPM_SIGGEN("TONE"), +SND_SOC_DAPM_SIGGEN("NOISE"), +SND_SOC_DAPM_SIGGEN("HAPTICS"), + +SND_SOC_DAPM_INPUT("IN1L"), +SND_SOC_DAPM_INPUT("IN1R"), +SND_SOC_DAPM_INPUT("IN2L"), +SND_SOC_DAPM_INPUT("IN2R"), + +SND_SOC_DAPM_PGA_E("IN1L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN1L_ENA_SHIFT, + 0, NULL, 0, arizona_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("IN1R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN1R_ENA_SHIFT, + 0, NULL, 0, arizona_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("IN2L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN2L_ENA_SHIFT, + 0, NULL, 0, arizona_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("IN2R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN2R_ENA_SHIFT, + 0, NULL, 0, arizona_in_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), + +SND_SOC_DAPM_SUPPLY("MICBIAS1", ARIZONA_MIC_BIAS_CTRL_1, + ARIZONA_MICB1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("MICBIAS2", ARIZONA_MIC_BIAS_CTRL_2, + ARIZONA_MICB2_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("MICBIAS3", ARIZONA_MIC_BIAS_CTRL_3, + ARIZONA_MICB3_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("Noise Generator", ARIZONA_COMFORT_NOISE_GENERATOR, + ARIZONA_NOISE_GEN_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("Tone Generator 1", ARIZONA_TONE_GENERATOR_1, + ARIZONA_TONE1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("Tone Generator 2", ARIZONA_TONE_GENERATOR_1, + ARIZONA_TONE2_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("Mic Mute Mixer", ARIZONA_MIC_NOISE_MIX_CONTROL_1, + ARIZONA_MICMUTE_MIX_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("EQ1", ARIZONA_EQ1_1, ARIZONA_EQ1_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("EQ2", ARIZONA_EQ2_1, ARIZONA_EQ2_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("EQ3", ARIZONA_EQ3_1, ARIZONA_EQ3_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("EQ4", ARIZONA_EQ4_1, ARIZONA_EQ4_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("DRC1L", ARIZONA_DRC1_CTRL1, ARIZONA_DRC1L_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("DRC1R", ARIZONA_DRC1_CTRL1, ARIZONA_DRC1R_ENA_SHIFT, 0, + NULL, 0), + +SND_SOC_DAPM_PGA("LHPF1", ARIZONA_HPLPF1_1, ARIZONA_LHPF1_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("LHPF2", ARIZONA_HPLPF2_1, ARIZONA_LHPF2_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("LHPF3", ARIZONA_HPLPF3_1, ARIZONA_LHPF3_ENA_SHIFT, 0, + NULL, 0), +SND_SOC_DAPM_PGA("LHPF4", ARIZONA_HPLPF4_1, ARIZONA_LHPF4_ENA_SHIFT, 0, + NULL, 0), + +SND_SOC_DAPM_PGA("PWM1 Driver", ARIZONA_PWM_DRIVE_1, ARIZONA_PWM1_ENA_SHIFT, + 0, NULL, 0), +SND_SOC_DAPM_PGA("PWM2 Driver", ARIZONA_PWM_DRIVE_1, ARIZONA_PWM2_ENA_SHIFT, + 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC1INT1", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_INT0_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1INT2", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_INT1_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC1DEC1", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_DEC0_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1DEC2", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_DEC1_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC2INT1", ARIZONA_ISRC_2_CTRL_3, + ARIZONA_ISRC2_INT0_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC2INT2", ARIZONA_ISRC_2_CTRL_3, + ARIZONA_ISRC2_INT1_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC2DEC1", ARIZONA_ISRC_2_CTRL_3, + ARIZONA_ISRC2_DEC0_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC2DEC2", ARIZONA_ISRC_2_CTRL_3, + ARIZONA_ISRC2_DEC1_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_AIF_OUT("AIF1TX1", NULL, 0, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX2", NULL, 0, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX3", NULL, 0, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX4", NULL, 0, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX4_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX5", NULL, 0, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX5_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX6", NULL, 0, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX6_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX7", NULL, 0, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX7_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF1TX8", NULL, 0, + ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX8_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_IN("AIF1RX1", NULL, 0, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX2", NULL, 0, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX3", NULL, 0, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX4", NULL, 0, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX4_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX5", NULL, 0, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX5_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX6", NULL, 0, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX6_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX7", NULL, 0, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX7_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF1RX8", NULL, 0, + ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX8_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_OUT("AIF2TX1", NULL, 0, + ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("AIF2TX2", NULL, 0, + ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX2_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_IN("AIF2RX1", NULL, 0, + ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("AIF2RX2", NULL, 0, + ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX2_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_OUT("SLIMTX1", NULL, 0, + ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, + ARIZONA_SLIMTX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX2", NULL, 0, + ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, + ARIZONA_SLIMTX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX3", NULL, 0, + ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, + ARIZONA_SLIMTX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX4", NULL, 0, + ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, + ARIZONA_SLIMTX4_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX5", NULL, 0, + ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, + ARIZONA_SLIMTX5_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX6", NULL, 0, + ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, + ARIZONA_SLIMTX6_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX7", NULL, 0, + ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, + ARIZONA_SLIMTX7_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_OUT("SLIMTX8", NULL, 0, + ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE, + ARIZONA_SLIMTX8_ENA_SHIFT, 0), + +SND_SOC_DAPM_AIF_IN("SLIMRX1", NULL, 0, + ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, + ARIZONA_SLIMRX1_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("SLIMRX2", NULL, 0, + ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, + ARIZONA_SLIMRX2_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("SLIMRX3", NULL, 0, + ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, + ARIZONA_SLIMRX3_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("SLIMRX4", NULL, 0, + ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, + ARIZONA_SLIMRX4_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("SLIMRX5", NULL, 0, + ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, + ARIZONA_SLIMRX5_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("SLIMRX6", NULL, 0, + ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, + ARIZONA_SLIMRX6_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("SLIMRX7", NULL, 0, + ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, + ARIZONA_SLIMRX7_ENA_SHIFT, 0), +SND_SOC_DAPM_AIF_IN("SLIMRX8", NULL, 0, + ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE, + ARIZONA_SLIMRX8_ENA_SHIFT, 0), + +SND_SOC_DAPM_VALUE_MUX("AEC Loopback", ARIZONA_DAC_AEC_CONTROL_1, + ARIZONA_AEC_LOOPBACK_ENA_SHIFT, 0, + &wm8997_aec_loopback_mux), + +SND_SOC_DAPM_PGA_E("OUT1L", SND_SOC_NOPM, + ARIZONA_OUT1L_ENA_SHIFT, 0, NULL, 0, arizona_hp_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT1R", SND_SOC_NOPM, + ARIZONA_OUT1R_ENA_SHIFT, 0, NULL, 0, arizona_hp_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT3L", ARIZONA_OUTPUT_ENABLES_1, + ARIZONA_OUT3L_ENA_SHIFT, 0, NULL, 0, arizona_out_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT5L", ARIZONA_OUTPUT_ENABLES_1, + ARIZONA_OUT5L_ENA_SHIFT, 0, NULL, 0, arizona_out_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), +SND_SOC_DAPM_PGA_E("OUT5R", ARIZONA_OUTPUT_ENABLES_1, + ARIZONA_OUT5R_ENA_SHIFT, 0, NULL, 0, arizona_out_ev, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), + +ARIZONA_MIXER_WIDGETS(EQ1, "EQ1"), +ARIZONA_MIXER_WIDGETS(EQ2, "EQ2"), +ARIZONA_MIXER_WIDGETS(EQ3, "EQ3"), +ARIZONA_MIXER_WIDGETS(EQ4, "EQ4"), + +ARIZONA_MIXER_WIDGETS(DRC1L, "DRC1L"), +ARIZONA_MIXER_WIDGETS(DRC1R, "DRC1R"), + +ARIZONA_MIXER_WIDGETS(LHPF1, "LHPF1"), +ARIZONA_MIXER_WIDGETS(LHPF2, "LHPF2"), +ARIZONA_MIXER_WIDGETS(LHPF3, "LHPF3"), +ARIZONA_MIXER_WIDGETS(LHPF4, "LHPF4"), + +ARIZONA_MIXER_WIDGETS(Mic, "Mic"), +ARIZONA_MIXER_WIDGETS(Noise, "Noise"), + +ARIZONA_MIXER_WIDGETS(PWM1, "PWM1"), +ARIZONA_MIXER_WIDGETS(PWM2, "PWM2"), + +ARIZONA_MIXER_WIDGETS(OUT1L, "HPOUT1L"), +ARIZONA_MIXER_WIDGETS(OUT1R, "HPOUT1R"), +ARIZONA_MIXER_WIDGETS(OUT3, "EPOUT"), +ARIZONA_MIXER_WIDGETS(SPKOUT, "SPKOUT"), +ARIZONA_MIXER_WIDGETS(SPKDAT1L, "SPKDAT1L"), +ARIZONA_MIXER_WIDGETS(SPKDAT1R, "SPKDAT1R"), + +ARIZONA_MIXER_WIDGETS(AIF1TX1, "AIF1TX1"), +ARIZONA_MIXER_WIDGETS(AIF1TX2, "AIF1TX2"), +ARIZONA_MIXER_WIDGETS(AIF1TX3, "AIF1TX3"), +ARIZONA_MIXER_WIDGETS(AIF1TX4, "AIF1TX4"), +ARIZONA_MIXER_WIDGETS(AIF1TX5, "AIF1TX5"), +ARIZONA_MIXER_WIDGETS(AIF1TX6, "AIF1TX6"), +ARIZONA_MIXER_WIDGETS(AIF1TX7, "AIF1TX7"), +ARIZONA_MIXER_WIDGETS(AIF1TX8, "AIF1TX8"), + +ARIZONA_MIXER_WIDGETS(AIF2TX1, "AIF2TX1"), +ARIZONA_MIXER_WIDGETS(AIF2TX2, "AIF2TX2"), + +ARIZONA_MIXER_WIDGETS(SLIMTX1, "SLIMTX1"), +ARIZONA_MIXER_WIDGETS(SLIMTX2, "SLIMTX2"), +ARIZONA_MIXER_WIDGETS(SLIMTX3, "SLIMTX3"), +ARIZONA_MIXER_WIDGETS(SLIMTX4, "SLIMTX4"), +ARIZONA_MIXER_WIDGETS(SLIMTX5, "SLIMTX5"), +ARIZONA_MIXER_WIDGETS(SLIMTX6, "SLIMTX6"), +ARIZONA_MIXER_WIDGETS(SLIMTX7, "SLIMTX7"), +ARIZONA_MIXER_WIDGETS(SLIMTX8, "SLIMTX8"), + +ARIZONA_MUX_WIDGETS(ISRC1DEC1, "ISRC1DEC1"), +ARIZONA_MUX_WIDGETS(ISRC1DEC2, "ISRC1DEC2"), + +ARIZONA_MUX_WIDGETS(ISRC1INT1, "ISRC1INT1"), +ARIZONA_MUX_WIDGETS(ISRC1INT2, "ISRC1INT2"), + +ARIZONA_MUX_WIDGETS(ISRC2DEC1, "ISRC2DEC1"), +ARIZONA_MUX_WIDGETS(ISRC2DEC2, "ISRC2DEC2"), + +ARIZONA_MUX_WIDGETS(ISRC2INT1, "ISRC2INT1"), +ARIZONA_MUX_WIDGETS(ISRC2INT2, "ISRC2INT2"), + +SND_SOC_DAPM_OUTPUT("HPOUT1L"), +SND_SOC_DAPM_OUTPUT("HPOUT1R"), +SND_SOC_DAPM_OUTPUT("EPOUTN"), +SND_SOC_DAPM_OUTPUT("EPOUTP"), +SND_SOC_DAPM_OUTPUT("SPKOUTN"), +SND_SOC_DAPM_OUTPUT("SPKOUTP"), +SND_SOC_DAPM_OUTPUT("SPKDAT1L"), +SND_SOC_DAPM_OUTPUT("SPKDAT1R"), + +SND_SOC_DAPM_OUTPUT("MICSUPP"), +}; + +#define ARIZONA_MIXER_INPUT_ROUTES(name) \ + { name, "Noise Generator", "Noise Generator" }, \ + { name, "Tone Generator 1", "Tone Generator 1" }, \ + { name, "Tone Generator 2", "Tone Generator 2" }, \ + { name, "Haptics", "HAPTICS" }, \ + { name, "AEC", "AEC Loopback" }, \ + { name, "IN1L", "IN1L PGA" }, \ + { name, "IN1R", "IN1R PGA" }, \ + { name, "IN2L", "IN2L PGA" }, \ + { name, "IN2R", "IN2R PGA" }, \ + { name, "Mic Mute Mixer", "Mic Mute Mixer" }, \ + { name, "AIF1RX1", "AIF1RX1" }, \ + { name, "AIF1RX2", "AIF1RX2" }, \ + { name, "AIF1RX3", "AIF1RX3" }, \ + { name, "AIF1RX4", "AIF1RX4" }, \ + { name, "AIF1RX5", "AIF1RX5" }, \ + { name, "AIF1RX6", "AIF1RX6" }, \ + { name, "AIF1RX7", "AIF1RX7" }, \ + { name, "AIF1RX8", "AIF1RX8" }, \ + { name, "AIF2RX1", "AIF2RX1" }, \ + { name, "AIF2RX2", "AIF2RX2" }, \ + { name, "SLIMRX1", "SLIMRX1" }, \ + { name, "SLIMRX2", "SLIMRX2" }, \ + { name, "SLIMRX3", "SLIMRX3" }, \ + { name, "SLIMRX4", "SLIMRX4" }, \ + { name, "SLIMRX5", "SLIMRX5" }, \ + { name, "SLIMRX6", "SLIMRX6" }, \ + { name, "SLIMRX7", "SLIMRX7" }, \ + { name, "SLIMRX8", "SLIMRX8" }, \ + { name, "EQ1", "EQ1" }, \ + { name, "EQ2", "EQ2" }, \ + { name, "EQ3", "EQ3" }, \ + { name, "EQ4", "EQ4" }, \ + { name, "DRC1L", "DRC1L" }, \ + { name, "DRC1R", "DRC1R" }, \ + { name, "LHPF1", "LHPF1" }, \ + { name, "LHPF2", "LHPF2" }, \ + { name, "LHPF3", "LHPF3" }, \ + { name, "LHPF4", "LHPF4" }, \ + { name, "ISRC1DEC1", "ISRC1DEC1" }, \ + { name, "ISRC1DEC2", "ISRC1DEC2" }, \ + { name, "ISRC1INT1", "ISRC1INT1" }, \ + { name, "ISRC1INT2", "ISRC1INT2" }, \ + { name, "ISRC2DEC1", "ISRC2DEC1" }, \ + { name, "ISRC2DEC2", "ISRC2DEC2" }, \ + { name, "ISRC2INT1", "ISRC2INT1" }, \ + { name, "ISRC2INT2", "ISRC2INT2" } + +static const struct snd_soc_dapm_route wm8997_dapm_routes[] = { + { "AIF2 Capture", NULL, "DBVDD2" }, + { "AIF2 Playback", NULL, "DBVDD2" }, + + { "OUT1L", NULL, "CPVDD" }, + { "OUT1R", NULL, "CPVDD" }, + { "OUT3L", NULL, "CPVDD" }, + + { "OUT4L", NULL, "SPKVDD" }, + + { "OUT1L", NULL, "SYSCLK" }, + { "OUT1R", NULL, "SYSCLK" }, + { "OUT3L", NULL, "SYSCLK" }, + { "OUT4L", NULL, "SYSCLK" }, + + { "IN1L", NULL, "SYSCLK" }, + { "IN1R", NULL, "SYSCLK" }, + { "IN2L", NULL, "SYSCLK" }, + { "IN2R", NULL, "SYSCLK" }, + + { "MICBIAS1", NULL, "MICVDD" }, + { "MICBIAS2", NULL, "MICVDD" }, + { "MICBIAS3", NULL, "MICVDD" }, + + { "Noise Generator", NULL, "SYSCLK" }, + { "Tone Generator 1", NULL, "SYSCLK" }, + { "Tone Generator 2", NULL, "SYSCLK" }, + + { "Noise Generator", NULL, "NOISE" }, + { "Tone Generator 1", NULL, "TONE" }, + { "Tone Generator 2", NULL, "TONE" }, + + { "Mic Mute Mixer", NULL, "Noise Mixer" }, + { "Mic Mute Mixer", NULL, "Mic Mixer" }, + + { "AIF1 Capture", NULL, "AIF1TX1" }, + { "AIF1 Capture", NULL, "AIF1TX2" }, + { "AIF1 Capture", NULL, "AIF1TX3" }, + { "AIF1 Capture", NULL, "AIF1TX4" }, + { "AIF1 Capture", NULL, "AIF1TX5" }, + { "AIF1 Capture", NULL, "AIF1TX6" }, + { "AIF1 Capture", NULL, "AIF1TX7" }, + { "AIF1 Capture", NULL, "AIF1TX8" }, + + { "AIF1RX1", NULL, "AIF1 Playback" }, + { "AIF1RX2", NULL, "AIF1 Playback" }, + { "AIF1RX3", NULL, "AIF1 Playback" }, + { "AIF1RX4", NULL, "AIF1 Playback" }, + { "AIF1RX5", NULL, "AIF1 Playback" }, + { "AIF1RX6", NULL, "AIF1 Playback" }, + { "AIF1RX7", NULL, "AIF1 Playback" }, + { "AIF1RX8", NULL, "AIF1 Playback" }, + + { "AIF2 Capture", NULL, "AIF2TX1" }, + { "AIF2 Capture", NULL, "AIF2TX2" }, + + { "AIF2RX1", NULL, "AIF2 Playback" }, + { "AIF2RX2", NULL, "AIF2 Playback" }, + + { "Slim1 Capture", NULL, "SLIMTX1" }, + { "Slim1 Capture", NULL, "SLIMTX2" }, + { "Slim1 Capture", NULL, "SLIMTX3" }, + { "Slim1 Capture", NULL, "SLIMTX4" }, + + { "SLIMRX1", NULL, "Slim1 Playback" }, + { "SLIMRX2", NULL, "Slim1 Playback" }, + { "SLIMRX3", NULL, "Slim1 Playback" }, + { "SLIMRX4", NULL, "Slim1 Playback" }, + + { "Slim2 Capture", NULL, "SLIMTX5" }, + { "Slim2 Capture", NULL, "SLIMTX6" }, + + { "SLIMRX5", NULL, "Slim2 Playback" }, + { "SLIMRX6", NULL, "Slim2 Playback" }, + + { "Slim3 Capture", NULL, "SLIMTX7" }, + { "Slim3 Capture", NULL, "SLIMTX8" }, + + { "SLIMRX7", NULL, "Slim3 Playback" }, + { "SLIMRX8", NULL, "Slim3 Playback" }, + + { "AIF1 Playback", NULL, "SYSCLK" }, + { "AIF2 Playback", NULL, "SYSCLK" }, + { "Slim1 Playback", NULL, "SYSCLK" }, + { "Slim2 Playback", NULL, "SYSCLK" }, + { "Slim3 Playback", NULL, "SYSCLK" }, + + { "AIF1 Capture", NULL, "SYSCLK" }, + { "AIF2 Capture", NULL, "SYSCLK" }, + { "Slim1 Capture", NULL, "SYSCLK" }, + { "Slim2 Capture", NULL, "SYSCLK" }, + { "Slim3 Capture", NULL, "SYSCLK" }, + + { "IN1L PGA", NULL, "IN1L" }, + { "IN1R PGA", NULL, "IN1R" }, + + { "IN2L PGA", NULL, "IN2L" }, + { "IN2R PGA", NULL, "IN2R" }, + + ARIZONA_MIXER_ROUTES("OUT1L", "HPOUT1L"), + ARIZONA_MIXER_ROUTES("OUT1R", "HPOUT1R"), + ARIZONA_MIXER_ROUTES("OUT3L", "EPOUT"), + + ARIZONA_MIXER_ROUTES("OUT4L", "SPKOUT"), + ARIZONA_MIXER_ROUTES("OUT5L", "SPKDAT1L"), + ARIZONA_MIXER_ROUTES("OUT5R", "SPKDAT1R"), + + ARIZONA_MIXER_ROUTES("PWM1 Driver", "PWM1"), + ARIZONA_MIXER_ROUTES("PWM2 Driver", "PWM2"), + + ARIZONA_MIXER_ROUTES("AIF1TX1", "AIF1TX1"), + ARIZONA_MIXER_ROUTES("AIF1TX2", "AIF1TX2"), + ARIZONA_MIXER_ROUTES("AIF1TX3", "AIF1TX3"), + ARIZONA_MIXER_ROUTES("AIF1TX4", "AIF1TX4"), + ARIZONA_MIXER_ROUTES("AIF1TX5", "AIF1TX5"), + ARIZONA_MIXER_ROUTES("AIF1TX6", "AIF1TX6"), + ARIZONA_MIXER_ROUTES("AIF1TX7", "AIF1TX7"), + ARIZONA_MIXER_ROUTES("AIF1TX8", "AIF1TX8"), + + ARIZONA_MIXER_ROUTES("AIF2TX1", "AIF2TX1"), + ARIZONA_MIXER_ROUTES("AIF2TX2", "AIF2TX2"), + + ARIZONA_MIXER_ROUTES("SLIMTX1", "SLIMTX1"), + ARIZONA_MIXER_ROUTES("SLIMTX2", "SLIMTX2"), + ARIZONA_MIXER_ROUTES("SLIMTX3", "SLIMTX3"), + ARIZONA_MIXER_ROUTES("SLIMTX4", "SLIMTX4"), + ARIZONA_MIXER_ROUTES("SLIMTX5", "SLIMTX5"), + ARIZONA_MIXER_ROUTES("SLIMTX6", "SLIMTX6"), + ARIZONA_MIXER_ROUTES("SLIMTX7", "SLIMTX7"), + ARIZONA_MIXER_ROUTES("SLIMTX8", "SLIMTX8"), + + ARIZONA_MIXER_ROUTES("EQ1", "EQ1"), + ARIZONA_MIXER_ROUTES("EQ2", "EQ2"), + ARIZONA_MIXER_ROUTES("EQ3", "EQ3"), + ARIZONA_MIXER_ROUTES("EQ4", "EQ4"), + + ARIZONA_MIXER_ROUTES("DRC1L", "DRC1L"), + ARIZONA_MIXER_ROUTES("DRC1R", "DRC1R"), + + ARIZONA_MIXER_ROUTES("LHPF1", "LHPF1"), + ARIZONA_MIXER_ROUTES("LHPF2", "LHPF2"), + ARIZONA_MIXER_ROUTES("LHPF3", "LHPF3"), + ARIZONA_MIXER_ROUTES("LHPF4", "LHPF4"), + + ARIZONA_MUX_ROUTES("ISRC1INT1", "ISRC1INT1"), + ARIZONA_MUX_ROUTES("ISRC1INT2", "ISRC2INT2"), + + ARIZONA_MUX_ROUTES("ISRC1DEC1", "ISRC1DEC1"), + ARIZONA_MUX_ROUTES("ISRC1DEC2", "ISRC1DEC2"), + + ARIZONA_MUX_ROUTES("ISRC2INT1", "ISRC2INT1"), + ARIZONA_MUX_ROUTES("ISRC2INT2", "ISRC2INT2"), + + ARIZONA_MUX_ROUTES("ISRC2DEC1", "ISRC2DEC1"), + ARIZONA_MUX_ROUTES("ISRC2DEC2", "ISRC2DEC2"), + + { "AEC Loopback", "HPOUT1L", "OUT1L" }, + { "AEC Loopback", "HPOUT1R", "OUT1R" }, + { "HPOUT1L", NULL, "OUT1L" }, + { "HPOUT1R", NULL, "OUT1R" }, + + { "AEC Loopback", "EPOUT", "OUT3L" }, + { "EPOUTN", NULL, "OUT3L" }, + { "EPOUTP", NULL, "OUT3L" }, + + { "AEC Loopback", "SPKOUT", "OUT4L" }, + { "SPKOUTN", NULL, "OUT4L" }, + { "SPKOUTP", NULL, "OUT4L" }, + + { "AEC Loopback", "SPKDAT1L", "OUT5L" }, + { "AEC Loopback", "SPKDAT1R", "OUT5R" }, + { "SPKDAT1L", NULL, "OUT5L" }, + { "SPKDAT1R", NULL, "OUT5R" }, + + { "MICSUPP", NULL, "SYSCLK" }, +}; + +static int wm8997_set_fll(struct snd_soc_codec *codec, int fll_id, int source, + unsigned int Fref, unsigned int Fout) +{ + struct wm8997_priv *wm8997 = snd_soc_codec_get_drvdata(codec); + + switch (fll_id) { + case WM8997_FLL1: + return arizona_set_fll(&wm8997->fll[0], source, Fref, Fout); + case WM8997_FLL2: + return arizona_set_fll(&wm8997->fll[1], source, Fref, Fout); + case WM8997_FLL1_REFCLK: + return arizona_set_fll_refclk(&wm8997->fll[0], source, Fref, + Fout); + case WM8997_FLL2_REFCLK: + return arizona_set_fll_refclk(&wm8997->fll[1], source, Fref, + Fout); + default: + return -EINVAL; + } +} + +#define WM8997_RATES SNDRV_PCM_RATE_8000_192000 + +#define WM8997_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +static struct snd_soc_dai_driver wm8997_dai[] = { + { + .name = "wm8997-aif1", + .id = 1, + .base = ARIZONA_AIF1_BCLK_CTRL, + .playback = { + .stream_name = "AIF1 Playback", + .channels_min = 1, + .channels_max = 8, + .rates = WM8997_RATES, + .formats = WM8997_FORMATS, + }, + .capture = { + .stream_name = "AIF1 Capture", + .channels_min = 1, + .channels_max = 8, + .rates = WM8997_RATES, + .formats = WM8997_FORMATS, + }, + .ops = &arizona_dai_ops, + .symmetric_rates = 1, + }, + { + .name = "wm8997-aif2", + .id = 2, + .base = ARIZONA_AIF2_BCLK_CTRL, + .playback = { + .stream_name = "AIF2 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = WM8997_RATES, + .formats = WM8997_FORMATS, + }, + .capture = { + .stream_name = "AIF2 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = WM8997_RATES, + .formats = WM8997_FORMATS, + }, + .ops = &arizona_dai_ops, + .symmetric_rates = 1, + }, + { + .name = "wm8997-slim1", + .id = 3, + .playback = { + .stream_name = "Slim1 Playback", + .channels_min = 1, + .channels_max = 4, + .rates = WM8997_RATES, + .formats = WM8997_FORMATS, + }, + .capture = { + .stream_name = "Slim1 Capture", + .channels_min = 1, + .channels_max = 4, + .rates = WM8997_RATES, + .formats = WM8997_FORMATS, + }, + .ops = &arizona_simple_dai_ops, + }, + { + .name = "wm8997-slim2", + .id = 4, + .playback = { + .stream_name = "Slim2 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = WM8997_RATES, + .formats = WM8997_FORMATS, + }, + .capture = { + .stream_name = "Slim2 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = WM8997_RATES, + .formats = WM8997_FORMATS, + }, + .ops = &arizona_simple_dai_ops, + }, + { + .name = "wm8997-slim3", + .id = 5, + .playback = { + .stream_name = "Slim3 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = WM8997_RATES, + .formats = WM8997_FORMATS, + }, + .capture = { + .stream_name = "Slim3 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = WM8997_RATES, + .formats = WM8997_FORMATS, + }, + .ops = &arizona_simple_dai_ops, + }, +}; + +static int wm8997_codec_probe(struct snd_soc_codec *codec) +{ + struct wm8997_priv *priv = snd_soc_codec_get_drvdata(codec); + int ret; + + codec->control_data = priv->core.arizona->regmap; + + ret = snd_soc_codec_set_cache_io(codec, 32, 16, SND_SOC_REGMAP); + if (ret != 0) + return ret; + + arizona_init_spk(codec); + + snd_soc_dapm_disable_pin(&codec->dapm, "HAPTICS"); + + priv->core.arizona->dapm = &codec->dapm; + + return 0; +} + +static int wm8997_codec_remove(struct snd_soc_codec *codec) +{ + struct wm8997_priv *priv = snd_soc_codec_get_drvdata(codec); + + priv->core.arizona->dapm = NULL; + + return 0; +} + +#define WM8997_DIG_VU 0x0200 + +static unsigned int wm8997_digital_vu[] = { + ARIZONA_DAC_DIGITAL_VOLUME_1L, + ARIZONA_DAC_DIGITAL_VOLUME_1R, + ARIZONA_DAC_DIGITAL_VOLUME_3L, + ARIZONA_DAC_DIGITAL_VOLUME_4L, + ARIZONA_DAC_DIGITAL_VOLUME_5L, + ARIZONA_DAC_DIGITAL_VOLUME_5R, +}; + +static struct snd_soc_codec_driver soc_codec_dev_wm8997 = { + .probe = wm8997_codec_probe, + .remove = wm8997_codec_remove, + + .idle_bias_off = true, + + .set_sysclk = arizona_set_sysclk, + .set_pll = wm8997_set_fll, + + .controls = wm8997_snd_controls, + .num_controls = ARRAY_SIZE(wm8997_snd_controls), + .dapm_widgets = wm8997_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8997_dapm_widgets), + .dapm_routes = wm8997_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(wm8997_dapm_routes), +}; + +static int wm8997_probe(struct platform_device *pdev) +{ + struct arizona *arizona = dev_get_drvdata(pdev->dev.parent); + struct wm8997_priv *wm8997; + int i; + + wm8997 = devm_kzalloc(&pdev->dev, sizeof(struct wm8997_priv), + GFP_KERNEL); + if (wm8997 == NULL) + return -ENOMEM; + platform_set_drvdata(pdev, wm8997); + + wm8997->core.arizona = arizona; + wm8997->core.num_inputs = 4; + + for (i = 0; i < ARRAY_SIZE(wm8997->fll); i++) + wm8997->fll[i].vco_mult = 1; + + arizona_init_fll(arizona, 1, ARIZONA_FLL1_CONTROL_1 - 1, + ARIZONA_IRQ_FLL1_LOCK, ARIZONA_IRQ_FLL1_CLOCK_OK, + &wm8997->fll[0]); + arizona_init_fll(arizona, 2, ARIZONA_FLL2_CONTROL_1 - 1, + ARIZONA_IRQ_FLL2_LOCK, ARIZONA_IRQ_FLL2_CLOCK_OK, + &wm8997->fll[1]); + + /* SR2 fixed at 8kHz, SR3 fixed at 16kHz */ + regmap_update_bits(arizona->regmap, ARIZONA_SAMPLE_RATE_2, + ARIZONA_SAMPLE_RATE_2_MASK, 0x11); + regmap_update_bits(arizona->regmap, ARIZONA_SAMPLE_RATE_3, + ARIZONA_SAMPLE_RATE_3_MASK, 0x12); + + for (i = 0; i < ARRAY_SIZE(wm8997_dai); i++) + arizona_init_dai(&wm8997->core, i); + + /* Latch volume update bits */ + for (i = 0; i < ARRAY_SIZE(wm8997_digital_vu); i++) + regmap_update_bits(arizona->regmap, wm8997_digital_vu[i], + WM8997_DIG_VU, WM8997_DIG_VU); + + pm_runtime_enable(&pdev->dev); + pm_runtime_idle(&pdev->dev); + + return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm8997, + wm8997_dai, ARRAY_SIZE(wm8997_dai)); +} + +static int wm8997_remove(struct platform_device *pdev) +{ + snd_soc_unregister_codec(&pdev->dev); + pm_runtime_disable(&pdev->dev); + + return 0; +} + +static struct platform_driver wm8997_codec_driver = { + .driver = { + .name = "wm8997-codec", + .owner = THIS_MODULE, + }, + .probe = wm8997_probe, + .remove = wm8997_remove, +}; + +module_platform_driver(wm8997_codec_driver); + +MODULE_DESCRIPTION("ASoC WM8997 driver"); +MODULE_AUTHOR("Charles Keepax "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:wm8997-codec"); diff --git a/sound/soc/codecs/wm8997.h b/sound/soc/codecs/wm8997.h new file mode 100644 index 0000000..5e91c6a --- /dev/null +++ b/sound/soc/codecs/wm8997.h @@ -0,0 +1,23 @@ +/* + * wm8997.h -- WM8997 ALSA SoC Audio driver + * + * Copyright 2012 Wolfson Microelectronics plc + * + * Author: Mark Brown + * + * 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. + */ + +#ifndef _WM8997_H +#define _WM8997_H + +#include "arizona.h" + +#define WM8997_FLL1 1 +#define WM8997_FLL2 2 +#define WM8997_FLL1_REFCLK 3 +#define WM8997_FLL2_REFCLK 4 + +#endif -- cgit v0.10.2 From 4601736a6f8e7ae09f1010df02e1ced605043cad Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sun, 11 Aug 2013 12:28:42 +0100 Subject: ASoC: ak4554: Add DAPM support This makes it possible to hook the device into a more complex board and ensures it will continue to work with non-DAPM support removed from the core. Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/ak4554.c b/sound/soc/codecs/ak4554.c index 6aed9c4..79e9555 100644 --- a/sound/soc/codecs/ak4554.c +++ b/sound/soc/codecs/ak4554.c @@ -29,6 +29,22 @@ * CPU-DAI2 (capture only fmt = LEFT_J) ---+ */ +static const struct snd_soc_dapm_widget ak4554_dapm_widgets[] = { +SND_SOC_DAPM_INPUT("AINL"), +SND_SOC_DAPM_INPUT("AINR"), + +SND_SOC_DAPM_OUTPUT("AOUTL"), +SND_SOC_DAPM_OUTPUT("AOUTR"), +}; + +static const struct snd_soc_dapm_route ak4554_dapm_routes[] = { + { "Capture", NULL, "AINL" }, + { "Capture", NULL, "AINR" }, + + { "AOUTL", NULL, "Playback" }, + { "AOUTR", NULL, "Playback" }, +}; + static struct snd_soc_dai_driver ak4554_dai = { .name = "ak4554-hifi", .playback = { @@ -49,6 +65,10 @@ static struct snd_soc_dai_driver ak4554_dai = { }; static struct snd_soc_codec_driver soc_codec_dev_ak4554 = { + .dapm_widgets = ak4554_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(ak4554_dapm_widgets), + .dapm_routes = ak4554_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(ak4554_dapm_routes), }; static int ak4554_soc_probe(struct platform_device *pdev) -- cgit v0.10.2 From 997288e3824e4c6a7ab4ca4f580fe35e138d62e8 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 13 Aug 2013 13:10:19 +0100 Subject: ASoC: max9877: Convert to use regmap API Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/max9877.c b/sound/soc/codecs/max9877.c index 6b6c74c..7e2fe50 100644 --- a/sound/soc/codecs/max9877.c +++ b/sound/soc/codecs/max9877.c @@ -14,27 +14,21 @@ #include #include #include +#include #include #include #include "max9877.h" -static struct i2c_client *i2c; +static struct regmap *regmap; -static u8 max9877_regs[5] = { 0x40, 0x00, 0x00, 0x00, 0x49 }; - -static void max9877_write_regs(void) -{ - unsigned int i; - u8 data[6]; - - data[0] = MAX9877_INPUT_MODE; - for (i = 0; i < ARRAY_SIZE(max9877_regs); i++) - data[i + 1] = max9877_regs[i]; - - if (i2c_master_send(i2c, data, 6) != 6) - dev_err(&i2c->dev, "i2c write failed\n"); -} +static struct reg_default max9877_regs[] = { + { 0, 0x40 }, + { 1, 0x00 }, + { 2, 0x00 }, + { 3, 0x00 }, + { 4, 0x49 }, +}; static int max9877_get_reg(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -45,8 +39,14 @@ static int max9877_get_reg(struct snd_kcontrol *kcontrol, unsigned int shift = mc->shift; unsigned int mask = mc->max; unsigned int invert = mc->invert; + unsigned int val; + int ret; + + ret = regmap_read(regmap, reg, &val); + if (ret != 0) + return ret; - ucontrol->value.integer.value[0] = (max9877_regs[reg] >> shift) & mask; + ucontrol->value.integer.value[0] = (val >> shift) & mask; if (invert) ucontrol->value.integer.value[0] = @@ -65,18 +65,21 @@ static int max9877_set_reg(struct snd_kcontrol *kcontrol, unsigned int mask = mc->max; unsigned int invert = mc->invert; unsigned int val = (ucontrol->value.integer.value[0] & mask); + bool change; + int ret; if (invert) val = mask - val; - if (((max9877_regs[reg] >> shift) & mask) == val) - return 0; - - max9877_regs[reg] &= ~(mask << shift); - max9877_regs[reg] |= val << shift; - max9877_write_regs(); + ret = regmap_update_bits_check(regmap, reg, mask << shift, + val << shift, &change); + if (ret != 0) + return ret; - return 1; + if (change) + return 1; + else + return 0; } static int max9877_get_2reg(struct snd_kcontrol *kcontrol, @@ -88,9 +91,18 @@ static int max9877_get_2reg(struct snd_kcontrol *kcontrol, unsigned int reg2 = mc->rreg; unsigned int shift = mc->shift; unsigned int mask = mc->max; + unsigned int val; + int ret; + + ret = regmap_read(regmap, reg, &val); + if (ret != 0) + return ret; + ucontrol->value.integer.value[0] = (val >> shift) & mask; - ucontrol->value.integer.value[0] = (max9877_regs[reg] >> shift) & mask; - ucontrol->value.integer.value[1] = (max9877_regs[reg2] >> shift) & mask; + ret = regmap_read(regmap, reg2, &val); + if (ret != 0) + return ret; + ucontrol->value.integer.value[1] = (val >> shift) & mask; return 0; } @@ -106,77 +118,99 @@ static int max9877_set_2reg(struct snd_kcontrol *kcontrol, unsigned int mask = mc->max; unsigned int val = (ucontrol->value.integer.value[0] & mask); unsigned int val2 = (ucontrol->value.integer.value[1] & mask); - unsigned int change = 0; - - if (((max9877_regs[reg] >> shift) & mask) != val) - change = 1; - - if (((max9877_regs[reg2] >> shift) & mask) != val2) - change = 1; - - if (change) { - max9877_regs[reg] &= ~(mask << shift); - max9877_regs[reg] |= val << shift; - max9877_regs[reg2] &= ~(mask << shift); - max9877_regs[reg2] |= val2 << shift; - max9877_write_regs(); - } - - return change; + bool change1, change2; + int ret; + + ret = regmap_update_bits_check(regmap, reg, mask << shift, + val << shift, &change1); + if (ret != 0) + return ret; + + ret = regmap_update_bits_check(regmap, reg2, mask << shift, + val2 << shift, &change2); + if (ret != 0) + return ret; + + if (change1 || change2) + return 1; + else + return 0; } static int max9877_get_out_mode(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - u8 value = max9877_regs[MAX9877_OUTPUT_MODE] & MAX9877_OUTMODE_MASK; + unsigned int val; + int ret; + + ret = regmap_read(regmap, MAX9877_OUTPUT_MODE, &val); + if (ret != 0) + return ret; + + val &= MAX9877_OUTMODE_MASK; + if (val) + val--; - if (value) - value -= 1; + ucontrol->value.integer.value[0] = val; - ucontrol->value.integer.value[0] = value; return 0; } static int max9877_set_out_mode(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - u8 value = ucontrol->value.integer.value[0]; + unsigned int val; + bool change; + int ret; - value += 1; + val = ucontrol->value.integer.value[0] + 1; - if ((max9877_regs[MAX9877_OUTPUT_MODE] & MAX9877_OUTMODE_MASK) == value) - return 0; + ret = regmap_update_bits_check(regmap, MAX9877_OUTPUT_MODE, + MAX9877_OUTMODE_MASK, val, &change); + if (ret != 0) + return ret; - max9877_regs[MAX9877_OUTPUT_MODE] &= ~MAX9877_OUTMODE_MASK; - max9877_regs[MAX9877_OUTPUT_MODE] |= value; - max9877_write_regs(); - return 1; + if (change) + return 1; + else + return 0; } static int max9877_get_osc_mode(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - u8 value = (max9877_regs[MAX9877_OUTPUT_MODE] & MAX9877_OSC_MASK); + unsigned int val; + int ret; + + ret = regmap_read(regmap, MAX9877_OUTPUT_MODE, &val); + if (ret != 0) + return ret; + + val &= MAX9877_OSC_MASK; + val >>= MAX9877_OSC_OFFSET; - value = value >> MAX9877_OSC_OFFSET; + ucontrol->value.integer.value[0] = val; - ucontrol->value.integer.value[0] = value; return 0; } static int max9877_set_osc_mode(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - u8 value = ucontrol->value.integer.value[0]; - - value = value << MAX9877_OSC_OFFSET; - if ((max9877_regs[MAX9877_OUTPUT_MODE] & MAX9877_OSC_MASK) == value) + unsigned int val; + bool change; + int ret; + + val = ucontrol->value.integer.value[0] << MAX9877_OSC_OFFSET; + ret = regmap_update_bits_check(regmap, MAX9877_OUTPUT_MODE, + MAX9877_OSC_MASK, val, &change); + if (ret != 0) + return ret; + + if (change) + return 1; + else return 0; - - max9877_regs[MAX9877_OUTPUT_MODE] &= ~MAX9877_OSC_MASK; - max9877_regs[MAX9877_OUTPUT_MODE] |= value; - max9877_write_regs(); - return 1; } static const unsigned int max9877_pgain_tlv[] = { @@ -258,19 +292,34 @@ int max9877_add_controls(struct snd_soc_codec *codec) } EXPORT_SYMBOL_GPL(max9877_add_controls); +static const struct regmap_config max9877_regmap = { + .reg_bits = 8, + .val_bits = 8, + + .reg_defaults = max9877_regs, + .num_reg_defaults = ARRAY_SIZE(max9877_regs), + .cache_type = REGCACHE_RBTREE, +}; + static int max9877_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { - i2c = client; + int i; + + regmap = devm_regmap_init_i2c(client, &max9877_regmap); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); - max9877_write_regs(); + /* Ensure the device is in reset state */ + for (i = 0; i < ARRAY_SIZE(max9877_regs); i++) + regmap_write(regmap, max9877_regs[i].reg, max9877_regs[i].def); return 0; } static int max9877_i2c_remove(struct i2c_client *client) { - i2c = NULL; + regmap = NULL; return 0; } -- cgit v0.10.2 From d76a96174b31bd916c1dfaa81a3db82fc8c54b91 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 13 Aug 2013 13:20:15 +0100 Subject: ASoC: max9877: Convert to standard CODEC driver Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/max9877.c b/sound/soc/codecs/max9877.c index 7e2fe50..8505b40 100644 --- a/sound/soc/codecs/max9877.c +++ b/sound/soc/codecs/max9877.c @@ -30,189 +30,6 @@ static struct reg_default max9877_regs[] = { { 4, 0x49 }, }; -static int max9877_get_reg(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; - unsigned int reg = mc->reg; - unsigned int shift = mc->shift; - unsigned int mask = mc->max; - unsigned int invert = mc->invert; - unsigned int val; - int ret; - - ret = regmap_read(regmap, reg, &val); - if (ret != 0) - return ret; - - ucontrol->value.integer.value[0] = (val >> shift) & mask; - - if (invert) - ucontrol->value.integer.value[0] = - mask - ucontrol->value.integer.value[0]; - - return 0; -} - -static int max9877_set_reg(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; - unsigned int reg = mc->reg; - unsigned int shift = mc->shift; - unsigned int mask = mc->max; - unsigned int invert = mc->invert; - unsigned int val = (ucontrol->value.integer.value[0] & mask); - bool change; - int ret; - - if (invert) - val = mask - val; - - ret = regmap_update_bits_check(regmap, reg, mask << shift, - val << shift, &change); - if (ret != 0) - return ret; - - if (change) - return 1; - else - return 0; -} - -static int max9877_get_2reg(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; - unsigned int reg = mc->reg; - unsigned int reg2 = mc->rreg; - unsigned int shift = mc->shift; - unsigned int mask = mc->max; - unsigned int val; - int ret; - - ret = regmap_read(regmap, reg, &val); - if (ret != 0) - return ret; - ucontrol->value.integer.value[0] = (val >> shift) & mask; - - ret = regmap_read(regmap, reg2, &val); - if (ret != 0) - return ret; - ucontrol->value.integer.value[1] = (val >> shift) & mask; - - return 0; -} - -static int max9877_set_2reg(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; - unsigned int reg = mc->reg; - unsigned int reg2 = mc->rreg; - unsigned int shift = mc->shift; - unsigned int mask = mc->max; - unsigned int val = (ucontrol->value.integer.value[0] & mask); - unsigned int val2 = (ucontrol->value.integer.value[1] & mask); - bool change1, change2; - int ret; - - ret = regmap_update_bits_check(regmap, reg, mask << shift, - val << shift, &change1); - if (ret != 0) - return ret; - - ret = regmap_update_bits_check(regmap, reg2, mask << shift, - val2 << shift, &change2); - if (ret != 0) - return ret; - - if (change1 || change2) - return 1; - else - return 0; -} - -static int max9877_get_out_mode(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - unsigned int val; - int ret; - - ret = regmap_read(regmap, MAX9877_OUTPUT_MODE, &val); - if (ret != 0) - return ret; - - val &= MAX9877_OUTMODE_MASK; - if (val) - val--; - - ucontrol->value.integer.value[0] = val; - - return 0; -} - -static int max9877_set_out_mode(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - unsigned int val; - bool change; - int ret; - - val = ucontrol->value.integer.value[0] + 1; - - ret = regmap_update_bits_check(regmap, MAX9877_OUTPUT_MODE, - MAX9877_OUTMODE_MASK, val, &change); - if (ret != 0) - return ret; - - if (change) - return 1; - else - return 0; -} - -static int max9877_get_osc_mode(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - unsigned int val; - int ret; - - ret = regmap_read(regmap, MAX9877_OUTPUT_MODE, &val); - if (ret != 0) - return ret; - - val &= MAX9877_OSC_MASK; - val >>= MAX9877_OSC_OFFSET; - - ucontrol->value.integer.value[0] = val; - - return 0; -} - -static int max9877_set_osc_mode(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - unsigned int val; - bool change; - int ret; - - val = ucontrol->value.integer.value[0] << MAX9877_OSC_OFFSET; - ret = regmap_update_bits_check(regmap, MAX9877_OUTPUT_MODE, - MAX9877_OSC_MASK, val, &change); - if (ret != 0) - return ret; - - if (change) - return 1; - else - return 0; -} - static const unsigned int max9877_pgain_tlv[] = { TLV_DB_RANGE_HEAD(2), 0, 1, TLV_DB_SCALE_ITEM(0, 900, 0), @@ -246,51 +63,40 @@ static const char *max9877_osc_mode[] = { }; static const struct soc_enum max9877_enum[] = { - SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(max9877_out_mode), max9877_out_mode), - SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(max9877_osc_mode), max9877_osc_mode), + SOC_ENUM_SINGLE(MAX9877_OUTPUT_MODE, 0, ARRAY_SIZE(max9877_out_mode), + max9877_out_mode), + SOC_ENUM_SINGLE(MAX9877_OUTPUT_MODE, MAX9877_OSC_OFFSET, + ARRAY_SIZE(max9877_osc_mode), max9877_osc_mode), }; static const struct snd_kcontrol_new max9877_controls[] = { - SOC_SINGLE_EXT_TLV("MAX9877 PGAINA Playback Volume", - MAX9877_INPUT_MODE, 0, 2, 0, - max9877_get_reg, max9877_set_reg, max9877_pgain_tlv), - SOC_SINGLE_EXT_TLV("MAX9877 PGAINB Playback Volume", - MAX9877_INPUT_MODE, 2, 2, 0, - max9877_get_reg, max9877_set_reg, max9877_pgain_tlv), - SOC_SINGLE_EXT_TLV("MAX9877 Amp Speaker Playback Volume", - MAX9877_SPK_VOLUME, 0, 31, 0, - max9877_get_reg, max9877_set_reg, max9877_output_tlv), - SOC_DOUBLE_R_EXT_TLV("MAX9877 Amp HP Playback Volume", - MAX9877_HPL_VOLUME, MAX9877_HPR_VOLUME, 0, 31, 0, - max9877_get_2reg, max9877_set_2reg, max9877_output_tlv), - SOC_SINGLE_EXT("MAX9877 INB Stereo Switch", - MAX9877_INPUT_MODE, 4, 1, 1, - max9877_get_reg, max9877_set_reg), - SOC_SINGLE_EXT("MAX9877 INA Stereo Switch", - MAX9877_INPUT_MODE, 5, 1, 1, - max9877_get_reg, max9877_set_reg), - SOC_SINGLE_EXT("MAX9877 Zero-crossing detection Switch", - MAX9877_INPUT_MODE, 6, 1, 0, - max9877_get_reg, max9877_set_reg), - SOC_SINGLE_EXT("MAX9877 Bypass Mode Switch", - MAX9877_OUTPUT_MODE, 6, 1, 0, - max9877_get_reg, max9877_set_reg), - SOC_SINGLE_EXT("MAX9877 Shutdown Mode Switch", - MAX9877_OUTPUT_MODE, 7, 1, 1, - max9877_get_reg, max9877_set_reg), - SOC_ENUM_EXT("MAX9877 Output Mode", max9877_enum[0], - max9877_get_out_mode, max9877_set_out_mode), - SOC_ENUM_EXT("MAX9877 Oscillator Mode", max9877_enum[1], - max9877_get_osc_mode, max9877_set_osc_mode), + SOC_SINGLE_TLV("MAX9877 PGAINA Playback Volume", + MAX9877_INPUT_MODE, 0, 2, 0, max9877_pgain_tlv), + SOC_SINGLE_TLV("MAX9877 PGAINB Playback Volume", + MAX9877_INPUT_MODE, 2, 2, 0, max9877_pgain_tlv), + SOC_SINGLE_TLV("MAX9877 Amp Speaker Playback Volume", + MAX9877_SPK_VOLUME, 0, 31, 0, max9877_output_tlv), + SOC_DOUBLE_R_TLV("MAX9877 Amp HP Playback Volume", + MAX9877_HPL_VOLUME, MAX9877_HPR_VOLUME, 0, 31, 0, + max9877_output_tlv), + SOC_SINGLE("MAX9877 INB Stereo Switch", + MAX9877_INPUT_MODE, 4, 1, 1), + SOC_SINGLE("MAX9877 INA Stereo Switch", + MAX9877_INPUT_MODE, 5, 1, 1), + SOC_SINGLE("MAX9877 Zero-crossing detection Switch", + MAX9877_INPUT_MODE, 6, 1, 0), + SOC_SINGLE("MAX9877 Bypass Mode Switch", + MAX9877_OUTPUT_MODE, 6, 1, 0), + SOC_SINGLE("MAX9877 Shutdown Mode Switch", + MAX9877_OUTPUT_MODE, 7, 1, 1), + SOC_ENUM("MAX9877 Output Mode", max9877_enum[0]), + SOC_ENUM("MAX9877 Oscillator Mode", max9877_enum[1]), }; -/* This function is called from ASoC machine driver */ -int max9877_add_controls(struct snd_soc_codec *codec) -{ - return snd_soc_add_codec_controls(codec, max9877_controls, - ARRAY_SIZE(max9877_controls)); -} -EXPORT_SYMBOL_GPL(max9877_add_controls); +static const struct snd_soc_codec_driver max9877_codec = { + .controls = max9877_controls, + .num_controls = ARRAY_SIZE(max9877_controls), +}; static const struct regmap_config max9877_regmap = { .reg_bits = 8, @@ -314,12 +120,12 @@ static int max9877_i2c_probe(struct i2c_client *client, for (i = 0; i < ARRAY_SIZE(max9877_regs); i++) regmap_write(regmap, max9877_regs[i].reg, max9877_regs[i].def); - return 0; + return snd_soc_register_codec(&client->dev, &max9877_codec, NULL, 0); } static int max9877_i2c_remove(struct i2c_client *client) { - regmap = NULL; + snd_soc_unregister_codec(&client->dev); return 0; } -- cgit v0.10.2 From 5cf9da8aacbfaed72ada8c195859f49d5d7f5f6c Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 13 Aug 2013 13:32:03 +0100 Subject: ASoC: max9877: Add basic DAPM support This does not fully map the power control available within the device but it provides the hooks for routing signals through the device and allows automatic management of the device low power mode. Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/max9877.c b/sound/soc/codecs/max9877.c index 8505b40..29549cd 100644 --- a/sound/soc/codecs/max9877.c +++ b/sound/soc/codecs/max9877.c @@ -87,15 +87,50 @@ static const struct snd_kcontrol_new max9877_controls[] = { MAX9877_INPUT_MODE, 6, 1, 0), SOC_SINGLE("MAX9877 Bypass Mode Switch", MAX9877_OUTPUT_MODE, 6, 1, 0), - SOC_SINGLE("MAX9877 Shutdown Mode Switch", - MAX9877_OUTPUT_MODE, 7, 1, 1), SOC_ENUM("MAX9877 Output Mode", max9877_enum[0]), SOC_ENUM("MAX9877 Oscillator Mode", max9877_enum[1]), }; +static const struct snd_soc_dapm_widget max9877_dapm_widgets[] = { +SND_SOC_DAPM_INPUT("INA1"), +SND_SOC_DAPM_INPUT("INA2"), +SND_SOC_DAPM_INPUT("INB1"), +SND_SOC_DAPM_INPUT("INB2"), +SND_SOC_DAPM_INPUT("RXIN+"), +SND_SOC_DAPM_INPUT("RXIN-"), + +SND_SOC_DAPM_PGA("SHDN", MAX9877_OUTPUT_MODE, 7, 1, NULL, 0), + +SND_SOC_DAPM_OUTPUT("OUT+"), +SND_SOC_DAPM_OUTPUT("OUT-"), +SND_SOC_DAPM_OUTPUT("HPL"), +SND_SOC_DAPM_OUTPUT("HPR"), +}; + +static const struct snd_soc_dapm_route max9877_dapm_routes[] = { + { "SHDN", NULL, "INA1" }, + { "SHDN", NULL, "INA2" }, + { "SHDN", NULL, "INB1" }, + { "SHDN", NULL, "INB2" }, + + { "OUT+", NULL, "RXIN+" }, + { "OUT+", NULL, "SHDN" }, + + { "OUT-", NULL, "SHDN" }, + { "OUT-", NULL, "RXIN-" }, + + { "HPL", NULL, "SHDN" }, + { "HPR", NULL, "SHDN" }, +}; + static const struct snd_soc_codec_driver max9877_codec = { .controls = max9877_controls, .num_controls = ARRAY_SIZE(max9877_controls), + + .dapm_widgets = max9877_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(max9877_dapm_widgets), + .dapm_routes = max9877_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(max9877_dapm_routes), }; static const struct regmap_config max9877_regmap = { -- cgit v0.10.2 From 7da493e9229c737c399886f57996f6bfd4454e21 Mon Sep 17 00:00:00 2001 From: Padmavathi Venna Date: Mon, 12 Aug 2013 15:19:51 +0530 Subject: ASoC: Samsung: I2S: Add quirks as driver data in I2S Samsung has different versions of I2S introduced in different platforms. Each version has some new support added for multichannel, secondary fifo, s/w reset control and internal mux for rclk src clk. Each newly added change has a quirk. So this patch adds all the required quirks as driver data and based on compatible string from dtsi fetches the quirks. Signed-off-by: Padmavathi Venna Reviewed-by: Tomasz Figa Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/sound/samsung-i2s.txt b/Documentation/devicetree/bindings/sound/samsung-i2s.txt index 025e66b..25a0024 100644 --- a/Documentation/devicetree/bindings/sound/samsung-i2s.txt +++ b/Documentation/devicetree/bindings/sound/samsung-i2s.txt @@ -2,7 +2,11 @@ Required SoC Specific Properties: -- compatible : "samsung,i2s-v5" +- compatible : should be one of the following. + - samsung,s3c6410-i2s: for 8/16/24bit stereo I2S. + - samsung,s5pv210-i2s: for 8/16/24bit multichannel(5.1) I2S with + secondary fifo, s/w reset control and internal mux for root clk src. + - reg: physical base address of the controller and length of memory mapped region. - dmas: list of DMA controller phandle and DMA request line ordered pairs. @@ -21,13 +25,6 @@ Required SoC Specific Properties: Optional SoC Specific Properties: -- samsung,supports-6ch: If the I2S Primary sound source has 5.1 Channel - support, this flag is enabled. -- samsung,supports-rstclr: This flag should be set if I2S software reset bit - control is required. When this flag is set I2S software reset bit will be - enabled or disabled based on need. -- samsung,supports-secdai:If I2S block has a secondary FIFO and internal DMA, - then this flag is enabled. - samsung,idma-addr: Internal DMA register base address of the audio sub system(used in secondary sound source). - pinctrl-0: Should specify pin control groups used for this controller. @@ -36,7 +33,7 @@ Optional SoC Specific Properties: Example: i2s0: i2s@03830000 { - compatible = "samsung,i2s-v5"; + compatible = "samsung,s5pv210-i2s"; reg = <0x03830000 0x100>; dmas = <&pdma0 10 &pdma0 9 @@ -46,9 +43,6 @@ i2s0: i2s@03830000 { <&clock_audss EXYNOS_I2S_BUS>, <&clock_audss EXYNOS_SCLK_I2S>; clock-names = "iis", "i2s_opclk0", "i2s_opclk1"; - samsung,supports-6ch; - samsung,supports-rstclr; - samsung,supports-secdai; samsung,idma-addr = <0x03000000>; pinctrl-names = "default"; pinctrl-0 = <&i2s0_bus>; diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index 849ac0e..3b4835a 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -40,6 +40,7 @@ enum samsung_dai_type { struct samsung_i2s_dai_data { int dai_type; + u32 quirks; }; struct i2s_dai { @@ -1032,18 +1033,18 @@ static struct i2s_dai *i2s_alloc_dai(struct platform_device *pdev, bool sec) static const struct of_device_id exynos_i2s_match[]; -static inline int samsung_i2s_get_driver_data(struct platform_device *pdev) +static inline const struct samsung_i2s_dai_data *samsung_i2s_get_driver_data( + struct platform_device *pdev) { #ifdef CONFIG_OF - struct samsung_i2s_dai_data *data; if (pdev->dev.of_node) { const struct of_device_id *match; match = of_match_node(exynos_i2s_match, pdev->dev.of_node); - data = (struct samsung_i2s_dai_data *) match->data; - return data->dai_type; + return match->data; } else #endif - return platform_get_device_id(pdev)->driver_data; + return (struct samsung_i2s_dai_data *) + platform_get_device_id(pdev)->driver_data; } #ifdef CONFIG_PM_RUNTIME @@ -1074,13 +1075,13 @@ static int samsung_i2s_probe(struct platform_device *pdev) struct resource *res; u32 regs_base, quirks = 0, idma_addr = 0; struct device_node *np = pdev->dev.of_node; - enum samsung_dai_type samsung_dai_type; + const struct samsung_i2s_dai_data *i2s_dai_data; int ret = 0; /* Call during Seconday interface registration */ - samsung_dai_type = samsung_i2s_get_driver_data(pdev); + i2s_dai_data = samsung_i2s_get_driver_data(pdev); - if (samsung_dai_type == TYPE_SEC) { + if (i2s_dai_data->dai_type == TYPE_SEC) { sec_dai = dev_get_drvdata(&pdev->dev); if (!sec_dai) { dev_err(&pdev->dev, "Unable to get drvdata\n"); @@ -1129,15 +1130,7 @@ static int samsung_i2s_probe(struct platform_device *pdev) idma_addr = i2s_cfg->idma_addr; } } else { - if (of_find_property(np, "samsung,supports-6ch", NULL)) - quirks |= QUIRK_PRI_6CHAN; - - if (of_find_property(np, "samsung,supports-secdai", NULL)) - quirks |= QUIRK_SEC_DAI; - - if (of_find_property(np, "samsung,supports-rstclr", NULL)) - quirks |= QUIRK_NEED_RSTCLR; - + quirks = i2s_dai_data->quirks; if (of_property_read_u32(np, "samsung,idma-addr", &idma_addr)) { if (quirks & QUIRK_SEC_DAI) { @@ -1250,27 +1243,44 @@ static int samsung_i2s_remove(struct platform_device *pdev) return 0; } +static const struct samsung_i2s_dai_data i2sv3_dai_type = { + .dai_type = TYPE_PRI, + .quirks = QUIRK_NO_MUXPSR, +}; + +static const struct samsung_i2s_dai_data i2sv5_dai_type = { + .dai_type = TYPE_PRI, + .quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR, +}; + +static const struct samsung_i2s_dai_data samsung_dai_type_pri = { + .dai_type = TYPE_PRI, +}; + +static const struct samsung_i2s_dai_data samsung_dai_type_sec = { + .dai_type = TYPE_SEC, +}; + static struct platform_device_id samsung_i2s_driver_ids[] = { { .name = "samsung-i2s", - .driver_data = TYPE_PRI, + .driver_data = (kernel_ulong_t)&samsung_dai_type_pri, }, { .name = "samsung-i2s-sec", - .driver_data = TYPE_SEC, + .driver_data = (kernel_ulong_t)&samsung_dai_type_sec, }, {}, }; MODULE_DEVICE_TABLE(platform, samsung_i2s_driver_ids); #ifdef CONFIG_OF -static struct samsung_i2s_dai_data samsung_i2s_dai_data_array[] = { - [TYPE_PRI] = { TYPE_PRI }, - [TYPE_SEC] = { TYPE_SEC }, -}; - static const struct of_device_id exynos_i2s_match[] = { - { .compatible = "samsung,i2s-v5", - .data = &samsung_i2s_dai_data_array[TYPE_PRI], + { + .compatible = "samsung,s3c6410-i2s", + .data = &i2sv3_dai_type, + }, { + .compatible = "samsung,s5pv210-i2s", + .data = &i2sv5_dai_type, }, {}, }; -- cgit v0.10.2 From 4ca0c0d4784fa82d68733f7793e3487023e12282 Mon Sep 17 00:00:00 2001 From: Padmavathi Venna Date: Mon, 12 Aug 2013 15:19:52 +0530 Subject: ASoC: Samsung: I2S: Modify the I2S driver to support I2S on Exynos5420 Exynos5420 added support for I2S TDM mode. For this, there are some register changes in the I2S controller. This patch adds the relevant register changes to support I2S in normal mode. This patch adds a quirk for TDM mode and if TDM mode is present all the relevent changes will be applied. Signed-off-by: Padmavathi Venna Reviewed-by: Tomasz Figa Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/sound/samsung-i2s.txt b/Documentation/devicetree/bindings/sound/samsung-i2s.txt index 25a0024..7386d44 100644 --- a/Documentation/devicetree/bindings/sound/samsung-i2s.txt +++ b/Documentation/devicetree/bindings/sound/samsung-i2s.txt @@ -6,6 +6,10 @@ Required SoC Specific Properties: - samsung,s3c6410-i2s: for 8/16/24bit stereo I2S. - samsung,s5pv210-i2s: for 8/16/24bit multichannel(5.1) I2S with secondary fifo, s/w reset control and internal mux for root clk src. + - samsung,exynos5420-i2s: for 8/16/24bit multichannel(7.1) I2S with + secondary fifo, s/w reset control, internal mux for root clk src and + TDM support. TDM (Time division multiplexing) is to allow transfer of + multiple channel audio data on single data line. - reg: physical base address of the controller and length of memory mapped region. diff --git a/include/linux/platform_data/asoc-s3c.h b/include/linux/platform_data/asoc-s3c.h index 8827259..9efc04d 100644 --- a/include/linux/platform_data/asoc-s3c.h +++ b/include/linux/platform_data/asoc-s3c.h @@ -36,6 +36,7 @@ struct samsung_i2s { */ #define QUIRK_NO_MUXPSR (1 << 2) #define QUIRK_NEED_RSTCLR (1 << 3) +#define QUIRK_SUPPORTS_TDM (1 << 4) /* Quirks of the I2S controller */ u32 quirks; dma_addr_t idma_addr; diff --git a/sound/soc/samsung/i2s-regs.h b/sound/soc/samsung/i2s-regs.h index 30513b7..821a502 100644 --- a/sound/soc/samsung/i2s-regs.h +++ b/sound/soc/samsung/i2s-regs.h @@ -31,6 +31,10 @@ #define I2SLVL1ADDR 0x34 #define I2SLVL2ADDR 0x38 #define I2SLVL3ADDR 0x3c +#define I2SSTR1 0x40 +#define I2SVER 0x44 +#define I2SFIC2 0x48 +#define I2STDM 0x4c #define CON_RSTCLR (1 << 31) #define CON_FRXOFSTATUS (1 << 26) @@ -117,6 +121,17 @@ #define MOD_BCLK_MASK 3 #define MOD_8BIT (1 << 0) +#define EXYNOS5420_MOD_LRP_SHIFT 15 +#define EXYNOS5420_MOD_SDF_SHIFT 6 +#define EXYNOS5420_MOD_RCLK_SHIFT 4 +#define EXYNOS5420_MOD_BCLK_SHIFT 0 +#define EXYNOS5420_MOD_BCLK_64FS 4 +#define EXYNOS5420_MOD_BCLK_96FS 5 +#define EXYNOS5420_MOD_BCLK_128FS 6 +#define EXYNOS5420_MOD_BCLK_192FS 7 +#define EXYNOS5420_MOD_BCLK_256FS 8 +#define EXYNOS5420_MOD_BCLK_MASK 0xf + #define MOD_CDCLKCON (1 << 12) #define PSR_PSREN (1 << 15) diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index 3b4835a..dd995a7 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -199,7 +199,12 @@ static inline bool is_manager(struct i2s_dai *i2s) /* Read RCLK of I2S (in multiples of LRCLK) */ static inline unsigned get_rfs(struct i2s_dai *i2s) { - u32 rfs = (readl(i2s->addr + I2SMOD) >> MOD_RCLK_SHIFT); + u32 rfs; + + if (i2s->quirks & QUIRK_SUPPORTS_TDM) + rfs = readl(i2s->addr + I2SMOD) >> EXYNOS5420_MOD_RCLK_SHIFT; + else + rfs = (readl(i2s->addr + I2SMOD) >> MOD_RCLK_SHIFT); rfs &= MOD_RCLK_MASK; switch (rfs) { @@ -214,8 +219,12 @@ static inline unsigned get_rfs(struct i2s_dai *i2s) static inline void set_rfs(struct i2s_dai *i2s, unsigned rfs) { u32 mod = readl(i2s->addr + I2SMOD); - int rfs_shift = MOD_RCLK_SHIFT; + int rfs_shift; + if (i2s->quirks & QUIRK_SUPPORTS_TDM) + rfs_shift = EXYNOS5420_MOD_RCLK_SHIFT; + else + rfs_shift = MOD_RCLK_SHIFT; mod &= ~(MOD_RCLK_MASK << rfs_shift); switch (rfs) { @@ -239,10 +248,22 @@ static inline void set_rfs(struct i2s_dai *i2s, unsigned rfs) /* Read Bit-Clock of I2S (in multiples of LRCLK) */ static inline unsigned get_bfs(struct i2s_dai *i2s) { - u32 bfs = readl(i2s->addr + I2SMOD) >> MOD_BCLK_SHIFT; - bfs &= MOD_BCLK_MASK; + u32 bfs; + + if (i2s->quirks & QUIRK_SUPPORTS_TDM) { + bfs = readl(i2s->addr + I2SMOD) >> EXYNOS5420_MOD_BCLK_SHIFT; + bfs &= EXYNOS5420_MOD_BCLK_MASK; + } else { + bfs = readl(i2s->addr + I2SMOD) >> MOD_BCLK_SHIFT; + bfs &= MOD_BCLK_MASK; + } switch (bfs) { + case 8: return 256; + case 7: return 192; + case 6: return 128; + case 5: return 96; + case 4: return 64; case 3: return 24; case 2: return 16; case 1: return 48; @@ -254,9 +275,22 @@ static inline unsigned get_bfs(struct i2s_dai *i2s) static inline void set_bfs(struct i2s_dai *i2s, unsigned bfs) { u32 mod = readl(i2s->addr + I2SMOD); - int bfs_shift = MOD_BCLK_SHIFT; + int bfs_shift; + int tdm = i2s->quirks & QUIRK_SUPPORTS_TDM; - mod &= ~(MOD_BCLK_MASK << bfs_shift); + if (i2s->quirks & QUIRK_SUPPORTS_TDM) { + bfs_shift = EXYNOS5420_MOD_BCLK_SHIFT; + mod &= ~(EXYNOS5420_MOD_BCLK_MASK << bfs_shift); + } else { + bfs_shift = MOD_BCLK_SHIFT; + mod &= ~(MOD_BCLK_MASK << bfs_shift); + } + + /* Non-TDM I2S controllers do not support BCLK > 48 * FS */ + if (!tdm && bfs > 48) { + dev_err(&i2s->pdev->dev, "Unsupported BCLK divider\n"); + return; + } switch (bfs) { case 48: @@ -271,6 +305,21 @@ static inline void set_bfs(struct i2s_dai *i2s, unsigned bfs) case 16: mod |= (MOD_BCLK_16FS << bfs_shift); break; + case 64: + mod |= (EXYNOS5420_MOD_BCLK_64FS << bfs_shift); + break; + case 96: + mod |= (EXYNOS5420_MOD_BCLK_96FS << bfs_shift); + break; + case 128: + mod |= (EXYNOS5420_MOD_BCLK_128FS << bfs_shift); + break; + case 192: + mod |= (EXYNOS5420_MOD_BCLK_192FS << bfs_shift); + break; + case 256: + mod |= (EXYNOS5420_MOD_BCLK_256FS << bfs_shift); + break; default: dev_err(&i2s->pdev->dev, "Wrong BCLK Divider!\n"); return; @@ -496,10 +545,17 @@ static int i2s_set_fmt(struct snd_soc_dai *dai, { struct i2s_dai *i2s = to_info(dai); u32 mod = readl(i2s->addr + I2SMOD); - int lrp_shift = MOD_LRP_SHIFT, sdf_shift = MOD_SDF_SHIFT; - int sdf_mask, lrp_rlow; + int lrp_shift, sdf_shift, sdf_mask, lrp_rlow; u32 tmp = 0; + if (i2s->quirks & QUIRK_SUPPORTS_TDM) { + lrp_shift = EXYNOS5420_MOD_LRP_SHIFT; + sdf_shift = EXYNOS5420_MOD_SDF_SHIFT; + } else { + lrp_shift = MOD_LRP_SHIFT; + sdf_shift = MOD_SDF_SHIFT; + } + sdf_mask = MOD_SDF_MASK << sdf_shift; lrp_rlow = MOD_LR_RLOW << lrp_shift; @@ -1253,6 +1309,12 @@ static const struct samsung_i2s_dai_data i2sv5_dai_type = { .quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR, }; +static const struct samsung_i2s_dai_data i2sv6_dai_type = { + .dai_type = TYPE_PRI, + .quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR | + QUIRK_SUPPORTS_TDM, +}; + static const struct samsung_i2s_dai_data samsung_dai_type_pri = { .dai_type = TYPE_PRI, }; @@ -1281,6 +1343,9 @@ static const struct of_device_id exynos_i2s_match[] = { }, { .compatible = "samsung,s5pv210-i2s", .data = &i2sv5_dai_type, + }, { + .compatible = "samsung,exynos5420-i2s", + .data = &i2sv6_dai_type, }, {}, }; -- cgit v0.10.2 From 6187288f15bcb430b62df1dcb0f588048f4622f6 Mon Sep 17 00:00:00 2001 From: Padmavathi Venna Date: Mon, 12 Aug 2013 15:19:53 +0530 Subject: ARM: dts: exynos5250: move common i2s properties to exynos5 dtsi I2S nodes shares some properties across exynos5 SoCs (exynos5250 and exyno5420). Common code is moved to exynos5.dtsi which is included in exyno5250 and exynos5420 SoC files. Signed-off-by: Padmavathi Venna Reviewed-by: Tomasz Figa Signed-off-by: Mark Brown diff --git a/arch/arm/boot/dts/exynos5.dtsi b/arch/arm/boot/dts/exynos5.dtsi index f65e124..aae2fa1 100644 --- a/arch/arm/boot/dts/exynos5.dtsi +++ b/arch/arm/boot/dts/exynos5.dtsi @@ -108,4 +108,25 @@ interrupts = <0 42 0>; status = "disabled"; }; + + i2s0: i2s@03830000 { + reg = <0x03830000 0x100>; + samsung,idma-addr = <0x03000000>; + }; + + i2s1: i2s@12D60000 { + compatible = "samsung,i2s-v5"; + reg = <0x12D60000 0x100>; + dmas = <&pdma1 12 + &pdma1 11>; + dma-names = "tx", "rx"; + }; + + i2s2: i2s@12D70000 { + compatible = "samsung,i2s-v5"; + reg = <0x12D70000 0x100>; + dmas = <&pdma0 12 + &pdma0 11>; + dma-names = "tx", "rx"; + }; }; diff --git a/arch/arm/boot/dts/exynos5250.dtsi b/arch/arm/boot/dts/exynos5250.dtsi index ef57277..f941d52 100644 --- a/arch/arm/boot/dts/exynos5250.dtsi +++ b/arch/arm/boot/dts/exynos5250.dtsi @@ -406,7 +406,6 @@ i2s0: i2s@03830000 { compatible = "samsung,i2s-v5"; - reg = <0x03830000 0x100>; dmas = <&pdma0 10 &pdma0 9 &pdma0 8>; @@ -418,17 +417,11 @@ samsung,supports-6ch; samsung,supports-rstclr; samsung,supports-secdai; - samsung,idma-addr = <0x03000000>; pinctrl-names = "default"; pinctrl-0 = <&i2s0_bus>; }; i2s1: i2s@12D60000 { - compatible = "samsung,i2s-v5"; - reg = <0x12D60000 0x100>; - dmas = <&pdma1 12 - &pdma1 11>; - dma-names = "tx", "rx"; clocks = <&clock 307>, <&clock 157>; clock-names = "iis", "i2s_opclk0"; pinctrl-names = "default"; @@ -436,11 +429,6 @@ }; i2s2: i2s@12D70000 { - compatible = "samsung,i2s-v5"; - reg = <0x12D70000 0x100>; - dmas = <&pdma0 12 - &pdma0 11>; - dma-names = "tx", "rx"; clocks = <&clock 308>, <&clock 158>; clock-names = "iis", "i2s_opclk0"; pinctrl-names = "default"; -- cgit v0.10.2 From c7f7e607513f6e8bc41d92e86c5989228b3bcbe8 Mon Sep 17 00:00:00 2001 From: Padmavathi Venna Date: Mon, 12 Aug 2013 15:19:54 +0530 Subject: ARM: dts: Change i2s compatible string on exynos5250 This patch removes quirks from i2s node and change the i2s compatible names. Signed-off-by: Padmavathi Venna Reviewed-by: Tomasz Figa Signed-off-by: Mark Brown diff --git a/arch/arm/boot/dts/exynos5.dtsi b/arch/arm/boot/dts/exynos5.dtsi index aae2fa1..309894e 100644 --- a/arch/arm/boot/dts/exynos5.dtsi +++ b/arch/arm/boot/dts/exynos5.dtsi @@ -115,7 +115,7 @@ }; i2s1: i2s@12D60000 { - compatible = "samsung,i2s-v5"; + compatible = "samsung,s3c6410-i2s"; reg = <0x12D60000 0x100>; dmas = <&pdma1 12 &pdma1 11>; @@ -123,7 +123,7 @@ }; i2s2: i2s@12D70000 { - compatible = "samsung,i2s-v5"; + compatible = "samsung,s3c6410-i2s"; reg = <0x12D70000 0x100>; dmas = <&pdma0 12 &pdma0 11>; diff --git a/arch/arm/boot/dts/exynos5250.dtsi b/arch/arm/boot/dts/exynos5250.dtsi index f941d52..ac5f5a1 100644 --- a/arch/arm/boot/dts/exynos5250.dtsi +++ b/arch/arm/boot/dts/exynos5250.dtsi @@ -405,7 +405,7 @@ }; i2s0: i2s@03830000 { - compatible = "samsung,i2s-v5"; + compatible = "samsung,s5pv210-i2s"; dmas = <&pdma0 10 &pdma0 9 &pdma0 8>; @@ -414,9 +414,6 @@ <&clock_audss EXYNOS_I2S_BUS>, <&clock_audss EXYNOS_SCLK_I2S>; clock-names = "iis", "i2s_opclk0", "i2s_opclk1"; - samsung,supports-6ch; - samsung,supports-rstclr; - samsung,supports-secdai; pinctrl-names = "default"; pinctrl-0 = <&i2s0_bus>; }; -- cgit v0.10.2 From b892ca1c9fe71e829e7b9ed79b8398649de259d7 Mon Sep 17 00:00:00 2001 From: Knut Petersen Date: Tue, 13 Aug 2013 21:18:12 +0200 Subject: ALSA: rme96: Add pcm stream synchronization The hardware does support synchronized start/pause/stop of pcm streams, so there is no reason not to add that feature after more than ten years. Some minor coding style / white space fixes in the surroundings of the changes. Signed-off-by: Knut Petersen Signed-off-by: Takashi Iwai diff --git a/sound/pci/rme96.c b/sound/pci/rme96.c index 2a8ad9d..4e9a556 100644 --- a/sound/pci/rme96.c +++ b/sound/pci/rme96.c @@ -198,6 +198,31 @@ MODULE_PARM_DESC(enable, "Enable RME Digi96 soundcard."); #define RME96_AD1852_VOL_BITS 14 #define RME96_AD1855_VOL_BITS 10 +/* Defines for snd_rme96_trigger */ +#define RME96_TB_START_PLAYBACK 1 +#define RME96_TB_START_CAPTURE 2 +#define RME96_TB_STOP_PLAYBACK 4 +#define RME96_TB_STOP_CAPTURE 8 +#define RME96_TB_RESET_PLAYPOS 16 +#define RME96_TB_RESET_CAPTUREPOS 32 +#define RME96_TB_CLEAR_PLAYBACK_IRQ 64 +#define RME96_TB_CLEAR_CAPTURE_IRQ 128 +#define RME96_RESUME_PLAYBACK (RME96_TB_START_PLAYBACK) +#define RME96_RESUME_CAPTURE (RME96_TB_START_CAPTURE) +#define RME96_RESUME_BOTH (RME96_RESUME_PLAYBACK \ + | RME96_RESUME_CAPTURE) +#define RME96_START_PLAYBACK (RME96_TB_START_PLAYBACK \ + | RME96_TB_RESET_PLAYPOS) +#define RME96_START_CAPTURE (RME96_TB_START_CAPTURE \ + | RME96_TB_RESET_CAPTUREPOS) +#define RME96_START_BOTH (RME96_START_PLAYBACK \ + | RME96_START_CAPTURE) +#define RME96_STOP_PLAYBACK (RME96_TB_STOP_PLAYBACK \ + | RME96_TB_CLEAR_PLAYBACK_IRQ) +#define RME96_STOP_CAPTURE (RME96_TB_STOP_CAPTURE \ + | RME96_TB_CLEAR_CAPTURE_IRQ) +#define RME96_STOP_BOTH (RME96_STOP_PLAYBACK \ + | RME96_STOP_CAPTURE) struct rme96 { spinlock_t lock; @@ -344,6 +369,7 @@ static struct snd_pcm_hardware snd_rme96_playback_spdif_info = { .info = (SNDRV_PCM_INFO_MMAP_IOMEM | SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_SYNC_START | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_PAUSE), .formats = (SNDRV_PCM_FMTBIT_S16_LE | @@ -373,6 +399,7 @@ static struct snd_pcm_hardware snd_rme96_capture_spdif_info = { .info = (SNDRV_PCM_INFO_MMAP_IOMEM | SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_SYNC_START | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_PAUSE), .formats = (SNDRV_PCM_FMTBIT_S16_LE | @@ -402,6 +429,7 @@ static struct snd_pcm_hardware snd_rme96_playback_adat_info = { .info = (SNDRV_PCM_INFO_MMAP_IOMEM | SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_SYNC_START | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_PAUSE), .formats = (SNDRV_PCM_FMTBIT_S16_LE | @@ -427,6 +455,7 @@ static struct snd_pcm_hardware snd_rme96_capture_adat_info = { .info = (SNDRV_PCM_INFO_MMAP_IOMEM | SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_SYNC_START | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_PAUSE), .formats = (SNDRV_PCM_FMTBIT_S16_LE | @@ -1045,54 +1074,35 @@ snd_rme96_capture_hw_params(struct snd_pcm_substream *substream, } static void -snd_rme96_playback_start(struct rme96 *rme96, - int from_pause) +snd_rme96_trigger(struct rme96 *rme96, + int op) { - if (!from_pause) { + if (op & RME96_TB_RESET_PLAYPOS) writel(0, rme96->iobase + RME96_IO_RESET_PLAY_POS); - } - - rme96->wcreg |= RME96_WCR_START; - writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER); -} - -static void -snd_rme96_capture_start(struct rme96 *rme96, - int from_pause) -{ - if (!from_pause) { + if (op & RME96_TB_RESET_CAPTUREPOS) writel(0, rme96->iobase + RME96_IO_RESET_REC_POS); - } - - rme96->wcreg |= RME96_WCR_START_2; + if (op & RME96_TB_CLEAR_PLAYBACK_IRQ) { + rme96->rcreg = readl(rme96->iobase + RME96_IO_CONTROL_REGISTER); + if (rme96->rcreg & RME96_RCR_IRQ) + writel(0, rme96->iobase + RME96_IO_CONFIRM_PLAY_IRQ); + } + if (op & RME96_TB_CLEAR_CAPTURE_IRQ) { + rme96->rcreg = readl(rme96->iobase + RME96_IO_CONTROL_REGISTER); + if (rme96->rcreg & RME96_RCR_IRQ_2) + writel(0, rme96->iobase + RME96_IO_CONFIRM_REC_IRQ); + } + if (op & RME96_TB_START_PLAYBACK) + rme96->wcreg |= RME96_WCR_START; + if (op & RME96_TB_STOP_PLAYBACK) + rme96->wcreg &= ~RME96_WCR_START; + if (op & RME96_TB_START_CAPTURE) + rme96->wcreg |= RME96_WCR_START_2; + if (op & RME96_TB_STOP_CAPTURE) + rme96->wcreg &= ~RME96_WCR_START_2; writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER); } -static void -snd_rme96_playback_stop(struct rme96 *rme96) -{ - /* - * Check if there is an unconfirmed IRQ, if so confirm it, or else - * the hardware will not stop generating interrupts - */ - rme96->rcreg = readl(rme96->iobase + RME96_IO_CONTROL_REGISTER); - if (rme96->rcreg & RME96_RCR_IRQ) { - writel(0, rme96->iobase + RME96_IO_CONFIRM_PLAY_IRQ); - } - rme96->wcreg &= ~RME96_WCR_START; - writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER); -} -static void -snd_rme96_capture_stop(struct rme96 *rme96) -{ - rme96->rcreg = readl(rme96->iobase + RME96_IO_CONTROL_REGISTER); - if (rme96->rcreg & RME96_RCR_IRQ_2) { - writel(0, rme96->iobase + RME96_IO_CONFIRM_REC_IRQ); - } - rme96->wcreg &= ~RME96_WCR_START_2; - writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER); -} static irqreturn_t snd_rme96_interrupt(int irq, @@ -1155,6 +1165,7 @@ snd_rme96_playback_spdif_open(struct snd_pcm_substream *substream) struct rme96 *rme96 = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; + snd_pcm_set_sync(substream); spin_lock_irq(&rme96->lock); if (rme96->playback_substream != NULL) { spin_unlock_irq(&rme96->lock); @@ -1191,6 +1202,7 @@ snd_rme96_capture_spdif_open(struct snd_pcm_substream *substream) struct rme96 *rme96 = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; + snd_pcm_set_sync(substream); runtime->hw = snd_rme96_capture_spdif_info; if (snd_rme96_getinputtype(rme96) != RME96_INPUT_ANALOG && (rate = snd_rme96_capture_getrate(rme96, &isadat)) > 0) @@ -1222,6 +1234,7 @@ snd_rme96_playback_adat_open(struct snd_pcm_substream *substream) struct rme96 *rme96 = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; + snd_pcm_set_sync(substream); spin_lock_irq(&rme96->lock); if (rme96->playback_substream != NULL) { spin_unlock_irq(&rme96->lock); @@ -1253,6 +1266,7 @@ snd_rme96_capture_adat_open(struct snd_pcm_substream *substream) struct rme96 *rme96 = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; + snd_pcm_set_sync(substream); runtime->hw = snd_rme96_capture_adat_info; if (snd_rme96_getinputtype(rme96) == RME96_INPUT_ANALOG) { /* makes no sense to use analog input. Note that analog @@ -1288,7 +1302,7 @@ snd_rme96_playback_close(struct snd_pcm_substream *substream) spin_lock_irq(&rme96->lock); if (RME96_ISPLAYING(rme96)) { - snd_rme96_playback_stop(rme96); + snd_rme96_trigger(rme96, RME96_STOP_PLAYBACK); } rme96->playback_substream = NULL; rme96->playback_periodsize = 0; @@ -1309,7 +1323,7 @@ snd_rme96_capture_close(struct snd_pcm_substream *substream) spin_lock_irq(&rme96->lock); if (RME96_ISRECORDING(rme96)) { - snd_rme96_capture_stop(rme96); + snd_rme96_trigger(rme96, RME96_STOP_CAPTURE); } rme96->capture_substream = NULL; rme96->capture_periodsize = 0; @@ -1324,7 +1338,7 @@ snd_rme96_playback_prepare(struct snd_pcm_substream *substream) spin_lock_irq(&rme96->lock); if (RME96_ISPLAYING(rme96)) { - snd_rme96_playback_stop(rme96); + snd_rme96_trigger(rme96, RME96_STOP_PLAYBACK); } writel(0, rme96->iobase + RME96_IO_RESET_PLAY_POS); spin_unlock_irq(&rme96->lock); @@ -1338,7 +1352,7 @@ snd_rme96_capture_prepare(struct snd_pcm_substream *substream) spin_lock_irq(&rme96->lock); if (RME96_ISRECORDING(rme96)) { - snd_rme96_capture_stop(rme96); + snd_rme96_trigger(rme96, RME96_STOP_CAPTURE); } writel(0, rme96->iobase + RME96_IO_RESET_REC_POS); spin_unlock_irq(&rme96->lock); @@ -1350,41 +1364,53 @@ snd_rme96_playback_trigger(struct snd_pcm_substream *substream, int cmd) { struct rme96 *rme96 = snd_pcm_substream_chip(substream); + struct snd_pcm_substream *s; + bool sync; + + snd_pcm_group_for_each_entry(s, substream) { + if (snd_pcm_substream_chip(s) == rme96) + snd_pcm_trigger_done(s, substream); + } + + sync = (rme96->playback_substream && rme96->capture_substream) && + (rme96->playback_substream->group == + rme96->capture_substream->group); switch (cmd) { case SNDRV_PCM_TRIGGER_START: if (!RME96_ISPLAYING(rme96)) { - if (substream != rme96->playback_substream) { + if (substream != rme96->playback_substream) return -EBUSY; - } - snd_rme96_playback_start(rme96, 0); + snd_rme96_trigger(rme96, sync ? RME96_START_BOTH + : RME96_START_PLAYBACK); } break; case SNDRV_PCM_TRIGGER_STOP: if (RME96_ISPLAYING(rme96)) { - if (substream != rme96->playback_substream) { + if (substream != rme96->playback_substream) return -EBUSY; - } - snd_rme96_playback_stop(rme96); + snd_rme96_trigger(rme96, sync ? RME96_STOP_BOTH + : RME96_STOP_PLAYBACK); } break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - if (RME96_ISPLAYING(rme96)) { - snd_rme96_playback_stop(rme96); - } + if (RME96_ISPLAYING(rme96)) + snd_rme96_trigger(rme96, sync ? RME96_STOP_BOTH + : RME96_STOP_PLAYBACK); break; case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - if (!RME96_ISPLAYING(rme96)) { - snd_rme96_playback_start(rme96, 1); - } + if (!RME96_ISPLAYING(rme96)) + snd_rme96_trigger(rme96, sync ? RME96_RESUME_BOTH + : RME96_RESUME_PLAYBACK); break; - + default: return -EINVAL; } + return 0; } @@ -1393,38 +1419,49 @@ snd_rme96_capture_trigger(struct snd_pcm_substream *substream, int cmd) { struct rme96 *rme96 = snd_pcm_substream_chip(substream); + struct snd_pcm_substream *s; + bool sync; + + snd_pcm_group_for_each_entry(s, substream) { + if (snd_pcm_substream_chip(s) == rme96) + snd_pcm_trigger_done(s, substream); + } + + sync = (rme96->playback_substream && rme96->capture_substream) && + (rme96->playback_substream->group == + rme96->capture_substream->group); switch (cmd) { case SNDRV_PCM_TRIGGER_START: if (!RME96_ISRECORDING(rme96)) { - if (substream != rme96->capture_substream) { + if (substream != rme96->capture_substream) return -EBUSY; - } - snd_rme96_capture_start(rme96, 0); + snd_rme96_trigger(rme96, sync ? RME96_START_BOTH + : RME96_START_CAPTURE); } break; case SNDRV_PCM_TRIGGER_STOP: if (RME96_ISRECORDING(rme96)) { - if (substream != rme96->capture_substream) { + if (substream != rme96->capture_substream) return -EBUSY; - } - snd_rme96_capture_stop(rme96); + snd_rme96_trigger(rme96, sync ? RME96_STOP_BOTH + : RME96_STOP_CAPTURE); } break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - if (RME96_ISRECORDING(rme96)) { - snd_rme96_capture_stop(rme96); - } + if (RME96_ISRECORDING(rme96)) + snd_rme96_trigger(rme96, sync ? RME96_STOP_BOTH + : RME96_STOP_CAPTURE); break; case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - if (!RME96_ISRECORDING(rme96)) { - snd_rme96_capture_start(rme96, 1); - } + if (!RME96_ISRECORDING(rme96)) + snd_rme96_trigger(rme96, sync ? RME96_RESUME_BOTH + : RME96_RESUME_CAPTURE); break; - + default: return -EINVAL; } @@ -1505,8 +1542,7 @@ snd_rme96_free(void *private_data) return; } if (rme96->irq >= 0) { - snd_rme96_playback_stop(rme96); - snd_rme96_capture_stop(rme96); + snd_rme96_trigger(rme96, RME96_STOP_BOTH); rme96->areg &= ~RME96_AR_DAC_EN; writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG); free_irq(rme96->irq, (void *)rme96); @@ -1606,8 +1642,7 @@ snd_rme96_create(struct rme96 *rme96) rme96->capture_periodsize = 0; /* make sure playback/capture is stopped, if by some reason active */ - snd_rme96_playback_stop(rme96); - snd_rme96_capture_stop(rme96); + snd_rme96_trigger(rme96, RME96_STOP_BOTH); /* set default values in registers */ rme96->wcreg = -- cgit v0.10.2 From 64efc5a0f272b370e5ae6e95ff3cd5023ce9fefc Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Wed, 14 Aug 2013 11:11:16 +0200 Subject: ASoC: samsung-ac97: simplify use of devm_ioremap_resource Remove unneeded error handling on the result of a call to platform_get_resource when the value is passed to devm_ioremap_resource. Move the call to platform_get_resource adjacent to the call to devm_ioremap_resource to make the connection between them more clear. A simplified version of the semantic patch that makes this change is as follows: (http://coccinelle.lip6.fr/) // @@ expression pdev,res,n,e,e1; expression ret != 0; identifier l; @@ - res = platform_get_resource(pdev, IORESOURCE_MEM, n); ... when != res - if (res == NULL) { ... \(goto l;\|return ret;\) } ... when != res + res = platform_get_resource(pdev, IORESOURCE_MEM, n); e = devm_ioremap_resource(e1, res); // Signed-off-by: Julia Lawall Signed-off-by: Mark Brown diff --git a/sound/soc/samsung/ac97.c b/sound/soc/samsung/ac97.c index 2dd623f..c732df9 100644 --- a/sound/soc/samsung/ac97.c +++ b/sound/soc/samsung/ac97.c @@ -404,18 +404,13 @@ static int s3c_ac97_probe(struct platform_device *pdev) return -ENXIO; } - mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!mem_res) { - dev_err(&pdev->dev, "Unable to get register resource\n"); - return -ENXIO; - } - irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (!irq_res) { dev_err(&pdev->dev, "AC97 IRQ not provided!\n"); return -ENXIO; } + mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); s3c_ac97.regs = devm_ioremap_resource(&pdev->dev, mem_res); if (IS_ERR(s3c_ac97.regs)) return PTR_ERR(s3c_ac97.regs); -- cgit v0.10.2 From c324aac01be55253488aba9481523cd6f546f4ca Mon Sep 17 00:00:00 2001 From: Ma Haijun Date: Wed, 14 Aug 2013 09:15:38 +0800 Subject: ASoC: wm8960: Fix ADC volume bits Signed-off-by: Ma Haijun Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c index 0a4ffdd..368d39f 100644 --- a/sound/soc/codecs/wm8960.c +++ b/sound/soc/codecs/wm8960.c @@ -263,8 +263,8 @@ SOC_SINGLE("ALC Attack", WM8960_ALC3, 0, 15, 0), SOC_SINGLE("Noise Gate Threshold", WM8960_NOISEG, 3, 31, 0), SOC_SINGLE("Noise Gate Switch", WM8960_NOISEG, 0, 1, 0), -SOC_DOUBLE_R("ADC PCM Capture Volume", WM8960_LINPATH, WM8960_RINPATH, - 0, 127, 0), +SOC_DOUBLE_R_TLV("ADC PCM Capture Volume", WM8960_LADC, WM8960_RADC, + 0, 255, 0, adc_tlv), SOC_SINGLE_TLV("Left Output Mixer Boost Bypass Volume", WM8960_BYPASS1, 4, 7, 1, bypass_tlv), -- cgit v0.10.2 From 64be28146f746681c5f5625d321dd67602bb264c Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Mon, 12 Aug 2013 10:37:14 +0200 Subject: ARM: pxa: ssp: remove unnecessary warning on kzalloc() failure The memory subsystem will already complain loudly enough in such cases. Signed-off-by: Daniel Mack Acked-by: Haojian Zhuang Signed-off-by: Mark Brown diff --git a/arch/arm/plat-pxa/ssp.c b/arch/arm/plat-pxa/ssp.c index 8e11e96..f746b6a 100644 --- a/arch/arm/plat-pxa/ssp.c +++ b/arch/arm/plat-pxa/ssp.c @@ -80,10 +80,9 @@ static int pxa_ssp_probe(struct platform_device *pdev) int ret = 0; ssp = kzalloc(sizeof(struct ssp_device), GFP_KERNEL); - if (ssp == NULL) { - dev_err(&pdev->dev, "failed to allocate memory"); + if (ssp == NULL) return -ENOMEM; - } + ssp->pdev = pdev; ssp->clk = clk_get(&pdev->dev, NULL); -- cgit v0.10.2 From 970d8a7152aa884b9bab6a8db1ff148ee22df899 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Mon, 12 Aug 2013 10:37:15 +0200 Subject: ARM: pxa: ssp: add shortcut for &pdev->dev No functional change, just a cosmetic cleanup. Signed-off-by: Daniel Mack Acked-by: Haojian Zhuang Signed-off-by: Mark Brown diff --git a/arch/arm/plat-pxa/ssp.c b/arch/arm/plat-pxa/ssp.c index f746b6a..65ba28a 100644 --- a/arch/arm/plat-pxa/ssp.c +++ b/arch/arm/plat-pxa/ssp.c @@ -77,6 +77,7 @@ static int pxa_ssp_probe(struct platform_device *pdev) const struct platform_device_id *id = platform_get_device_id(pdev); struct resource *res; struct ssp_device *ssp; + struct device *dev = &pdev->dev; int ret = 0; ssp = kzalloc(sizeof(struct ssp_device), GFP_KERNEL); @@ -85,7 +86,7 @@ static int pxa_ssp_probe(struct platform_device *pdev) ssp->pdev = pdev; - ssp->clk = clk_get(&pdev->dev, NULL); + ssp->clk = clk_get(dev, NULL); if (IS_ERR(ssp->clk)) { ret = PTR_ERR(ssp->clk); goto err_free; @@ -93,7 +94,7 @@ static int pxa_ssp_probe(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_DMA, 0); if (res == NULL) { - dev_err(&pdev->dev, "no SSP RX DRCMR defined\n"); + dev_err(dev, "no SSP RX DRCMR defined\n"); ret = -ENODEV; goto err_free_clk; } @@ -101,7 +102,7 @@ static int pxa_ssp_probe(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_DMA, 1); if (res == NULL) { - dev_err(&pdev->dev, "no SSP TX DRCMR defined\n"); + dev_err(dev, "no SSP TX DRCMR defined\n"); ret = -ENODEV; goto err_free_clk; } @@ -109,7 +110,7 @@ static int pxa_ssp_probe(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (res == NULL) { - dev_err(&pdev->dev, "no memory resource defined\n"); + dev_err(dev, "no memory resource defined\n"); ret = -ENODEV; goto err_free_clk; } @@ -117,7 +118,7 @@ static int pxa_ssp_probe(struct platform_device *pdev) res = request_mem_region(res->start, resource_size(res), pdev->name); if (res == NULL) { - dev_err(&pdev->dev, "failed to request memory resource\n"); + dev_err(dev, "failed to request memory resource\n"); ret = -EBUSY; goto err_free_clk; } @@ -126,14 +127,14 @@ static int pxa_ssp_probe(struct platform_device *pdev) ssp->mmio_base = ioremap(res->start, resource_size(res)); if (ssp->mmio_base == NULL) { - dev_err(&pdev->dev, "failed to ioremap() registers\n"); + dev_err(dev, "failed to ioremap() registers\n"); ret = -ENODEV; goto err_free_mem; } ssp->irq = platform_get_irq(pdev, 0); if (ssp->irq < 0) { - dev_err(&pdev->dev, "no IRQ resource defined\n"); + dev_err(dev, "no IRQ resource defined\n"); ret = -ENODEV; goto err_free_io; } -- cgit v0.10.2 From a6e56c28a178cef5f93d1e11698a23a5482175d9 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Mon, 12 Aug 2013 10:37:16 +0200 Subject: ARM: pxa: ssp: add DT bindings This patch contains an ugly hack for looking up the the DMA request number. The problem here is that the implementation as it stands will allocate the DMA channel from the user of the ssp port, and hence we cannot allocate a real channel here. Signed-off-by: Daniel Mack Acked-by: Haojian Zhuang Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/serial/mrvl,pxa-ssp.txt b/Documentation/devicetree/bindings/serial/mrvl,pxa-ssp.txt new file mode 100644 index 0000000..669b814 --- /dev/null +++ b/Documentation/devicetree/bindings/serial/mrvl,pxa-ssp.txt @@ -0,0 +1,65 @@ +Device tree bindings for Marvell PXA SSP ports + +Required properties: + + - compatible: Must be one of + mrvl,pxa25x-ssp + mvrl,pxa25x-nssp + mrvl,pxa27x-ssp + mrvl,pxa3xx-ssp + mvrl,pxa168-ssp + mrvl,pxa910-ssp + mrvl,ce4100-ssp + mrvl,lpss-ssp + + - reg: The memory base + - dmas: Two dma phandles, one for rx, one for tx + - dma-names: Must be "rx", "tx" + + +Example for PXA3xx: + + ssp0: ssp@41000000 { + compatible = "mrvl,pxa3xx-ssp"; + reg = <0x41000000 0x40>; + ssp-id = <1>; + interrupts = <24>; + clock-names = "pxa27x-ssp.0"; + dmas = <&dma 13 + &dma 14>; + dma-names = "rx", "tx"; + }; + + ssp1: ssp@41700000 { + compatible = "mrvl,pxa3xx-ssp"; + reg = <0x41700000 0x40>; + ssp-id = <2>; + interrupts = <16>; + clock-names = "pxa27x-ssp.1"; + dmas = <&dma 15 + &dma 16>; + dma-names = "rx", "tx"; + }; + + ssp2: ssp@41900000 { + compatibl3 = "mrvl,pxa3xx-ssp"; + reg = <0x41900000 0x40>; + ssp-id = <3>; + interrupts = <0>; + clock-names = "pxa27x-ssp.2"; + dmas = <&dma 66 + &dma 67>; + dma-names = "rx", "tx"; + }; + + ssp3: ssp@41a00000 { + compatible = "mrvl,pxa3xx-ssp"; + reg = <0x41a00000 0x40>; + ssp-id = <4>; + interrupts = <13>; + clock-names = "pxa27x-ssp.3"; + dmas = <&dma 2 + &dma 3>; + dma-names = "rx", "tx"; + }; + diff --git a/arch/arm/plat-pxa/ssp.c b/arch/arm/plat-pxa/ssp.c index 65ba28a..c3afcec 100644 --- a/arch/arm/plat-pxa/ssp.c +++ b/arch/arm/plat-pxa/ssp.c @@ -30,6 +30,8 @@ #include #include #include +#include +#include #include #include @@ -72,9 +74,23 @@ void pxa_ssp_free(struct ssp_device *ssp) } EXPORT_SYMBOL(pxa_ssp_free); +#ifdef CONFIG_OF +static const struct of_device_id pxa_ssp_of_ids[] = { + { .compatible = "mrvl,pxa25x-ssp", .data = (void *) PXA25x_SSP }, + { .compatible = "mvrl,pxa25x-nssp", .data = (void *) PXA25x_NSSP }, + { .compatible = "mrvl,pxa27x-ssp", .data = (void *) PXA27x_SSP }, + { .compatible = "mrvl,pxa3xx-ssp", .data = (void *) PXA3xx_SSP }, + { .compatible = "mvrl,pxa168-ssp", .data = (void *) PXA168_SSP }, + { .compatible = "mrvl,pxa910-ssp", .data = (void *) PXA910_SSP }, + { .compatible = "mrvl,ce4100-ssp", .data = (void *) CE4100_SSP }, + { .compatible = "mrvl,lpss-ssp", .data = (void *) LPSS_SSP }, + { }, +}; +MODULE_DEVICE_TABLE(of, pxa_ssp_of_ids); +#endif + static int pxa_ssp_probe(struct platform_device *pdev) { - const struct platform_device_id *id = platform_get_device_id(pdev); struct resource *res; struct ssp_device *ssp; struct device *dev = &pdev->dev; @@ -92,21 +108,42 @@ static int pxa_ssp_probe(struct platform_device *pdev) goto err_free; } - res = platform_get_resource(pdev, IORESOURCE_DMA, 0); - if (res == NULL) { - dev_err(dev, "no SSP RX DRCMR defined\n"); - ret = -ENODEV; - goto err_free_clk; - } - ssp->drcmr_rx = res->start; + if (dev->of_node) { + struct of_phandle_args dma_spec; + struct device_node *np = dev->of_node; + + /* + * FIXME: we should allocate the DMA channel from this + * context and pass the channel down to the ssp users. + * For now, we lookup the rx and tx indices manually + */ + + /* rx */ + of_parse_phandle_with_args(np, "dmas", "#dma-cells", + 0, &dma_spec); + ssp->drcmr_rx = dma_spec.args[0]; + of_node_put(dma_spec.np); + + /* tx */ + of_parse_phandle_with_args(np, "dmas", "#dma-cells", + 1, &dma_spec); + ssp->drcmr_tx = dma_spec.args[0]; + of_node_put(dma_spec.np); + } else { + res = platform_get_resource(pdev, IORESOURCE_DMA, 0); + if (res == NULL) { + dev_err(dev, "no SSP RX DRCMR defined\n"); + return -ENODEV; + } + ssp->drcmr_rx = res->start; - res = platform_get_resource(pdev, IORESOURCE_DMA, 1); - if (res == NULL) { - dev_err(dev, "no SSP TX DRCMR defined\n"); - ret = -ENODEV; - goto err_free_clk; + res = platform_get_resource(pdev, IORESOURCE_DMA, 1); + if (res == NULL) { + dev_err(dev, "no SSP TX DRCMR defined\n"); + return -ENODEV; + } + ssp->drcmr_tx = res->start; } - ssp->drcmr_tx = res->start; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (res == NULL) { @@ -139,12 +176,22 @@ static int pxa_ssp_probe(struct platform_device *pdev) goto err_free_io; } - /* PXA2xx/3xx SSP ports starts from 1 and the internal pdev->id - * starts from 0, do a translation here - */ - ssp->port_id = pdev->id + 1; + if (dev->of_node) { + const struct of_device_id *id = + of_match_device(of_match_ptr(pxa_ssp_of_ids), dev); + ssp->type = (int) id->data; + } else { + const struct platform_device_id *id = + platform_get_device_id(pdev); + ssp->type = (int) id->driver_data; + + /* PXA2xx/3xx SSP ports starts from 1 and the internal pdev->id + * starts from 0, do a translation here + */ + ssp->port_id = pdev->id + 1; + } + ssp->use_count = 0; - ssp->type = (int)id->driver_data; mutex_lock(&ssp_lock); list_add(&ssp->node, &ssp_list); @@ -201,8 +248,9 @@ static struct platform_driver pxa_ssp_driver = { .probe = pxa_ssp_probe, .remove = pxa_ssp_remove, .driver = { - .owner = THIS_MODULE, - .name = "pxa2xx-ssp", + .owner = THIS_MODULE, + .name = "pxa2xx-ssp", + .of_match_table = of_match_ptr(pxa_ssp_of_ids), }, .id_table = ssp_id_table, }; -- cgit v0.10.2 From 1c459de1e645b213a07b9492884a54f5861409f5 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Mon, 12 Aug 2013 10:37:17 +0200 Subject: ARM: pxa: ssp: use devm_ functions Use devm_ functions to allocate memory, ioremap, clk_get etc to clean up the error unwind path. Signed-off-by: Daniel Mack Acked-by: Haojian Zhuang Signed-off-by: Mark Brown diff --git a/arch/arm/plat-pxa/ssp.c b/arch/arm/plat-pxa/ssp.c index c3afcec..f266135 100644 --- a/arch/arm/plat-pxa/ssp.c +++ b/arch/arm/plat-pxa/ssp.c @@ -94,19 +94,16 @@ static int pxa_ssp_probe(struct platform_device *pdev) struct resource *res; struct ssp_device *ssp; struct device *dev = &pdev->dev; - int ret = 0; - ssp = kzalloc(sizeof(struct ssp_device), GFP_KERNEL); + ssp = devm_kzalloc(dev, sizeof(struct ssp_device), GFP_KERNEL); if (ssp == NULL) return -ENOMEM; ssp->pdev = pdev; - ssp->clk = clk_get(dev, NULL); - if (IS_ERR(ssp->clk)) { - ret = PTR_ERR(ssp->clk); - goto err_free; - } + ssp->clk = devm_clk_get(dev, NULL); + if (IS_ERR(ssp->clk)) + return PTR_ERR(ssp->clk); if (dev->of_node) { struct of_phandle_args dma_spec; @@ -148,32 +145,28 @@ static int pxa_ssp_probe(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (res == NULL) { dev_err(dev, "no memory resource defined\n"); - ret = -ENODEV; - goto err_free_clk; + return -ENODEV; } - res = request_mem_region(res->start, resource_size(res), - pdev->name); + res = devm_request_mem_region(dev, res->start, resource_size(res), + pdev->name); if (res == NULL) { dev_err(dev, "failed to request memory resource\n"); - ret = -EBUSY; - goto err_free_clk; + return -EBUSY; } ssp->phys_base = res->start; - ssp->mmio_base = ioremap(res->start, resource_size(res)); + ssp->mmio_base = devm_ioremap(dev, res->start, resource_size(res)); if (ssp->mmio_base == NULL) { dev_err(dev, "failed to ioremap() registers\n"); - ret = -ENODEV; - goto err_free_mem; + return -ENODEV; } ssp->irq = platform_get_irq(pdev, 0); if (ssp->irq < 0) { dev_err(dev, "no IRQ resource defined\n"); - ret = -ENODEV; - goto err_free_io; + return -ENODEV; } if (dev->of_node) { @@ -198,17 +191,8 @@ static int pxa_ssp_probe(struct platform_device *pdev) mutex_unlock(&ssp_lock); platform_set_drvdata(pdev, ssp); - return 0; -err_free_io: - iounmap(ssp->mmio_base); -err_free_mem: - release_mem_region(res->start, resource_size(res)); -err_free_clk: - clk_put(ssp->clk); -err_free: - kfree(ssp); - return ret; + return 0; } static int pxa_ssp_remove(struct platform_device *pdev) -- cgit v0.10.2 From 6446221c14ef3bf58754cf1948631128dbe62700 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Mon, 12 Aug 2013 10:37:18 +0200 Subject: ARM: pxa: ssp: add pxa_ssp_request_of() Add a function to lookup ssp devices from device tree. This way, users can reference the ssp devices in order to register to them. Signed-off-by: Daniel Mack Acked-by: Haojian Zhuang Signed-off-by: Mark Brown diff --git a/arch/arm/plat-pxa/ssp.c b/arch/arm/plat-pxa/ssp.c index f266135..c83f27b 100644 --- a/arch/arm/plat-pxa/ssp.c +++ b/arch/arm/plat-pxa/ssp.c @@ -62,6 +62,30 @@ struct ssp_device *pxa_ssp_request(int port, const char *label) } EXPORT_SYMBOL(pxa_ssp_request); +struct ssp_device *pxa_ssp_request_of(const struct device_node *of_node, + const char *label) +{ + struct ssp_device *ssp = NULL; + + mutex_lock(&ssp_lock); + + list_for_each_entry(ssp, &ssp_list, node) { + if (ssp->of_node == of_node && ssp->use_count == 0) { + ssp->use_count++; + ssp->label = label; + break; + } + } + + mutex_unlock(&ssp_lock); + + if (&ssp->node == &ssp_list) + return NULL; + + return ssp; +} +EXPORT_SYMBOL(pxa_ssp_request_of); + void pxa_ssp_free(struct ssp_device *ssp) { mutex_lock(&ssp_lock); @@ -185,6 +209,7 @@ static int pxa_ssp_probe(struct platform_device *pdev) } ssp->use_count = 0; + ssp->of_node = dev->of_node; mutex_lock(&ssp_lock); list_add(&ssp->node, &ssp_list); diff --git a/include/linux/pxa2xx_ssp.h b/include/linux/pxa2xx_ssp.h index 467cc63..4944420 100644 --- a/include/linux/pxa2xx_ssp.h +++ b/include/linux/pxa2xx_ssp.h @@ -21,6 +21,8 @@ #include #include +#include + /* * SSP Serial Port Registers @@ -190,6 +192,8 @@ struct ssp_device { int irq; int drcmr_rx; int drcmr_tx; + + struct device_node *of_node; }; /** @@ -218,11 +222,18 @@ static inline u32 pxa_ssp_read_reg(struct ssp_device *dev, u32 reg) #ifdef CONFIG_ARCH_PXA struct ssp_device *pxa_ssp_request(int port, const char *label); void pxa_ssp_free(struct ssp_device *); +struct ssp_device *pxa_ssp_request_of(const struct device_node *of_node, + const char *label); #else static inline struct ssp_device *pxa_ssp_request(int port, const char *label) { return NULL; } +static inline struct ssp_device *pxa_ssp_request_of(const struct device_node *n, + const char *name) +{ + return NULL; +} static inline void pxa_ssp_free(struct ssp_device *ssp) {} #endif -- cgit v0.10.2 From 4d8cfa4642f7d8fafa4d60f05dd34fe8c3b9fa45 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Mon, 12 Aug 2013 22:49:24 +0200 Subject: ASoC: mioa701_wm9713: Remove definition of ARRAY_AND_SIZE() Signed-off-by: Julia Lawall Signed-off-by: Mark Brown diff --git a/sound/soc/pxa/mioa701_wm9713.c b/sound/soc/pxa/mioa701_wm9713.c index 20fdce6..bbea778 100644 --- a/sound/soc/pxa/mioa701_wm9713.c +++ b/sound/soc/pxa/mioa701_wm9713.c @@ -56,8 +56,6 @@ #include "pxa2xx-ac97.h" #include "../codecs/wm9713.h" -#define ARRAY_AND_SIZE(x) (x), ARRAY_SIZE(x) - #define AC97_GPIO_PULL 0x58 /* Use GPIO8 for rear speaker amplifier */ -- cgit v0.10.2 From 7ac0da8cd38cb09d0addf708a8abbb93cf325c68 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Wed, 14 Aug 2013 14:26:29 -0600 Subject: ASoC: tegra: support a Mic Jack in the Tegra+RT5640 machine driver Add a Mic Jack widget to the Tegra+RT5640 machine driver, and document this in the DT binding. This enables the DT to include the Mic Jack in the audio routing table, and hence enables capture of audio, in addition to the previously-working playback. Signed-off-by: Stephen Warren Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-rt5640.txt b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-rt5640.txt index cba4f88..dc62249 100644 --- a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-rt5640.txt +++ b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-rt5640.txt @@ -16,6 +16,7 @@ Required properties: * Headphones * Speakers + * Mic Jack - nvidia,i2s-controller : The phandle of the Tegra I2S controller that's connected to the CODEC. diff --git a/sound/soc/tegra/tegra_rt5640.c b/sound/soc/tegra/tegra_rt5640.c index 08794f9..4511c5a 100644 --- a/sound/soc/tegra/tegra_rt5640.c +++ b/sound/soc/tegra/tegra_rt5640.c @@ -99,6 +99,7 @@ static struct snd_soc_jack_gpio tegra_rt5640_hp_jack_gpio = { static const struct snd_soc_dapm_widget tegra_rt5640_dapm_widgets[] = { SND_SOC_DAPM_HP("Headphones", NULL), SND_SOC_DAPM_SPK("Speakers", NULL), + SND_SOC_DAPM_MIC("Mic Jack", NULL), }; static const struct snd_kcontrol_new tegra_rt5640_controls[] = { -- cgit v0.10.2 From b4345006423d45622bc17198a598baefcea27c93 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Wed, 14 Aug 2013 11:11:19 +0200 Subject: ASoC: tegra20-ac97: simplify use of devm_ioremap_resource Remove unneeded error handling on the result of a call to platform_get_resource when the value is passed to devm_ioremap_resource. A simplified version of the semantic patch that makes this change is as follows: (http://coccinelle.lip6.fr/) // @@ expression pdev,res,n,e,e1; expression ret != 0; identifier l; @@ - res = platform_get_resource(pdev, IORESOURCE_MEM, n); ... when != res - if (res == NULL) { ... \(goto l;\|return ret;\) } ... when != res + res = platform_get_resource(pdev, IORESOURCE_MEM, n); e = devm_ioremap_resource(e1, res); // Signed-off-by: Julia Lawall Signed-off-by: Mark Brown diff --git a/sound/soc/tegra/tegra20_ac97.c b/sound/soc/tegra/tegra20_ac97.c index 87b845f..964cedf 100644 --- a/sound/soc/tegra/tegra20_ac97.c +++ b/sound/soc/tegra/tegra20_ac97.c @@ -334,12 +334,6 @@ static int tegra20_ac97_platform_probe(struct platform_device *pdev) } mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!mem) { - dev_err(&pdev->dev, "No memory resource\n"); - ret = -ENODEV; - goto err_clk_put; - } - regs = devm_ioremap_resource(&pdev->dev, mem); if (IS_ERR(regs)) { ret = PTR_ERR(regs); -- cgit v0.10.2 From b7ae6f31d8243ec684af16bc5c763eccdfabaec0 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Mon, 12 Aug 2013 10:42:37 +0200 Subject: ALSA: move dmaengine implementation from ASoC to ALSA core For the PXA DMA rework, we need the generic dmaengine implementation that currently lives in sound/soc for standalone (non-ASoC) AC'97 support. Move it to sound/core, and rename the Kconfig symbol. Signed-off-by: Daniel Mack Signed-off-by: Mark Brown diff --git a/sound/core/Kconfig b/sound/core/Kconfig index c0c2f57..94ce1c4 100644 --- a/sound/core/Kconfig +++ b/sound/core/Kconfig @@ -6,6 +6,9 @@ config SND_PCM tristate select SND_TIMER +config SND_DMAENGINE_PCM + bool + config SND_HWDEP tristate diff --git a/sound/core/Makefile b/sound/core/Makefile index 43d4117..5e890cf 100644 --- a/sound/core/Makefile +++ b/sound/core/Makefile @@ -13,6 +13,8 @@ snd-$(CONFIG_SND_JACK) += jack.o snd-pcm-objs := pcm.o pcm_native.o pcm_lib.o pcm_timer.o pcm_misc.o \ pcm_memory.o +snd-pcm-dmaengine-objs := pcm_dmaengine.o + snd-page-alloc-y := memalloc.o snd-page-alloc-$(CONFIG_SND_DMA_SGBUF) += sgbuf.o @@ -30,6 +32,7 @@ obj-$(CONFIG_SND_TIMER) += snd-timer.o obj-$(CONFIG_SND_HRTIMER) += snd-hrtimer.o obj-$(CONFIG_SND_RTCTIMER) += snd-rtctimer.o obj-$(CONFIG_SND_PCM) += snd-pcm.o snd-page-alloc.o +obj-$(CONFIG_SND_DMAENGINE_PCM) += snd-pcm-dmaengine.o obj-$(CONFIG_SND_RAWMIDI) += snd-rawmidi.o obj-$(CONFIG_SND_OSSEMUL) += oss/ diff --git a/sound/core/pcm_dmaengine.c b/sound/core/pcm_dmaengine.c new file mode 100644 index 0000000..aa924d9 --- /dev/null +++ b/sound/core/pcm_dmaengine.c @@ -0,0 +1,367 @@ +/* + * Copyright (C) 2012, Analog Devices Inc. + * Author: Lars-Peter Clausen + * + * Based on: + * imx-pcm-dma-mx2.c, Copyright 2009 Sascha Hauer + * mxs-pcm.c, Copyright (C) 2011 Freescale Semiconductor, Inc. + * ep93xx-pcm.c, Copyright (C) 2006 Lennert Buytenhek + * Copyright (C) 2006 Applied Data Systems + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * 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., + * 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ +#include +#include +#include +#include +#include +#include +#include + +#include + +struct dmaengine_pcm_runtime_data { + struct dma_chan *dma_chan; + dma_cookie_t cookie; + + unsigned int pos; +}; + +static inline struct dmaengine_pcm_runtime_data *substream_to_prtd( + const struct snd_pcm_substream *substream) +{ + return substream->runtime->private_data; +} + +struct dma_chan *snd_dmaengine_pcm_get_chan(struct snd_pcm_substream *substream) +{ + struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream); + + return prtd->dma_chan; +} +EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_get_chan); + +/** + * snd_hwparams_to_dma_slave_config - Convert hw_params to dma_slave_config + * @substream: PCM substream + * @params: hw_params + * @slave_config: DMA slave config + * + * This function can be used to initialize a dma_slave_config from a substream + * and hw_params in a dmaengine based PCM driver implementation. + */ +int snd_hwparams_to_dma_slave_config(const struct snd_pcm_substream *substream, + const struct snd_pcm_hw_params *params, + struct dma_slave_config *slave_config) +{ + enum dma_slave_buswidth buswidth; + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S8: + buswidth = DMA_SLAVE_BUSWIDTH_1_BYTE; + break; + case SNDRV_PCM_FORMAT_S16_LE: + buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES; + break; + case SNDRV_PCM_FORMAT_S18_3LE: + case SNDRV_PCM_FORMAT_S20_3LE: + case SNDRV_PCM_FORMAT_S24_LE: + case SNDRV_PCM_FORMAT_S32_LE: + buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES; + break; + default: + return -EINVAL; + } + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + slave_config->direction = DMA_MEM_TO_DEV; + slave_config->dst_addr_width = buswidth; + } else { + slave_config->direction = DMA_DEV_TO_MEM; + slave_config->src_addr_width = buswidth; + } + + slave_config->device_fc = false; + + return 0; +} +EXPORT_SYMBOL_GPL(snd_hwparams_to_dma_slave_config); + +/** + * snd_dmaengine_pcm_set_config_from_dai_data() - Initializes a dma slave config + * using DAI DMA data. + * @substream: PCM substream + * @dma_data: DAI DMA data + * @slave_config: DMA slave configuration + * + * Initializes the {dst,src}_addr, {dst,src}_maxburst, {dst,src}_addr_width and + * slave_id fields of the DMA slave config from the same fields of the DAI DMA + * data struct. The src and dst fields will be initialized depending on the + * direction of the substream. If the substream is a playback stream the dst + * fields will be initialized, if it is a capture stream the src fields will be + * initialized. The {dst,src}_addr_width field will only be initialized if the + * addr_width field of the DAI DMA data struct is not equal to + * DMA_SLAVE_BUSWIDTH_UNDEFINED. + */ +void snd_dmaengine_pcm_set_config_from_dai_data( + const struct snd_pcm_substream *substream, + const struct snd_dmaengine_dai_dma_data *dma_data, + struct dma_slave_config *slave_config) +{ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + slave_config->dst_addr = dma_data->addr; + slave_config->dst_maxburst = dma_data->maxburst; + if (dma_data->addr_width != DMA_SLAVE_BUSWIDTH_UNDEFINED) + slave_config->dst_addr_width = dma_data->addr_width; + } else { + slave_config->src_addr = dma_data->addr; + slave_config->src_maxburst = dma_data->maxburst; + if (dma_data->addr_width != DMA_SLAVE_BUSWIDTH_UNDEFINED) + slave_config->src_addr_width = dma_data->addr_width; + } + + slave_config->slave_id = dma_data->slave_id; +} +EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_set_config_from_dai_data); + +static void dmaengine_pcm_dma_complete(void *arg) +{ + struct snd_pcm_substream *substream = arg; + struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream); + + prtd->pos += snd_pcm_lib_period_bytes(substream); + if (prtd->pos >= snd_pcm_lib_buffer_bytes(substream)) + prtd->pos = 0; + + snd_pcm_period_elapsed(substream); +} + +static int dmaengine_pcm_prepare_and_submit(struct snd_pcm_substream *substream) +{ + struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream); + struct dma_chan *chan = prtd->dma_chan; + struct dma_async_tx_descriptor *desc; + enum dma_transfer_direction direction; + unsigned long flags = DMA_CTRL_ACK; + + direction = snd_pcm_substream_to_dma_direction(substream); + + if (!substream->runtime->no_period_wakeup) + flags |= DMA_PREP_INTERRUPT; + + prtd->pos = 0; + desc = dmaengine_prep_dma_cyclic(chan, + substream->runtime->dma_addr, + snd_pcm_lib_buffer_bytes(substream), + snd_pcm_lib_period_bytes(substream), direction, flags); + + if (!desc) + return -ENOMEM; + + desc->callback = dmaengine_pcm_dma_complete; + desc->callback_param = substream; + prtd->cookie = dmaengine_submit(desc); + + return 0; +} + +/** + * snd_dmaengine_pcm_trigger - dmaengine based PCM trigger implementation + * @substream: PCM substream + * @cmd: Trigger command + * + * Returns 0 on success, a negative error code otherwise. + * + * This function can be used as the PCM trigger callback for dmaengine based PCM + * driver implementations. + */ +int snd_dmaengine_pcm_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream); + int ret; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + ret = dmaengine_pcm_prepare_and_submit(substream); + if (ret) + return ret; + dma_async_issue_pending(prtd->dma_chan); + break; + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + dmaengine_resume(prtd->dma_chan); + break; + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + dmaengine_pause(prtd->dma_chan); + break; + case SNDRV_PCM_TRIGGER_STOP: + dmaengine_terminate_all(prtd->dma_chan); + break; + default: + return -EINVAL; + } + + return 0; +} +EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_trigger); + +/** + * snd_dmaengine_pcm_pointer_no_residue - dmaengine based PCM pointer implementation + * @substream: PCM substream + * + * This function is deprecated and should not be used by new drivers, as its + * results may be unreliable. + */ +snd_pcm_uframes_t snd_dmaengine_pcm_pointer_no_residue(struct snd_pcm_substream *substream) +{ + struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream); + return bytes_to_frames(substream->runtime, prtd->pos); +} +EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_pointer_no_residue); + +/** + * snd_dmaengine_pcm_pointer - dmaengine based PCM pointer implementation + * @substream: PCM substream + * + * This function can be used as the PCM pointer callback for dmaengine based PCM + * driver implementations. + */ +snd_pcm_uframes_t snd_dmaengine_pcm_pointer(struct snd_pcm_substream *substream) +{ + struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream); + struct dma_tx_state state; + enum dma_status status; + unsigned int buf_size; + unsigned int pos = 0; + + status = dmaengine_tx_status(prtd->dma_chan, prtd->cookie, &state); + if (status == DMA_IN_PROGRESS || status == DMA_PAUSED) { + buf_size = snd_pcm_lib_buffer_bytes(substream); + if (state.residue > 0 && state.residue <= buf_size) + pos = buf_size - state.residue; + } + + return bytes_to_frames(substream->runtime, pos); +} +EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_pointer); + +/** + * snd_dmaengine_pcm_request_channel - Request channel for the dmaengine PCM + * @filter_fn: Filter function used to request the DMA channel + * @filter_data: Data passed to the DMA filter function + * + * Returns NULL or the requested DMA channel. + * + * This function request a DMA channel for usage with dmaengine PCM. + */ +struct dma_chan *snd_dmaengine_pcm_request_channel(dma_filter_fn filter_fn, + void *filter_data) +{ + dma_cap_mask_t mask; + + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + dma_cap_set(DMA_CYCLIC, mask); + + return dma_request_channel(mask, filter_fn, filter_data); +} +EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_request_channel); + +/** + * snd_dmaengine_pcm_open - Open a dmaengine based PCM substream + * @substream: PCM substream + * @chan: DMA channel to use for data transfers + * + * Returns 0 on success, a negative error code otherwise. + * + * The function should usually be called from the pcm open callback. Note that + * this function will use private_data field of the substream's runtime. So it + * is not availabe to your pcm driver implementation. + */ +int snd_dmaengine_pcm_open(struct snd_pcm_substream *substream, + struct dma_chan *chan) +{ + struct dmaengine_pcm_runtime_data *prtd; + int ret; + + if (!chan) + return -ENXIO; + + ret = snd_pcm_hw_constraint_integer(substream->runtime, + SNDRV_PCM_HW_PARAM_PERIODS); + if (ret < 0) + return ret; + + prtd = kzalloc(sizeof(*prtd), GFP_KERNEL); + if (!prtd) + return -ENOMEM; + + prtd->dma_chan = chan; + + substream->runtime->private_data = prtd; + + return 0; +} +EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_open); + +/** + * snd_dmaengine_pcm_open_request_chan - Open a dmaengine based PCM substream and request channel + * @substream: PCM substream + * @filter_fn: Filter function used to request the DMA channel + * @filter_data: Data passed to the DMA filter function + * + * Returns 0 on success, a negative error code otherwise. + * + * This function will request a DMA channel using the passed filter function and + * data. The function should usually be called from the pcm open callback. Note + * that this function will use private_data field of the substream's runtime. So + * it is not availabe to your pcm driver implementation. + */ +int snd_dmaengine_pcm_open_request_chan(struct snd_pcm_substream *substream, + dma_filter_fn filter_fn, void *filter_data) +{ + return snd_dmaengine_pcm_open(substream, + snd_dmaengine_pcm_request_channel(filter_fn, filter_data)); +} +EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_open_request_chan); + +/** + * snd_dmaengine_pcm_close - Close a dmaengine based PCM substream + * @substream: PCM substream + */ +int snd_dmaengine_pcm_close(struct snd_pcm_substream *substream) +{ + struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream); + + kfree(prtd); + + return 0; +} +EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_close); + +/** + * snd_dmaengine_pcm_release_chan_close - Close a dmaengine based PCM substream and release channel + * @substream: PCM substream + * + * Releases the DMA channel associated with the PCM substream. + */ +int snd_dmaengine_pcm_close_release_chan(struct snd_pcm_substream *substream) +{ + struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream); + + dma_release_channel(prtd->dma_chan); + + return snd_dmaengine_pcm_close(substream); +} +EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_close_release_chan); + +MODULE_LICENSE("GPL"); diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index 45eeaa9..5138b84 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig @@ -26,12 +26,9 @@ if SND_SOC config SND_SOC_AC97_BUS bool -config SND_SOC_DMAENGINE_PCM - bool - config SND_SOC_GENERIC_DMAENGINE_PCM bool - select SND_SOC_DMAENGINE_PCM + select SND_DMAENGINE_PCM # All the supported SoCs source "sound/soc/atmel/Kconfig" diff --git a/sound/soc/Makefile b/sound/soc/Makefile index bc02614..61a64d2 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile @@ -1,10 +1,6 @@ snd-soc-core-objs := soc-core.o soc-dapm.o soc-jack.o soc-cache.o soc-utils.o snd-soc-core-objs += soc-pcm.o soc-compress.o soc-io.o -ifneq ($(CONFIG_SND_SOC_DMAENGINE_PCM),) -snd-soc-core-objs += soc-dmaengine-pcm.o -endif - ifneq ($(CONFIG_SND_SOC_GENERIC_DMAENGINE_PCM),) snd-soc-core-objs += soc-generic-dmaengine-pcm.o endif diff --git a/sound/soc/omap/Kconfig b/sound/soc/omap/Kconfig index 9f5d55e..accd0ff 100644 --- a/sound/soc/omap/Kconfig +++ b/sound/soc/omap/Kconfig @@ -1,7 +1,7 @@ config SND_OMAP_SOC tristate "SoC Audio for the Texas Instruments OMAP chips" depends on ARCH_OMAP && DMA_OMAP - select SND_SOC_DMAENGINE_PCM + select SND_DMAENGINE_PCM config SND_OMAP_SOC_DMIC tristate diff --git a/sound/soc/pxa/Kconfig b/sound/soc/pxa/Kconfig index b358094..4db74a0 100644 --- a/sound/soc/pxa/Kconfig +++ b/sound/soc/pxa/Kconfig @@ -11,7 +11,7 @@ config SND_PXA2XX_SOC config SND_MMP_SOC bool "Soc Audio for Marvell MMP chips" depends on ARCH_MMP - select SND_SOC_DMAENGINE_PCM + select SND_DMAENGINE_PCM select SND_ARM help Say Y if you want to add support for codecs attached to diff --git a/sound/soc/soc-dmaengine-pcm.c b/sound/soc/soc-dmaengine-pcm.c deleted file mode 100644 index aa924d9..0000000 --- a/sound/soc/soc-dmaengine-pcm.c +++ /dev/null @@ -1,367 +0,0 @@ -/* - * Copyright (C) 2012, Analog Devices Inc. - * Author: Lars-Peter Clausen - * - * Based on: - * imx-pcm-dma-mx2.c, Copyright 2009 Sascha Hauer - * mxs-pcm.c, Copyright (C) 2011 Freescale Semiconductor, Inc. - * ep93xx-pcm.c, Copyright (C) 2006 Lennert Buytenhek - * Copyright (C) 2006 Applied Data Systems - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * 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., - * 675 Mass Ave, Cambridge, MA 02139, USA. - * - */ -#include -#include -#include -#include -#include -#include -#include - -#include - -struct dmaengine_pcm_runtime_data { - struct dma_chan *dma_chan; - dma_cookie_t cookie; - - unsigned int pos; -}; - -static inline struct dmaengine_pcm_runtime_data *substream_to_prtd( - const struct snd_pcm_substream *substream) -{ - return substream->runtime->private_data; -} - -struct dma_chan *snd_dmaengine_pcm_get_chan(struct snd_pcm_substream *substream) -{ - struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream); - - return prtd->dma_chan; -} -EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_get_chan); - -/** - * snd_hwparams_to_dma_slave_config - Convert hw_params to dma_slave_config - * @substream: PCM substream - * @params: hw_params - * @slave_config: DMA slave config - * - * This function can be used to initialize a dma_slave_config from a substream - * and hw_params in a dmaengine based PCM driver implementation. - */ -int snd_hwparams_to_dma_slave_config(const struct snd_pcm_substream *substream, - const struct snd_pcm_hw_params *params, - struct dma_slave_config *slave_config) -{ - enum dma_slave_buswidth buswidth; - - switch (params_format(params)) { - case SNDRV_PCM_FORMAT_S8: - buswidth = DMA_SLAVE_BUSWIDTH_1_BYTE; - break; - case SNDRV_PCM_FORMAT_S16_LE: - buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES; - break; - case SNDRV_PCM_FORMAT_S18_3LE: - case SNDRV_PCM_FORMAT_S20_3LE: - case SNDRV_PCM_FORMAT_S24_LE: - case SNDRV_PCM_FORMAT_S32_LE: - buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES; - break; - default: - return -EINVAL; - } - - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - slave_config->direction = DMA_MEM_TO_DEV; - slave_config->dst_addr_width = buswidth; - } else { - slave_config->direction = DMA_DEV_TO_MEM; - slave_config->src_addr_width = buswidth; - } - - slave_config->device_fc = false; - - return 0; -} -EXPORT_SYMBOL_GPL(snd_hwparams_to_dma_slave_config); - -/** - * snd_dmaengine_pcm_set_config_from_dai_data() - Initializes a dma slave config - * using DAI DMA data. - * @substream: PCM substream - * @dma_data: DAI DMA data - * @slave_config: DMA slave configuration - * - * Initializes the {dst,src}_addr, {dst,src}_maxburst, {dst,src}_addr_width and - * slave_id fields of the DMA slave config from the same fields of the DAI DMA - * data struct. The src and dst fields will be initialized depending on the - * direction of the substream. If the substream is a playback stream the dst - * fields will be initialized, if it is a capture stream the src fields will be - * initialized. The {dst,src}_addr_width field will only be initialized if the - * addr_width field of the DAI DMA data struct is not equal to - * DMA_SLAVE_BUSWIDTH_UNDEFINED. - */ -void snd_dmaengine_pcm_set_config_from_dai_data( - const struct snd_pcm_substream *substream, - const struct snd_dmaengine_dai_dma_data *dma_data, - struct dma_slave_config *slave_config) -{ - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - slave_config->dst_addr = dma_data->addr; - slave_config->dst_maxburst = dma_data->maxburst; - if (dma_data->addr_width != DMA_SLAVE_BUSWIDTH_UNDEFINED) - slave_config->dst_addr_width = dma_data->addr_width; - } else { - slave_config->src_addr = dma_data->addr; - slave_config->src_maxburst = dma_data->maxburst; - if (dma_data->addr_width != DMA_SLAVE_BUSWIDTH_UNDEFINED) - slave_config->src_addr_width = dma_data->addr_width; - } - - slave_config->slave_id = dma_data->slave_id; -} -EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_set_config_from_dai_data); - -static void dmaengine_pcm_dma_complete(void *arg) -{ - struct snd_pcm_substream *substream = arg; - struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream); - - prtd->pos += snd_pcm_lib_period_bytes(substream); - if (prtd->pos >= snd_pcm_lib_buffer_bytes(substream)) - prtd->pos = 0; - - snd_pcm_period_elapsed(substream); -} - -static int dmaengine_pcm_prepare_and_submit(struct snd_pcm_substream *substream) -{ - struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream); - struct dma_chan *chan = prtd->dma_chan; - struct dma_async_tx_descriptor *desc; - enum dma_transfer_direction direction; - unsigned long flags = DMA_CTRL_ACK; - - direction = snd_pcm_substream_to_dma_direction(substream); - - if (!substream->runtime->no_period_wakeup) - flags |= DMA_PREP_INTERRUPT; - - prtd->pos = 0; - desc = dmaengine_prep_dma_cyclic(chan, - substream->runtime->dma_addr, - snd_pcm_lib_buffer_bytes(substream), - snd_pcm_lib_period_bytes(substream), direction, flags); - - if (!desc) - return -ENOMEM; - - desc->callback = dmaengine_pcm_dma_complete; - desc->callback_param = substream; - prtd->cookie = dmaengine_submit(desc); - - return 0; -} - -/** - * snd_dmaengine_pcm_trigger - dmaengine based PCM trigger implementation - * @substream: PCM substream - * @cmd: Trigger command - * - * Returns 0 on success, a negative error code otherwise. - * - * This function can be used as the PCM trigger callback for dmaengine based PCM - * driver implementations. - */ -int snd_dmaengine_pcm_trigger(struct snd_pcm_substream *substream, int cmd) -{ - struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream); - int ret; - - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - ret = dmaengine_pcm_prepare_and_submit(substream); - if (ret) - return ret; - dma_async_issue_pending(prtd->dma_chan); - break; - case SNDRV_PCM_TRIGGER_RESUME: - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - dmaengine_resume(prtd->dma_chan); - break; - case SNDRV_PCM_TRIGGER_SUSPEND: - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - dmaengine_pause(prtd->dma_chan); - break; - case SNDRV_PCM_TRIGGER_STOP: - dmaengine_terminate_all(prtd->dma_chan); - break; - default: - return -EINVAL; - } - - return 0; -} -EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_trigger); - -/** - * snd_dmaengine_pcm_pointer_no_residue - dmaengine based PCM pointer implementation - * @substream: PCM substream - * - * This function is deprecated and should not be used by new drivers, as its - * results may be unreliable. - */ -snd_pcm_uframes_t snd_dmaengine_pcm_pointer_no_residue(struct snd_pcm_substream *substream) -{ - struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream); - return bytes_to_frames(substream->runtime, prtd->pos); -} -EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_pointer_no_residue); - -/** - * snd_dmaengine_pcm_pointer - dmaengine based PCM pointer implementation - * @substream: PCM substream - * - * This function can be used as the PCM pointer callback for dmaengine based PCM - * driver implementations. - */ -snd_pcm_uframes_t snd_dmaengine_pcm_pointer(struct snd_pcm_substream *substream) -{ - struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream); - struct dma_tx_state state; - enum dma_status status; - unsigned int buf_size; - unsigned int pos = 0; - - status = dmaengine_tx_status(prtd->dma_chan, prtd->cookie, &state); - if (status == DMA_IN_PROGRESS || status == DMA_PAUSED) { - buf_size = snd_pcm_lib_buffer_bytes(substream); - if (state.residue > 0 && state.residue <= buf_size) - pos = buf_size - state.residue; - } - - return bytes_to_frames(substream->runtime, pos); -} -EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_pointer); - -/** - * snd_dmaengine_pcm_request_channel - Request channel for the dmaengine PCM - * @filter_fn: Filter function used to request the DMA channel - * @filter_data: Data passed to the DMA filter function - * - * Returns NULL or the requested DMA channel. - * - * This function request a DMA channel for usage with dmaengine PCM. - */ -struct dma_chan *snd_dmaengine_pcm_request_channel(dma_filter_fn filter_fn, - void *filter_data) -{ - dma_cap_mask_t mask; - - dma_cap_zero(mask); - dma_cap_set(DMA_SLAVE, mask); - dma_cap_set(DMA_CYCLIC, mask); - - return dma_request_channel(mask, filter_fn, filter_data); -} -EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_request_channel); - -/** - * snd_dmaengine_pcm_open - Open a dmaengine based PCM substream - * @substream: PCM substream - * @chan: DMA channel to use for data transfers - * - * Returns 0 on success, a negative error code otherwise. - * - * The function should usually be called from the pcm open callback. Note that - * this function will use private_data field of the substream's runtime. So it - * is not availabe to your pcm driver implementation. - */ -int snd_dmaengine_pcm_open(struct snd_pcm_substream *substream, - struct dma_chan *chan) -{ - struct dmaengine_pcm_runtime_data *prtd; - int ret; - - if (!chan) - return -ENXIO; - - ret = snd_pcm_hw_constraint_integer(substream->runtime, - SNDRV_PCM_HW_PARAM_PERIODS); - if (ret < 0) - return ret; - - prtd = kzalloc(sizeof(*prtd), GFP_KERNEL); - if (!prtd) - return -ENOMEM; - - prtd->dma_chan = chan; - - substream->runtime->private_data = prtd; - - return 0; -} -EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_open); - -/** - * snd_dmaengine_pcm_open_request_chan - Open a dmaengine based PCM substream and request channel - * @substream: PCM substream - * @filter_fn: Filter function used to request the DMA channel - * @filter_data: Data passed to the DMA filter function - * - * Returns 0 on success, a negative error code otherwise. - * - * This function will request a DMA channel using the passed filter function and - * data. The function should usually be called from the pcm open callback. Note - * that this function will use private_data field of the substream's runtime. So - * it is not availabe to your pcm driver implementation. - */ -int snd_dmaengine_pcm_open_request_chan(struct snd_pcm_substream *substream, - dma_filter_fn filter_fn, void *filter_data) -{ - return snd_dmaengine_pcm_open(substream, - snd_dmaengine_pcm_request_channel(filter_fn, filter_data)); -} -EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_open_request_chan); - -/** - * snd_dmaengine_pcm_close - Close a dmaengine based PCM substream - * @substream: PCM substream - */ -int snd_dmaengine_pcm_close(struct snd_pcm_substream *substream) -{ - struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream); - - kfree(prtd); - - return 0; -} -EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_close); - -/** - * snd_dmaengine_pcm_release_chan_close - Close a dmaengine based PCM substream and release channel - * @substream: PCM substream - * - * Releases the DMA channel associated with the PCM substream. - */ -int snd_dmaengine_pcm_close_release_chan(struct snd_pcm_substream *substream) -{ - struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream); - - dma_release_channel(prtd->dma_chan); - - return snd_dmaengine_pcm_close(substream); -} -EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_close_release_chan); - -MODULE_LICENSE("GPL"); diff --git a/sound/soc/spear/Kconfig b/sound/soc/spear/Kconfig index 3567d73..0a53053 100644 --- a/sound/soc/spear/Kconfig +++ b/sound/soc/spear/Kconfig @@ -1,6 +1,6 @@ config SND_SPEAR_SOC tristate - select SND_SOC_DMAENGINE_PCM + select SND_DMAENGINE_PCM config SND_SPEAR_SPDIF_OUT tristate -- cgit v0.10.2 From 2023c90c3a2c4c1aeb7f47649367d551c676da07 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Mon, 12 Aug 2013 10:42:38 +0200 Subject: ASoC: pxa: pxa-ssp: add DT bindings The pxa ssp DAI acts as a user of a pxa ssp port, and needs an appropriate 'port' phandle in DT to reference the upstream. Signed-off-by: Daniel Mack Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/sound/mrvl,pxa-ssp.txt b/Documentation/devicetree/bindings/sound/mrvl,pxa-ssp.txt new file mode 100644 index 0000000..74c9ba6 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/mrvl,pxa-ssp.txt @@ -0,0 +1,28 @@ +Marvell PXA SSP CPU DAI bindings + +Required properties: + + compatible Must be "mrvl,pxa-ssp-dai" + port A phandle reference to a PXA ssp upstream device + +Example: + + /* upstream device */ + + ssp0: ssp@41000000 { + compatible = "mrvl,pxa3xx-ssp"; + reg = <0x41000000 0x40>; + interrupts = <24>; + clock-names = "pxa27x-ssp.0"; + dmas = <&dma 13 + &dma 14>; + dma-names = "rx", "tx"; + }; + + /* DAI as user */ + + ssp_dai0: ssp_dai@0 { + compatible = "mrvl,pxa-ssp-dai"; + port = <&ssp0>; + }; + diff --git a/sound/soc/pxa/pxa-ssp.c b/sound/soc/pxa/pxa-ssp.c index 6f4dd75..19296f2 100644 --- a/sound/soc/pxa/pxa-ssp.c +++ b/sound/soc/pxa/pxa-ssp.c @@ -21,6 +21,7 @@ #include #include #include +#include #include @@ -719,6 +720,7 @@ static int pxa_ssp_trigger(struct snd_pcm_substream *substream, int cmd, static int pxa_ssp_probe(struct snd_soc_dai *dai) { + struct device *dev = dai->dev; struct ssp_priv *priv; int ret; @@ -726,10 +728,26 @@ static int pxa_ssp_probe(struct snd_soc_dai *dai) if (!priv) return -ENOMEM; - priv->ssp = pxa_ssp_request(dai->id + 1, "SoC audio"); - if (priv->ssp == NULL) { - ret = -ENODEV; - goto err_priv; + if (dev->of_node) { + struct device_node *ssp_handle; + + ssp_handle = of_parse_phandle(dev->of_node, "port", 0); + if (!ssp_handle) { + dev_err(dev, "unable to get 'port' phandle\n"); + return -ENODEV; + } + + priv->ssp = pxa_ssp_request_of(ssp_handle, "SoC audio"); + if (priv->ssp == NULL) { + ret = -ENODEV; + goto err_priv; + } + } else { + priv->ssp = pxa_ssp_request(dai->id + 1, "SoC audio"); + if (priv->ssp == NULL) { + ret = -ENODEV; + goto err_priv; + } } priv->dai_fmt = (unsigned int) -1; @@ -798,6 +816,12 @@ static const struct snd_soc_component_driver pxa_ssp_component = { .name = "pxa-ssp", }; +#ifdef CONFIG_OF +static const struct of_device_id pxa_ssp_of_ids[] = { + { .compatible = "mrvl,pxa-ssp-dai" }, +}; +#endif + static int asoc_ssp_probe(struct platform_device *pdev) { return snd_soc_register_component(&pdev->dev, &pxa_ssp_component, @@ -812,8 +836,9 @@ static int asoc_ssp_remove(struct platform_device *pdev) static struct platform_driver asoc_ssp_driver = { .driver = { - .name = "pxa-ssp-dai", - .owner = THIS_MODULE, + .name = "pxa-ssp-dai", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(pxa_ssp_of_ids), }, .probe = asoc_ssp_probe, -- cgit v0.10.2 From d65a14587a9be853a887a1407db133df1fb68e29 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Mon, 12 Aug 2013 10:42:39 +0200 Subject: ASoC: pxa: use snd_dmaengine_dai_dma_data Use snd_dmaengine_dai_dma_data for passing the dma parameters from clients to the pxa pcm lib. This does no functional change, it's just an intermedia step to migrate the pxa bits over to dmaengine. The calculation of dcmd is a transition hack which will be removed again in a later patch. It's just there to make the transition more readable. Signed-off-by: Daniel Mack Acked-by: Mark Brown Signed-off-by: Mark Brown diff --git a/include/sound/pxa2xx-lib.h b/include/sound/pxa2xx-lib.h index 2fd3d25..56e818e 100644 --- a/include/sound/pxa2xx-lib.h +++ b/include/sound/pxa2xx-lib.h @@ -6,13 +6,6 @@ /* PCM */ -struct pxa2xx_pcm_dma_params { - char *name; /* stream identifier */ - u32 dcmd; /* DMA descriptor dcmd field */ - volatile u32 *drcmr; /* the DMA request channel to use */ - u32 dev_addr; /* device physical address for DMA */ -}; - extern int __pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params); extern int __pxa2xx_pcm_hw_free(struct snd_pcm_substream *substream); diff --git a/sound/arm/pxa2xx-ac97.c b/sound/arm/pxa2xx-ac97.c index ce431e6..5066a37 100644 --- a/sound/arm/pxa2xx-ac97.c +++ b/sound/arm/pxa2xx-ac97.c @@ -14,12 +14,14 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include @@ -41,20 +43,20 @@ static struct snd_ac97_bus_ops pxa2xx_ac97_ops = { .reset = pxa2xx_ac97_reset, }; -static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_out = { - .name = "AC97 PCM out", - .dev_addr = __PREG(PCDR), - .drcmr = &DRCMR(12), - .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG | - DCMD_BURST32 | DCMD_WIDTH4, +static unsigned long pxa2xx_ac97_pcm_out_req = 12; +static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_out = { + .addr = __PREG(PCDR), + .addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, + .maxburst = 32, + .filter_data = &pxa2xx_ac97_pcm_out_req, }; -static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_in = { - .name = "AC97 PCM in", - .dev_addr = __PREG(PCDR), - .drcmr = &DRCMR(11), - .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC | - DCMD_BURST32 | DCMD_WIDTH4, +static unsigned long pxa2xx_ac97_pcm_in_req = 11; +static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_in = { + .addr = __PREG(PCDR), + .addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, + .maxburst = 32, + .filter_data = &pxa2xx_ac97_pcm_in_req, }; static struct snd_pcm *pxa2xx_ac97_pcm; diff --git a/sound/arm/pxa2xx-pcm-lib.c b/sound/arm/pxa2xx-pcm-lib.c index 823359e..a61d7a9 100644 --- a/sound/arm/pxa2xx-pcm-lib.c +++ b/sound/arm/pxa2xx-pcm-lib.c @@ -7,11 +7,13 @@ #include #include #include +#include #include #include #include #include +#include #include @@ -43,6 +45,35 @@ int __pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream, size_t period = params_period_bytes(params); pxa_dma_desc *dma_desc; dma_addr_t dma_buff_phys, next_desc_phys; + u32 dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG; + + /* temporary transition hack */ + switch (rtd->params->addr_width) { + case DMA_SLAVE_BUSWIDTH_1_BYTE: + dcmd |= DCMD_WIDTH1; + break; + case DMA_SLAVE_BUSWIDTH_2_BYTES: + dcmd |= DCMD_WIDTH2; + break; + case DMA_SLAVE_BUSWIDTH_4_BYTES: + dcmd |= DCMD_WIDTH4; + break; + default: + /* can't happen */ + break; + } + + switch (rtd->params->maxburst) { + case 8: + dcmd |= DCMD_BURST8; + break; + case 16: + dcmd |= DCMD_BURST16; + break; + case 32: + dcmd |= DCMD_BURST32; + break; + } snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); runtime->dma_bytes = totsize; @@ -55,14 +86,14 @@ int __pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream, dma_desc->ddadr = next_desc_phys; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { dma_desc->dsadr = dma_buff_phys; - dma_desc->dtadr = rtd->params->dev_addr; + dma_desc->dtadr = rtd->params->addr; } else { - dma_desc->dsadr = rtd->params->dev_addr; + dma_desc->dsadr = rtd->params->addr; dma_desc->dtadr = dma_buff_phys; } if (period > totsize) period = totsize; - dma_desc->dcmd = rtd->params->dcmd | period | DCMD_ENDIRQEN; + dma_desc->dcmd = dcmd | period | DCMD_ENDIRQEN; dma_desc++; dma_buff_phys += period; } while (totsize -= period); @@ -76,8 +107,10 @@ int __pxa2xx_pcm_hw_free(struct snd_pcm_substream *substream) { struct pxa2xx_runtime_data *rtd = substream->runtime->private_data; - if (rtd && rtd->params && rtd->params->drcmr) - *rtd->params->drcmr = 0; + if (rtd && rtd->params && rtd->params->filter_data) { + unsigned long req = *(unsigned long *) rtd->params->filter_data; + DRCMR(req) = 0; + } snd_pcm_set_runtime_buffer(substream, NULL); return 0; @@ -136,6 +169,7 @@ EXPORT_SYMBOL(pxa2xx_pcm_pointer); int __pxa2xx_pcm_prepare(struct snd_pcm_substream *substream) { struct pxa2xx_runtime_data *prtd = substream->runtime->private_data; + unsigned long req; if (!prtd || !prtd->params) return 0; @@ -146,7 +180,8 @@ int __pxa2xx_pcm_prepare(struct snd_pcm_substream *substream) DCSR(prtd->dma_ch) &= ~DCSR_RUN; DCSR(prtd->dma_ch) = 0; DCMD(prtd->dma_ch) = 0; - *prtd->params->drcmr = prtd->dma_ch | DRCMR_MAPVLD; + req = *(unsigned long *) prtd->params->filter_data; + DRCMR(req) = prtd->dma_ch | DRCMR_MAPVLD; return 0; } @@ -155,7 +190,6 @@ EXPORT_SYMBOL(__pxa2xx_pcm_prepare); void pxa2xx_pcm_dma_irq(int dma_ch, void *dev_id) { struct snd_pcm_substream *substream = dev_id; - struct pxa2xx_runtime_data *rtd = substream->runtime->private_data; int dcsr; dcsr = DCSR(dma_ch); @@ -164,8 +198,8 @@ void pxa2xx_pcm_dma_irq(int dma_ch, void *dev_id) if (dcsr & DCSR_ENDINTR) { snd_pcm_period_elapsed(substream); } else { - printk(KERN_ERR "%s: DMA error on channel %d (DCSR=%#x)\n", - rtd->params->name, dma_ch, dcsr); + printk(KERN_ERR "DMA error on channel %d (DCSR=%#x)\n", + dma_ch, dcsr); snd_pcm_stream_lock(substream); snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); snd_pcm_stream_unlock(substream); diff --git a/sound/arm/pxa2xx-pcm.c b/sound/arm/pxa2xx-pcm.c index 26422a3..69a2455 100644 --- a/sound/arm/pxa2xx-pcm.c +++ b/sound/arm/pxa2xx-pcm.c @@ -11,8 +11,11 @@ */ #include +#include + #include #include +#include #include "pxa2xx-pcm.h" @@ -40,7 +43,7 @@ static int pxa2xx_pcm_open(struct snd_pcm_substream *substream) rtd->params = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? client->playback_params : client->capture_params; - ret = pxa_request_dma(rtd->params->name, DMA_PRIO_LOW, + ret = pxa_request_dma("dma", DMA_PRIO_LOW, pxa2xx_pcm_dma_irq, substream); if (ret < 0) goto err2; diff --git a/sound/arm/pxa2xx-pcm.h b/sound/arm/pxa2xx-pcm.h index 65f86b5..2a8fc08 100644 --- a/sound/arm/pxa2xx-pcm.h +++ b/sound/arm/pxa2xx-pcm.h @@ -13,14 +13,14 @@ struct pxa2xx_runtime_data { int dma_ch; - struct pxa2xx_pcm_dma_params *params; + struct snd_dmaengine_dai_dma_data *params; pxa_dma_desc *dma_desc_array; dma_addr_t dma_desc_array_phys; }; struct pxa2xx_pcm_client { - struct pxa2xx_pcm_dma_params *playback_params; - struct pxa2xx_pcm_dma_params *capture_params; + struct snd_dmaengine_dai_dma_data *playback_params; + struct snd_dmaengine_dai_dma_data *capture_params; int (*startup)(struct snd_pcm_substream *); void (*shutdown)(struct snd_pcm_substream *); int (*prepare)(struct snd_pcm_substream *); diff --git a/sound/soc/pxa/mmp-pcm.c b/sound/soc/pxa/mmp-pcm.c index 5d57e07..9a97843 100644 --- a/sound/soc/pxa/mmp-pcm.c +++ b/sound/soc/pxa/mmp-pcm.c @@ -17,6 +17,8 @@ #include #include #include +#include + #include #include #include @@ -67,7 +69,7 @@ static int mmp_pcm_hw_params(struct snd_pcm_substream *substream, { struct dma_chan *chan = snd_dmaengine_pcm_get_chan(substream); struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct pxa2xx_pcm_dma_params *dma_params; + struct snd_dmaengine_dai_dma_data *dma_params; struct dma_slave_config slave_config; int ret; @@ -80,10 +82,10 @@ static int mmp_pcm_hw_params(struct snd_pcm_substream *substream, return ret; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - slave_config.dst_addr = dma_params->dev_addr; + slave_config.dst_addr = dma_params->addr; slave_config.dst_maxburst = 4; } else { - slave_config.src_addr = dma_params->dev_addr; + slave_config.src_addr = dma_params->addr; slave_config.src_maxburst = 4; } diff --git a/sound/soc/pxa/mmp-sspa.c b/sound/soc/pxa/mmp-sspa.c index 1605934..41752a5 100644 --- a/sound/soc/pxa/mmp-sspa.c +++ b/sound/soc/pxa/mmp-sspa.c @@ -27,12 +27,15 @@ #include #include #include +#include + #include #include #include #include #include #include +#include #include "mmp-sspa.h" /* @@ -40,7 +43,7 @@ */ struct sspa_priv { struct ssp_device *sspa; - struct pxa2xx_pcm_dma_params *dma_params; + struct snd_dmaengine_dai_dma_data *dma_params; struct clk *audio_clk; struct clk *sysclk; int dai_fmt; @@ -266,7 +269,7 @@ static int mmp_sspa_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct sspa_priv *sspa_priv = snd_soc_dai_get_drvdata(dai); struct ssp_device *sspa = sspa_priv->sspa; - struct pxa2xx_pcm_dma_params *dma_params; + struct snd_dmaengine_dai_dma_data *dma_params; u32 sspa_ctrl; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) @@ -309,7 +312,7 @@ static int mmp_sspa_hw_params(struct snd_pcm_substream *substream, } dma_params = &sspa_priv->dma_params[substream->stream]; - dma_params->dev_addr = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? + dma_params->addr = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? (sspa->phys_base + SSPA_TXD) : (sspa->phys_base + SSPA_RXD); snd_soc_dai_set_dma_data(cpu_dai, substream, dma_params); @@ -425,7 +428,8 @@ static int asoc_mmp_sspa_probe(struct platform_device *pdev) return -ENOMEM; priv->dma_params = devm_kzalloc(&pdev->dev, - 2 * sizeof(struct pxa2xx_pcm_dma_params), GFP_KERNEL); + 2 * sizeof(struct snd_dmaengine_dai_dma_data), + GFP_KERNEL); if (priv->dma_params == NULL) return -ENOMEM; diff --git a/sound/soc/pxa/pxa-ssp.c b/sound/soc/pxa/pxa-ssp.c index 19296f2..c0dcc35 100644 --- a/sound/soc/pxa/pxa-ssp.c +++ b/sound/soc/pxa/pxa-ssp.c @@ -22,6 +22,7 @@ #include #include #include +#include #include @@ -31,9 +32,9 @@ #include #include #include +#include #include -#include #include "../../arm/pxa2xx-pcm.h" #include "pxa-ssp.h" @@ -80,27 +81,14 @@ static void pxa_ssp_disable(struct ssp_device *ssp) __raw_writel(sscr0, ssp->mmio_base + SSCR0); } -struct pxa2xx_pcm_dma_data { - struct pxa2xx_pcm_dma_params params; - char name[20]; -}; - static void pxa_ssp_set_dma_params(struct ssp_device *ssp, int width4, - int out, struct pxa2xx_pcm_dma_params *dma_data) + int out, struct snd_dmaengine_dai_dma_data *dma) { - struct pxa2xx_pcm_dma_data *dma; - - dma = container_of(dma_data, struct pxa2xx_pcm_dma_data, params); - - snprintf(dma->name, 20, "SSP%d PCM %s %s", ssp->port_id, - width4 ? "32-bit" : "16-bit", out ? "out" : "in"); - - dma->params.name = dma->name; - dma->params.drcmr = &DRCMR(out ? ssp->drcmr_tx : ssp->drcmr_rx); - dma->params.dcmd = (out ? (DCMD_INCSRCADDR | DCMD_FLOWTRG) : - (DCMD_INCTRGADDR | DCMD_FLOWSRC)) | - (width4 ? DCMD_WIDTH4 : DCMD_WIDTH2) | DCMD_BURST16; - dma->params.dev_addr = ssp->phys_base + SSDR; + dma->filter_data = out ? &ssp->drcmr_tx : &ssp->drcmr_rx; + dma->addr_width = width4 ? DMA_SLAVE_BUSWIDTH_4_BYTES : + DMA_SLAVE_BUSWIDTH_2_BYTES; + dma->maxburst = 16; + dma->addr = ssp->phys_base + SSDR; } static int pxa_ssp_startup(struct snd_pcm_substream *substream, @@ -108,7 +96,7 @@ static int pxa_ssp_startup(struct snd_pcm_substream *substream, { struct ssp_priv *priv = snd_soc_dai_get_drvdata(cpu_dai); struct ssp_device *ssp = priv->ssp; - struct pxa2xx_pcm_dma_data *dma; + struct snd_dmaengine_dai_dma_data *dma; int ret = 0; if (!cpu_dai->active) { @@ -116,10 +104,10 @@ static int pxa_ssp_startup(struct snd_pcm_substream *substream, pxa_ssp_disable(ssp); } - dma = kzalloc(sizeof(struct pxa2xx_pcm_dma_data), GFP_KERNEL); + dma = kzalloc(sizeof(struct snd_dmaengine_dai_dma_data), GFP_KERNEL); if (!dma) return -ENOMEM; - snd_soc_dai_set_dma_data(cpu_dai, substream, &dma->params); + snd_soc_dai_set_dma_data(cpu_dai, substream, dma); return ret; } @@ -560,7 +548,7 @@ static int pxa_ssp_hw_params(struct snd_pcm_substream *substream, u32 sspsp; int width = snd_pcm_format_physical_width(params_format(params)); int ttsa = pxa_ssp_read_reg(ssp, SSTSA) & 0xf; - struct pxa2xx_pcm_dma_params *dma_data; + struct snd_dmaengine_dai_dma_data *dma_data; dma_data = snd_soc_dai_get_dma_data(cpu_dai, substream); diff --git a/sound/soc/pxa/pxa2xx-ac97.c b/sound/soc/pxa/pxa2xx-ac97.c index 1475515..f1059d9 100644 --- a/sound/soc/pxa/pxa2xx-ac97.c +++ b/sound/soc/pxa/pxa2xx-ac97.c @@ -14,15 +14,16 @@ #include #include #include +#include #include #include #include #include +#include #include #include -#include #include #include "pxa2xx-ac97.h" @@ -48,44 +49,44 @@ static struct snd_ac97_bus_ops pxa2xx_ac97_ops = { .reset = pxa2xx_ac97_cold_reset, }; -static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_stereo_out = { - .name = "AC97 PCM Stereo out", - .dev_addr = __PREG(PCDR), - .drcmr = &DRCMR(12), - .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG | - DCMD_BURST32 | DCMD_WIDTH4, +static unsigned long pxa2xx_ac97_pcm_stereo_in_req = 12; +static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_stereo_in = { + .addr = __PREG(PCDR), + .addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, + .maxburst = 32, + .filter_data = &pxa2xx_ac97_pcm_stereo_in_req, }; -static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_stereo_in = { - .name = "AC97 PCM Stereo in", - .dev_addr = __PREG(PCDR), - .drcmr = &DRCMR(11), - .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC | - DCMD_BURST32 | DCMD_WIDTH4, +static unsigned long pxa2xx_ac97_pcm_stereo_out_req = 11; +static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_stereo_out = { + .addr = __PREG(PCDR), + .addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, + .maxburst = 32, + .filter_data = &pxa2xx_ac97_pcm_stereo_out_req, }; -static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_aux_mono_out = { - .name = "AC97 Aux PCM (Slot 5) Mono out", - .dev_addr = __PREG(MODR), - .drcmr = &DRCMR(10), - .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG | - DCMD_BURST16 | DCMD_WIDTH2, +static unsigned long pxa2xx_ac97_pcm_aux_mono_out_req = 10; +static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_aux_mono_out = { + .addr = __PREG(MODR), + .addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES, + .maxburst = 16, + .filter_data = &pxa2xx_ac97_pcm_aux_mono_out_req, }; -static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_aux_mono_in = { - .name = "AC97 Aux PCM (Slot 5) Mono in", - .dev_addr = __PREG(MODR), - .drcmr = &DRCMR(9), - .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC | - DCMD_BURST16 | DCMD_WIDTH2, +static unsigned long pxa2xx_ac97_pcm_aux_mono_in_req = 9; +static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_aux_mono_in = { + .addr = __PREG(MODR), + .addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES, + .maxburst = 16, + .filter_data = &pxa2xx_ac97_pcm_aux_mono_in_req, }; -static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_mic_mono_in = { - .name = "AC97 Mic PCM (Slot 6) Mono in", - .dev_addr = __PREG(MCDR), - .drcmr = &DRCMR(8), - .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC | - DCMD_BURST16 | DCMD_WIDTH2, +static unsigned long pxa2xx_ac97_pcm_aux_mic_mono_req = 8; +static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_mic_mono_in = { + .addr = __PREG(MCDR), + .addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES, + .maxburst = 16, + .filter_data = &pxa2xx_ac97_pcm_aux_mic_mono_req, }; #ifdef CONFIG_PM @@ -119,7 +120,7 @@ static int pxa2xx_ac97_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *cpu_dai) { - struct pxa2xx_pcm_dma_params *dma_data; + struct snd_dmaengine_dai_dma_data *dma_data; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) dma_data = &pxa2xx_ac97_pcm_stereo_out; @@ -135,7 +136,7 @@ static int pxa2xx_ac97_hw_aux_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *cpu_dai) { - struct pxa2xx_pcm_dma_params *dma_data; + struct snd_dmaengine_dai_dma_data *dma_data; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) dma_data = &pxa2xx_ac97_pcm_aux_mono_out; diff --git a/sound/soc/pxa/pxa2xx-i2s.c b/sound/soc/pxa/pxa2xx-i2s.c index f7ca716..d5340a0 100644 --- a/sound/soc/pxa/pxa2xx-i2s.c +++ b/sound/soc/pxa/pxa2xx-i2s.c @@ -23,9 +23,9 @@ #include #include #include +#include #include -#include #include #include "pxa2xx-i2s.h" @@ -82,20 +82,20 @@ static struct pxa_i2s_port pxa_i2s; static struct clk *clk_i2s; static int clk_ena = 0; -static struct pxa2xx_pcm_dma_params pxa2xx_i2s_pcm_stereo_out = { - .name = "I2S PCM Stereo out", - .dev_addr = __PREG(SADR), - .drcmr = &DRCMR(3), - .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG | - DCMD_BURST32 | DCMD_WIDTH4, +static unsigned long pxa2xx_i2s_pcm_stereo_out_req = 3; +static struct snd_dmaengine_dai_dma_data pxa2xx_i2s_pcm_stereo_out = { + .addr = __PREG(SADR), + .addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, + .maxburst = 32, + .filter_data = &pxa2xx_i2s_pcm_stereo_out_req, }; -static struct pxa2xx_pcm_dma_params pxa2xx_i2s_pcm_stereo_in = { - .name = "I2S PCM Stereo in", - .dev_addr = __PREG(SADR), - .drcmr = &DRCMR(2), - .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC | - DCMD_BURST32 | DCMD_WIDTH4, +static unsigned long pxa2xx_i2s_pcm_stereo_in_req = 2; +static struct snd_dmaengine_dai_dma_data pxa2xx_i2s_pcm_stereo_in = { + .addr = __PREG(SADR), + .addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, + .maxburst = 32, + .filter_data = &pxa2xx_i2s_pcm_stereo_in_req, }; static int pxa2xx_i2s_startup(struct snd_pcm_substream *substream, @@ -163,7 +163,7 @@ static int pxa2xx_i2s_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { - struct pxa2xx_pcm_dma_params *dma_data; + struct snd_dmaengine_dai_dma_data *dma_data; BUG_ON(IS_ERR(clk_i2s)); clk_prepare_enable(clk_i2s); diff --git a/sound/soc/pxa/pxa2xx-pcm.c b/sound/soc/pxa/pxa2xx-pcm.c index ecff116..0aa2d69 100644 --- a/sound/soc/pxa/pxa2xx-pcm.c +++ b/sound/soc/pxa/pxa2xx-pcm.c @@ -12,10 +12,12 @@ #include #include +#include #include #include #include +#include #include "../../arm/pxa2xx-pcm.h" @@ -25,7 +27,7 @@ static int pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_runtime *runtime = substream->runtime; struct pxa2xx_runtime_data *prtd = runtime->private_data; struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct pxa2xx_pcm_dma_params *dma; + struct snd_dmaengine_dai_dma_data *dma; int ret; dma = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); @@ -39,7 +41,7 @@ static int pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream, * with different params */ if (prtd->params == NULL) { prtd->params = dma; - ret = pxa_request_dma(prtd->params->name, DMA_PRIO_LOW, + ret = pxa_request_dma("name", DMA_PRIO_LOW, pxa2xx_pcm_dma_irq, substream); if (ret < 0) return ret; @@ -47,7 +49,7 @@ static int pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream, } else if (prtd->params != dma) { pxa_free_dma(prtd->dma_ch); prtd->params = dma; - ret = pxa_request_dma(prtd->params->name, DMA_PRIO_LOW, + ret = pxa_request_dma("name", DMA_PRIO_LOW, pxa2xx_pcm_dma_irq, substream); if (ret < 0) return ret; -- cgit v0.10.2 From a671468d336bc6c482ab04e88e6eaf38532270ee Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Mon, 12 Aug 2013 10:42:40 +0200 Subject: ASoC: pxa: pxa-ssp: set dma filter data from startup hook With the new dmaengine implementation, the filter_data parameter has to be set earlier, from pxa_ssp_startup(). Signed-off-by: Daniel Mack Acked-by: Mark Brown Signed-off-by: Mark Brown diff --git a/sound/soc/pxa/pxa-ssp.c b/sound/soc/pxa/pxa-ssp.c index c0dcc35..a3119a0 100644 --- a/sound/soc/pxa/pxa-ssp.c +++ b/sound/soc/pxa/pxa-ssp.c @@ -84,7 +84,6 @@ static void pxa_ssp_disable(struct ssp_device *ssp) static void pxa_ssp_set_dma_params(struct ssp_device *ssp, int width4, int out, struct snd_dmaengine_dai_dma_data *dma) { - dma->filter_data = out ? &ssp->drcmr_tx : &ssp->drcmr_rx; dma->addr_width = width4 ? DMA_SLAVE_BUSWIDTH_4_BYTES : DMA_SLAVE_BUSWIDTH_2_BYTES; dma->maxburst = 16; @@ -107,6 +106,10 @@ static int pxa_ssp_startup(struct snd_pcm_substream *substream, dma = kzalloc(sizeof(struct snd_dmaengine_dai_dma_data), GFP_KERNEL); if (!dma) return -ENOMEM; + + dma->filter_data = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? + &ssp->drcmr_tx : &ssp->drcmr_rx; + snd_soc_dai_set_dma_data(cpu_dai, substream, dma); return ret; -- cgit v0.10.2 From c529ca4ab935c5a836bddec44cc80614df078a07 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Mon, 12 Aug 2013 10:42:41 +0200 Subject: ASoC: pxa: add DT bindings for pxa2xx-pcm The bindings do not carry any resources, as the module only registers the ASoC platform driver. Signed-off-by: Daniel Mack Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/sound/mrvl,pxa2xx-pcm.txt b/Documentation/devicetree/bindings/sound/mrvl,pxa2xx-pcm.txt new file mode 100644 index 0000000..551fbb8 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/mrvl,pxa2xx-pcm.txt @@ -0,0 +1,15 @@ +DT bindings for ARM PXA2xx PCM platform driver + +This is just a dummy driver that registers the PXA ASoC platform driver. +It does not have any resources assigned. + +Required properties: + + - compatible 'mrvl,pxa-pcm-audio' + +Example: + + pxa_pcm_audio: snd_soc_pxa_audio { + compatible = "mrvl,pxa-pcm-audio"; + }; + diff --git a/sound/soc/pxa/pxa2xx-pcm.c b/sound/soc/pxa/pxa2xx-pcm.c index 0aa2d69..806da27 100644 --- a/sound/soc/pxa/pxa2xx-pcm.c +++ b/sound/soc/pxa/pxa2xx-pcm.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -133,10 +134,18 @@ static int pxa2xx_soc_platform_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_OF +static const struct of_device_id snd_soc_pxa_audio_match[] = { + { .compatible = "mrvl,pxa-pcm-audio" }, + { } +}; +#endif + static struct platform_driver pxa_pcm_driver = { .driver = { - .name = "pxa-pcm-audio", - .owner = THIS_MODULE, + .name = "pxa-pcm-audio", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(snd_soc_pxa_audio_match), }, .probe = pxa2xx_soc_platform_probe, -- cgit v0.10.2 From 5332e1d26fd182444e15e6481029347ab032d7cb Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 13 Aug 2013 18:29:44 +0100 Subject: ASoC: pcm1792a: Remove empty capture DAI stub These intialisations are just what will be done for static data anyway so remove them. Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/pcm1792a.c b/sound/soc/codecs/pcm1792a.c index 72cf835..c57d3a5 100644 --- a/sound/soc/codecs/pcm1792a.c +++ b/sound/soc/codecs/pcm1792a.c @@ -162,10 +162,6 @@ static struct snd_soc_dai_driver pcm1792a_dai = { .channels_max = 2, .rates = PCM1792A_RATES, .formats = PCM1792A_FORMATS, }, - .capture = { - .channels_min = 0, - .channels_max = 0, - }, .ops = &pcm1792a_dai_ops, }; -- cgit v0.10.2 From e7a5cb4223c86df522a97e21742aeef153db4ebb Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 13 Aug 2013 18:30:00 +0100 Subject: ASoC: pcm1792a: Add DAPM support Provide DAPM for the device, ensuring operation with DAPM required by the core and making it easier to hook up external hardware to it. Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/pcm1792a.c b/sound/soc/codecs/pcm1792a.c index c57d3a5..2a8eccf 100644 --- a/sound/soc/codecs/pcm1792a.c +++ b/sound/soc/codecs/pcm1792a.c @@ -154,6 +154,20 @@ static const struct snd_kcontrol_new pcm1792a_controls[] = { pcm1792a_dac_tlv), }; +static const struct snd_soc_dapm_widget pcm1792a_dapm_widgets[] = { +SND_SOC_DAPM_OUTPUT("IOUTL+"), +SND_SOC_DAPM_OUTPUT("IOUTL-"), +SND_SOC_DAPM_OUTPUT("IOUTR+"), +SND_SOC_DAPM_OUTPUT("IOUTR-"), +}; + +static const struct snd_soc_dapm_route pcm1792a_dapm_routes[] = { + { "IOUTL+", NULL, "Playback" }, + { "IOUTL-", NULL, "Playback" }, + { "IOUTR+", NULL, "Playback" }, + { "IOUTR-", NULL, "Playback" }, +}; + static struct snd_soc_dai_driver pcm1792a_dai = { .name = "pcm1792a-hifi", .playback = { @@ -184,6 +198,10 @@ static const struct regmap_config pcm1792a_regmap = { static struct snd_soc_codec_driver soc_codec_dev_pcm1792a = { .controls = pcm1792a_controls, .num_controls = ARRAY_SIZE(pcm1792a_controls), + .dapm_widgets = pcm1792a_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(pcm1792a_dapm_widgets), + .dapm_routes = pcm1792a_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(pcm1792a_dapm_routes), }; static int pcm1792a_spi_probe(struct spi_device *spi) -- cgit v0.10.2 From b9281f99e30f795f28f6ea216289900b6e870d01 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 13 Aug 2013 18:25:41 +0100 Subject: ASoC: pcm1681: Add DAPM support Provide DAPM for the device, ensuring operation with DAPM required by the core and making it easier to hook up external hardware to it. Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/pcm1681.c b/sound/soc/codecs/pcm1681.c index 51b1866..651ce09 100644 --- a/sound/soc/codecs/pcm1681.c +++ b/sound/soc/codecs/pcm1681.c @@ -206,6 +206,28 @@ static const struct snd_soc_dai_ops pcm1681_dai_ops = { .digital_mute = pcm1681_digital_mute, }; +static const struct snd_soc_dapm_widget pcm1681_dapm_widgets[] = { +SND_SOC_DAPM_OUTPUT("VOUT1"), +SND_SOC_DAPM_OUTPUT("VOUT2"), +SND_SOC_DAPM_OUTPUT("VOUT3"), +SND_SOC_DAPM_OUTPUT("VOUT4"), +SND_SOC_DAPM_OUTPUT("VOUT5"), +SND_SOC_DAPM_OUTPUT("VOUT6"), +SND_SOC_DAPM_OUTPUT("VOUT7"), +SND_SOC_DAPM_OUTPUT("VOUT8"), +}; + +static const struct snd_soc_dapm_route pcm1681_dapm_routes[] = { + { "VOUT1", NULL, "Playback" }, + { "VOUT2", NULL, "Playback" }, + { "VOUT3", NULL, "Playback" }, + { "VOUT4", NULL, "Playback" }, + { "VOUT5", NULL, "Playback" }, + { "VOUT6", NULL, "Playback" }, + { "VOUT7", NULL, "Playback" }, + { "VOUT8", NULL, "Playback" }, +}; + static const DECLARE_TLV_DB_SCALE(pcm1681_dac_tlv, -6350, 50, 1); static const struct snd_kcontrol_new pcm1681_controls[] = { @@ -258,6 +280,10 @@ static const struct regmap_config pcm1681_regmap = { static struct snd_soc_codec_driver soc_codec_dev_pcm1681 = { .controls = pcm1681_controls, .num_controls = ARRAY_SIZE(pcm1681_controls), + .dapm_widgets = pcm1681_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(pcm1681_dapm_widgets), + .dapm_routes = pcm1681_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(pcm1681_dapm_routes), }; static const struct i2c_device_id pcm1681_i2c_id[] = { -- cgit v0.10.2 From 903eb3187e1c322d2e6838fd0275f13a072c4b63 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Thu, 15 Aug 2013 16:03:52 +0200 Subject: ALSA: core: allow SND_DMAENGINE_PCM use from modules When users of SND_DMAENGINE_PCM are built as module, the config symbol SND_DMAENGINE_PCM must be tristate, otherwise the linker will fail. Signed-off-by: Daniel Mack Reported-by: Fengguang Wu Signed-off-by: Mark Brown diff --git a/sound/core/Kconfig b/sound/core/Kconfig index 94ce1c4..313f22e 100644 --- a/sound/core/Kconfig +++ b/sound/core/Kconfig @@ -7,7 +7,7 @@ config SND_PCM select SND_TIMER config SND_DMAENGINE_PCM - bool + tristate config SND_HWDEP tristate -- cgit v0.10.2 From a0b5f81e712bddd6b05b77c84d0f3211527e6f2f Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 16 Aug 2013 01:37:12 +0100 Subject: ASoC: samsung: Fix DTS breakage from missing dependencies Revert "ARM: dts: Change i2s compatible string on exynos5250" (c7f7e6) and "ARM: dts: exynos5250: move common i2s properties to exynos5 dtsi" 618728) since they reference DMA controller nodes that don't exist causing DT build issues. Reported-by: Olof Johansson Signed-off-by: Mark Brown diff --git a/arch/arm/boot/dts/exynos5.dtsi b/arch/arm/boot/dts/exynos5.dtsi index 309894e..f65e124 100644 --- a/arch/arm/boot/dts/exynos5.dtsi +++ b/arch/arm/boot/dts/exynos5.dtsi @@ -108,25 +108,4 @@ interrupts = <0 42 0>; status = "disabled"; }; - - i2s0: i2s@03830000 { - reg = <0x03830000 0x100>; - samsung,idma-addr = <0x03000000>; - }; - - i2s1: i2s@12D60000 { - compatible = "samsung,s3c6410-i2s"; - reg = <0x12D60000 0x100>; - dmas = <&pdma1 12 - &pdma1 11>; - dma-names = "tx", "rx"; - }; - - i2s2: i2s@12D70000 { - compatible = "samsung,s3c6410-i2s"; - reg = <0x12D70000 0x100>; - dmas = <&pdma0 12 - &pdma0 11>; - dma-names = "tx", "rx"; - }; }; diff --git a/arch/arm/boot/dts/exynos5250.dtsi b/arch/arm/boot/dts/exynos5250.dtsi index ac5f5a1..ef57277 100644 --- a/arch/arm/boot/dts/exynos5250.dtsi +++ b/arch/arm/boot/dts/exynos5250.dtsi @@ -405,7 +405,8 @@ }; i2s0: i2s@03830000 { - compatible = "samsung,s5pv210-i2s"; + compatible = "samsung,i2s-v5"; + reg = <0x03830000 0x100>; dmas = <&pdma0 10 &pdma0 9 &pdma0 8>; @@ -414,11 +415,20 @@ <&clock_audss EXYNOS_I2S_BUS>, <&clock_audss EXYNOS_SCLK_I2S>; clock-names = "iis", "i2s_opclk0", "i2s_opclk1"; + samsung,supports-6ch; + samsung,supports-rstclr; + samsung,supports-secdai; + samsung,idma-addr = <0x03000000>; pinctrl-names = "default"; pinctrl-0 = <&i2s0_bus>; }; i2s1: i2s@12D60000 { + compatible = "samsung,i2s-v5"; + reg = <0x12D60000 0x100>; + dmas = <&pdma1 12 + &pdma1 11>; + dma-names = "tx", "rx"; clocks = <&clock 307>, <&clock 157>; clock-names = "iis", "i2s_opclk0"; pinctrl-names = "default"; @@ -426,6 +436,11 @@ }; i2s2: i2s@12D70000 { + compatible = "samsung,i2s-v5"; + reg = <0x12D70000 0x100>; + dmas = <&pdma0 12 + &pdma0 11>; + dma-names = "tx", "rx"; clocks = <&clock 308>, <&clock 158>; clock-names = "iis", "i2s_opclk0"; pinctrl-names = "default"; -- cgit v0.10.2 From 74b77b1510c90787d4e2e3a8412b85b235590ba5 Mon Sep 17 00:00:00 2001 From: Markus Pargmann Date: Fri, 16 Aug 2013 11:45:33 +0200 Subject: ASoC: imx-audmux: Move definitions to dt-bindings Move imx-audmux macro definitions to include/dt-bindings, so they can be used for devicetree. Signed-off-by: Markus Pargmann Signed-off-by: Mark Brown diff --git a/include/dt-bindings/sound/fsl-imx-audmux.h b/include/dt-bindings/sound/fsl-imx-audmux.h new file mode 100644 index 0000000..50b09e9 --- /dev/null +++ b/include/dt-bindings/sound/fsl-imx-audmux.h @@ -0,0 +1,56 @@ +#ifndef __DT_FSL_IMX_AUDMUX_H +#define __DT_FSL_IMX_AUDMUX_H + +#define MX27_AUDMUX_HPCR1_SSI0 0 +#define MX27_AUDMUX_HPCR2_SSI1 1 +#define MX27_AUDMUX_HPCR3_SSI_PINS_4 2 +#define MX27_AUDMUX_PPCR1_SSI_PINS_1 3 +#define MX27_AUDMUX_PPCR2_SSI_PINS_2 4 +#define MX27_AUDMUX_PPCR3_SSI_PINS_3 5 + +#define MX31_AUDMUX_PORT1_SSI0 0 +#define MX31_AUDMUX_PORT2_SSI1 1 +#define MX31_AUDMUX_PORT3_SSI_PINS_3 2 +#define MX31_AUDMUX_PORT4_SSI_PINS_4 3 +#define MX31_AUDMUX_PORT5_SSI_PINS_5 4 +#define MX31_AUDMUX_PORT6_SSI_PINS_6 5 +#define MX31_AUDMUX_PORT7_SSI_PINS_7 6 + +#define MX51_AUDMUX_PORT1_SSI0 0 +#define MX51_AUDMUX_PORT2_SSI1 1 +#define MX51_AUDMUX_PORT3 2 +#define MX51_AUDMUX_PORT4 3 +#define MX51_AUDMUX_PORT5 4 +#define MX51_AUDMUX_PORT6 5 +#define MX51_AUDMUX_PORT7 6 + +/* Register definitions for the i.MX21/27 Digital Audio Multiplexer */ +#define IMX_AUDMUX_V1_PCR_INMMASK(x) ((x) & 0xff) +#define IMX_AUDMUX_V1_PCR_INMEN (1 << 8) +#define IMX_AUDMUX_V1_PCR_TXRXEN (1 << 10) +#define IMX_AUDMUX_V1_PCR_SYN (1 << 12) +#define IMX_AUDMUX_V1_PCR_RXDSEL(x) (((x) & 0x7) << 13) +#define IMX_AUDMUX_V1_PCR_RFCSEL(x) (((x) & 0xf) << 20) +#define IMX_AUDMUX_V1_PCR_RCLKDIR (1 << 24) +#define IMX_AUDMUX_V1_PCR_RFSDIR (1 << 25) +#define IMX_AUDMUX_V1_PCR_TFCSEL(x) (((x) & 0xf) << 26) +#define IMX_AUDMUX_V1_PCR_TCLKDIR (1 << 30) +#define IMX_AUDMUX_V1_PCR_TFSDIR (1 << 31) + +/* Register definitions for the i.MX25/31/35/51 Digital Audio Multiplexer */ +#define IMX_AUDMUX_V2_PTCR_TFSDIR (1 << 31) +#define IMX_AUDMUX_V2_PTCR_TFSEL(x) (((x) & 0xf) << 27) +#define IMX_AUDMUX_V2_PTCR_TCLKDIR (1 << 26) +#define IMX_AUDMUX_V2_PTCR_TCSEL(x) (((x) & 0xf) << 22) +#define IMX_AUDMUX_V2_PTCR_RFSDIR (1 << 21) +#define IMX_AUDMUX_V2_PTCR_RFSEL(x) (((x) & 0xf) << 17) +#define IMX_AUDMUX_V2_PTCR_RCLKDIR (1 << 16) +#define IMX_AUDMUX_V2_PTCR_RCSEL(x) (((x) & 0xf) << 12) +#define IMX_AUDMUX_V2_PTCR_SYN (1 << 11) + +#define IMX_AUDMUX_V2_PDCR_RXDSEL(x) (((x) & 0x7) << 13) +#define IMX_AUDMUX_V2_PDCR_TXRXEN (1 << 12) +#define IMX_AUDMUX_V2_PDCR_MODE(x) (((x) & 0x3) << 8) +#define IMX_AUDMUX_V2_PDCR_INMMASK(x) ((x) & 0xff) + +#endif /* __DT_FSL_IMX_AUDMUX_H */ diff --git a/sound/soc/fsl/imx-audmux.h b/sound/soc/fsl/imx-audmux.h index b8ff44b..38a4209 100644 --- a/sound/soc/fsl/imx-audmux.h +++ b/sound/soc/fsl/imx-audmux.h @@ -1,57 +1,7 @@ #ifndef __IMX_AUDMUX_H #define __IMX_AUDMUX_H -#define MX27_AUDMUX_HPCR1_SSI0 0 -#define MX27_AUDMUX_HPCR2_SSI1 1 -#define MX27_AUDMUX_HPCR3_SSI_PINS_4 2 -#define MX27_AUDMUX_PPCR1_SSI_PINS_1 3 -#define MX27_AUDMUX_PPCR2_SSI_PINS_2 4 -#define MX27_AUDMUX_PPCR3_SSI_PINS_3 5 - -#define MX31_AUDMUX_PORT1_SSI0 0 -#define MX31_AUDMUX_PORT2_SSI1 1 -#define MX31_AUDMUX_PORT3_SSI_PINS_3 2 -#define MX31_AUDMUX_PORT4_SSI_PINS_4 3 -#define MX31_AUDMUX_PORT5_SSI_PINS_5 4 -#define MX31_AUDMUX_PORT6_SSI_PINS_6 5 -#define MX31_AUDMUX_PORT7_SSI_PINS_7 6 - -#define MX51_AUDMUX_PORT1_SSI0 0 -#define MX51_AUDMUX_PORT2_SSI1 1 -#define MX51_AUDMUX_PORT3 2 -#define MX51_AUDMUX_PORT4 3 -#define MX51_AUDMUX_PORT5 4 -#define MX51_AUDMUX_PORT6 5 -#define MX51_AUDMUX_PORT7 6 - -/* Register definitions for the i.MX21/27 Digital Audio Multiplexer */ -#define IMX_AUDMUX_V1_PCR_INMMASK(x) ((x) & 0xff) -#define IMX_AUDMUX_V1_PCR_INMEN (1 << 8) -#define IMX_AUDMUX_V1_PCR_TXRXEN (1 << 10) -#define IMX_AUDMUX_V1_PCR_SYN (1 << 12) -#define IMX_AUDMUX_V1_PCR_RXDSEL(x) (((x) & 0x7) << 13) -#define IMX_AUDMUX_V1_PCR_RFCSEL(x) (((x) & 0xf) << 20) -#define IMX_AUDMUX_V1_PCR_RCLKDIR (1 << 24) -#define IMX_AUDMUX_V1_PCR_RFSDIR (1 << 25) -#define IMX_AUDMUX_V1_PCR_TFCSEL(x) (((x) & 0xf) << 26) -#define IMX_AUDMUX_V1_PCR_TCLKDIR (1 << 30) -#define IMX_AUDMUX_V1_PCR_TFSDIR (1 << 31) - -/* Register definitions for the i.MX25/31/35/51 Digital Audio Multiplexer */ -#define IMX_AUDMUX_V2_PTCR_TFSDIR (1 << 31) -#define IMX_AUDMUX_V2_PTCR_TFSEL(x) (((x) & 0xf) << 27) -#define IMX_AUDMUX_V2_PTCR_TCLKDIR (1 << 26) -#define IMX_AUDMUX_V2_PTCR_TCSEL(x) (((x) & 0xf) << 22) -#define IMX_AUDMUX_V2_PTCR_RFSDIR (1 << 21) -#define IMX_AUDMUX_V2_PTCR_RFSEL(x) (((x) & 0xf) << 17) -#define IMX_AUDMUX_V2_PTCR_RCLKDIR (1 << 16) -#define IMX_AUDMUX_V2_PTCR_RCSEL(x) (((x) & 0xf) << 12) -#define IMX_AUDMUX_V2_PTCR_SYN (1 << 11) - -#define IMX_AUDMUX_V2_PDCR_RXDSEL(x) (((x) & 0x7) << 13) -#define IMX_AUDMUX_V2_PDCR_TXRXEN (1 << 12) -#define IMX_AUDMUX_V2_PDCR_MODE(x) (((x) & 0x3) << 8) -#define IMX_AUDMUX_V2_PDCR_INMMASK(x) ((x) & 0xff) +#include int imx_audmux_v1_configure_port(unsigned int port, unsigned int pcr); -- cgit v0.10.2 From 6418365688844125441d8df19fa65ebe2a49af53 Mon Sep 17 00:00:00 2001 From: Padmavathi Venna Date: Fri, 16 Aug 2013 09:56:18 +0530 Subject: ARM: dts: Change i2s compatible string on exynos5250 This patch removes quirks from i2s node and change the i2s compatible names. Signed-off-by: Padmavathi Venna Signed-off-by: Mark Brown diff --git a/arch/arm/boot/dts/exynos5250.dtsi b/arch/arm/boot/dts/exynos5250.dtsi index ef57277..376090f 100644 --- a/arch/arm/boot/dts/exynos5250.dtsi +++ b/arch/arm/boot/dts/exynos5250.dtsi @@ -405,7 +405,7 @@ }; i2s0: i2s@03830000 { - compatible = "samsung,i2s-v5"; + compatible = "samsung,s5pv210-i2s"; reg = <0x03830000 0x100>; dmas = <&pdma0 10 &pdma0 9 @@ -415,16 +415,13 @@ <&clock_audss EXYNOS_I2S_BUS>, <&clock_audss EXYNOS_SCLK_I2S>; clock-names = "iis", "i2s_opclk0", "i2s_opclk1"; - samsung,supports-6ch; - samsung,supports-rstclr; - samsung,supports-secdai; samsung,idma-addr = <0x03000000>; pinctrl-names = "default"; pinctrl-0 = <&i2s0_bus>; }; i2s1: i2s@12D60000 { - compatible = "samsung,i2s-v5"; + compatible = "samsung,s3c6410-i2s"; reg = <0x12D60000 0x100>; dmas = <&pdma1 12 &pdma1 11>; @@ -436,7 +433,7 @@ }; i2s2: i2s@12D70000 { - compatible = "samsung,i2s-v5"; + compatible = "samsung,s3c6410-i2s"; reg = <0x12D70000 0x100>; dmas = <&pdma0 12 &pdma0 11>; -- cgit v0.10.2 From 12201398fc9ad25f5c6568527e70c9a4bcf5fcee Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 16 Aug 2013 12:13:50 +0100 Subject: ASoC: tlv320aic26: Remove direct use of internal I/O functions Use the core to do I/O rather than directly calling the driver operations in order to support further refactoring. Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/tlv320aic26.c b/sound/soc/codecs/tlv320aic26.c index b192cd4..a4f9360 100644 --- a/sound/soc/codecs/tlv320aic26.c +++ b/sound/soc/codecs/tlv320aic26.c @@ -174,9 +174,9 @@ static int aic26_hw_params(struct snd_pcm_substream *substream, dev_dbg(&aic26->spi->dev, "Setting PLLM to %d.%04d\n", jval, dval); qval = 0; reg = 0x8000 | qval << 11 | pval << 8 | jval << 2; - aic26_reg_write(codec, AIC26_REG_PLL_PROG1, reg); + snd_soc_write(codec, AIC26_REG_PLL_PROG1, reg); reg = dval << 2; - aic26_reg_write(codec, AIC26_REG_PLL_PROG2, reg); + snd_soc_write(codec, AIC26_REG_PLL_PROG2, reg); /* Audio Control 3 (master mode, fsref rate) */ reg = aic26_reg_read_cache(codec, AIC26_REG_AUDIO_CTRL3); @@ -185,13 +185,13 @@ static int aic26_hw_params(struct snd_pcm_substream *substream, reg |= 0x0800; if (fsref == 48000) reg |= 0x2000; - aic26_reg_write(codec, AIC26_REG_AUDIO_CTRL3, reg); + snd_soc_write(codec, AIC26_REG_AUDIO_CTRL3, reg); /* Audio Control 1 (FSref divisor) */ reg = aic26_reg_read_cache(codec, AIC26_REG_AUDIO_CTRL1); reg &= ~0x0fff; reg |= wlen | aic26->datfm | (divisor << 3) | divisor; - aic26_reg_write(codec, AIC26_REG_AUDIO_CTRL1, reg); + snd_soc_write(codec, AIC26_REG_AUDIO_CTRL1, reg); return 0; } @@ -212,7 +212,7 @@ static int aic26_mute(struct snd_soc_dai *dai, int mute) reg |= 0x8080; else reg &= ~0x8080; - aic26_reg_write(codec, AIC26_REG_DAC_GAIN, reg); + snd_soc_write(codec, AIC26_REG_DAC_GAIN, reg); return 0; } @@ -348,7 +348,7 @@ static ssize_t aic26_keyclick_set(struct device *dev, val = aic26_reg_read_cache(aic26->codec, AIC26_REG_AUDIO_CTRL2); val |= 0x8000; - aic26_reg_write(aic26->codec, AIC26_REG_AUDIO_CTRL2, val); + snd_soc_write(aic26->codec, AIC26_REG_AUDIO_CTRL2, val); return count; } @@ -368,20 +368,20 @@ static int aic26_probe(struct snd_soc_codec *codec) dev_info(codec->dev, "Probing AIC26 SoC CODEC driver\n"); /* Reset the codec to power on defaults */ - aic26_reg_write(codec, AIC26_REG_RESET, 0xBB00); + snd_soc_write(codec, AIC26_REG_RESET, 0xBB00); /* Power up CODEC */ - aic26_reg_write(codec, AIC26_REG_POWER_CTRL, 0); + snd_soc_write(codec, AIC26_REG_POWER_CTRL, 0); /* Audio Control 3 (master mode, fsref rate) */ - reg = aic26_reg_read(codec, AIC26_REG_AUDIO_CTRL3); + reg = snd_soc_read(codec, AIC26_REG_AUDIO_CTRL3); reg &= ~0xf800; reg |= 0x0800; /* set master mode */ - aic26_reg_write(codec, AIC26_REG_AUDIO_CTRL3, reg); + snd_soc_write(codec, AIC26_REG_AUDIO_CTRL3, reg); /* Fill register cache */ for (i = 0; i < codec->driver->reg_cache_size; i++) - aic26_reg_read(codec, i); + snd_soc_read(codec, i); /* Register the sysfs files for debugging */ /* Create SysFS files */ -- cgit v0.10.2 From c21bb9b1b7de87ee33c8ebf94a155be2aa551849 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 16 Aug 2013 12:14:42 +0100 Subject: ASoC: tlv320aic26: Remove noisy print Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/tlv320aic26.c b/sound/soc/codecs/tlv320aic26.c index a4f9360..93cf692 100644 --- a/sound/soc/codecs/tlv320aic26.c +++ b/sound/soc/codecs/tlv320aic26.c @@ -365,8 +365,6 @@ static int aic26_probe(struct snd_soc_codec *codec) aic26->codec = codec; - dev_info(codec->dev, "Probing AIC26 SoC CODEC driver\n"); - /* Reset the codec to power on defaults */ snd_soc_write(codec, AIC26_REG_RESET, 0xBB00); -- cgit v0.10.2 From 4a11bc2fdd7f526c70e013366171d66f27656203 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 16 Aug 2013 12:24:47 +0100 Subject: ASoC: tlv320aic26: Add basic DAPM support Provide external widgets for the CODEC to ensure the device continues to function with DAPM mandatory and to make it easier to hook the device up to other components. Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/tlv320aic26.c b/sound/soc/codecs/tlv320aic26.c index 93cf692..7b8f3d9 100644 --- a/sound/soc/codecs/tlv320aic26.c +++ b/sound/soc/codecs/tlv320aic26.c @@ -119,6 +119,22 @@ static int aic26_reg_write(struct snd_soc_codec *codec, unsigned int reg, return 0; } +static const struct snd_soc_dapm_widget tlv320aic26_dapm_widgets[] = { +SND_SOC_DAPM_INPUT("MICIN"), +SND_SOC_DAPM_INPUT("AUX"), + +SND_SOC_DAPM_OUTPUT("HPL"), +SND_SOC_DAPM_OUTPUT("HPR"), +}; + +static const struct snd_soc_dapm_route tlv320aic26_dapm_routes[] = { + { "Capture", NULL, "MICIN" }, + { "Capture", NULL, "AUX" }, + + { "HPL", NULL, "Playback" }, + { "HPR", NULL, "Playback" }, +}; + /* --------------------------------------------------------------------- * Digital Audio Interface Operations */ @@ -402,6 +418,10 @@ static struct snd_soc_codec_driver aic26_soc_codec_dev = { .write = aic26_reg_write, .reg_cache_size = AIC26_NUM_REGS, .reg_word_size = sizeof(u16), + .dapm_widgets = tlv320aic26_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(tlv320aic26_dapm_widgets), + .dapm_routes = tlv320aic26_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(tlv320aic26_dapm_routes), }; /* --------------------------------------------------------------------- -- cgit v0.10.2 From a4a9e082671d2f1e9d3b49a3692313087b036aff Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Fri, 16 Aug 2013 14:09:01 +0200 Subject: ALSA: hda - Fix internal mic boost on three Thinkpad machines The internal mic boost is so noisy on boosts 2 and 3 so they are unusable in practice. BugLink: https://bugs.launchpad.net/bugs/1213055 Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 6ac4810..333d1a6 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -3694,6 +3694,9 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x21fb, "Thinkpad T430s", ALC269_FIXUP_LENOVO_DOCK), SND_PCI_QUIRK(0x17aa, 0x2208, "Thinkpad T431s", ALC269_FIXUP_LENOVO_DOCK), SND_PCI_QUIRK(0x17aa, 0x2203, "Thinkpad X230 Tablet", ALC269_FIXUP_LENOVO_DOCK), + SND_PCI_QUIRK(0x17aa, 0x2214, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), + SND_PCI_QUIRK(0x17aa, 0x2215, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), + SND_PCI_QUIRK(0x17aa, 0x5013, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x17aa, 0x3bf8, "Quanta FL1", ALC269_FIXUP_PCM_44K), SND_PCI_QUIRK(0x17aa, 0x9e54, "LENOVO NB", ALC269_FIXUP_LENOVO_EAPD), SND_PCI_QUIRK(0x1b7d, 0xa831, "Ordissimo EVE2 ", ALC269VB_FIXUP_ORDISSIMO_EVE2), /* Also known as Malata PC-B1303 */ -- cgit v0.10.2 From aaedfb4761697e6fe24a7443e8d288636ccc69ef Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Fri, 16 Aug 2013 14:09:02 +0200 Subject: ALSA: hda - Fix the order of a quirk table (janitorial) This just cleans up the table, no functional changes. Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 333d1a6..96dcb68 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -3622,6 +3622,11 @@ static const struct hda_fixup alc269_fixups[] = { static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1025, 0x029b, "Acer 1810TZ", ALC269_FIXUP_INV_DMIC), SND_PCI_QUIRK(0x1025, 0x0349, "Acer AOD260", ALC269_FIXUP_INV_DMIC), + SND_PCI_QUIRK(0x1025, 0x047c, "Acer AC700", ALC269_FIXUP_ACER_AC700), + SND_PCI_QUIRK(0x1025, 0x0740, "Acer AO725", ALC271_FIXUP_HP_GATE_MIC_JACK), + SND_PCI_QUIRK(0x1025, 0x0742, "Acer AO756", ALC271_FIXUP_HP_GATE_MIC_JACK), + SND_PCI_QUIRK_VENDOR(0x1025, "Acer Aspire", ALC271_FIXUP_DMIC), + SND_PCI_QUIRK(0x1028, 0x0470, "Dell M101z", ALC269_FIXUP_DELL_M101Z), SND_PCI_QUIRK(0x1028, 0x05bd, "Dell", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x05be, "Dell", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x05c4, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE), @@ -3677,11 +3682,6 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x104d, 0x907b, "Sony VAIO", ALC275_FIXUP_SONY_HWEQ), SND_PCI_QUIRK(0x104d, 0x9084, "Sony VAIO", ALC275_FIXUP_SONY_HWEQ), SND_PCI_QUIRK_VENDOR(0x104d, "Sony VAIO", ALC269_FIXUP_SONY_VAIO), - SND_PCI_QUIRK(0x1028, 0x0470, "Dell M101z", ALC269_FIXUP_DELL_M101Z), - SND_PCI_QUIRK(0x1025, 0x047c, "Acer AC700", ALC269_FIXUP_ACER_AC700), - SND_PCI_QUIRK(0x1025, 0x0740, "Acer AO725", ALC271_FIXUP_HP_GATE_MIC_JACK), - SND_PCI_QUIRK(0x1025, 0x0742, "Acer AO756", ALC271_FIXUP_HP_GATE_MIC_JACK), - SND_PCI_QUIRK_VENDOR(0x1025, "Acer Aspire", ALC271_FIXUP_DMIC), SND_PCI_QUIRK(0x10cf, 0x1475, "Lifebook", ALC269_FIXUP_LIFEBOOK), SND_PCI_QUIRK(0x17aa, 0x20f2, "Thinkpad SL410/510", ALC269_FIXUP_SKU_IGNORE), SND_PCI_QUIRK(0x17aa, 0x215e, "Thinkpad L512", ALC269_FIXUP_SKU_IGNORE), @@ -3692,8 +3692,8 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x21fa, "Thinkpad X230", ALC269_FIXUP_LENOVO_DOCK), SND_PCI_QUIRK(0x17aa, 0x21f3, "Thinkpad T430", ALC269_FIXUP_LENOVO_DOCK), SND_PCI_QUIRK(0x17aa, 0x21fb, "Thinkpad T430s", ALC269_FIXUP_LENOVO_DOCK), - SND_PCI_QUIRK(0x17aa, 0x2208, "Thinkpad T431s", ALC269_FIXUP_LENOVO_DOCK), SND_PCI_QUIRK(0x17aa, 0x2203, "Thinkpad X230 Tablet", ALC269_FIXUP_LENOVO_DOCK), + SND_PCI_QUIRK(0x17aa, 0x2208, "Thinkpad T431s", ALC269_FIXUP_LENOVO_DOCK), SND_PCI_QUIRK(0x17aa, 0x2214, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x17aa, 0x2215, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x17aa, 0x5013, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), -- cgit v0.10.2 From ac0b82b17894120b21937d0031fd0080b3ee2d83 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 16 Aug 2013 11:54:51 +0100 Subject: ASoC: si476x: Add DAPM support This ensures the driver continues to work with DAPM mandatory and makes it easier to connect the device up to other components in the subsystem. Signed-off-by: Mark Brown Acked-by: Andrey Smirnov diff --git a/sound/soc/codecs/si476x.c b/sound/soc/codecs/si476x.c index 73e205c..38f3b10 100644 --- a/sound/soc/codecs/si476x.c +++ b/sound/soc/codecs/si476x.c @@ -102,6 +102,16 @@ static int si476x_codec_write(struct snd_soc_codec *codec, return err; } +static const struct snd_soc_dapm_widget si476x_dapm_widgets[] = { +SND_SOC_DAPM_OUTPUT("LOUT"), +SND_SOC_DAPM_OUTPUT("ROUT"), +}; + +static const struct snd_soc_dapm_route si476x_dapm_routes[] = { + { "Capture", NULL, "LOUT" }, + { "Capture", NULL, "ROUT" }, +}; + static int si476x_codec_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) { @@ -260,6 +270,10 @@ static struct snd_soc_codec_driver soc_codec_dev_si476x = { .probe = si476x_codec_probe, .read = si476x_codec_read, .write = si476x_codec_write, + .dapm_widgets = si476x_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(si476x_dapm_widgets), + .dapm_routes = si476x_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(si476x_dapm_routes), }; static int si476x_platform_probe(struct platform_device *pdev) -- cgit v0.10.2 From 70a39b930f286d9d2b68391291dc02b85a2128e3 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Sat, 17 Aug 2013 16:38:12 -0300 Subject: ASoC: fsl: Drop SND_SOC_FSL_UTILS from i.mx machine code SND_SOC_FSL_UTILS is only used by PowerPC machines, so let's drop it in the i.mx case. Signed-off-by: Fabio Estevam Acked-by: Shawn Guo Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index e15f771..3a4808d 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig @@ -175,7 +175,6 @@ config SND_SOC_IMX_WM8962 select SND_SOC_IMX_PCM_DMA select SND_SOC_IMX_AUDMUX select SND_SOC_FSL_SSI - select SND_SOC_FSL_UTILS help Say Y if you want to add support for SoC audio on an i.MX board with a wm8962 codec. @@ -187,7 +186,6 @@ config SND_SOC_IMX_SGTL5000 select SND_SOC_IMX_PCM_DMA select SND_SOC_IMX_AUDMUX select SND_SOC_FSL_SSI - select SND_SOC_FSL_UTILS help Say Y if you want to add support for SoC audio on an i.MX board with a sgtl5000 codec. -- cgit v0.10.2 From 85fa532b6ef920b32598df86b194571a7059a77c Mon Sep 17 00:00:00 2001 From: Mike Dyer Date: Fri, 16 Aug 2013 18:36:28 +0100 Subject: ASoC: wm8960: Fix PLL register writes Bit 9 of PLL2,3 and 4 is reserved as '0'. The 24bit fractional part should be split across each register in 8bit chunks. Signed-off-by: Mike Dyer Signed-off-by: Mark Brown Cc: stable@vger.kernel.org diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c index 0a4ffdd..5e5af89 100644 --- a/sound/soc/codecs/wm8960.c +++ b/sound/soc/codecs/wm8960.c @@ -857,9 +857,9 @@ static int wm8960_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, if (pll_div.k) { reg |= 0x20; - snd_soc_write(codec, WM8960_PLL2, (pll_div.k >> 18) & 0x3f); - snd_soc_write(codec, WM8960_PLL3, (pll_div.k >> 9) & 0x1ff); - snd_soc_write(codec, WM8960_PLL4, pll_div.k & 0x1ff); + snd_soc_write(codec, WM8960_PLL2, (pll_div.k >> 16) & 0xff); + snd_soc_write(codec, WM8960_PLL3, (pll_div.k >> 8) & 0xff); + snd_soc_write(codec, WM8960_PLL4, pll_div.k & 0xff); } snd_soc_write(codec, WM8960_PLL1, reg); -- cgit v0.10.2 From ea67afc3fdbe9196d76ee79503a3809a54300b5a Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 15 Aug 2013 11:53:28 +0100 Subject: ASoC: pcm3008: Use gpio_set_value_cansleep() We don't set the GPIO values from atomic context so support GPIOs that can't be controlled from atomic context. Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/pcm3008.c b/sound/soc/codecs/pcm3008.c index b883f99..8b9b378 100644 --- a/sound/soc/codecs/pcm3008.c +++ b/sound/soc/codecs/pcm3008.c @@ -54,8 +54,8 @@ static int pcm3008_soc_suspend(struct snd_soc_codec *codec) { struct pcm3008_setup_data *setup = codec->dev->platform_data; - gpio_set_value(setup->pdad_pin, 0); - gpio_set_value(setup->pdda_pin, 0); + gpio_set_value_cansleep(setup->pdad_pin, 0); + gpio_set_value_cansleep(setup->pdda_pin, 0); return 0; } @@ -64,8 +64,8 @@ static int pcm3008_soc_resume(struct snd_soc_codec *codec) { struct pcm3008_setup_data *setup = codec->dev->platform_data; - gpio_set_value(setup->pdad_pin, 1); - gpio_set_value(setup->pdda_pin, 1); + gpio_set_value_cansleep(setup->pdad_pin, 1); + gpio_set_value_cansleep(setup->pdda_pin, 1); return 0; } -- cgit v0.10.2 From faaf36f21642a140715b7d6cf897ab4f4f5a924d Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 15 Aug 2013 12:01:40 +0100 Subject: ASoC: pcm3008: Add DAPM support Make it possible to connect external devices to the CODEC and ensure continued operation with non-DAPM support removed from the core. Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/pcm3008.c b/sound/soc/codecs/pcm3008.c index 8b9b378..19f5028 100644 --- a/sound/soc/codecs/pcm3008.c +++ b/sound/soc/codecs/pcm3008.c @@ -28,6 +28,22 @@ #include "pcm3008.h" +static const struct snd_soc_dapm_widget pcm3008_dapm_widgets[] = { +SND_SOC_DAPM_INPUT("VINL"), +SND_SOC_DAPM_INPUT("VINR"), + +SND_SOC_DAPM_OUTPUT("VOUTL"), +SND_SOC_DAPM_OUTPUT("VOUTR"), +}; + +static const struct snd_soc_dapm_route pcm3008_dapm_routes[] = { + { "PCM3008 Capture", NULL, "VINL" }, + { "PCM3008 Capture", NULL, "VINR" }, + + { "VOUTL", NULL, "PCM3008 Playback" }, + { "VOUTR", NULL, "PCM3008 Playback" }, +}; + #define PCM3008_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ SNDRV_PCM_RATE_48000) @@ -77,6 +93,10 @@ static int pcm3008_soc_resume(struct snd_soc_codec *codec) static struct snd_soc_codec_driver soc_codec_dev_pcm3008 = { .suspend = pcm3008_soc_suspend, .resume = pcm3008_soc_resume, + .dapm_widgets = pcm3008_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(pcm3008_dapm_widgets), + .dapm_routes = pcm3008_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(pcm3008_dapm_routes), }; static int pcm3008_codec_probe(struct platform_device *pdev) -- cgit v0.10.2 From 4fc932c6d8c0d2715bb7f2a2f657230ea360af87 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 15 Aug 2013 12:04:28 +0100 Subject: ASoC: pcm3008: Manage DAC and ADC power with DAPM Rather than leaving the DAC and ADC active whenever the system is running manage their power with DAPM. Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/pcm3008.c b/sound/soc/codecs/pcm3008.c index 19f5028..b6618c4 100644 --- a/sound/soc/codecs/pcm3008.c +++ b/sound/soc/codecs/pcm3008.c @@ -28,20 +28,53 @@ #include "pcm3008.h" +static int pcm3008_dac_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = w->codec; + struct pcm3008_setup_data *setup = codec->dev->platform_data; + + gpio_set_value_cansleep(setup->pdda_pin, + SND_SOC_DAPM_EVENT_ON(event)); + + return 0; +} + +static int pcm3008_adc_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = w->codec; + struct pcm3008_setup_data *setup = codec->dev->platform_data; + + gpio_set_value_cansleep(setup->pdad_pin, + SND_SOC_DAPM_EVENT_ON(event)); + + return 0; +} + static const struct snd_soc_dapm_widget pcm3008_dapm_widgets[] = { SND_SOC_DAPM_INPUT("VINL"), SND_SOC_DAPM_INPUT("VINR"), +SND_SOC_DAPM_DAC_E("DAC", NULL, SND_SOC_NOPM, 0, 0, pcm3008_dac_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_ADC_E("ADC", NULL, SND_SOC_NOPM, 0, 0, pcm3008_adc_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_OUTPUT("VOUTL"), SND_SOC_DAPM_OUTPUT("VOUTR"), }; static const struct snd_soc_dapm_route pcm3008_dapm_routes[] = { - { "PCM3008 Capture", NULL, "VINL" }, - { "PCM3008 Capture", NULL, "VINR" }, + { "PCM3008 Capture", NULL, "ADC" }, + { "ADC", NULL, "VINL" }, + { "ADC", NULL, "VINR" }, - { "VOUTL", NULL, "PCM3008 Playback" }, - { "VOUTR", NULL, "PCM3008 Playback" }, + { "DAC", NULL, "PCM3008 Playback" }, + { "VOUTL", NULL, "DAC" }, + { "VOUTR", NULL, "DAC" }, }; #define PCM3008_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ @@ -65,34 +98,7 @@ static struct snd_soc_dai_driver pcm3008_dai = { }, }; -#ifdef CONFIG_PM -static int pcm3008_soc_suspend(struct snd_soc_codec *codec) -{ - struct pcm3008_setup_data *setup = codec->dev->platform_data; - - gpio_set_value_cansleep(setup->pdad_pin, 0); - gpio_set_value_cansleep(setup->pdda_pin, 0); - - return 0; -} - -static int pcm3008_soc_resume(struct snd_soc_codec *codec) -{ - struct pcm3008_setup_data *setup = codec->dev->platform_data; - - gpio_set_value_cansleep(setup->pdad_pin, 1); - gpio_set_value_cansleep(setup->pdda_pin, 1); - - return 0; -} -#else -#define pcm3008_soc_suspend NULL -#define pcm3008_soc_resume NULL -#endif - static struct snd_soc_codec_driver soc_codec_dev_pcm3008 = { - .suspend = pcm3008_soc_suspend, - .resume = pcm3008_soc_resume, .dapm_widgets = pcm3008_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(pcm3008_dapm_widgets), .dapm_routes = pcm3008_dapm_routes, @@ -128,13 +134,13 @@ static int pcm3008_codec_probe(struct platform_device *pdev) /* Configure PDAD GPIO. */ ret = devm_gpio_request_one(&pdev->dev, setup->pdad_pin, - GPIOF_OUT_INIT_HIGH, "codec_pdad"); + GPIOF_OUT_INIT_LOW, "codec_pdad"); if (ret != 0) return ret; /* Configure PDDA GPIO. */ ret = devm_gpio_request_one(&pdev->dev, setup->pdda_pin, - GPIOF_OUT_INIT_HIGH, "codec_pdda"); + GPIOF_OUT_INIT_LOW, "codec_pdda"); if (ret != 0) return ret; -- cgit v0.10.2 From e29deb48189cea0be8b24f5912d8c21a18cb0244 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sun, 18 Aug 2013 18:25:53 +0100 Subject: ASoC: wl1273: Add stub DAPM support In order to ensure that the device continues to work with DAPM support being mandatory provide stub DAPM widgets and routes. Note that the public information on the device appears to make no mention of the FM support the driver appears to have. Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wl1273.c b/sound/soc/codecs/wl1273.c index 54cd3da..b7ab2ef 100644 --- a/sound/soc/codecs/wl1273.c +++ b/sound/soc/codecs/wl1273.c @@ -290,6 +290,18 @@ static const struct snd_kcontrol_new wl1273_controls[] = { snd_wl1273_fm_volume_get, snd_wl1273_fm_volume_put), }; +static const struct snd_soc_dapm_widget wl1273_dapm_widgets[] = { + SND_SOC_DAPM_INPUT("RX"), + + SND_SOC_DAPM_OUTPUT("TX"), +}; + +static const struct snd_soc_dapm_route wl1273_dapm_routes[] = { + { "Capture", NULL, "RX" }, + + { "TX", NULL, "Playback" }, +}; + static int wl1273_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { @@ -483,6 +495,11 @@ static int wl1273_remove(struct snd_soc_codec *codec) static struct snd_soc_codec_driver soc_codec_dev_wl1273 = { .probe = wl1273_probe, .remove = wl1273_remove, + + .dapm_widgets = wl1273_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wl1273_dapm_widgets), + .dapm_routes = wl1273_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(wl1273_dapm_routes), }; static int wl1273_platform_probe(struct platform_device *pdev) -- cgit v0.10.2 From 782fbaba36731d46820f3a4f358a7b46a9cd795c Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sun, 11 Aug 2013 12:29:07 +0100 Subject: ASoC: cs4270: Add DAPM support This makes it possible to hook the device into a more complex board and ensures it will continue to work with non-DAPM support removed from the core. Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/cs4270.c b/sound/soc/codecs/cs4270.c index 8e47798..83c835d 100644 --- a/sound/soc/codecs/cs4270.c +++ b/sound/soc/codecs/cs4270.c @@ -139,6 +139,22 @@ struct cs4270_private { struct regulator_bulk_data supplies[ARRAY_SIZE(supply_names)]; }; +static const struct snd_soc_dapm_widget cs4270_dapm_widgets[] = { +SND_SOC_DAPM_INPUT("AINL"), +SND_SOC_DAPM_INPUT("AINR"), + +SND_SOC_DAPM_OUTPUT("AOUTL"), +SND_SOC_DAPM_OUTPUT("AOUTR"), +}; + +static const struct snd_soc_dapm_route cs4270_dapm_routes[] = { + { "Capture", NULL, "AINA" }, + { "Capture", NULL, "AINB" }, + + { "AOUTA", NULL, "Playback" }, + { "AOUTB", NULL, "Playback" }, +}; + /** * struct cs4270_mode_ratios - clock ratio tables * @ratio: the ratio of MCLK to the sample rate @@ -612,6 +628,10 @@ static const struct snd_soc_codec_driver soc_codec_device_cs4270 = { .controls = cs4270_snd_controls, .num_controls = ARRAY_SIZE(cs4270_snd_controls), + .dapm_widgets = cs4270_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(cs4270_dapm_widgets), + .dapm_routes = cs4270_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(cs4270_dapm_routes), }; /* -- cgit v0.10.2 From 72a061f763c8af8ace650ccb1d01f484a6465608 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sun, 18 Aug 2013 18:35:54 +0100 Subject: ASoC: wm8727: Add DAPM support In order to make the device easier to hook up to external components in system designs and ensure operation when DAPM support becomes mandatory add DAPM support. Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm8727.c b/sound/soc/codecs/wm8727.c index 462f5e4..7b1a6d5 100644 --- a/sound/soc/codecs/wm8727.c +++ b/sound/soc/codecs/wm8727.c @@ -23,6 +23,16 @@ #include #include +static const struct snd_soc_dapm_widget wm8727_dapm_widgets[] = { +SND_SOC_DAPM_OUTPUT("VOUTL"), +SND_SOC_DAPM_OUTPUT("VOUTR"), +}; + +static const struct snd_soc_dapm_route wm8727_dapm_routes[] = { + { "VOUTL", NULL, "Playback" }, + { "VOUTR", NULL, "Playback" }, +}; + /* * Note this is a simple chip with no configuration interface, sample rate is * determined automatically by examining the Master clock and Bit clock ratios @@ -43,7 +53,12 @@ static struct snd_soc_dai_driver wm8727_dai = { }, }; -static struct snd_soc_codec_driver soc_codec_dev_wm8727; +static struct snd_soc_codec_driver soc_codec_dev_wm8727 = { + .dapm_widgets = wm8727_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8727_dapm_widgets), + .dapm_routes = wm8727_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(wm8727_dapm_routes), +}; static int wm8727_probe(struct platform_device *pdev) { -- cgit v0.10.2 From 226059e1cdbb5d747bd008eba114af0b1a4a621e Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sun, 18 Aug 2013 18:36:06 +0100 Subject: ASoC: wm8782: Add DAPM support In order to make the device easier to hook up to external components in system designs and ensure operation when DAPM support becomes mandatory add DAPM support. Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm8782.c b/sound/soc/codecs/wm8782.c index f1fdbf6..8092495 100644 --- a/sound/soc/codecs/wm8782.c +++ b/sound/soc/codecs/wm8782.c @@ -26,6 +26,16 @@ #include #include +static const struct snd_soc_dapm_widget wm8782_dapm_widgets[] = { +SND_SOC_DAPM_INPUT("AINL"), +SND_SOC_DAPM_INPUT("AINR"), +}; + +static const struct snd_soc_dapm_route wm8782_dapm_routes[] = { + { "Capture", NULL, "AINL" }, + { "Capture", NULL, "AINR" }, +}; + static struct snd_soc_dai_driver wm8782_dai = { .name = "wm8782", .capture = { @@ -40,7 +50,12 @@ static struct snd_soc_dai_driver wm8782_dai = { }, }; -static struct snd_soc_codec_driver soc_codec_dev_wm8782; +static struct snd_soc_codec_driver soc_codec_dev_wm8782 = { + .dapm_widgets = wm8782_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8782_dapm_widgets), + .dapm_routes = wm8782_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(wm8782_dapm_routes), +}; static int wm8782_probe(struct platform_device *pdev) { -- cgit v0.10.2 From 3efd8a6f1a74b4bbf54c992e1cf23381c64de216 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 12 Aug 2013 23:58:58 +0100 Subject: ASoC: wm5102: Add inputs for noise and mic mixers The noise and mic mixer inputs were not connected, do so. Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c index f38c52d..8bbddc1 100644 --- a/sound/soc/codecs/wm5102.c +++ b/sound/soc/codecs/wm5102.c @@ -1423,9 +1423,6 @@ static const struct snd_soc_dapm_route wm5102_dapm_routes[] = { { "Tone Generator 1", NULL, "TONE" }, { "Tone Generator 2", NULL, "TONE" }, - { "Mic Mute Mixer", NULL, "Noise Mixer" }, - { "Mic Mute Mixer", NULL, "Mic Mixer" }, - { "AIF1 Capture", NULL, "AIF1TX1" }, { "AIF1 Capture", NULL, "AIF1TX2" }, { "AIF1 Capture", NULL, "AIF1TX3" }, @@ -1552,6 +1549,9 @@ static const struct snd_soc_dapm_route wm5102_dapm_routes[] = { ARIZONA_MIXER_ROUTES("LHPF3", "LHPF3"), ARIZONA_MIXER_ROUTES("LHPF4", "LHPF4"), + ARIZONA_MIXER_ROUTES("Mic Mute Mixer", "Noise"), + ARIZONA_MIXER_ROUTES("Mic Mute Mixer", "Mic"), + ARIZONA_MUX_ROUTES("ASRC1L", "ASRC1L"), ARIZONA_MUX_ROUTES("ASRC1R", "ASRC1R"), ARIZONA_MUX_ROUTES("ASRC2L", "ASRC2L"), -- cgit v0.10.2 From 66e7aa22c751af82567f9af82fe7e1254f751870 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 12 Aug 2013 23:59:08 +0100 Subject: ASoC: wm5110: Add inputs for noise and mic mixers The noise and mic mixer inputs were not connected, do so. Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm5110.c b/sound/soc/codecs/wm5110.c index 38e50c8..bbd6438 100644 --- a/sound/soc/codecs/wm5110.c +++ b/sound/soc/codecs/wm5110.c @@ -841,9 +841,6 @@ static const struct snd_soc_dapm_route wm5110_dapm_routes[] = { { "Tone Generator 1", NULL, "TONE" }, { "Tone Generator 2", NULL, "TONE" }, - { "Mic Mute Mixer", NULL, "Noise Mixer" }, - { "Mic Mute Mixer", NULL, "Mic Mixer" }, - { "AIF1 Capture", NULL, "AIF1TX1" }, { "AIF1 Capture", NULL, "AIF1TX2" }, { "AIF1 Capture", NULL, "AIF1TX3" }, @@ -978,6 +975,9 @@ static const struct snd_soc_dapm_route wm5110_dapm_routes[] = { ARIZONA_MIXER_ROUTES("LHPF3", "LHPF3"), ARIZONA_MIXER_ROUTES("LHPF4", "LHPF4"), + ARIZONA_MIXER_ROUTES("Mic Mute Mixer", "Noise"), + ARIZONA_MIXER_ROUTES("Mic Mute Mixer", "Mic"), + ARIZONA_MUX_ROUTES("ASRC1L", "ASRC1L"), ARIZONA_MUX_ROUTES("ASRC1R", "ASRC1R"), ARIZONA_MUX_ROUTES("ASRC2L", "ASRC2L"), -- cgit v0.10.2 From c5efb38a1354890297aed2a7e197ec5b23ce966a Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 12 Aug 2013 23:59:19 +0100 Subject: ASoC: wm8997: Add inputs for noise and mic mixers The noise and mic mixer inputs were not connected, do so. Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm8997.c b/sound/soc/codecs/wm8997.c index 0a43bac..6ec3de3 100644 --- a/sound/soc/codecs/wm8997.c +++ b/sound/soc/codecs/wm8997.c @@ -774,9 +774,6 @@ static const struct snd_soc_dapm_route wm8997_dapm_routes[] = { { "Tone Generator 1", NULL, "TONE" }, { "Tone Generator 2", NULL, "TONE" }, - { "Mic Mute Mixer", NULL, "Noise Mixer" }, - { "Mic Mute Mixer", NULL, "Mic Mixer" }, - { "AIF1 Capture", NULL, "AIF1TX1" }, { "AIF1 Capture", NULL, "AIF1TX2" }, { "AIF1 Capture", NULL, "AIF1TX3" }, @@ -886,6 +883,9 @@ static const struct snd_soc_dapm_route wm8997_dapm_routes[] = { ARIZONA_MIXER_ROUTES("LHPF3", "LHPF3"), ARIZONA_MIXER_ROUTES("LHPF4", "LHPF4"), + ARIZONA_MIXER_ROUTES("Mic Mute Mixer", "Noise"), + ARIZONA_MIXER_ROUTES("Mic Mute Mixer", "Mic"), + ARIZONA_MUX_ROUTES("ISRC1INT1", "ISRC1INT1"), ARIZONA_MUX_ROUTES("ISRC1INT2", "ISRC2INT2"), -- cgit v0.10.2 From d2a369cb53a3f3733800d5160d60f9a5271fe44c Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 19 Aug 2013 12:18:07 +0100 Subject: ASoC: ac97: Provide stub DAPM integration Ensure continued operation with DAPM being mandatory. Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/ac97.c b/sound/soc/codecs/ac97.c index ec73518..8d9ba4b 100644 --- a/sound/soc/codecs/ac97.c +++ b/sound/soc/codecs/ac97.c @@ -23,6 +23,16 @@ #include #include +static const struct snd_soc_dapm_widget ac97_widgets[] = { + SND_SOC_DAPM_INPUT("RX"), + SND_SOC_DAPM_OUTPUT("TX"), +}; + +static const struct snd_soc_dapm_route ac97_routes[] = { + { "AC97 Capture", NULL, "RX" }, + { "TX", NULL, "AC97 Playback" }, +}; + static int ac97_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { @@ -117,6 +127,11 @@ static struct snd_soc_codec_driver soc_codec_dev_ac97 = { .probe = ac97_soc_probe, .suspend = ac97_soc_suspend, .resume = ac97_soc_resume, + + .dapm_widgets = ac97_widgets, + .num_dapm_widgets = ARRAY_SIZE(ac97_widgets), + .dapm_routes = ac97_routes, + .num_dapm_routes = ARRAY_SIZE(ac97_routes), }; static int ac97_probe(struct platform_device *pdev) -- cgit v0.10.2 From c34e51b12751c3e81c752b385f02a97bf3f862da Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 19 Aug 2013 12:17:36 +0100 Subject: ASoC: hdmi: Provide stub DAPM integration Ensure continued operation with DAPM being mandatory. Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/hdmi.c b/sound/soc/codecs/hdmi.c index f0986b9..68342b1 100644 --- a/sound/soc/codecs/hdmi.c +++ b/sound/soc/codecs/hdmi.c @@ -23,11 +23,20 @@ #define DRV_NAME "hdmi-audio-codec" -static struct snd_soc_codec_driver hdmi_codec; +static const struct snd_soc_dapm_widget hdmi_widgets[] = { + SND_SOC_DAPM_INPUT("RX"), + SND_SOC_DAPM_OUTPUT("TX"), +}; + +static const struct snd_soc_dapm_route hdmi_routes[] = { + { "Capture", NULL, "RX" }, + { "TX", NULL, "Playback" }, +}; static struct snd_soc_dai_driver hdmi_codec_dai = { .name = "hdmi-hifi", .playback = { + .stream_name = "Playback", .channels_min = 2, .channels_max = 8, .rates = SNDRV_PCM_RATE_32000 | @@ -38,6 +47,7 @@ static struct snd_soc_dai_driver hdmi_codec_dai = { SNDRV_PCM_FMTBIT_S24_LE, }, .capture = { + .stream_name = "Capture", .channels_min = 2, .channels_max = 2, .rates = SNDRV_PCM_RATE_32000 | @@ -50,6 +60,13 @@ static struct snd_soc_dai_driver hdmi_codec_dai = { }; +static struct snd_soc_codec_driver hdmi_codec = { + .dapm_widgets = hdmi_widgets, + .num_dapm_widgets = ARRAY_SIZE(hdmi_widgets), + .dapm_routes = hdmi_routes, + .num_dapm_routes = ARRAY_SIZE(hdmi_routes), +}; + static int hdmi_codec_probe(struct platform_device *pdev) { return snd_soc_register_codec(&pdev->dev, &hdmi_codec, -- cgit v0.10.2 From b9dff9c3d2c6c4a9cdb936f263ed293274e2f05a Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 19 Aug 2013 12:13:14 +0100 Subject: ASoC: bt-sco: Add generic compatible string Provide a common compatible string for device trees to list as a fallback for simplicity. We don't currently have a binding document but let's not fix that right now... Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/bt-sco.c b/sound/soc/codecs/bt-sco.c index a081d9f..5c040ce 100644 --- a/sound/soc/codecs/bt-sco.c +++ b/sound/soc/codecs/bt-sco.c @@ -50,6 +50,9 @@ static struct platform_device_id bt_sco_driver_ids[] = { { .name = "dfbmcs320", }, + { + .name = "bt-sco", + }, {}, }; MODULE_DEVICE_TABLE(platform, bt_sco_driver_ids); -- cgit v0.10.2 From 5195ca4902fe0b2ae01eb43ce522b89163672804 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 19 Aug 2013 12:16:19 +0100 Subject: ASoC: bt-sco: Provide stub DAPM integration Ensure continued operation with DAPM being mandatory. Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/bt-sco.c b/sound/soc/codecs/bt-sco.c index 5c040ce..c4cf069 100644 --- a/sound/soc/codecs/bt-sco.c +++ b/sound/soc/codecs/bt-sco.c @@ -15,15 +15,27 @@ #include +static const struct snd_soc_dapm_widget bt_sco_widgets[] = { + SND_SOC_DAPM_INPUT("RX"), + SND_SOC_DAPM_OUTPUT("TX"), +}; + +static const struct snd_soc_dapm_route bt_sco_routes[] = { + { "Capture", NULL, "RX" }, + { "TX", NULL, "Playback" }, +}; + static struct snd_soc_dai_driver bt_sco_dai = { .name = "bt-sco-pcm", .playback = { + .stream_name = "Playback", .channels_min = 1, .channels_max = 1, .rates = SNDRV_PCM_RATE_8000, .formats = SNDRV_PCM_FMTBIT_S16_LE, }, .capture = { + .stream_name = "Capture", .channels_min = 1, .channels_max = 1, .rates = SNDRV_PCM_RATE_8000, @@ -31,7 +43,12 @@ static struct snd_soc_dai_driver bt_sco_dai = { }, }; -static struct snd_soc_codec_driver soc_codec_dev_bt_sco; +static struct snd_soc_codec_driver soc_codec_dev_bt_sco = { + .dapm_widgets = bt_sco_widgets, + .num_dapm_widgets = ARRAY_SIZE(bt_sco_widgets), + .dapm_routes = bt_sco_routes, + .num_dapm_routes = ARRAY_SIZE(bt_sco_routes), +}; static int bt_sco_probe(struct platform_device *pdev) { -- cgit v0.10.2 From 2f6e3ba0e0645011cbbd0289e9082d8007141498 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 19 Aug 2013 12:09:39 +0100 Subject: ASoC: spdif: Add stub DAPM widgets for Rx Ensure that the driver continues to work with mandatory DAPM. Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/spdif_receiver.c b/sound/soc/codecs/spdif_receiver.c index 26d3474..e3501f4 100644 --- a/sound/soc/codecs/spdif_receiver.c +++ b/sound/soc/codecs/spdif_receiver.c @@ -23,13 +23,26 @@ #include #include +static const struct snd_soc_dapm_widget dir_widgets[] = { + SND_SOC_DAPM_INPUT("spdif-in"), +}; + +static const struct snd_soc_dapm_route dir_routes[] = { + { "Capture", NULL, "spdif-in" }, +}; + #define STUB_RATES SNDRV_PCM_RATE_8000_192000 #define STUB_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ SNDRV_PCM_FMTBIT_S20_3LE | \ SNDRV_PCM_FMTBIT_S24_LE | \ SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE) -static struct snd_soc_codec_driver soc_codec_spdif_dir; +static struct snd_soc_codec_driver soc_codec_spdif_dir = { + .dapm_widgets = dir_widgets, + .num_dapm_widgets = ARRAY_SIZE(dir_widgets), + .dapm_routes = dir_routes, + .num_dapm_routes = ARRAY_SIZE(dir_routes), +}; static struct snd_soc_dai_driver dir_stub_dai = { .name = "dir-hifi", -- cgit v0.10.2 From fc6061486534a8dfee02dd6b9dd523789abd9a3d Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 19 Aug 2013 12:10:08 +0100 Subject: ASoC: spdif: Remove duplicate const Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/spdif_transmitter.c b/sound/soc/codecs/spdif_transmitter.c index 4e96d10..a078aa3 100644 --- a/sound/soc/codecs/spdif_transmitter.c +++ b/sound/soc/codecs/spdif_transmitter.c @@ -33,7 +33,7 @@ static const struct snd_soc_dapm_widget dit_widgets[] = { SND_SOC_DAPM_OUTPUT("spdif-out"), }; -static const const struct snd_soc_dapm_route dit_routes[] = { +static const struct snd_soc_dapm_route dit_routes[] = { { "spdif-out", NULL, "Playback" }, }; -- cgit v0.10.2 From 3c1c32d3765876b72570966c819fac4b8c646394 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 16 Aug 2013 12:07:19 +0100 Subject: ASoC: imx: Add MODULE_LICENSE to DMA drivers Reported-by: Ben Hutchings Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/imx-pcm-dma.c b/sound/soc/fsl/imx-pcm-dma.c index f323ce0..4dc1296 100644 --- a/sound/soc/fsl/imx-pcm-dma.c +++ b/sound/soc/fsl/imx-pcm-dma.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -73,3 +74,5 @@ void imx_pcm_dma_exit(struct platform_device *pdev) snd_dmaengine_pcm_unregister(&pdev->dev); } EXPORT_SYMBOL_GPL(imx_pcm_dma_exit); + +MODULE_LICENSE("GPL"); diff --git a/sound/soc/fsl/imx-pcm-fiq.c b/sound/soc/fsl/imx-pcm-fiq.c index 3b2ba99..34043c5 100644 --- a/sound/soc/fsl/imx-pcm-fiq.c +++ b/sound/soc/fsl/imx-pcm-fiq.c @@ -408,3 +408,5 @@ void imx_pcm_fiq_exit(struct platform_device *pdev) snd_soc_unregister_platform(&pdev->dev); } EXPORT_SYMBOL_GPL(imx_pcm_fiq_exit); + +MODULE_LICENSE("GPL"); -- cgit v0.10.2 From cd5302c0d4b79bef7660bb4be300d169e38f39c3 Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Mon, 19 Aug 2013 12:22:33 +0200 Subject: ALSA: hda - Limit internal mic boost for a few more Thinkpad machines The higher mic boosts (on internal mic) are so noisy they're unusable in practice. BugLink: https://bugs.launchpad.net/bugs/1213820 Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 96dcb68..7d00639 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -3694,9 +3694,14 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x21fb, "Thinkpad T430s", ALC269_FIXUP_LENOVO_DOCK), SND_PCI_QUIRK(0x17aa, 0x2203, "Thinkpad X230 Tablet", ALC269_FIXUP_LENOVO_DOCK), SND_PCI_QUIRK(0x17aa, 0x2208, "Thinkpad T431s", ALC269_FIXUP_LENOVO_DOCK), + SND_PCI_QUIRK(0x17aa, 0x220c, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), + SND_PCI_QUIRK(0x17aa, 0x2212, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x17aa, 0x2214, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x17aa, 0x2215, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x17aa, 0x5013, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), + SND_PCI_QUIRK(0x17aa, 0x501a, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), + SND_PCI_QUIRK(0x17aa, 0x5026, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), + SND_PCI_QUIRK(0x17aa, 0x5109, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x17aa, 0x3bf8, "Quanta FL1", ALC269_FIXUP_PCM_44K), SND_PCI_QUIRK(0x17aa, 0x9e54, "LENOVO NB", ALC269_FIXUP_LENOVO_EAPD), SND_PCI_QUIRK(0x1b7d, 0xa831, "Ordissimo EVE2 ", ALC269VB_FIXUP_ORDISSIMO_EVE2), /* Also known as Malata PC-B1303 */ -- cgit v0.10.2 From c841ad2a9b86c7317dc7e4fe4e03bc56a6c0d6e8 Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Mon, 19 Aug 2013 13:32:30 +0200 Subject: ALSA: hda - Try to allow haswell HDMI audio even without powerwell If compiled without CONFIG_SND_HDA_I915, the audio driver cannot request power well. However, if the power well is on for other reasons, maybe audio can still work. Therefore, do not skip the card completely if compiled without CONFIG_SND_HDA_I915. Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 7f9e406..c6c9829 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -3855,11 +3855,13 @@ static int azx_probe_continue(struct azx *chip) /* Request power well for Haswell HDA controller and codec */ if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) { +#ifdef CONFIG_SND_HDA_I915 err = hda_i915_init(); if (err < 0) { snd_printk(KERN_ERR SFX "Error request power-well from i915\n"); goto out_free; } +#endif hda_display_power(true); } -- cgit v0.10.2 From bcbb15530ebe9737622cb7779b35c61f48b49734 Mon Sep 17 00:00:00 2001 From: Tim Gardner Date: Fri, 16 Aug 2013 11:18:59 -0600 Subject: ALSA: pcm: Add snd_printd_ratelimit() Direct calls to printk_limit() will emit log noise even when CONFIG_SND_DEBUG is not defined. Add a wrapper macro around printk_limit() that is conditionally defined by CONFIG_SND_DEBUG. Cc: Jaroslav Kysela Cc: Takashi Iwai Cc: Pierre-Louis Bossart Cc: Lars-Peter Clausen Cc: Yacine Belkadi Signed-off-by: Tim Gardner Signed-off-by: Takashi Iwai diff --git a/include/sound/core.h b/include/sound/core.h index c586617..2a14f1f 100644 --- a/include/sound/core.h +++ b/include/sound/core.h @@ -27,6 +27,7 @@ #include /* struct rw_semaphore */ #include /* pm_message_t */ #include +#include /* number of supported soundcards */ #ifdef CONFIG_SND_DYNAMIC_MINORS @@ -376,6 +377,11 @@ void __snd_printk(unsigned int level, const char *file, int line, #define snd_BUG() WARN(1, "BUG?\n") /** + * Suppress high rates of output when CONFIG_SND_DEBUG is enabled. + */ +#define snd_printd_ratelimit() printk_ratelimit() + +/** * snd_BUG_ON - debugging check macro * @cond: condition to evaluate * @@ -398,6 +404,8 @@ static inline void _snd_printd(int level, const char *format, ...) {} unlikely(__ret_warn_on); \ }) +static inline bool snd_printd_ratelimit(void) { return false; } + #endif /* CONFIG_SND_DEBUG */ #ifdef CONFIG_SND_DEBUG_VERBOSE -- cgit v0.10.2 From 74d779ab7c9f9024cfead259206e0e0b20ee37e4 Mon Sep 17 00:00:00 2001 From: Tim Gardner Date: Fri, 16 Aug 2013 11:19:00 -0600 Subject: ALSA: pcm: Use snd_printd_ratelimit() The use of snd_printd_ratelimit() supresses superfluous output from printk_ratelimit() when CONFIG_SND_DEBUG is not defined. For example, [ 43.753692] snd_pcm_update_hw_ptr0: 26 callbacks suppressed [ 48.822131] snd_pcm_update_hw_ptr0: 25 callbacks suppressed [ 53.894953] snd_pcm_update_hw_ptr0: 25 callbacks suppressed [ 58.997761] snd_pcm_update_hw_ptr0: 25 callbacks suppressed [ 64.100952] snd_pcm_update_hw_ptr0: 25 callbacks suppressed fills the log even when no debug output is actually produced. Cc: Jaroslav Kysela Cc: Takashi Iwai Cc: Pierre-Louis Bossart Cc: Lars-Peter Clausen Cc: Yacine Belkadi Signed-off-by: Tim Gardner Signed-off-by: Takashi Iwai diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index 82bb029..6e03b46 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -184,7 +184,7 @@ static void xrun(struct snd_pcm_substream *substream) do { \ if (xrun_debug(substream, XRUN_DEBUG_BASIC)) { \ xrun_log_show(substream); \ - if (printk_ratelimit()) { \ + if (snd_printd_ratelimit()) { \ snd_printd("PCM: " fmt, ##args); \ } \ dump_stack_on_xrun(substream); \ @@ -342,7 +342,7 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, return -EPIPE; } if (pos >= runtime->buffer_size) { - if (printk_ratelimit()) { + if (snd_printd_ratelimit()) { char name[16]; snd_pcm_debug_name(substream, name, sizeof(name)); xrun_log_show(substream); -- cgit v0.10.2 From 17d2f00836cce9b1a24e65670ad78dbab275777b Mon Sep 17 00:00:00 2001 From: Adrian Knoth Date: Mon, 19 Aug 2013 17:20:30 +0200 Subject: ALSA: hdspm - Fix default value in SNDRV_HDSPM_IOCTL_GET_LTC Use enum hdspm_ltc_format's fps_30 (corresponds to 4) instead of 30, Other case branches return 1, 2 or 3 respectively, so 30 obviously is wrong. Since SNDRV_HDSPM_IOCTL_GET_LTC had never been working due to a copy&paste error in hdspm.h, this change doesn't break userspace. Signed-off-by: Adrian Knoth Signed-off-by: Takashi Iwai diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c index ec6335e..e4d76a6 100644 --- a/sound/pci/rme9652/hdspm.c +++ b/sound/pci/rme9652/hdspm.c @@ -6311,7 +6311,7 @@ static int snd_hdspm_hwdep_ioctl(struct snd_hwdep *hw, struct file *file, ltc.format = fps_2997; break; default: - ltc.format = 30; + ltc.format = fps_30; break; } if (i & HDSPM_TCO1_set_drop_frame_flag) { -- cgit v0.10.2 From 1568b8802227f4e7b0ad79a49cd35d4e285570f2 Mon Sep 17 00:00:00 2001 From: Adrian Knoth Date: Mon, 19 Aug 2013 17:20:31 +0200 Subject: ALSA: hdspm - Use enums in hdspm_tco_ltc_frames() This patch doesn't change functionality, it only improves readability and fixes a copy&paste error in a comment. Signed-off-by: Adrian Knoth Signed-off-by: Takashi Iwai diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c index e4d76a6..3cde55b 100644 --- a/sound/pci/rme9652/hdspm.c +++ b/sound/pci/rme9652/hdspm.c @@ -3173,19 +3173,19 @@ static int hdspm_tco_ltc_frames(struct hdspm *hdspm) HDSPM_TCO1_LTC_Format_MSB)) { case 0: /* 24 fps */ - ret = 1; + ret = fps_24; break; case HDSPM_TCO1_LTC_Format_LSB: /* 25 fps */ - ret = 2; + ret = fps_25; break; case HDSPM_TCO1_LTC_Format_MSB: - /* 25 fps */ - ret = 3; + /* 29.97 fps */ + ret = fps_2997; break; default: /* 30 fps */ - ret = 4; + ret = fps_30; break; } } -- cgit v0.10.2 From b43dd416be21bc8ad60984e13def032f01aaaa18 Mon Sep 17 00:00:00 2001 From: Adrian Knoth Date: Mon, 19 Aug 2013 17:20:32 +0200 Subject: ALSA: hdspm - Fix SNDRV_HDSPM_IOCTL_GET_LTC Use struct hdspm_ltc to query the LTC, using a mixer struct is just plain wrong. Due to the wrong struct, this ioctl was never working, so we're free to fix it without breaking userspace compatibility. Signed-off-by: Adrian Knoth Signed-off-by: Takashi Iwai diff --git a/include/uapi/sound/hdspm.h b/include/uapi/sound/hdspm.h index 1f59ea2..d956c35 100644 --- a/include/uapi/sound/hdspm.h +++ b/include/uapi/sound/hdspm.h @@ -111,7 +111,7 @@ struct hdspm_ltc { enum hdspm_ltc_input_format input_format; }; -#define SNDRV_HDSPM_IOCTL_GET_LTC _IOR('H', 0x46, struct hdspm_mixer_ioctl) +#define SNDRV_HDSPM_IOCTL_GET_LTC _IOR('H', 0x46, struct hdspm_ltc) /** * The status data reflects the device's current state -- cgit v0.10.2 From 37e6071787908fa9009cbd002c86402720becc5f Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 19 Aug 2013 20:33:20 +0100 Subject: ASoC: samsung: Check to see if we managed to allocate a channel Signed-off-by: Mark Brown Acked-by: Sangbeom Kim diff --git a/sound/soc/samsung/dma.c b/sound/soc/samsung/dma.c index 21b7926..50c1eb6 100644 --- a/sound/soc/samsung/dma.c +++ b/sound/soc/samsung/dma.c @@ -176,6 +176,10 @@ static int dma_hw_params(struct snd_pcm_substream *substream, prtd->params->ch = prtd->params->ops->request( prtd->params->channel, &req, rtd->cpu_dai->dev, prtd->params->ch_name); + if (!prtd->params->ch) { + pr_err("Failed to allocate DMA channel\n"); + return -ENXIO; + } prtd->params->ops->config(prtd->params->ch, &config); } -- cgit v0.10.2 From 85ff3c29d720fddddf35681bf8f244dfd91f66fa Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 19 Aug 2013 22:59:05 +0100 Subject: ASoC: samsung: Rename DMA platform registration functions The current naming with a simple asoc_ prefix is too generic for use in multiplatform kernels. Signed-off-by: Mark Brown Acked-by: Sangbeom Kim diff --git a/sound/soc/samsung/ac97.c b/sound/soc/samsung/ac97.c index c732df9..2acf987 100644 --- a/sound/soc/samsung/ac97.c +++ b/sound/soc/samsung/ac97.c @@ -457,7 +457,7 @@ static int s3c_ac97_probe(struct platform_device *pdev) if (ret) goto err5; - ret = asoc_dma_platform_register(&pdev->dev); + ret = samsung_asoc_dma_platform_register(&pdev->dev); if (ret) { dev_err(&pdev->dev, "failed to get register DMA: %d\n", ret); goto err6; @@ -480,7 +480,7 @@ static int s3c_ac97_remove(struct platform_device *pdev) { struct resource *irq_res; - asoc_dma_platform_unregister(&pdev->dev); + samsung_asoc_dma_platform_unregister(&pdev->dev); snd_soc_unregister_component(&pdev->dev); irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); diff --git a/sound/soc/samsung/dma.c b/sound/soc/samsung/dma.c index 50c1eb6..a0c67f6 100644 --- a/sound/soc/samsung/dma.c +++ b/sound/soc/samsung/dma.c @@ -437,17 +437,17 @@ static struct snd_soc_platform_driver samsung_asoc_platform = { .pcm_free = dma_free_dma_buffers, }; -int asoc_dma_platform_register(struct device *dev) +int samsung_asoc_dma_platform_register(struct device *dev) { return snd_soc_register_platform(dev, &samsung_asoc_platform); } -EXPORT_SYMBOL_GPL(asoc_dma_platform_register); +EXPORT_SYMBOL_GPL(samsung_asoc_dma_platform_register); -void asoc_dma_platform_unregister(struct device *dev) +void samsung_asoc_dma_platform_unregister(struct device *dev) { snd_soc_unregister_platform(dev); } -EXPORT_SYMBOL_GPL(asoc_dma_platform_unregister); +EXPORT_SYMBOL_GPL(samsung_asoc_dma_platform_unregister); MODULE_AUTHOR("Ben Dooks, "); MODULE_DESCRIPTION("Samsung ASoC DMA Driver"); diff --git a/sound/soc/samsung/dma.h b/sound/soc/samsung/dma.h index 189a7a6..0e86315 100644 --- a/sound/soc/samsung/dma.h +++ b/sound/soc/samsung/dma.h @@ -22,7 +22,7 @@ struct s3c_dma_params { char *ch_name; }; -int asoc_dma_platform_register(struct device *dev); -void asoc_dma_platform_unregister(struct device *dev); +int samsung_asoc_dma_platform_register(struct device *dev); +void samsung_asoc_dma_platform_unregister(struct device *dev); #endif diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index dd995a7..8200fc1 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -1146,7 +1146,7 @@ static int samsung_i2s_probe(struct platform_device *pdev) snd_soc_register_component(&sec_dai->pdev->dev, &samsung_i2s_component, &sec_dai->i2s_dai_drv, 1); - asoc_dma_platform_register(&pdev->dev); + samsung_asoc_dma_platform_register(&pdev->dev); return 0; } @@ -1263,7 +1263,7 @@ static int samsung_i2s_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); - asoc_dma_platform_register(&pdev->dev); + samsung_asoc_dma_platform_register(&pdev->dev); return 0; err: @@ -1293,7 +1293,7 @@ static int samsung_i2s_remove(struct platform_device *pdev) i2s->pri_dai = NULL; i2s->sec_dai = NULL; - asoc_dma_platform_unregister(&pdev->dev); + samsung_asoc_dma_platform_unregister(&pdev->dev); snd_soc_unregister_component(&pdev->dev); return 0; -- cgit v0.10.2 From 741a509f34d8d702f70d0ad99b8152c57d76961e Mon Sep 17 00:00:00 2001 From: Markus Pargmann Date: Mon, 19 Aug 2013 17:05:55 +0200 Subject: ASoC: core: Generic ac97 link reset functions This patch adds generic ac97 reset functions using pincontrol and gpio parsed from devicetree. Signed-off-by: Markus Pargmann Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/sound/soc-ac97link.txt b/Documentation/devicetree/bindings/sound/soc-ac97link.txt new file mode 100644 index 0000000..80152a8 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/soc-ac97link.txt @@ -0,0 +1,28 @@ +AC97 link bindings + +These bindings can be included within any other device node. + +Required properties: + - pinctrl-names: Has to contain following states to setup the correct + pinmuxing for the used gpios: + "ac97-running": AC97-link is active + "ac97-reset": AC97-link reset state + "ac97-warm-reset": AC97-link warm reset state + - ac97-gpios: List of gpio phandles with args in the order ac97-sync, + ac97-sdata, ac97-reset + + +Example: + +ssi { + ... + + pinctrl-names = "default", "ac97-running", "ac97-reset", "ac97-warm-reset"; + pinctrl-0 = <&ac97link_running>; + pinctrl-1 = <&ac97link_running>; + pinctrl-2 = <&ac97link_reset>; + pinctrl-3 = <&ac97link_warm_reset>; + ac97-gpios = <&gpio3 20 0 &gpio3 22 0 &gpio3 28 0>; + + ... +}; diff --git a/include/sound/soc.h b/include/sound/soc.h index 6eabee7..c0ac3bc 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -468,6 +468,8 @@ int snd_soc_new_ac97_codec(struct snd_soc_codec *codec, void snd_soc_free_ac97_codec(struct snd_soc_codec *codec); int snd_soc_set_ac97_ops(struct snd_ac97_bus_ops *ops); +int snd_soc_set_ac97_ops_of_reset(struct snd_ac97_bus_ops *ops, + struct platform_device *pdev); /* *Controls diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index d82ee38..b5c91f9 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -30,9 +30,12 @@ #include #include #include +#include #include #include #include +#include +#include #include #include #include @@ -69,6 +72,16 @@ static int pmdown_time = 5000; module_param(pmdown_time, int, 0); MODULE_PARM_DESC(pmdown_time, "DAPM stream powerdown time (msecs)"); +struct snd_ac97_reset_cfg { + struct pinctrl *pctl; + struct pinctrl_state *pstate_reset; + struct pinctrl_state *pstate_warm_reset; + struct pinctrl_state *pstate_run; + int gpio_sdata; + int gpio_sync; + int gpio_reset; +}; + /* returns the minimum number of bytes needed to represent * a particular given value */ static int min_bytes_needed(unsigned long val) @@ -2080,6 +2093,117 @@ int snd_soc_new_ac97_codec(struct snd_soc_codec *codec, } EXPORT_SYMBOL_GPL(snd_soc_new_ac97_codec); +static struct snd_ac97_reset_cfg snd_ac97_rst_cfg; + +static void snd_soc_ac97_warm_reset(struct snd_ac97 *ac97) +{ + struct pinctrl *pctl = snd_ac97_rst_cfg.pctl; + + pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_warm_reset); + + gpio_direction_output(snd_ac97_rst_cfg.gpio_sync, 1); + + udelay(10); + + gpio_direction_output(snd_ac97_rst_cfg.gpio_sync, 0); + + pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_run); + msleep(2); +} + +static void snd_soc_ac97_reset(struct snd_ac97 *ac97) +{ + struct pinctrl *pctl = snd_ac97_rst_cfg.pctl; + + pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_reset); + + gpio_direction_output(snd_ac97_rst_cfg.gpio_sync, 0); + gpio_direction_output(snd_ac97_rst_cfg.gpio_sdata, 0); + gpio_direction_output(snd_ac97_rst_cfg.gpio_reset, 0); + + udelay(10); + + gpio_direction_output(snd_ac97_rst_cfg.gpio_reset, 1); + + pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_run); + msleep(2); +} + +static int snd_soc_ac97_parse_pinctl(struct device *dev, + struct snd_ac97_reset_cfg *cfg) +{ + struct pinctrl *p; + struct pinctrl_state *state; + int gpio; + int ret; + + p = devm_pinctrl_get(dev); + if (IS_ERR(p)) { + dev_err(dev, "Failed to get pinctrl\n"); + return PTR_RET(p); + } + cfg->pctl = p; + + state = pinctrl_lookup_state(p, "ac97-reset"); + if (IS_ERR(state)) { + dev_err(dev, "Can't find pinctrl state ac97-reset\n"); + return PTR_RET(state); + } + cfg->pstate_reset = state; + + state = pinctrl_lookup_state(p, "ac97-warm-reset"); + if (IS_ERR(state)) { + dev_err(dev, "Can't find pinctrl state ac97-warm-reset\n"); + return PTR_RET(state); + } + cfg->pstate_warm_reset = state; + + state = pinctrl_lookup_state(p, "ac97-running"); + if (IS_ERR(state)) { + dev_err(dev, "Can't find pinctrl state ac97-running\n"); + return PTR_RET(state); + } + cfg->pstate_run = state; + + gpio = of_get_named_gpio(dev->of_node, "ac97-gpios", 0); + if (gpio < 0) { + dev_err(dev, "Can't find ac97-sync gpio\n"); + return gpio; + } + ret = devm_gpio_request(dev, gpio, "AC97 link sync"); + if (ret) { + dev_err(dev, "Failed requesting ac97-sync gpio\n"); + return ret; + } + cfg->gpio_sync = gpio; + + gpio = of_get_named_gpio(dev->of_node, "ac97-gpios", 1); + if (gpio < 0) { + dev_err(dev, "Can't find ac97-sdata gpio %d\n", gpio); + return gpio; + } + ret = devm_gpio_request(dev, gpio, "AC97 link sdata"); + if (ret) { + dev_err(dev, "Failed requesting ac97-sdata gpio\n"); + return ret; + } + cfg->gpio_sdata = gpio; + + gpio = of_get_named_gpio(dev->of_node, "ac97-gpios", 2); + if (gpio < 0) { + dev_err(dev, "Can't find ac97-reset gpio\n"); + return gpio; + } + ret = devm_gpio_request(dev, gpio, "AC97 link reset"); + if (ret) { + dev_err(dev, "Failed requesting ac97-reset gpio\n"); + return ret; + } + cfg->gpio_reset = gpio; + + return 0; +} + struct snd_ac97_bus_ops *soc_ac97_ops; EXPORT_SYMBOL_GPL(soc_ac97_ops); @@ -2098,6 +2222,35 @@ int snd_soc_set_ac97_ops(struct snd_ac97_bus_ops *ops) EXPORT_SYMBOL_GPL(snd_soc_set_ac97_ops); /** + * snd_soc_set_ac97_ops_of_reset - Set ac97 ops with generic ac97 reset functions + * + * This function sets the reset and warm_reset properties of ops and parses + * the device node of pdev to get pinctrl states and gpio numbers to use. + */ +int snd_soc_set_ac97_ops_of_reset(struct snd_ac97_bus_ops *ops, + struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct snd_ac97_reset_cfg cfg; + int ret; + + ret = snd_soc_ac97_parse_pinctl(dev, &cfg); + if (ret) + return ret; + + ret = snd_soc_set_ac97_ops(ops); + if (ret) + return ret; + + ops->warm_reset = snd_soc_ac97_warm_reset; + ops->reset = snd_soc_ac97_reset; + + snd_ac97_rst_cfg = cfg; + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_set_ac97_ops_of_reset); + +/** * snd_soc_free_ac97_codec - free AC97 codec device * @codec: audio codec * -- cgit v0.10.2 From 0783e648988a2ccef6eac9b1c376e7832e09cd94 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Sat, 17 Aug 2013 18:13:00 -0300 Subject: ASoC: fsl: fsl_ssi: Fix the order of resources removal In fsl_ssi_remove() we need to remove the resources in the opposite order that they were acquired in probe. Signed-off-by: Fabio Estevam Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 0c072ff..3168998 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -928,14 +928,14 @@ static int fsl_ssi_remove(struct platform_device *pdev) if (!ssi_private->new_binding) platform_device_unregister(ssi_private->pdev); - if (ssi_private->ssi_on_imx) { + if (ssi_private->ssi_on_imx) imx_pcm_dma_exit(pdev); - clk_disable_unprepare(ssi_private->clk); - } snd_soc_unregister_component(&pdev->dev); + dev_set_drvdata(&pdev->dev, NULL); device_remove_file(&pdev->dev, &ssi_private->dev_attr); + if (ssi_private->ssi_on_imx) + clk_disable_unprepare(ssi_private->clk); irq_dispose_mapping(ssi_private->irq); - dev_set_drvdata(&pdev->dev, NULL); return 0; } -- cgit v0.10.2 From 673c24e957db4be85a12e4260ace12dea805fa97 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Mon, 19 Aug 2013 10:51:51 +0200 Subject: ASoC: omap: simplify platform_get_resource_byname/devm_ioremap_resource Remove unneeded error handling on the result of a call to platform_get_resource_byname when the value is passed to devm_ioremap_resource. In the case of omap-dmic.c, the error-handling code of devm_ioremap_resource is also corrected to include releasing the clock. A simplified version of the semantic patch that makes this change is as follows: (http://coccinelle.lip6.fr/) // @@ expression pdev,res,e,e1; expression ret != 0; identifier l; @@ res = platform_get_resource_byname(...); - if (res == NULL) { ... \(goto l;\|return ret;\) } e = devm_ioremap_resource(e1, res); // Signed-off-by: Julia Lawall Acked-by: Jarkko Nikula Signed-off-by: Mark Brown diff --git a/sound/soc/omap/omap-dmic.c b/sound/soc/omap/omap-dmic.c index 4db1f8e..12e566b 100644 --- a/sound/soc/omap/omap-dmic.c +++ b/sound/soc/omap/omap-dmic.c @@ -480,15 +480,12 @@ static int asoc_dmic_probe(struct platform_device *pdev) dmic->dma_data.filter_data = "up_link"; res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mpu"); - if (!res) { - dev_err(dmic->dev, "invalid memory resource\n"); - ret = -ENODEV; + dmic->io_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(dmic->io_base)) { + ret = PTR_ERR(dmic->io_base); goto err_put_clk; } - dmic->io_base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(dmic->io_base)) - return PTR_ERR(dmic->io_base); ret = snd_soc_register_component(&pdev->dev, &omap_dmic_component, &omap_dmic_dai, 1); diff --git a/sound/soc/omap/omap-mcpdm.c b/sound/soc/omap/omap-mcpdm.c index a49dc52..90d2a7c 100644 --- a/sound/soc/omap/omap-mcpdm.c +++ b/sound/soc/omap/omap-mcpdm.c @@ -480,9 +480,6 @@ static int asoc_mcpdm_probe(struct platform_device *pdev) mcpdm->dma_data[1].filter_data = "up_link"; res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mpu"); - if (res == NULL) - return -ENOMEM; - mcpdm->io_base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(mcpdm->io_base)) return PTR_ERR(mcpdm->io_base); -- cgit v0.10.2 From a1ce31388dfc954fa034e5e840f7323a81cb9e90 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Thu, 22 Aug 2013 13:30:15 +0530 Subject: ASoC: pxa: Remove duplicate inclusion of dmaengine.h dmaengine.h header file was included twice. Signed-off-by: Sachin Kamat Signed-off-by: Mark Brown diff --git a/sound/soc/pxa/mmp-pcm.c b/sound/soc/pxa/mmp-pcm.c index 9a97843..8235e23 100644 --- a/sound/soc/pxa/mmp-pcm.c +++ b/sound/soc/pxa/mmp-pcm.c @@ -17,7 +17,6 @@ #include #include #include -#include #include #include -- cgit v0.10.2 From a2388a498ad2f85be01aca29e364abf427d9b53c Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Wed, 21 Aug 2013 11:13:16 +0800 Subject: ASoC: fsl: Add S/PDIF CPU DAI driver This patch implements a device-tree-only CPU DAI driver for Freescale S/PDIF controller that supports stereo playback and record feature. Signed-off-by: Nicolin Chen Acked-by: Stephen Warren Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/sound/fsl,spdif.txt b/Documentation/devicetree/bindings/sound/fsl,spdif.txt new file mode 100644 index 0000000..f2ae335 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/fsl,spdif.txt @@ -0,0 +1,54 @@ +Freescale Sony/Philips Digital Interface Format (S/PDIF) Controller + +The Freescale S/PDIF audio block is a stereo transceiver that allows the +processor to receive and transmit digital audio via an coaxial cable or +a fibre cable. + +Required properties: + + - compatible : Compatible list, must contain "fsl,imx35-spdif". + + - reg : Offset and length of the register set for the device. + + - interrupts : Contains the spdif interrupt. + + - dmas : Generic dma devicetree binding as described in + Documentation/devicetree/bindings/dma/dma.txt. + + - dma-names : Two dmas have to be defined, "tx" and "rx". + + - clocks : Contains an entry for each entry in clock-names. + + - clock-names : Includes the following entries: + "core" The core clock of spdif controller + "rxtx<0-7>" Clock source list for tx and rx clock. + This clock list should be identical to + the source list connecting to the spdif + clock mux in "SPDIF Transceiver Clock + Diagram" of SoC reference manual. It + can also be referred to TxClk_Source + bit of register SPDIF_STC. + +Example: + +spdif: spdif@02004000 { + compatible = "fsl,imx35-spdif"; + reg = <0x02004000 0x4000>; + interrupts = <0 52 0x04>; + dmas = <&sdma 14 18 0>, + <&sdma 15 18 0>; + dma-names = "rx", "tx"; + + clocks = <&clks 197>, <&clks 3>, + <&clks 197>, <&clks 107>, + <&clks 0>, <&clks 118>, + <&clks 62>, <&clks 139>, + <&clks 0>; + clock-names = "core", "rxtx0", + "rxtx1", "rxtx2", + "rxtx3", "rxtx4", + "rxtx5", "rxtx6", + "rxtx7"; + + status = "okay"; +}; diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index 3a4808d..cd088cc 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig @@ -1,6 +1,9 @@ config SND_SOC_FSL_SSI tristate +config SND_SOC_FSL_SPDIF + tristate + config SND_SOC_FSL_UTILS tristate diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile index d4b4aa8b..4b5970e 100644 --- a/sound/soc/fsl/Makefile +++ b/sound/soc/fsl/Makefile @@ -12,9 +12,11 @@ obj-$(CONFIG_SND_SOC_P1022_RDK) += snd-soc-p1022-rdk.o # Freescale PowerPC SSI/DMA Platform Support snd-soc-fsl-ssi-objs := fsl_ssi.o +snd-soc-fsl-spdif-objs := fsl_spdif.o snd-soc-fsl-utils-objs := fsl_utils.o snd-soc-fsl-dma-objs := fsl_dma.o obj-$(CONFIG_SND_SOC_FSL_SSI) += snd-soc-fsl-ssi.o +obj-$(CONFIG_SND_SOC_FSL_SPDIF) += snd-soc-fsl-spdif.o obj-$(CONFIG_SND_SOC_FSL_UTILS) += snd-soc-fsl-utils.o obj-$(CONFIG_SND_SOC_POWERPC_DMA) += snd-soc-fsl-dma.o diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c new file mode 100644 index 0000000..42a4382 --- /dev/null +++ b/sound/soc/fsl/fsl_spdif.c @@ -0,0 +1,1236 @@ +/* + * Freescale S/PDIF ALSA SoC Digital Audio Interface (DAI) driver + * + * Copyright (C) 2013 Freescale Semiconductor, Inc. + * + * Based on stmp3xxx_spdif_dai.c + * Vladimir Barinov + * Copyright 2008 SigmaTel, Inc + * Copyright 2008 Embedded Alley Solutions, Inc + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "fsl_spdif.h" +#include "imx-pcm.h" + +#define FSL_SPDIF_TXFIFO_WML 0x8 +#define FSL_SPDIF_RXFIFO_WML 0x8 + +#define INTR_FOR_PLAYBACK (INT_TXFIFO_RESYNC) +#define INTR_FOR_CAPTURE (INT_SYM_ERR | INT_BIT_ERR | INT_URX_FUL | INT_URX_OV|\ + INT_QRX_FUL | INT_QRX_OV | INT_UQ_SYNC | INT_UQ_ERR |\ + INT_RXFIFO_RESYNC | INT_LOSS_LOCK | INT_DPLL_LOCKED) + +/* Index list for the values that has if (DPLL Locked) condition */ +static u8 srpc_dpll_locked[] = { 0x0, 0x1, 0x2, 0x3, 0x4, 0xa, 0xb }; +#define SRPC_NODPLL_START1 0x5 +#define SRPC_NODPLL_START2 0xc + +#define DEFAULT_RXCLK_SRC 1 + +/* + * SPDIF control structure + * Defines channel status, subcode and Q sub + */ +struct spdif_mixer_control { + /* spinlock to access control data */ + spinlock_t ctl_lock; + + /* IEC958 channel tx status bit */ + unsigned char ch_status[4]; + + /* User bits */ + unsigned char subcode[2 * SPDIF_UBITS_SIZE]; + + /* Q subcode part of user bits */ + unsigned char qsub[2 * SPDIF_QSUB_SIZE]; + + /* Buffer offset for U/Q */ + u32 upos; + u32 qpos; + + /* Ready buffer index of the two buffers */ + u32 ready_buf; +}; + +struct fsl_spdif_priv { + struct spdif_mixer_control fsl_spdif_control; + struct snd_soc_dai_driver cpu_dai_drv; + struct platform_device *pdev; + struct regmap *regmap; + bool dpll_locked; + u8 txclk_div[SPDIF_TXRATE_MAX]; + u8 txclk_src[SPDIF_TXRATE_MAX]; + u8 rxclk_src; + struct clk *txclk[SPDIF_TXRATE_MAX]; + struct clk *rxclk; + struct snd_dmaengine_dai_dma_data dma_params_tx; + struct snd_dmaengine_dai_dma_data dma_params_rx; + + /* The name space will be allocated dynamically */ + char name[0]; +}; + + +/* DPLL locked and lock loss interrupt handler */ +static void spdif_irq_dpll_lock(struct fsl_spdif_priv *spdif_priv) +{ + struct regmap *regmap = spdif_priv->regmap; + struct platform_device *pdev = spdif_priv->pdev; + u32 locked; + + regmap_read(regmap, REG_SPDIF_SRPC, &locked); + locked &= SRPC_DPLL_LOCKED; + + dev_dbg(&pdev->dev, "isr: Rx dpll %s \n", + locked ? "locked" : "loss lock"); + + spdif_priv->dpll_locked = locked ? true : false; +} + +/* Receiver found illegal symbol interrupt handler */ +static void spdif_irq_sym_error(struct fsl_spdif_priv *spdif_priv) +{ + struct regmap *regmap = spdif_priv->regmap; + struct platform_device *pdev = spdif_priv->pdev; + + dev_dbg(&pdev->dev, "isr: receiver found illegal symbol\n"); + + if (!spdif_priv->dpll_locked) { + /* DPLL unlocked seems no audio stream */ + regmap_update_bits(regmap, REG_SPDIF_SIE, INT_SYM_ERR, 0); + } +} + +/* U/Q Channel receive register full */ +static void spdif_irq_uqrx_full(struct fsl_spdif_priv *spdif_priv, char name) +{ + struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control; + struct regmap *regmap = spdif_priv->regmap; + struct platform_device *pdev = spdif_priv->pdev; + u32 *pos, size, val, reg; + + switch (name) { + case 'U': + pos = &ctrl->upos; + size = SPDIF_UBITS_SIZE; + reg = REG_SPDIF_SRU; + break; + case 'Q': + pos = &ctrl->qpos; + size = SPDIF_QSUB_SIZE; + reg = REG_SPDIF_SRQ; + break; + default: + dev_err(&pdev->dev, "unsupported channel name\n"); + return; + } + + dev_dbg(&pdev->dev, "isr: %c Channel receive register full\n", name); + + if (*pos >= size * 2) { + *pos = 0; + } else if (unlikely((*pos % size) + 3 > size)) { + dev_err(&pdev->dev, "User bit receivce buffer overflow\n"); + return; + } + + regmap_read(regmap, reg, &val); + ctrl->subcode[*pos++] = val >> 16; + ctrl->subcode[*pos++] = val >> 8; + ctrl->subcode[*pos++] = val; +} + +/* U/Q Channel sync found */ +static void spdif_irq_uq_sync(struct fsl_spdif_priv *spdif_priv) +{ + struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control; + struct platform_device *pdev = spdif_priv->pdev; + + dev_dbg(&pdev->dev, "isr: U/Q Channel sync found\n"); + + /* U/Q buffer reset */ + if (ctrl->qpos == 0) + return; + + /* Set ready to this buffer */ + ctrl->ready_buf = (ctrl->qpos - 1) / SPDIF_QSUB_SIZE + 1; +} + +/* U/Q Channel framing error */ +static void spdif_irq_uq_err(struct fsl_spdif_priv *spdif_priv) +{ + struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control; + struct regmap *regmap = spdif_priv->regmap; + struct platform_device *pdev = spdif_priv->pdev; + u32 val; + + dev_dbg(&pdev->dev, "isr: U/Q Channel framing error\n"); + + /* Read U/Q data to clear the irq and do buffer reset */ + regmap_read(regmap, REG_SPDIF_SRU, &val); + regmap_read(regmap, REG_SPDIF_SRQ, &val); + + /* Drop this U/Q buffer */ + ctrl->ready_buf = 0; + ctrl->upos = 0; + ctrl->qpos = 0; +} + +/* Get spdif interrupt status and clear the interrupt */ +static u32 spdif_intr_status_clear(struct fsl_spdif_priv *spdif_priv) +{ + struct regmap *regmap = spdif_priv->regmap; + u32 val, val2; + + regmap_read(regmap, REG_SPDIF_SIS, &val); + regmap_read(regmap, REG_SPDIF_SIE, &val2); + + regmap_write(regmap, REG_SPDIF_SIC, val & val2); + + return val; +} + +static irqreturn_t spdif_isr(int irq, void *devid) +{ + struct fsl_spdif_priv *spdif_priv = (struct fsl_spdif_priv *)devid; + struct platform_device *pdev = spdif_priv->pdev; + u32 sis; + + sis = spdif_intr_status_clear(spdif_priv); + + if (sis & INT_DPLL_LOCKED) + spdif_irq_dpll_lock(spdif_priv); + + if (sis & INT_TXFIFO_UNOV) + dev_dbg(&pdev->dev, "isr: Tx FIFO under/overrun\n"); + + if (sis & INT_TXFIFO_RESYNC) + dev_dbg(&pdev->dev, "isr: Tx FIFO resync\n"); + + if (sis & INT_CNEW) + dev_dbg(&pdev->dev, "isr: cstatus new\n"); + + if (sis & INT_VAL_NOGOOD) + dev_dbg(&pdev->dev, "isr: validity flag no good\n"); + + if (sis & INT_SYM_ERR) + spdif_irq_sym_error(spdif_priv); + + if (sis & INT_BIT_ERR) + dev_dbg(&pdev->dev, "isr: receiver found parity bit error\n"); + + if (sis & INT_URX_FUL) + spdif_irq_uqrx_full(spdif_priv, 'U'); + + if (sis & INT_URX_OV) + dev_dbg(&pdev->dev, "isr: U Channel receive register overrun\n"); + + if (sis & INT_QRX_FUL) + spdif_irq_uqrx_full(spdif_priv, 'Q'); + + if (sis & INT_QRX_OV) + dev_dbg(&pdev->dev, "isr: Q Channel receive register overrun\n"); + + if (sis & INT_UQ_SYNC) + spdif_irq_uq_sync(spdif_priv); + + if (sis & INT_UQ_ERR) + spdif_irq_uq_err(spdif_priv); + + if (sis & INT_RXFIFO_UNOV) + dev_dbg(&pdev->dev, "isr: Rx FIFO under/overrun\n"); + + if (sis & INT_RXFIFO_RESYNC) + dev_dbg(&pdev->dev, "isr: Rx FIFO resync\n"); + + if (sis & INT_LOSS_LOCK) + spdif_irq_dpll_lock(spdif_priv); + + /* FIXME: Write Tx FIFO to clear TxEm */ + if (sis & INT_TX_EM) + dev_dbg(&pdev->dev, "isr: Tx FIFO empty\n"); + + /* FIXME: Read Rx FIFO to clear RxFIFOFul */ + if (sis & INT_RXFIFO_FUL) + dev_dbg(&pdev->dev, "isr: Rx FIFO full\n"); + + return IRQ_HANDLED; +} + +static int spdif_softreset(struct fsl_spdif_priv *spdif_priv) +{ + struct regmap *regmap = spdif_priv->regmap; + u32 val, cycle = 1000; + + regmap_write(regmap, REG_SPDIF_SCR, SCR_SOFT_RESET); + + /* + * RESET bit would be cleared after finishing its reset procedure, + * which typically lasts 8 cycles. 1000 cycles will keep it safe. + */ + do { + regmap_read(regmap, REG_SPDIF_SCR, &val); + } while ((val & SCR_SOFT_RESET) && cycle--); + + if (cycle) + return 0; + else + return -EBUSY; +} + +static void spdif_set_cstatus(struct spdif_mixer_control *ctrl, + u8 mask, u8 cstatus) +{ + ctrl->ch_status[3] &= ~mask; + ctrl->ch_status[3] |= cstatus & mask; +} + +static void spdif_write_channel_status(struct fsl_spdif_priv *spdif_priv) +{ + struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control; + struct regmap *regmap = spdif_priv->regmap; + struct platform_device *pdev = spdif_priv->pdev; + u32 ch_status; + + ch_status = (bitrev8(ctrl->ch_status[0]) << 16) | + (bitrev8(ctrl->ch_status[1]) << 8) | + bitrev8(ctrl->ch_status[2]); + regmap_write(regmap, REG_SPDIF_STCSCH, ch_status); + + dev_dbg(&pdev->dev, "STCSCH: 0x%06x\n", ch_status); + + ch_status = bitrev8(ctrl->ch_status[3]) << 16; + regmap_write(regmap, REG_SPDIF_STCSCL, ch_status); + + dev_dbg(&pdev->dev, "STCSCL: 0x%06x\n", ch_status); +} + +/* Set SPDIF PhaseConfig register for rx clock */ +static int spdif_set_rx_clksrc(struct fsl_spdif_priv *spdif_priv, + enum spdif_gainsel gainsel, int dpll_locked) +{ + struct regmap *regmap = spdif_priv->regmap; + u8 clksrc = spdif_priv->rxclk_src; + + if (clksrc >= SRPC_CLKSRC_MAX || gainsel >= GAINSEL_MULTI_MAX) + return -EINVAL; + + regmap_update_bits(regmap, REG_SPDIF_SRPC, + SRPC_CLKSRC_SEL_MASK | SRPC_GAINSEL_MASK, + SRPC_CLKSRC_SEL_SET(clksrc) | SRPC_GAINSEL_SET(gainsel)); + + return 0; +} + +static int spdif_set_sample_rate(struct snd_pcm_substream *substream, + int sample_rate) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(rtd->cpu_dai); + struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control; + struct regmap *regmap = spdif_priv->regmap; + struct platform_device *pdev = spdif_priv->pdev; + unsigned long csfs = 0; + u32 stc, mask, rate; + u8 clk, div; + int ret; + + switch (sample_rate) { + case 32000: + rate = SPDIF_TXRATE_32000; + csfs = IEC958_AES3_CON_FS_32000; + break; + case 44100: + rate = SPDIF_TXRATE_44100; + csfs = IEC958_AES3_CON_FS_44100; + break; + case 48000: + rate = SPDIF_TXRATE_48000; + csfs = IEC958_AES3_CON_FS_48000; + break; + default: + dev_err(&pdev->dev, "unsupported sample rate %d\n", sample_rate); + return -EINVAL; + } + + clk = spdif_priv->txclk_src[rate]; + if (clk >= STC_TXCLK_SRC_MAX) { + dev_err(&pdev->dev, "tx clock source is out of range\n"); + return -EINVAL; + } + + div = spdif_priv->txclk_div[rate]; + if (div == 0) { + dev_err(&pdev->dev, "the divisor can't be zero\n"); + return -EINVAL; + } + + /* + * The S/PDIF block needs a clock of 64 * fs * div. The S/PDIF block + * will divide by (div). So request 64 * fs * (div+1) which will + * get rounded. + */ + ret = clk_set_rate(spdif_priv->txclk[rate], 64 * sample_rate * (div + 1)); + if (ret) { + dev_err(&pdev->dev, "failed to set tx clock rate\n"); + return ret; + } + + dev_dbg(&pdev->dev, "expected clock rate = %d\n", + (64 * sample_rate * div)); + dev_dbg(&pdev->dev, "actual clock rate = %ld\n", + clk_get_rate(spdif_priv->txclk[rate])); + + /* set fs field in consumer channel status */ + spdif_set_cstatus(ctrl, IEC958_AES3_CON_FS, csfs); + + /* select clock source and divisor */ + stc = STC_TXCLK_ALL_EN | STC_TXCLK_SRC_SET(clk) | STC_TXCLK_DIV(div); + mask = STC_TXCLK_ALL_EN_MASK | STC_TXCLK_SRC_MASK | STC_TXCLK_DIV_MASK; + regmap_update_bits(regmap, REG_SPDIF_STC, mask, stc); + + dev_dbg(&pdev->dev, "set sample rate to %d\n", sample_rate); + + return 0; +} + +int fsl_spdif_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(rtd->cpu_dai); + struct platform_device *pdev = spdif_priv->pdev; + struct regmap *regmap = spdif_priv->regmap; + u32 scr, mask, i; + int ret; + + /* Reset module and interrupts only for first initialization */ + if (!cpu_dai->active) { + ret = spdif_softreset(spdif_priv); + if (ret) { + dev_err(&pdev->dev, "failed to soft reset\n"); + return ret; + } + + /* Disable all the interrupts */ + regmap_update_bits(regmap, REG_SPDIF_SIE, 0xffffff, 0); + } + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + scr = SCR_TXFIFO_AUTOSYNC | SCR_TXFIFO_CTRL_NORMAL | + SCR_TXSEL_NORMAL | SCR_USRC_SEL_CHIP | + SCR_TXFIFO_FSEL_IF8; + mask = SCR_TXFIFO_AUTOSYNC_MASK | SCR_TXFIFO_CTRL_MASK | + SCR_TXSEL_MASK | SCR_USRC_SEL_MASK | + SCR_TXFIFO_FSEL_MASK; + for (i = 0; i < SPDIF_TXRATE_MAX; i++) + clk_prepare_enable(spdif_priv->txclk[i]); + } else { + scr = SCR_RXFIFO_FSEL_IF8 | SCR_RXFIFO_AUTOSYNC; + mask = SCR_RXFIFO_FSEL_MASK | SCR_RXFIFO_AUTOSYNC_MASK| + SCR_RXFIFO_CTL_MASK | SCR_RXFIFO_OFF_MASK; + clk_prepare_enable(spdif_priv->rxclk); + } + regmap_update_bits(regmap, REG_SPDIF_SCR, mask, scr); + + /* Power up SPDIF module */ + regmap_update_bits(regmap, REG_SPDIF_SCR, SCR_LOW_POWER, 0); + + return 0; +} + +static void fsl_spdif_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(rtd->cpu_dai); + struct regmap *regmap = spdif_priv->regmap; + u32 scr, mask, i; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + scr = 0; + mask = SCR_TXFIFO_AUTOSYNC_MASK | SCR_TXFIFO_CTRL_MASK | + SCR_TXSEL_MASK | SCR_USRC_SEL_MASK | + SCR_TXFIFO_FSEL_MASK; + for (i = 0; i < SPDIF_TXRATE_MAX; i++) + clk_disable_unprepare(spdif_priv->txclk[i]); + } else { + scr = SCR_RXFIFO_OFF | SCR_RXFIFO_CTL_ZERO; + mask = SCR_RXFIFO_FSEL_MASK | SCR_RXFIFO_AUTOSYNC_MASK| + SCR_RXFIFO_CTL_MASK | SCR_RXFIFO_OFF_MASK; + clk_disable_unprepare(spdif_priv->rxclk); + } + regmap_update_bits(regmap, REG_SPDIF_SCR, mask, scr); + + /* Power down SPDIF module only if tx&rx are both inactive */ + if (!cpu_dai->active) { + spdif_intr_status_clear(spdif_priv); + regmap_update_bits(regmap, REG_SPDIF_SCR, + SCR_LOW_POWER, SCR_LOW_POWER); + } +} + +static int fsl_spdif_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(rtd->cpu_dai); + struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control; + struct platform_device *pdev = spdif_priv->pdev; + u32 sample_rate = params_rate(params); + int ret = 0; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + ret = spdif_set_sample_rate(substream, sample_rate); + if (ret) { + dev_err(&pdev->dev, "%s: set sample rate failed: %d\n", + __func__, sample_rate); + return ret; + } + spdif_set_cstatus(ctrl, IEC958_AES3_CON_CLOCK, + IEC958_AES3_CON_CLOCK_1000PPM); + spdif_write_channel_status(spdif_priv); + } else { + /* Setup rx clock source */ + ret = spdif_set_rx_clksrc(spdif_priv, SPDIF_DEFAULT_GAINSEL, 1); + } + + return ret; +} + +static int fsl_spdif_trigger(struct snd_pcm_substream *substream, + int cmd, struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(rtd->cpu_dai); + struct regmap *regmap = spdif_priv->regmap; + int is_playack = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK); + u32 intr = is_playack ? INTR_FOR_PLAYBACK : INTR_FOR_CAPTURE; + u32 dmaen = is_playack ? SCR_DMA_TX_EN : SCR_DMA_RX_EN;; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + regmap_update_bits(regmap, REG_SPDIF_SIE, intr, intr); + regmap_update_bits(regmap, REG_SPDIF_SCR, dmaen, dmaen); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + regmap_update_bits(regmap, REG_SPDIF_SCR, dmaen, 0); + regmap_update_bits(regmap, REG_SPDIF_SIE, intr, 0); + break; + default: + return -EINVAL; + } + + return 0; +} + +struct snd_soc_dai_ops fsl_spdif_dai_ops = { + .startup = fsl_spdif_startup, + .hw_params = fsl_spdif_hw_params, + .trigger = fsl_spdif_trigger, + .shutdown = fsl_spdif_shutdown, +}; + + +/* + * ============================================ + * FSL SPDIF IEC958 controller(mixer) functions + * + * Channel status get/put control + * User bit value get/put control + * Valid bit value get control + * DPLL lock status get control + * User bit sync mode selection control + * ============================================ + */ + +static int fsl_spdif_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + + return 0; +} + +static int fsl_spdif_pb_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *uvalue) +{ + struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); + struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai); + struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control; + + uvalue->value.iec958.status[0] = ctrl->ch_status[0]; + uvalue->value.iec958.status[1] = ctrl->ch_status[1]; + uvalue->value.iec958.status[2] = ctrl->ch_status[2]; + uvalue->value.iec958.status[3] = ctrl->ch_status[3]; + + return 0; +} + +static int fsl_spdif_pb_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *uvalue) +{ + struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); + struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai); + struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control; + + ctrl->ch_status[0] = uvalue->value.iec958.status[0]; + ctrl->ch_status[1] = uvalue->value.iec958.status[1]; + ctrl->ch_status[2] = uvalue->value.iec958.status[2]; + ctrl->ch_status[3] = uvalue->value.iec958.status[3]; + + spdif_write_channel_status(spdif_priv); + + return 0; +} + +/* Get channel status from SPDIF_RX_CCHAN register */ +static int fsl_spdif_capture_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); + struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai); + struct regmap *regmap = spdif_priv->regmap; + u32 cstatus, val; + + regmap_read(regmap, REG_SPDIF_SIS, &val); + if (!(val & INT_CNEW)) { + return -EAGAIN; + } + + regmap_read(regmap, REG_SPDIF_SRCSH, &cstatus); + ucontrol->value.iec958.status[0] = (cstatus >> 16) & 0xFF; + ucontrol->value.iec958.status[1] = (cstatus >> 8) & 0xFF; + ucontrol->value.iec958.status[2] = cstatus & 0xFF; + + regmap_read(regmap, REG_SPDIF_SRCSL, &cstatus); + ucontrol->value.iec958.status[3] = (cstatus >> 16) & 0xFF; + ucontrol->value.iec958.status[4] = (cstatus >> 8) & 0xFF; + ucontrol->value.iec958.status[5] = cstatus & 0xFF; + + /* Clear intr */ + regmap_write(regmap, REG_SPDIF_SIC, INT_CNEW); + + return 0; +} + +/* + * Get User bits (subcode) from chip value which readed out + * in UChannel register. + */ +static int fsl_spdif_subcode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); + struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai); + struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control; + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(&ctrl->ctl_lock, flags); + if (ctrl->ready_buf) { + int idx = (ctrl->ready_buf - 1) * SPDIF_UBITS_SIZE; + memcpy(&ucontrol->value.iec958.subcode[0], + &ctrl->subcode[idx], SPDIF_UBITS_SIZE); + } else { + ret = -EAGAIN; + } + spin_unlock_irqrestore(&ctrl->ctl_lock, flags); + + return ret; +} + +/* Q-subcode infomation. The byte size is SPDIF_UBITS_SIZE/8 */ +static int fsl_spdif_qinfo(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; + uinfo->count = SPDIF_QSUB_SIZE; + + return 0; +} + +/* Get Q subcode from chip value which readed out in QChannel register */ +static int fsl_spdif_qget(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); + struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai); + struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control; + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(&ctrl->ctl_lock, flags); + if (ctrl->ready_buf) { + int idx = (ctrl->ready_buf - 1) * SPDIF_QSUB_SIZE; + memcpy(&ucontrol->value.bytes.data[0], + &ctrl->qsub[idx], SPDIF_QSUB_SIZE); + } else { + ret = -EAGAIN; + } + spin_unlock_irqrestore(&ctrl->ctl_lock, flags); + + return ret; +} + +/* Valid bit infomation */ +static int fsl_spdif_vbit_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + + return 0; +} + +/* Get valid good bit from interrupt status register */ +static int fsl_spdif_vbit_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); + struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai); + struct regmap *regmap = spdif_priv->regmap; + u32 val; + + val = regmap_read(regmap, REG_SPDIF_SIS, &val); + ucontrol->value.integer.value[0] = (val & INT_VAL_NOGOOD) != 0; + regmap_write(regmap, REG_SPDIF_SIC, INT_VAL_NOGOOD); + + return 0; +} + +/* DPLL lock infomation */ +static int fsl_spdif_rxrate_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 16000; + uinfo->value.integer.max = 96000; + + return 0; +} + +static u32 gainsel_multi[GAINSEL_MULTI_MAX] = { + 24, 16, 12, 8, 6, 4, 3, +}; + +/* Get RX data clock rate given the SPDIF bus_clk */ +static int spdif_get_rxclk_rate(struct fsl_spdif_priv *spdif_priv, + enum spdif_gainsel gainsel) +{ + struct regmap *regmap = spdif_priv->regmap; + struct platform_device *pdev = spdif_priv->pdev; + u64 tmpval64, busclk_freq = 0; + u32 freqmeas, phaseconf; + u8 clksrc; + + regmap_read(regmap, REG_SPDIF_SRFM, &freqmeas); + regmap_read(regmap, REG_SPDIF_SRPC, &phaseconf); + + clksrc = (phaseconf >> SRPC_CLKSRC_SEL_OFFSET) & 0xf; + if (srpc_dpll_locked[clksrc] && (phaseconf & SRPC_DPLL_LOCKED)) { + /* Get bus clock from system */ + busclk_freq = clk_get_rate(spdif_priv->rxclk); + } + + /* FreqMeas_CLK = (BUS_CLK * FreqMeas) / 2 ^ 10 / GAINSEL / 128 */ + tmpval64 = (u64) busclk_freq * freqmeas; + do_div(tmpval64, gainsel_multi[gainsel] * 1024); + do_div(tmpval64, 128 * 1024); + + dev_dbg(&pdev->dev, "FreqMeas: %d\n", freqmeas); + dev_dbg(&pdev->dev, "BusclkFreq: %lld\n", busclk_freq); + dev_dbg(&pdev->dev, "RxRate: %lld\n", tmpval64); + + return (int)tmpval64; +} + +/* + * Get DPLL lock or not info from stable interrupt status register. + * User application must use this control to get locked, + * then can do next PCM operation + */ +static int fsl_spdif_rxrate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); + struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai); + int rate = spdif_get_rxclk_rate(spdif_priv, SPDIF_DEFAULT_GAINSEL); + + if (spdif_priv->dpll_locked) + ucontrol->value.integer.value[0] = rate; + else + ucontrol->value.integer.value[0] = 0; + + return 0; +} + +/* User bit sync mode info */ +static int fsl_spdif_usync_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + + return 0; +} + +/* + * User bit sync mode: + * 1 CD User channel subcode + * 0 Non-CD data + */ +static int fsl_spdif_usync_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); + struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai); + struct regmap *regmap = spdif_priv->regmap; + u32 val; + + regmap_read(regmap, REG_SPDIF_SRCD, &val); + ucontrol->value.integer.value[0] = (val & SRCD_CD_USER) != 0; + + return 0; +} + +/* + * User bit sync mode: + * 1 CD User channel subcode + * 0 Non-CD data + */ +static int fsl_spdif_usync_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); + struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai); + struct regmap *regmap = spdif_priv->regmap; + u32 val = ucontrol->value.integer.value[0] << SRCD_CD_USER_OFFSET; + + regmap_update_bits(regmap, REG_SPDIF_SRCD, SRCD_CD_USER, val); + + return 0; +} + +/* FSL SPDIF IEC958 controller defines */ +static struct snd_kcontrol_new fsl_spdif_ctrls[] = { + /* Status cchanel controller */ + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT), + .access = SNDRV_CTL_ELEM_ACCESS_READ | + SNDRV_CTL_ELEM_ACCESS_WRITE | + SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .info = fsl_spdif_info, + .get = fsl_spdif_pb_get, + .put = fsl_spdif_pb_put, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("", CAPTURE, DEFAULT), + .access = SNDRV_CTL_ELEM_ACCESS_READ | + SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .info = fsl_spdif_info, + .get = fsl_spdif_capture_get, + }, + /* User bits controller */ + { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "IEC958 Subcode Capture Default", + .access = SNDRV_CTL_ELEM_ACCESS_READ | + SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .info = fsl_spdif_info, + .get = fsl_spdif_subcode_get, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "IEC958 Q-subcode Capture Default", + .access = SNDRV_CTL_ELEM_ACCESS_READ | + SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .info = fsl_spdif_qinfo, + .get = fsl_spdif_qget, + }, + /* Valid bit error controller */ + { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "IEC958 V-Bit Errors", + .access = SNDRV_CTL_ELEM_ACCESS_READ | + SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .info = fsl_spdif_vbit_info, + .get = fsl_spdif_vbit_get, + }, + /* DPLL lock info get controller */ + { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "RX Sample Rate", + .access = SNDRV_CTL_ELEM_ACCESS_READ | + SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .info = fsl_spdif_rxrate_info, + .get = fsl_spdif_rxrate_get, + }, + /* User bit sync mode set/get controller */ + { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "IEC958 USyncMode CDText", + .access = SNDRV_CTL_ELEM_ACCESS_READ | + SNDRV_CTL_ELEM_ACCESS_WRITE | + SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .info = fsl_spdif_usync_info, + .get = fsl_spdif_usync_get, + .put = fsl_spdif_usync_put, + }, +}; + +static int fsl_spdif_dai_probe(struct snd_soc_dai *dai) +{ + struct fsl_spdif_priv *spdif_private = snd_soc_dai_get_drvdata(dai); + + dai->playback_dma_data = &spdif_private->dma_params_tx; + dai->capture_dma_data = &spdif_private->dma_params_rx; + + snd_soc_add_dai_controls(dai, fsl_spdif_ctrls, ARRAY_SIZE(fsl_spdif_ctrls)); + + return 0; +} + +struct snd_soc_dai_driver fsl_spdif_dai = { + .probe = &fsl_spdif_dai_probe, + .playback = { + .channels_min = 2, + .channels_max = 2, + .rates = FSL_SPDIF_RATES_PLAYBACK, + .formats = FSL_SPDIF_FORMATS_PLAYBACK, + }, + .capture = { + .channels_min = 2, + .channels_max = 2, + .rates = FSL_SPDIF_RATES_CAPTURE, + .formats = FSL_SPDIF_FORMATS_CAPTURE, + }, + .ops = &fsl_spdif_dai_ops, +}; + +static const struct snd_soc_component_driver fsl_spdif_component = { + .name = "fsl-spdif", +}; + +/* + * ================ + * FSL SPDIF REGMAP + * ================ + */ + +static bool fsl_spdif_readable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case REG_SPDIF_SCR: + case REG_SPDIF_SRCD: + case REG_SPDIF_SRPC: + case REG_SPDIF_SIE: + case REG_SPDIF_SIS: + case REG_SPDIF_SRL: + case REG_SPDIF_SRR: + case REG_SPDIF_SRCSH: + case REG_SPDIF_SRCSL: + case REG_SPDIF_SRU: + case REG_SPDIF_SRQ: + case REG_SPDIF_STCSCH: + case REG_SPDIF_STCSCL: + case REG_SPDIF_SRFM: + case REG_SPDIF_STC: + return true; + default: + return false; + }; +} + +static bool fsl_spdif_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case REG_SPDIF_SCR: + case REG_SPDIF_SRCD: + case REG_SPDIF_SRPC: + case REG_SPDIF_SIE: + case REG_SPDIF_SIC: + case REG_SPDIF_STL: + case REG_SPDIF_STR: + case REG_SPDIF_STCSCH: + case REG_SPDIF_STCSCL: + case REG_SPDIF_STC: + return true; + default: + return false; + }; +} + +static const struct regmap_config fsl_spdif_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + + .max_register = REG_SPDIF_STC, + .readable_reg = fsl_spdif_readable_reg, + .writeable_reg = fsl_spdif_writeable_reg, +}; + +static u32 fsl_spdif_txclk_caldiv(struct fsl_spdif_priv *spdif_priv, + struct clk *clk, u64 savesub, + enum spdif_txrate index) +{ + const u32 rate[] = { 32000, 44100, 48000 }; + u64 rate_ideal, rate_actual, sub; + u32 div, arate; + + for (div = 1; div <= 128; div++) { + rate_ideal = rate[index] * (div + 1) * 64; + rate_actual = clk_round_rate(clk, rate_ideal); + + arate = rate_actual / 64; + arate /= div; + + if (arate == rate[index]) { + /* We are lucky */ + savesub = 0; + spdif_priv->txclk_div[index] = div; + break; + } else if (arate / rate[index] == 1) { + /* A little bigger than expect */ + sub = (arate - rate[index]) * 100000; + do_div(sub, rate[index]); + if (sub < savesub) { + savesub = sub; + spdif_priv->txclk_div[index] = div; + } + } else if (rate[index] / arate == 1) { + /* A little smaller than expect */ + sub = (rate[index] - arate) * 100000; + do_div(sub, rate[index]); + if (sub < savesub) { + savesub = sub; + spdif_priv->txclk_div[index] = div; + } + } + } + + return savesub; +} + +static int fsl_spdif_probe_txclk(struct fsl_spdif_priv *spdif_priv, + enum spdif_txrate index) +{ + const u32 rate[] = { 32000, 44100, 48000 }; + struct platform_device *pdev = spdif_priv->pdev; + struct device *dev = &pdev->dev; + u64 savesub = 100000, ret; + struct clk *clk; + char tmp[16]; + int i; + + for (i = 0; i < STC_TXCLK_SRC_MAX; i++) { + sprintf(tmp, "rxtx%d", i); + clk = devm_clk_get(&pdev->dev, tmp); + if (IS_ERR(clk)) { + dev_err(dev, "no rxtx%d clock in devicetree\n", i); + return PTR_ERR(clk); + } + if (!clk_get_rate(clk)) + continue; + + ret = fsl_spdif_txclk_caldiv(spdif_priv, clk, savesub, index); + if (savesub == ret) + continue; + + savesub = ret; + spdif_priv->txclk[index] = clk; + spdif_priv->txclk_src[index] = i; + + /* To quick catch a divisor, we allow a 0.1% deviation */ + if (savesub < 100) + break; + } + + dev_dbg(&pdev->dev, "use rxtx%d as tx clock source for %dHz sample rate", + spdif_priv->txclk_src[index], rate[index]); + dev_dbg(&pdev->dev, "use divisor %d for %dHz sample rate", + spdif_priv->txclk_div[index], rate[index]); + + return 0; +} + +static int fsl_spdif_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct fsl_spdif_priv *spdif_priv; + struct spdif_mixer_control *ctrl; + struct resource *res; + void __iomem *regs; + int irq, ret, i; + + if (!np) + return -ENODEV; + + spdif_priv = devm_kzalloc(&pdev->dev, + sizeof(struct fsl_spdif_priv) + strlen(np->name) + 1, + GFP_KERNEL); + if (!spdif_priv) + return -ENOMEM; + + strcpy(spdif_priv->name, np->name); + + spdif_priv->pdev = pdev; + + /* Initialize this copy of the CPU DAI driver structure */ + memcpy(&spdif_priv->cpu_dai_drv, &fsl_spdif_dai, sizeof(fsl_spdif_dai)); + spdif_priv->cpu_dai_drv.name = spdif_priv->name; + + /* Get the addresses and IRQ */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (IS_ERR(res)) { + dev_err(&pdev->dev, "could not determine device resources\n"); + return PTR_ERR(res); + } + + regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(regs)) { + dev_err(&pdev->dev, "could not map device resources\n"); + return PTR_ERR(regs); + } + + spdif_priv->regmap = devm_regmap_init_mmio_clk(&pdev->dev, + "core", regs, &fsl_spdif_regmap_config); + if (IS_ERR(spdif_priv->regmap)) { + dev_err(&pdev->dev, "regmap init failed\n"); + return PTR_ERR(spdif_priv->regmap); + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "no irq for node %s\n", np->full_name); + return irq; + } + + ret = devm_request_irq(&pdev->dev, irq, spdif_isr, 0, + spdif_priv->name, spdif_priv); + if (ret) { + dev_err(&pdev->dev, "could not claim irq %u\n", irq); + return ret; + } + + /* Select clock source for rx/tx clock */ + spdif_priv->rxclk = devm_clk_get(&pdev->dev, "rxtx1"); + if (IS_ERR(spdif_priv->rxclk)) { + dev_err(&pdev->dev, "no rxtx1 clock in devicetree\n"); + return PTR_ERR(spdif_priv->rxclk); + } + spdif_priv->rxclk_src = DEFAULT_RXCLK_SRC; + + for (i = 0; i < SPDIF_TXRATE_MAX; i++) { + ret = fsl_spdif_probe_txclk(spdif_priv, i); + if (ret) + return ret; + } + + /* Initial spinlock for control data */ + ctrl = &spdif_priv->fsl_spdif_control; + spin_lock_init(&ctrl->ctl_lock); + + /* Init tx channel status default value */ + ctrl->ch_status[0] = + IEC958_AES0_CON_NOT_COPYRIGHT | IEC958_AES0_CON_EMPHASIS_5015; + ctrl->ch_status[1] = IEC958_AES1_CON_DIGDIGCONV_ID; + ctrl->ch_status[2] = 0x00; + ctrl->ch_status[3] = + IEC958_AES3_CON_FS_44100 | IEC958_AES3_CON_CLOCK_1000PPM; + + spdif_priv->dpll_locked = false; + + spdif_priv->dma_params_tx.maxburst = FSL_SPDIF_TXFIFO_WML; + spdif_priv->dma_params_rx.maxburst = FSL_SPDIF_RXFIFO_WML; + spdif_priv->dma_params_tx.addr = res->start + REG_SPDIF_STL; + spdif_priv->dma_params_rx.addr = res->start + REG_SPDIF_SRL; + + /* Register with ASoC */ + dev_set_drvdata(&pdev->dev, spdif_priv); + + ret = snd_soc_register_component(&pdev->dev, &fsl_spdif_component, + &spdif_priv->cpu_dai_drv, 1); + if (ret) { + dev_err(&pdev->dev, "failed to register DAI: %d\n", ret); + goto error_dev; + } + + ret = imx_pcm_dma_init(pdev); + if (ret) { + dev_err(&pdev->dev, "imx_pcm_dma_init failed: %d\n", ret); + goto error_component; + } + + return ret; + +error_component: + snd_soc_unregister_component(&pdev->dev); +error_dev: + dev_set_drvdata(&pdev->dev, NULL); + + return ret; +} + +static int fsl_spdif_remove(struct platform_device *pdev) +{ + imx_pcm_dma_exit(pdev); + snd_soc_unregister_component(&pdev->dev); + dev_set_drvdata(&pdev->dev, NULL); + + return 0; +} + +static const struct of_device_id fsl_spdif_dt_ids[] = { + { .compatible = "fsl,imx35-spdif", }, + {} +}; +MODULE_DEVICE_TABLE(of, fsl_spdif_dt_ids); + +static struct platform_driver fsl_spdif_driver = { + .driver = { + .name = "fsl-spdif-dai", + .owner = THIS_MODULE, + .of_match_table = fsl_spdif_dt_ids, + }, + .probe = fsl_spdif_probe, + .remove = fsl_spdif_remove, +}; + +module_platform_driver(fsl_spdif_driver); + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("Freescale S/PDIF CPU DAI Driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:fsl-spdif-dai"); diff --git a/sound/soc/fsl/fsl_spdif.h b/sound/soc/fsl/fsl_spdif.h new file mode 100644 index 0000000..b126679 --- /dev/null +++ b/sound/soc/fsl/fsl_spdif.h @@ -0,0 +1,191 @@ +/* + * fsl_spdif.h - ALSA S/PDIF interface for the Freescale i.MX SoC + * + * Copyright (C) 2013 Freescale Semiconductor, Inc. + * + * Author: Nicolin Chen + * + * Based on fsl_ssi.h + * Author: Timur Tabi + * Copyright 2007-2008 Freescale Semiconductor, Inc. + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +#ifndef _FSL_SPDIF_DAI_H +#define _FSL_SPDIF_DAI_H + +/* S/PDIF Register Map */ +#define REG_SPDIF_SCR 0x0 /* SPDIF Configuration Register */ +#define REG_SPDIF_SRCD 0x4 /* CDText Control Register */ +#define REG_SPDIF_SRPC 0x8 /* PhaseConfig Register */ +#define REG_SPDIF_SIE 0xc /* InterruptEn Register */ +#define REG_SPDIF_SIS 0x10 /* InterruptStat Register */ +#define REG_SPDIF_SIC 0x10 /* InterruptClear Register */ +#define REG_SPDIF_SRL 0x14 /* SPDIFRxLeft Register */ +#define REG_SPDIF_SRR 0x18 /* SPDIFRxRight Register */ +#define REG_SPDIF_SRCSH 0x1c /* SPDIFRxCChannel_h Register */ +#define REG_SPDIF_SRCSL 0x20 /* SPDIFRxCChannel_l Register */ +#define REG_SPDIF_SRU 0x24 /* UchannelRx Register */ +#define REG_SPDIF_SRQ 0x28 /* QchannelRx Register */ +#define REG_SPDIF_STL 0x2C /* SPDIFTxLeft Register */ +#define REG_SPDIF_STR 0x30 /* SPDIFTxRight Register */ +#define REG_SPDIF_STCSCH 0x34 /* SPDIFTxCChannelCons_h Register */ +#define REG_SPDIF_STCSCL 0x38 /* SPDIFTxCChannelCons_l Register */ +#define REG_SPDIF_SRFM 0x44 /* FreqMeas Register */ +#define REG_SPDIF_STC 0x50 /* SPDIFTxClk Register */ + + +/* SPDIF Configuration register */ +#define SCR_RXFIFO_CTL_OFFSET 23 +#define SCR_RXFIFO_CTL_MASK (1 << SCR_RXFIFO_CTL_OFFSET) +#define SCR_RXFIFO_CTL_ZERO (1 << SCR_RXFIFO_CTL_OFFSET) +#define SCR_RXFIFO_OFF_OFFSET 22 +#define SCR_RXFIFO_OFF_MASK (1 << SCR_RXFIFO_OFF_OFFSET) +#define SCR_RXFIFO_OFF (1 << SCR_RXFIFO_OFF_OFFSET) +#define SCR_RXFIFO_RST_OFFSET 21 +#define SCR_RXFIFO_RST_MASK (1 << SCR_RXFIFO_RST_OFFSET) +#define SCR_RXFIFO_RST (1 << SCR_RXFIFO_RST_OFFSET) +#define SCR_RXFIFO_FSEL_OFFSET 19 +#define SCR_RXFIFO_FSEL_MASK (0x3 << SCR_RXFIFO_FSEL_OFFSET) +#define SCR_RXFIFO_FSEL_IF0 (0x0 << SCR_RXFIFO_FSEL_OFFSET) +#define SCR_RXFIFO_FSEL_IF4 (0x1 << SCR_RXFIFO_FSEL_OFFSET) +#define SCR_RXFIFO_FSEL_IF8 (0x2 << SCR_RXFIFO_FSEL_OFFSET) +#define SCR_RXFIFO_FSEL_IF12 (0x3 << SCR_RXFIFO_FSEL_OFFSET) +#define SCR_RXFIFO_AUTOSYNC_OFFSET 18 +#define SCR_RXFIFO_AUTOSYNC_MASK (1 << SCR_RXFIFO_AUTOSYNC_OFFSET) +#define SCR_RXFIFO_AUTOSYNC (1 << SCR_RXFIFO_AUTOSYNC_OFFSET) +#define SCR_TXFIFO_AUTOSYNC_OFFSET 17 +#define SCR_TXFIFO_AUTOSYNC_MASK (1 << SCR_TXFIFO_AUTOSYNC_OFFSET) +#define SCR_TXFIFO_AUTOSYNC (1 << SCR_TXFIFO_AUTOSYNC_OFFSET) +#define SCR_TXFIFO_FSEL_OFFSET 15 +#define SCR_TXFIFO_FSEL_MASK (0x3 << SCR_TXFIFO_FSEL_OFFSET) +#define SCR_TXFIFO_FSEL_IF0 (0x0 << SCR_TXFIFO_FSEL_OFFSET) +#define SCR_TXFIFO_FSEL_IF4 (0x1 << SCR_TXFIFO_FSEL_OFFSET) +#define SCR_TXFIFO_FSEL_IF8 (0x2 << SCR_TXFIFO_FSEL_OFFSET) +#define SCR_TXFIFO_FSEL_IF12 (0x3 << SCR_TXFIFO_FSEL_OFFSET) +#define SCR_LOW_POWER (1 << 13) +#define SCR_SOFT_RESET (1 << 12) +#define SCR_TXFIFO_CTRL_OFFSET 10 +#define SCR_TXFIFO_CTRL_MASK (0x3 << SCR_TXFIFO_CTRL_OFFSET) +#define SCR_TXFIFO_CTRL_ZERO (0x0 << SCR_TXFIFO_CTRL_OFFSET) +#define SCR_TXFIFO_CTRL_NORMAL (0x1 << SCR_TXFIFO_CTRL_OFFSET) +#define SCR_TXFIFO_CTRL_ONESAMPLE (0x2 << SCR_TXFIFO_CTRL_OFFSET) +#define SCR_DMA_RX_EN_OFFSET 9 +#define SCR_DMA_RX_EN_MASK (1 << SCR_DMA_RX_EN_OFFSET) +#define SCR_DMA_RX_EN (1 << SCR_DMA_RX_EN_OFFSET) +#define SCR_DMA_TX_EN_OFFSET 8 +#define SCR_DMA_TX_EN_MASK (1 << SCR_DMA_TX_EN_OFFSET) +#define SCR_DMA_TX_EN (1 << SCR_DMA_TX_EN_OFFSET) +#define SCR_VAL_OFFSET 5 +#define SCR_VAL_MASK (1 << SCR_VAL_OFFSET) +#define SCR_VAL_CLEAR (1 << SCR_VAL_OFFSET) +#define SCR_TXSEL_OFFSET 2 +#define SCR_TXSEL_MASK (0x7 << SCR_TXSEL_OFFSET) +#define SCR_TXSEL_OFF (0 << SCR_TXSEL_OFFSET) +#define SCR_TXSEL_RX (1 << SCR_TXSEL_OFFSET) +#define SCR_TXSEL_NORMAL (0x5 << SCR_TXSEL_OFFSET) +#define SCR_USRC_SEL_OFFSET 0x0 +#define SCR_USRC_SEL_MASK (0x3 << SCR_USRC_SEL_OFFSET) +#define SCR_USRC_SEL_NONE (0x0 << SCR_USRC_SEL_OFFSET) +#define SCR_USRC_SEL_RECV (0x1 << SCR_USRC_SEL_OFFSET) +#define SCR_USRC_SEL_CHIP (0x3 << SCR_USRC_SEL_OFFSET) + +/* SPDIF CDText control */ +#define SRCD_CD_USER_OFFSET 1 +#define SRCD_CD_USER (1 << SRCD_CD_USER_OFFSET) + +/* SPDIF Phase Configuration register */ +#define SRPC_DPLL_LOCKED (1 << 6) +#define SRPC_CLKSRC_SEL_OFFSET 7 +#define SRPC_CLKSRC_SEL_MASK (0xf << SRPC_CLKSRC_SEL_OFFSET) +#define SRPC_CLKSRC_SEL_SET(x) ((x << SRPC_CLKSRC_SEL_OFFSET) & SRPC_CLKSRC_SEL_MASK) +#define SRPC_CLKSRC_SEL_LOCKED_OFFSET1 5 +#define SRPC_CLKSRC_SEL_LOCKED_OFFSET2 2 +#define SRPC_GAINSEL_OFFSET 3 +#define SRPC_GAINSEL_MASK (0x7 << SRPC_GAINSEL_OFFSET) +#define SRPC_GAINSEL_SET(x) ((x << SRPC_GAINSEL_OFFSET) & SRPC_GAINSEL_MASK) + +#define SRPC_CLKSRC_MAX 16 + +enum spdif_gainsel { + GAINSEL_MULTI_24 = 0, + GAINSEL_MULTI_16, + GAINSEL_MULTI_12, + GAINSEL_MULTI_8, + GAINSEL_MULTI_6, + GAINSEL_MULTI_4, + GAINSEL_MULTI_3, +}; +#define GAINSEL_MULTI_MAX (GAINSEL_MULTI_3 + 1) +#define SPDIF_DEFAULT_GAINSEL GAINSEL_MULTI_8 + +/* SPDIF interrupt mask define */ +#define INT_DPLL_LOCKED (1 << 20) +#define INT_TXFIFO_UNOV (1 << 19) +#define INT_TXFIFO_RESYNC (1 << 18) +#define INT_CNEW (1 << 17) +#define INT_VAL_NOGOOD (1 << 16) +#define INT_SYM_ERR (1 << 15) +#define INT_BIT_ERR (1 << 14) +#define INT_URX_FUL (1 << 10) +#define INT_URX_OV (1 << 9) +#define INT_QRX_FUL (1 << 8) +#define INT_QRX_OV (1 << 7) +#define INT_UQ_SYNC (1 << 6) +#define INT_UQ_ERR (1 << 5) +#define INT_RXFIFO_UNOV (1 << 4) +#define INT_RXFIFO_RESYNC (1 << 3) +#define INT_LOSS_LOCK (1 << 2) +#define INT_TX_EM (1 << 1) +#define INT_RXFIFO_FUL (1 << 0) + +/* SPDIF Clock register */ +#define STC_SYSCLK_DIV_OFFSET 11 +#define STC_SYSCLK_DIV_MASK (0x1ff << STC_TXCLK_SRC_OFFSET) +#define STC_SYSCLK_DIV(x) ((((x) - 1) << STC_TXCLK_DIV_OFFSET) & STC_SYSCLK_DIV_MASK) +#define STC_TXCLK_SRC_OFFSET 8 +#define STC_TXCLK_SRC_MASK (0x7 << STC_TXCLK_SRC_OFFSET) +#define STC_TXCLK_SRC_SET(x) ((x << STC_TXCLK_SRC_OFFSET) & STC_TXCLK_SRC_MASK) +#define STC_TXCLK_ALL_EN_OFFSET 7 +#define STC_TXCLK_ALL_EN_MASK (1 << STC_TXCLK_ALL_EN_OFFSET) +#define STC_TXCLK_ALL_EN (1 << STC_TXCLK_ALL_EN_OFFSET) +#define STC_TXCLK_DIV_OFFSET 0 +#define STC_TXCLK_DIV_MASK (0x7ff << STC_TXCLK_DIV_OFFSET) +#define STC_TXCLK_DIV(x) ((((x) - 1) << STC_TXCLK_DIV_OFFSET) & STC_TXCLK_DIV_MASK) +#define STC_TXCLK_SRC_MAX 8 + +/* SPDIF tx rate */ +enum spdif_txrate { + SPDIF_TXRATE_32000 = 0, + SPDIF_TXRATE_44100, + SPDIF_TXRATE_48000, +}; +#define SPDIF_TXRATE_MAX (SPDIF_TXRATE_48000 + 1) + + +#define SPDIF_CSTATUS_BYTE 6 +#define SPDIF_UBITS_SIZE 96 +#define SPDIF_QSUB_SIZE (SPDIF_UBITS_SIZE / 8) + + +#define FSL_SPDIF_RATES_PLAYBACK (SNDRV_PCM_RATE_32000 | \ + SNDRV_PCM_RATE_44100 | \ + SNDRV_PCM_RATE_48000) + +#define FSL_SPDIF_RATES_CAPTURE (SNDRV_PCM_RATE_16000 | \ + SNDRV_PCM_RATE_32000 | \ + SNDRV_PCM_RATE_44100 | \ + SNDRV_PCM_RATE_48000 | \ + SNDRV_PCM_RATE_64000 | \ + SNDRV_PCM_RATE_96000) + +#define FSL_SPDIF_FORMATS_PLAYBACK (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE) + +#define FSL_SPDIF_FORMATS_CAPTURE (SNDRV_PCM_FMTBIT_S24_LE) + +#endif /* _FSL_SPDIF_DAI_H */ -- cgit v0.10.2 From 528ba522e18b95d25adc62367f04290776c390e5 Mon Sep 17 00:00:00 2001 From: Knut Petersen Date: Wed, 21 Aug 2013 09:18:54 +0200 Subject: ALSA: rme96: Add PM support v3 Without proper power management handling, the first use of a Digi96/8 anytime after a suspend / resume cycle will start playback with distortions. v3: Abort if vmalloc() of suspend buffers fail, but do not leak memory in that case. [fixed wrong memory leak fix again -- tiwai] Signed-off-by: Knut Petersen Signed-off-by: Takashi Iwai diff --git a/sound/pci/rme96.c b/sound/pci/rme96.c index 4e9a556..0506530 100644 --- a/sound/pci/rme96.c +++ b/sound/pci/rme96.c @@ -239,6 +239,13 @@ struct rme96 { u8 rev; /* card revision number */ +#ifdef CONFIG_PM + u32 playback_pointer; + u32 capture_pointer; + void *playback_suspend_buffer; + void *capture_suspend_buffer; +#endif + struct snd_pcm_substream *playback_substream; struct snd_pcm_substream *capture_substream; @@ -370,6 +377,7 @@ static struct snd_pcm_hardware snd_rme96_playback_spdif_info = .info = (SNDRV_PCM_INFO_MMAP_IOMEM | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START | + SNDRV_PCM_INFO_RESUME | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_PAUSE), .formats = (SNDRV_PCM_FMTBIT_S16_LE | @@ -400,6 +408,7 @@ static struct snd_pcm_hardware snd_rme96_capture_spdif_info = .info = (SNDRV_PCM_INFO_MMAP_IOMEM | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START | + SNDRV_PCM_INFO_RESUME | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_PAUSE), .formats = (SNDRV_PCM_FMTBIT_S16_LE | @@ -430,6 +439,7 @@ static struct snd_pcm_hardware snd_rme96_playback_adat_info = .info = (SNDRV_PCM_INFO_MMAP_IOMEM | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START | + SNDRV_PCM_INFO_RESUME | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_PAUSE), .formats = (SNDRV_PCM_FMTBIT_S16_LE | @@ -456,6 +466,7 @@ static struct snd_pcm_hardware snd_rme96_capture_adat_info = .info = (SNDRV_PCM_INFO_MMAP_IOMEM | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START | + SNDRV_PCM_INFO_RESUME | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_PAUSE), .formats = (SNDRV_PCM_FMTBIT_S16_LE | @@ -1386,6 +1397,7 @@ snd_rme96_playback_trigger(struct snd_pcm_substream *substream, } break; + case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_STOP: if (RME96_ISPLAYING(rme96)) { if (substream != rme96->playback_substream) @@ -1401,6 +1413,7 @@ snd_rme96_playback_trigger(struct snd_pcm_substream *substream, : RME96_STOP_PLAYBACK); break; + case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: if (!RME96_ISPLAYING(rme96)) snd_rme96_trigger(rme96, sync ? RME96_RESUME_BOTH @@ -1441,6 +1454,7 @@ snd_rme96_capture_trigger(struct snd_pcm_substream *substream, } break; + case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_STOP: if (RME96_ISRECORDING(rme96)) { if (substream != rme96->capture_substream) @@ -1456,6 +1470,7 @@ snd_rme96_capture_trigger(struct snd_pcm_substream *substream, : RME96_STOP_CAPTURE); break; + case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: if (!RME96_ISRECORDING(rme96)) snd_rme96_trigger(rme96, sync ? RME96_RESUME_BOTH @@ -1556,6 +1571,10 @@ snd_rme96_free(void *private_data) pci_release_regions(rme96->pci); rme96->port = 0; } +#ifdef CONFIG_PM + vfree(rme96->playback_suspend_buffer); + vfree(rme96->capture_suspend_buffer); +#endif pci_disable_device(rme96->pci); } @@ -2354,6 +2373,83 @@ snd_rme96_create_switches(struct snd_card *card, * Card initialisation */ +#ifdef CONFIG_PM + +static int +snd_rme96_suspend(struct pci_dev *pci, + pm_message_t state) +{ + struct snd_card *card = pci_get_drvdata(pci); + struct rme96 *rme96 = card->private_data; + + snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); + snd_pcm_suspend(rme96->playback_substream); + snd_pcm_suspend(rme96->capture_substream); + + /* save capture & playback pointers */ + rme96->playback_pointer = readl(rme96->iobase + RME96_IO_GET_PLAY_POS) + & RME96_RCR_AUDIO_ADDR_MASK; + rme96->capture_pointer = readl(rme96->iobase + RME96_IO_GET_REC_POS) + & RME96_RCR_AUDIO_ADDR_MASK; + + /* save playback and capture buffers */ + memcpy_fromio(rme96->playback_suspend_buffer, + rme96->iobase + RME96_IO_PLAY_BUFFER, RME96_BUFFER_SIZE); + memcpy_fromio(rme96->capture_suspend_buffer, + rme96->iobase + RME96_IO_REC_BUFFER, RME96_BUFFER_SIZE); + + /* disable the DAC */ + rme96->areg &= ~RME96_AR_DAC_EN; + writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG); + + pci_disable_device(pci); + pci_save_state(pci); + + return 0; +} + +static int +snd_rme96_resume(struct pci_dev *pci) +{ + struct snd_card *card = pci_get_drvdata(pci); + struct rme96 *rme96 = card->private_data; + + pci_restore_state(pci); + pci_enable_device(pci); + + /* reset playback and record buffer pointers */ + writel(0, rme96->iobase + RME96_IO_SET_PLAY_POS + + rme96->playback_pointer); + writel(0, rme96->iobase + RME96_IO_SET_REC_POS + + rme96->capture_pointer); + + /* restore playback and capture buffers */ + memcpy_toio(rme96->iobase + RME96_IO_PLAY_BUFFER, + rme96->playback_suspend_buffer, RME96_BUFFER_SIZE); + memcpy_toio(rme96->iobase + RME96_IO_REC_BUFFER, + rme96->capture_suspend_buffer, RME96_BUFFER_SIZE); + + /* reset the ADC */ + writel(rme96->areg | RME96_AR_PD2, + rme96->iobase + RME96_IO_ADDITIONAL_REG); + writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG); + + /* reset and enable DAC, restore analog volume */ + snd_rme96_reset_dac(rme96); + rme96->areg |= RME96_AR_DAC_EN; + writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG); + if (RME96_HAS_ANALOG_OUT(rme96)) { + usleep_range(3000, 10000); + snd_rme96_apply_dac_volume(rme96); + } + + snd_power_change_state(card, SNDRV_CTL_POWER_D0); + + return 0; +} + +#endif + static void snd_rme96_card_free(struct snd_card *card) { snd_rme96_free(card->private_data); @@ -2390,6 +2486,23 @@ snd_rme96_probe(struct pci_dev *pci, return err; } +#ifdef CONFIG_PM + rme96->playback_suspend_buffer = vmalloc(RME96_BUFFER_SIZE); + if (!rme96->playback_suspend_buffer) { + snd_printk(KERN_ERR + "Failed to allocate playback suspend buffer!\n"); + snd_card_free(card); + return -ENOMEM; + } + rme96->capture_suspend_buffer = vmalloc(RME96_BUFFER_SIZE); + if (!rme96->capture_suspend_buffer) { + snd_printk(KERN_ERR + "Failed to allocate capture suspend buffer!\n"); + snd_card_free(card); + return -ENOMEM; + } +#endif + strcpy(card->driver, "Digi96"); switch (rme96->pci->device) { case PCI_DEVICE_ID_RME_DIGI96: @@ -2432,6 +2545,10 @@ static struct pci_driver rme96_driver = { .id_table = snd_rme96_ids, .probe = snd_rme96_probe, .remove = snd_rme96_remove, +#ifdef CONFIG_PM + .suspend = snd_rme96_suspend, + .resume = snd_rme96_resume, +#endif }; module_pci_driver(rme96_driver); -- cgit v0.10.2 From 2af02be71a8ae28ae4e3b82a2866b1aa1f43d8fb Mon Sep 17 00:00:00 2001 From: Kailang Yang Date: Thu, 22 Aug 2013 10:03:50 +0200 Subject: ALSA: hda - Fix ALC283 headphone pop-noise better Fixed ALC283 D3 to D0 and D0 to D3 Headphone pop noise. The previous fix [c5177c86: ALSA: hda - Fix the noise after suspend on ALC283 codec] doesn't work sufficiently for some laptops. Signed-off-by: Kailang Yang Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 7d00639..4bdccd1 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -2527,6 +2527,7 @@ enum { ALC269_TYPE_ALC269VD, ALC269_TYPE_ALC280, ALC269_TYPE_ALC282, + ALC269_TYPE_ALC283, ALC269_TYPE_ALC284, ALC269_TYPE_ALC286, }; @@ -2552,6 +2553,7 @@ static int alc269_parse_auto_config(struct hda_codec *codec) case ALC269_TYPE_ALC269VB: case ALC269_TYPE_ALC269VD: case ALC269_TYPE_ALC282: + case ALC269_TYPE_ALC283: case ALC269_TYPE_ALC286: ssids = alc269_ssids; break; @@ -2586,6 +2588,74 @@ static void alc269_shutup(struct hda_codec *codec) snd_hda_shutup_pins(codec); } +static void alc283_init(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + hda_nid_t hp_pin = spec->gen.autocfg.hp_pins[0]; + bool hp_pin_sense; + int val; + + if (!hp_pin) + return; + hp_pin_sense = snd_hda_jack_detect(codec, hp_pin); + + /* Index 0x43 Direct Drive HP AMP LPM Control 1 */ + /* Headphone capless set to high power mode */ + alc_write_coef_idx(codec, 0x43, 0x9004); + + snd_hda_codec_write(codec, hp_pin, 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); + + if (hp_pin_sense) + msleep(85); + + snd_hda_codec_write(codec, hp_pin, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); + + if (hp_pin_sense) + msleep(85); + /* Index 0x46 Combo jack auto switch control 2 */ + /* 3k pull low control for Headset jack. */ + val = alc_read_coef_idx(codec, 0x46); + alc_write_coef_idx(codec, 0x46, val & ~(3 << 12)); + /* Headphone capless set to normal mode */ + alc_write_coef_idx(codec, 0x43, 0x9614); +} + +static void alc283_shutup(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + hda_nid_t hp_pin = spec->gen.autocfg.hp_pins[0]; + bool hp_pin_sense; + int val; + + if (!hp_pin) { + alc269_shutup(codec); + return; + } + + hp_pin_sense = snd_hda_jack_detect(codec, hp_pin); + + alc_write_coef_idx(codec, 0x43, 0x9004); + + snd_hda_codec_write(codec, hp_pin, 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); + + if (hp_pin_sense) + msleep(85); + + snd_hda_codec_write(codec, hp_pin, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0); + + val = alc_read_coef_idx(codec, 0x46); + alc_write_coef_idx(codec, 0x46, val | (3 << 12)); + + if (hp_pin_sense) + msleep(85); + snd_hda_shutup_pins(codec); + alc_write_coef_idx(codec, 0x43, 0x9614); +} + static void alc5505_coef_set(struct hda_codec *codec, unsigned int index_reg, unsigned int val) { @@ -2715,12 +2785,6 @@ static int alc269_resume(struct hda_codec *codec) if (spec->has_alc5505_dsp) alc5505_dsp_resume(codec); - /* clear the power-save mode for ALC283 */ - if (codec->vendor_id == 0x10ec0283) { - alc_write_coef_idx(codec, 0x4, 0xaf01); - alc_write_coef_idx(codec, 0x6, 0x2104); - } - return 0; } #endif /* CONFIG_PM */ @@ -3815,30 +3879,6 @@ static void alc269_fill_coef(struct hda_codec *codec) alc_write_coef_idx(codec, 0x4, val | (1<<11)); } -/* don't clear mic pin; otherwise it results in noise in D3 */ -static void alc283_headset_shutup(struct hda_codec *codec) -{ - int i; - - if (codec->bus->shutdown) - return; - - for (i = 0; i < codec->init_pins.used; i++) { - struct hda_pincfg *pin = snd_array_elem(&codec->init_pins, i); - /* use read here for syncing after issuing each verb */ - if (pin->nid != 0x19) - snd_hda_codec_read(codec, pin->nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, 0); - } - - alc_write_coef_idx(codec, 0x4, 0x0f01); /* power save */ - alc_write_coef_idx(codec, 0x6, 0x2100); /* power save */ - snd_hda_codec_write(codec, 0x19, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - PIN_VREFHIZ); - codec->pins_shutup = 1; -} - /* */ static int patch_alc269(struct hda_codec *codec) @@ -3853,9 +3893,6 @@ static int patch_alc269(struct hda_codec *codec) spec = codec->spec; spec->gen.shared_mic_vref_pin = 0x18; - if (codec->vendor_id == 0x10ec0283) - spec->shutup = alc283_headset_shutup; - snd_hda_pick_fixup(codec, alc269_fixup_models, alc269_fixup_tbl, alc269_fixups); snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); @@ -3897,11 +3934,15 @@ static int patch_alc269(struct hda_codec *codec) case 0x10ec0290: spec->codec_variant = ALC269_TYPE_ALC280; break; - case 0x10ec0233: case 0x10ec0282: - case 0x10ec0283: spec->codec_variant = ALC269_TYPE_ALC282; break; + case 0x10ec0233: + case 0x10ec0283: + spec->codec_variant = ALC269_TYPE_ALC283; + spec->shutup = alc283_shutup; + spec->init_hook = alc283_init; + break; case 0x10ec0284: case 0x10ec0292: spec->codec_variant = ALC269_TYPE_ALC284; -- cgit v0.10.2 From cd217a6395ae1b14cd70908e190f566b8bbd282f Mon Sep 17 00:00:00 2001 From: Kailang Yang Date: Thu, 22 Aug 2013 10:15:24 +0200 Subject: ALSA: hda - Add workarounds for pop-noise on Chromebook with ALC283 The headphone automute on this machine triggers annoying pop noises. It seems that only the first DAC can be used, the secondary DAC always results in this problem. This patch disables the secondary DAC with a few additional workarounds. Signed-off-by: Kailang Yang Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 4bdccd1..134fbe8 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -3394,6 +3394,45 @@ static void alc269_fixup_limit_int_mic_boost(struct hda_codec *codec, } } +static void alc283_hp_automute_hook(struct hda_codec *codec, + struct hda_jack_tbl *jack) +{ + struct alc_spec *spec = codec->spec; + int vref; + + msleep(200); + snd_hda_gen_hp_automute(codec, jack); + + vref = spec->gen.hp_jack_present ? PIN_VREF80 : 0; + + msleep(600); + snd_hda_codec_write(codec, 0x19, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, + vref); +} + +static void alc283_chromebook_caps(struct hda_codec *codec) +{ + snd_hda_override_wcaps(codec, 0x03, 0); +} + +static void alc283_fixup_chromebook(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct alc_spec *spec = codec->spec; + int val; + + switch (action) { + case HDA_FIXUP_ACT_PRE_PROBE: + alc283_chromebook_caps(codec); + spec->gen.hp_automute_hook = alc283_hp_automute_hook; + /* MIC2-VREF control */ + /* Set to manual mode */ + val = alc_read_coef_idx(codec, 0x06); + alc_write_coef_idx(codec, 0x06, val & ~0x000c); + break; + } +} + enum { ALC269_FIXUP_SONY_VAIO, ALC275_FIXUP_SONY_VAIO_GPIO2, @@ -3430,6 +3469,7 @@ enum { ALC269_FIXUP_ACER_AC700, ALC269_FIXUP_LIMIT_INT_MIC_BOOST, ALC269VB_FIXUP_ORDISSIMO_EVE2, + ALC283_FIXUP_CHROME_BOOK, }; static const struct hda_fixup alc269_fixups[] = { @@ -3681,6 +3721,10 @@ static const struct hda_fixup alc269_fixups[] = { { } }, }, + [ALC283_FIXUP_CHROME_BOOK] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc283_fixup_chromebook, + }, }; static const struct snd_pci_quirk alc269_fixup_tbl[] = { @@ -3728,6 +3772,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x18e6, "HP", ALC269_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x1973, "HP Pavilion", ALC269_FIXUP_HP_MUTE_LED_MIC1), SND_PCI_QUIRK(0x103c, 0x1983, "HP Pavilion", ALC269_FIXUP_HP_MUTE_LED_MIC1), + SND_PCI_QUIRK(0x103c, 0x21ed, "HP Falco Chromebook", ALC283_FIXUP_CHROME_BOOK), SND_PCI_QUIRK_VENDOR(0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED), SND_PCI_QUIRK(0x1043, 0x106d, "Asus K53BE", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x1043, 0x115d, "Asus 1015E", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), -- cgit v0.10.2 From e58a244ff9ae264df1bf0fc8f09ecc135dbe3d0f Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 22 Aug 2013 12:02:31 +0200 Subject: ALSA: rme96: Check the return value of pci_enable_device() in resume callback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixing warning message: sound/pci/rme96.c: In function ‘snd_rme96_resume’: sound/pci/rme96.c:2418:19: warning: ignoring return value of ‘pci_enable_device’, declared with attribute warn_unused_result [-Wunused-result] Signed-off-by: Takashi Iwai diff --git a/sound/pci/rme96.c b/sound/pci/rme96.c index 0506530..9d2a81f 100644 --- a/sound/pci/rme96.c +++ b/sound/pci/rme96.c @@ -2415,7 +2415,11 @@ snd_rme96_resume(struct pci_dev *pci) struct rme96 *rme96 = card->private_data; pci_restore_state(pci); - pci_enable_device(pci); + if (pci_enable_device(pci) < 0) { + printk(KERN_ERR "rme96: pci_enable_device failed, disabling device\n"); + snd_card_disconnect(card); + return -EIO; + } /* reset playback and record buffer pointers */ writel(0, rme96->iobase + RME96_IO_SET_PLAY_POS -- cgit v0.10.2 From cd7f0295aab102acb77c19d6d77eab5f5145364c Mon Sep 17 00:00:00 2001 From: Markus Pargmann Date: Mon, 19 Aug 2013 17:05:58 +0200 Subject: ASoC: fsl-ssi: ac97-slave support This patch adds ac97-slave support. For ac97, the registers have to be setup earlier than for other ssi modes because there is some communication with the external device before streaming. So this patch introduces a fsl_ssi_setup function to setup the registers for different ssi operation modes seperately. This patch was tested with imx27-pca100. Signed-off-by: Markus Pargmann Tested-by: Shawn Guo Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/sound/fsl,ssi.txt b/Documentation/devicetree/bindings/sound/fsl,ssi.txt index 088a2c0..4303b6a 100644 --- a/Documentation/devicetree/bindings/sound/fsl,ssi.txt +++ b/Documentation/devicetree/bindings/sound/fsl,ssi.txt @@ -43,6 +43,10 @@ Required properties: together. This would still allow different sample sizes, but not different sample rates. +Required are also ac97 link bindings if ac97 is used. See +Documentation/devicetree/bindings/sound/soc-ac97link.txt for the necessary +bindings. + Optional properties: - codec-handle: Phandle to a 'codec' node that defines an audio codec connected to this SSI. This node is typically diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 3168998..9e410e1 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -141,6 +141,7 @@ struct fsl_ssi_private { bool new_binding; bool ssi_on_imx; + bool imx_ac97; bool use_dma; struct clk *clk; struct snd_dmaengine_dai_dma_data dma_params_tx; @@ -320,6 +321,124 @@ static irqreturn_t fsl_ssi_isr(int irq, void *dev_id) return ret; } +static int fsl_ssi_setup(struct fsl_ssi_private *ssi_private) +{ + struct ccsr_ssi __iomem *ssi = ssi_private->ssi; + u8 i2s_mode; + u8 wm; + int synchronous = ssi_private->cpu_dai_drv.symmetric_rates; + + if (ssi_private->imx_ac97) + i2s_mode = CCSR_SSI_SCR_I2S_MODE_NORMAL | CCSR_SSI_SCR_NET; + else + i2s_mode = CCSR_SSI_SCR_I2S_MODE_SLAVE; + + /* + * Section 16.5 of the MPC8610 reference manual says that the SSI needs + * to be disabled before updating the registers we set here. + */ + write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_SSIEN, 0); + + /* + * Program the SSI into I2S Slave Non-Network Synchronous mode. Also + * enable the transmit and receive FIFO. + * + * FIXME: Little-endian samples require a different shift dir + */ + write_ssi_mask(&ssi->scr, + CCSR_SSI_SCR_I2S_MODE_MASK | CCSR_SSI_SCR_SYN, + CCSR_SSI_SCR_TFR_CLK_DIS | + i2s_mode | + (synchronous ? CCSR_SSI_SCR_SYN : 0)); + + write_ssi(CCSR_SSI_STCR_TXBIT0 | CCSR_SSI_STCR_TFEN0 | + CCSR_SSI_STCR_TFSI | CCSR_SSI_STCR_TEFS | + CCSR_SSI_STCR_TSCKP, &ssi->stcr); + + write_ssi(CCSR_SSI_SRCR_RXBIT0 | CCSR_SSI_SRCR_RFEN0 | + CCSR_SSI_SRCR_RFSI | CCSR_SSI_SRCR_REFS | + CCSR_SSI_SRCR_RSCKP, &ssi->srcr); + /* + * The DC and PM bits are only used if the SSI is the clock master. + */ + + /* + * Set the watermark for transmit FIFI 0 and receive FIFO 0. We don't + * use FIFO 1. We program the transmit water to signal a DMA transfer + * if there are only two (or fewer) elements left in the FIFO. Two + * elements equals one frame (left channel, right channel). This value, + * however, depends on the depth of the transmit buffer. + * + * We set the watermark on the same level as the DMA burstsize. For + * fiq it is probably better to use the biggest possible watermark + * size. + */ + if (ssi_private->use_dma) + wm = ssi_private->fifo_depth - 2; + else + wm = ssi_private->fifo_depth; + + write_ssi(CCSR_SSI_SFCSR_TFWM0(wm) | CCSR_SSI_SFCSR_RFWM0(wm) | + CCSR_SSI_SFCSR_TFWM1(wm) | CCSR_SSI_SFCSR_RFWM1(wm), + &ssi->sfcsr); + + /* + * For non-ac97 setups, we keep the SSI disabled because if we enable + * it, then the DMA controller will start. It's not supposed to start + * until the SCR.TE (or SCR.RE) bit is set, but it does anyway. The DMA + * controller will transfer one "BWC" of data (i.e. the amount of data + * that the MR.BWC bits are set to). The reason this is bad is because + * at this point, the PCM driver has not finished initializing the DMA + * controller. + */ + + + /* + * For ac97 interrupts are enabled with the startup of the substream + * because it is also running without an active substream. Normally SSI + * is only enabled when there is a substream. + */ + if (!ssi_private->imx_ac97) { + /* Enable the interrupts and DMA requests */ + if (ssi_private->use_dma) + write_ssi(SIER_FLAGS, &ssi->sier); + else + write_ssi(CCSR_SSI_SIER_TIE | CCSR_SSI_SIER_TFE0_EN | + CCSR_SSI_SIER_RIE | + CCSR_SSI_SIER_RFF0_EN, &ssi->sier); + } else { + /* + * Setup the clock control register + */ + write_ssi(CCSR_SSI_SxCCR_WL(17) | CCSR_SSI_SxCCR_DC(13), + &ssi->stccr); + write_ssi(CCSR_SSI_SxCCR_WL(17) | CCSR_SSI_SxCCR_DC(13), + &ssi->srccr); + + /* + * Enable AC97 mode and startup the SSI + */ + write_ssi(CCSR_SSI_SACNT_AC97EN | CCSR_SSI_SACNT_FV, + &ssi->sacnt); + write_ssi(0xff, &ssi->saccdis); + write_ssi(0x300, &ssi->saccen); + + /* + * Enable SSI + */ + write_ssi_mask(&ssi->scr, 0, CCSR_SSI_SCR_SSIEN); + write_ssi(CCSR_SSI_SOR_WAIT(3), &ssi->sor); + + /* + * Enable Transmit and Receive + */ + write_ssi_mask(&ssi->scr, 0, CCSR_SSI_SCR_TE | CCSR_SSI_SCR_RE); + } + + return 0; +} + + /** * fsl_ssi_startup: create a new substream * @@ -341,75 +460,14 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream, * and initialize the SSI registers. */ if (!ssi_private->first_stream) { - struct ccsr_ssi __iomem *ssi = ssi_private->ssi; - ssi_private->first_stream = substream; /* - * Section 16.5 of the MPC8610 reference manual says that the - * SSI needs to be disabled before updating the registers we set - * here. - */ - write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_SSIEN, 0); - - /* - * Program the SSI into I2S Slave Non-Network Synchronous mode. - * Also enable the transmit and receive FIFO. - * - * FIXME: Little-endian samples require a different shift dir - */ - write_ssi_mask(&ssi->scr, - CCSR_SSI_SCR_I2S_MODE_MASK | CCSR_SSI_SCR_SYN, - CCSR_SSI_SCR_TFR_CLK_DIS | CCSR_SSI_SCR_I2S_MODE_SLAVE - | (synchronous ? CCSR_SSI_SCR_SYN : 0)); - - write_ssi(CCSR_SSI_STCR_TXBIT0 | CCSR_SSI_STCR_TFEN0 | - CCSR_SSI_STCR_TFSI | CCSR_SSI_STCR_TEFS | - CCSR_SSI_STCR_TSCKP, &ssi->stcr); - - write_ssi(CCSR_SSI_SRCR_RXBIT0 | CCSR_SSI_SRCR_RFEN0 | - CCSR_SSI_SRCR_RFSI | CCSR_SSI_SRCR_REFS | - CCSR_SSI_SRCR_RSCKP, &ssi->srcr); - - /* - * The DC and PM bits are only used if the SSI is the clock - * master. - */ - - /* Enable the interrupts and DMA requests */ - if (ssi_private->use_dma) - write_ssi(SIER_FLAGS, &ssi->sier); - else - write_ssi(CCSR_SSI_SIER_TIE | CCSR_SSI_SIER_TFE0_EN | - CCSR_SSI_SIER_RIE | - CCSR_SSI_SIER_RFF0_EN, &ssi->sier); - - /* - * Set the watermark for transmit FIFI 0 and receive FIFO 0. We - * don't use FIFO 1. We program the transmit water to signal a - * DMA transfer if there are only two (or fewer) elements left - * in the FIFO. Two elements equals one frame (left channel, - * right channel). This value, however, depends on the depth of - * the transmit buffer. - * - * We program the receive FIFO to notify us if at least two - * elements (one frame) have been written to the FIFO. We could - * make this value larger (and maybe we should), but this way - * data will be written to memory as soon as it's available. - */ - write_ssi(CCSR_SSI_SFCSR_TFWM0(ssi_private->fifo_depth - 2) | - CCSR_SSI_SFCSR_RFWM0(ssi_private->fifo_depth - 2), - &ssi->sfcsr); - - /* - * We keep the SSI disabled because if we enable it, then the - * DMA controller will start. It's not supposed to start until - * the SCR.TE (or SCR.RE) bit is set, but it does anyway. The - * DMA controller will transfer one "BWC" of data (i.e. the - * amount of data that the MR.BWC bits are set to). The reason - * this is bad is because at this point, the PCM driver has not - * finished initializing the DMA controller. + * fsl_ssi_setup was already called by ac97_init earlier if + * the driver is in ac97 mode. */ + if (!ssi_private->imx_ac97) + fsl_ssi_setup(ssi_private); } else { if (synchronous) { struct snd_pcm_runtime *first_runtime = @@ -538,7 +596,8 @@ static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd, else write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_RE, 0); - if ((read_ssi(&ssi->scr) & (CCSR_SSI_SCR_TE | CCSR_SSI_SCR_RE)) == 0) + if (!ssi_private->imx_ac97 && (read_ssi(&ssi->scr) & + (CCSR_SSI_SCR_TE | CCSR_SSI_SCR_RE)) == 0) write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_SSIEN, 0); break; @@ -608,6 +667,133 @@ static const struct snd_soc_component_driver fsl_ssi_component = { .name = "fsl-ssi", }; +/** + * fsl_ssi_ac97_trigger: start and stop the AC97 receive/transmit. + * + * This function is called by ALSA to start, stop, pause, and resume the + * transfer of data. + */ +static int fsl_ssi_ac97_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata( + rtd->cpu_dai); + struct ccsr_ssi __iomem *ssi = ssi_private->ssi; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + write_ssi_mask(&ssi->sier, 0, CCSR_SSI_SIER_TIE | + CCSR_SSI_SIER_TFE0_EN); + else + write_ssi_mask(&ssi->sier, 0, CCSR_SSI_SIER_RIE | + CCSR_SSI_SIER_RFF0_EN); + break; + + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + write_ssi_mask(&ssi->sier, CCSR_SSI_SIER_TIE | + CCSR_SSI_SIER_TFE0_EN, 0); + else + write_ssi_mask(&ssi->sier, CCSR_SSI_SIER_RIE | + CCSR_SSI_SIER_RFF0_EN, 0); + break; + + default: + return -EINVAL; + } + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + write_ssi(CCSR_SSI_SOR_TX_CLR, &ssi->sor); + else + write_ssi(CCSR_SSI_SOR_RX_CLR, &ssi->sor); + + return 0; +} + +static const struct snd_soc_dai_ops fsl_ssi_ac97_dai_ops = { + .startup = fsl_ssi_startup, + .shutdown = fsl_ssi_shutdown, + .trigger = fsl_ssi_ac97_trigger, +}; + +static struct snd_soc_dai_driver fsl_ssi_ac97_dai = { + .ac97_control = 1, + .playback = { + .stream_name = "AC97 Playback", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .stream_name = "AC97 Capture", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .ops = &fsl_ssi_ac97_dai_ops, +}; + + +static struct fsl_ssi_private *fsl_ac97_data; + +static void fsl_ssi_ac97_init(void) +{ + fsl_ssi_setup(fsl_ac97_data); +} + +void fsl_ssi_ac97_write(struct snd_ac97 *ac97, unsigned short reg, + unsigned short val) +{ + struct ccsr_ssi *ssi = fsl_ac97_data->ssi; + unsigned int lreg; + unsigned int lval; + + if (reg > 0x7f) + return; + + + lreg = reg << 12; + write_ssi(lreg, &ssi->sacadd); + + lval = val << 4; + write_ssi(lval , &ssi->sacdat); + + write_ssi_mask(&ssi->sacnt, CCSR_SSI_SACNT_RDWR_MASK, + CCSR_SSI_SACNT_WR); + udelay(100); +} + +unsigned short fsl_ssi_ac97_read(struct snd_ac97 *ac97, + unsigned short reg) +{ + struct ccsr_ssi *ssi = fsl_ac97_data->ssi; + + unsigned short val = -1; + unsigned int lreg; + + lreg = (reg & 0x7f) << 12; + write_ssi(lreg, &ssi->sacadd); + write_ssi_mask(&ssi->sacnt, CCSR_SSI_SACNT_RDWR_MASK, + CCSR_SSI_SACNT_RD); + + udelay(100); + + val = (read_ssi(&ssi->sacdat) >> 4) & 0xffff; + + return val; +} + +static struct snd_ac97_bus_ops fsl_ssi_ac97_ops = { + .read = fsl_ssi_ac97_read, + .write = fsl_ssi_ac97_write, +}; + /* Show the statistics of a flag only if its interrupt is enabled. The * compiler will optimze this code to a no-op if the interrupt is not * enabled. @@ -684,6 +870,7 @@ static int fsl_ssi_probe(struct platform_device *pdev) struct resource res; char name[64]; bool shared; + bool ac97 = false; /* SSIs that are not connected on the board should have a * status = "disabled" @@ -694,7 +881,13 @@ static int fsl_ssi_probe(struct platform_device *pdev) /* We only support the SSI in "I2S Slave" mode */ sprop = of_get_property(np, "fsl,mode", NULL); - if (!sprop || strcmp(sprop, "i2s-slave")) { + if (!sprop) { + dev_err(&pdev->dev, "fsl,mode property is necessary\n"); + return -EINVAL; + } + if (!strcmp(sprop, "ac97-slave")) { + ac97 = true; + } else if (strcmp(sprop, "i2s-slave")) { dev_notice(&pdev->dev, "mode %s is unsupported\n", sprop); return -ENODEV; } @@ -713,9 +906,19 @@ static int fsl_ssi_probe(struct platform_device *pdev) ssi_private->use_dma = !of_property_read_bool(np, "fsl,fiq-stream-filter"); - /* Initialize this copy of the CPU DAI driver structure */ - memcpy(&ssi_private->cpu_dai_drv, &fsl_ssi_dai_template, - sizeof(fsl_ssi_dai_template)); + if (ac97) { + memcpy(&ssi_private->cpu_dai_drv, &fsl_ssi_ac97_dai, + sizeof(fsl_ssi_ac97_dai)); + + fsl_ac97_data = ssi_private; + ssi_private->imx_ac97 = true; + + snd_soc_set_ac97_ops_of_reset(&fsl_ssi_ac97_ops, pdev); + } else { + /* Initialize this copy of the CPU DAI driver structure */ + memcpy(&ssi_private->cpu_dai_drv, &fsl_ssi_dai_template, + sizeof(fsl_ssi_dai_template)); + } ssi_private->cpu_dai_drv.name = ssi_private->name; /* Get the addresses and IRQ */ @@ -901,6 +1104,9 @@ static int fsl_ssi_probe(struct platform_device *pdev) } done: + if (ssi_private->imx_ac97) + fsl_ssi_ac97_init(); + return 0; error_dai: -- cgit v0.10.2 From f8fdf5375e2005f238ce9b430724752a6e3d55cc Mon Sep 17 00:00:00 2001 From: Steffen Trumtrar Date: Mon, 19 Aug 2013 17:05:59 +0200 Subject: ASoC: fsl-ssi: add SSIEN errata work around The chip errata for the i.MX35, Rev.2 has the following errata: ENGcm06222: SSI:Transmission does not take place in bit length early frame sync configuration The workaround states, that TX_EN and SSI_EN bits should be set in the same register write. As the next errata in the document (ENGcm06532) says to always write RX_EN and TX_EN in the same register write in network mode. Therefore include the whole write to CCSR_SSI_SCR_TE and CCSR_SSI_SCR_RE into the write to CCSR_SSI_SCR_SSIEN Signed-off-by: Steffen Trumtrar Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 9e410e1..6daeb5f 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -424,15 +424,12 @@ static int fsl_ssi_setup(struct fsl_ssi_private *ssi_private) write_ssi(0x300, &ssi->saccen); /* - * Enable SSI + * Enable SSI, Transmit and Receive */ - write_ssi_mask(&ssi->scr, 0, CCSR_SSI_SCR_SSIEN); - write_ssi(CCSR_SSI_SOR_WAIT(3), &ssi->sor); + write_ssi_mask(&ssi->scr, 0, CCSR_SSI_SCR_SSIEN | + CCSR_SSI_SCR_TE | CCSR_SSI_SCR_RE); - /* - * Enable Transmit and Receive - */ - write_ssi_mask(&ssi->scr, 0, CCSR_SSI_SCR_TE | CCSR_SSI_SCR_RE); + write_ssi(CCSR_SSI_SOR_WAIT(3), &ssi->sor); } return 0; -- cgit v0.10.2 From 9b443e3d89ba507ba5f51682f3896f859b2e5007 Mon Sep 17 00:00:00 2001 From: Michael Grzeschik Date: Mon, 19 Aug 2013 17:06:00 +0200 Subject: ASoC: fsl-ssi: imx-pcm-fiq bugfix imx-pcm-fiq is checking for TE RE bits, so enable them only if necessary. Signed-off-by: Michael Grzeschik Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 6daeb5f..198656f 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -383,30 +383,11 @@ static int fsl_ssi_setup(struct fsl_ssi_private *ssi_private) &ssi->sfcsr); /* - * For non-ac97 setups, we keep the SSI disabled because if we enable - * it, then the DMA controller will start. It's not supposed to start - * until the SCR.TE (or SCR.RE) bit is set, but it does anyway. The DMA - * controller will transfer one "BWC" of data (i.e. the amount of data - * that the MR.BWC bits are set to). The reason this is bad is because - * at this point, the PCM driver has not finished initializing the DMA - * controller. - */ - - - /* * For ac97 interrupts are enabled with the startup of the substream * because it is also running without an active substream. Normally SSI * is only enabled when there is a substream. */ - if (!ssi_private->imx_ac97) { - /* Enable the interrupts and DMA requests */ - if (ssi_private->use_dma) - write_ssi(SIER_FLAGS, &ssi->sier); - else - write_ssi(CCSR_SSI_SIER_TIE | CCSR_SSI_SIER_TFE0_EN | - CCSR_SSI_SIER_RIE | - CCSR_SSI_SIER_RFF0_EN, &ssi->sier); - } else { + if (ssi_private->imx_ac97) { /* * Setup the clock control register */ @@ -574,6 +555,27 @@ static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_pcm_runtime *rtd = substream->private_data; struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(rtd->cpu_dai); struct ccsr_ssi __iomem *ssi = ssi_private->ssi; + unsigned int sier_bits; + + /* + * Enable only the interrupts and DMA requests + * that are needed for the channel. As the fiq + * is polling for this bits, we have to ensure + * that this are aligned with the preallocated + * buffers + */ + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + if (ssi_private->use_dma) + sier_bits = SIER_FLAGS; + else + sier_bits = CCSR_SSI_SIER_TIE | CCSR_SSI_SIER_TFE0_EN; + } else { + if (ssi_private->use_dma) + sier_bits = SIER_FLAGS; + else + sier_bits = CCSR_SSI_SIER_RIE | CCSR_SSI_SIER_RFF0_EN; + } switch (cmd) { case SNDRV_PCM_TRIGGER_START: @@ -602,6 +604,8 @@ static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd, return -EINVAL; } + write_ssi(sier_bits, &ssi->sier); + return 0; } -- cgit v0.10.2 From f037708654eef9c5477ac2a88b3a1e8b5d190dc4 Mon Sep 17 00:00:00 2001 From: Michael Grzeschik Date: Mon, 19 Aug 2013 17:06:01 +0200 Subject: ASoC: fsl: disable ssi irq for imx We have to disable the ssi irq, as it is not safe for all platforms to write back into the status register. It also runs into non-linefetch aborts. Signed-off-by: Michael Grzeschik Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 198656f..5cf626c 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -941,18 +941,6 @@ static int fsl_ssi_probe(struct platform_device *pdev) return -ENXIO; } - if (ssi_private->use_dma) { - /* The 'name' should not have any slashes in it. */ - ret = devm_request_irq(&pdev->dev, ssi_private->irq, - fsl_ssi_isr, 0, ssi_private->name, - ssi_private); - if (ret < 0) { - dev_err(&pdev->dev, "could not claim irq %u\n", - ssi_private->irq); - goto error_irqmap; - } - } - /* Are the RX and the TX clocks locked? */ if (!of_find_property(np, "fsl,ssi-asynchronous", NULL)) ssi_private->cpu_dai_drv.symmetric_rates = 1; @@ -1020,6 +1008,16 @@ static int fsl_ssi_probe(struct platform_device *pdev) dma_events[0], shared ? IMX_DMATYPE_SSI_SP : IMX_DMATYPE_SSI); imx_pcm_dma_params_init_data(&ssi_private->filter_data_rx, dma_events[1], shared ? IMX_DMATYPE_SSI_SP : IMX_DMATYPE_SSI); + } else if (ssi_private->use_dma) { + /* The 'name' should not have any slashes in it. */ + ret = devm_request_irq(&pdev->dev, ssi_private->irq, + fsl_ssi_isr, 0, ssi_private->name, + ssi_private); + if (ret < 0) { + dev_err(&pdev->dev, "could not claim irq %u\n", + ssi_private->irq); + goto error_irqmap; + } } /* Initialize the the device_attribute structure */ -- cgit v0.10.2 From 06b10ff913f4d6b3e659e365ce5f70e82cca353c Mon Sep 17 00:00:00 2001 From: Tushar Behera Date: Thu, 22 Aug 2013 18:15:02 +0530 Subject: ASoC: samsung: Fix build error with dma function rename commit 85ff3c29d720 ("ASoC: samsung: Rename DMA platform registration functions") renames the DMA registration functions. Fix the places where it was left out. Signed-off-by: Tushar Behera Signed-off-by: Mark Brown diff --git a/sound/soc/samsung/pcm.c b/sound/soc/samsung/pcm.c index 1566afe..e54256f 100644 --- a/sound/soc/samsung/pcm.c +++ b/sound/soc/samsung/pcm.c @@ -594,7 +594,7 @@ static int s3c_pcm_dev_probe(struct platform_device *pdev) goto err5; } - ret = asoc_dma_platform_register(&pdev->dev); + ret = samsung_asoc_dma_platform_register(&pdev->dev); if (ret) { dev_err(&pdev->dev, "failed to get register DMA: %d\n", ret); goto err6; @@ -623,7 +623,7 @@ static int s3c_pcm_dev_remove(struct platform_device *pdev) struct s3c_pcm_info *pcm = &s3c_pcm[pdev->id]; struct resource *mem_res; - asoc_dma_platform_unregister(&pdev->dev); + samsung_asoc_dma_platform_unregister(&pdev->dev); snd_soc_unregister_component(&pdev->dev); pm_runtime_disable(&pdev->dev); diff --git a/sound/soc/samsung/s3c2412-i2s.c b/sound/soc/samsung/s3c2412-i2s.c index 47e2386..ea885cb 100644 --- a/sound/soc/samsung/s3c2412-i2s.c +++ b/sound/soc/samsung/s3c2412-i2s.c @@ -176,7 +176,7 @@ static int s3c2412_iis_dev_probe(struct platform_device *pdev) return ret; } - ret = asoc_dma_platform_register(&pdev->dev); + ret = samsung_asoc_dma_platform_register(&pdev->dev); if (ret) { pr_err("failed to register the DMA: %d\n", ret); goto err; @@ -190,7 +190,7 @@ err: static int s3c2412_iis_dev_remove(struct platform_device *pdev) { - asoc_dma_platform_unregister(&pdev->dev); + samsung_asoc_dma_platform_unregister(&pdev->dev); snd_soc_unregister_component(&pdev->dev); return 0; } diff --git a/sound/soc/samsung/s3c24xx-i2s.c b/sound/soc/samsung/s3c24xx-i2s.c index 8b34145..9c8ebd8 100644 --- a/sound/soc/samsung/s3c24xx-i2s.c +++ b/sound/soc/samsung/s3c24xx-i2s.c @@ -480,7 +480,7 @@ static int s3c24xx_iis_dev_probe(struct platform_device *pdev) return ret; } - ret = asoc_dma_platform_register(&pdev->dev); + ret = samsung_asoc_dma_platform_register(&pdev->dev); if (ret) { pr_err("failed to register the dma: %d\n", ret); goto err; @@ -494,7 +494,7 @@ err: static int s3c24xx_iis_dev_remove(struct platform_device *pdev) { - asoc_dma_platform_unregister(&pdev->dev); + samsung_asoc_dma_platform_unregister(&pdev->dev); snd_soc_unregister_component(&pdev->dev); return 0; } diff --git a/sound/soc/samsung/spdif.c b/sound/soc/samsung/spdif.c index 5ea70ab..28487dc 100644 --- a/sound/soc/samsung/spdif.c +++ b/sound/soc/samsung/spdif.c @@ -442,7 +442,7 @@ static int spdif_probe(struct platform_device *pdev) spdif->dma_playback = &spdif_stereo_out; - ret = asoc_dma_platform_register(&pdev->dev); + ret = samsung_asoc_dma_platform_register(&pdev->dev); if (ret) { dev_err(&pdev->dev, "failed to register DMA: %d\n", ret); goto err5; @@ -468,7 +468,7 @@ static int spdif_remove(struct platform_device *pdev) struct samsung_spdif_info *spdif = &spdif_info; struct resource *mem_res; - asoc_dma_platform_unregister(&pdev->dev); + samsung_asoc_dma_platform_unregister(&pdev->dev); snd_soc_unregister_component(&pdev->dev); iounmap(spdif->regs); -- cgit v0.10.2 From eb63231830360f5acfea5dd2b545d7a14476bc3a Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Wed, 14 Aug 2013 12:27:33 +0200 Subject: ASoc: kirkwood: add DT support to the mvebu audio subsystem This patch adds DT support to the audio subsystem of the mvebu family (Kirkwood, Dove, Armada 370). Signed-off-by: Jean-Francois Moine Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/sound/mvebu-audio.txt b/Documentation/devicetree/bindings/sound/mvebu-audio.txt new file mode 100644 index 0000000..7e5fd37 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/mvebu-audio.txt @@ -0,0 +1,29 @@ +* mvebu (Kirkwood, Dove, Armada 370) audio controller + +Required properties: + +- compatible: "marvell,mvebu-audio" + +- reg: physical base address of the controller and length of memory mapped + region. + +- interrupts: list of two irq numbers. + The first irq is used for data flow and the second one is used for errors. + +- clocks: one or two phandles. + The first one is mandatory and defines the internal clock. + The second one is optional and defines an external clock. + +- clock-names: names associated to the clocks: + "internal" for the internal clock + "extclk" for the external clock + +Example: + +i2s1: audio-controller@b4000 { + compatible = "marvell,mvebu-audio"; + reg = <0xb4000 0x2210>; + interrupts = <21>, <22>; + clocks = <&gate_clk 13>; + clock-names = "internal"; +}; diff --git a/sound/soc/kirkwood/kirkwood-i2s.c b/sound/soc/kirkwood/kirkwood-i2s.c index e5f3f7a..7fce340 100644 --- a/sound/soc/kirkwood/kirkwood-i2s.c +++ b/sound/soc/kirkwood/kirkwood-i2s.c @@ -22,6 +22,8 @@ #include #include #include +#include + #include "kirkwood.h" #define DRV_NAME "mvebu-audio" @@ -453,6 +455,7 @@ static int kirkwood_i2s_dev_probe(struct platform_device *pdev) struct snd_soc_dai_driver *soc_dai = &kirkwood_i2s_dai; struct kirkwood_dma_data *priv; struct resource *mem; + struct device_node *np = pdev->dev.of_node; int err; priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); @@ -473,14 +476,16 @@ static int kirkwood_i2s_dev_probe(struct platform_device *pdev) return -ENXIO; } - if (!data) { - dev_err(&pdev->dev, "no platform data ?!\n"); + if (np) { + priv->burst = 128; /* might be 32 or 128 */ + } else if (data) { + priv->burst = data->burst; + } else { + dev_err(&pdev->dev, "no DT nor platform data ?!\n"); return -EINVAL; } - priv->burst = data->burst; - - priv->clk = devm_clk_get(&pdev->dev, NULL); + priv->clk = devm_clk_get(&pdev->dev, np ? "internal" : NULL); if (IS_ERR(priv->clk)) { dev_err(&pdev->dev, "no clock\n"); return PTR_ERR(priv->clk); @@ -507,7 +512,7 @@ static int kirkwood_i2s_dev_probe(struct platform_device *pdev) priv->ctl_rec = KIRKWOOD_RECCTL_SIZE_24; /* Select the burst size */ - if (data->burst == 32) { + if (priv->burst == 32) { priv->ctl_play |= KIRKWOOD_PLAYCTL_BURST_32; priv->ctl_rec |= KIRKWOOD_RECCTL_BURST_32; } else { @@ -552,12 +557,21 @@ static int kirkwood_i2s_dev_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_OF +static struct of_device_id mvebu_audio_of_match[] = { + { .compatible = "marvell,mvebu-audio" }, + { } +}; +MODULE_DEVICE_TABLE(of, mvebu_audio_of_match); +#endif + static struct platform_driver kirkwood_i2s_driver = { .probe = kirkwood_i2s_dev_probe, .remove = kirkwood_i2s_dev_remove, .driver = { .name = DRV_NAME, .owner = THIS_MODULE, + .of_match_table = of_match_ptr(mvebu_audio_of_match), }, }; -- cgit v0.10.2 From a8cc20999799a94929a56393ff39b32245e33d64 Mon Sep 17 00:00:00 2001 From: Knut Petersen Date: Thu, 22 Aug 2013 14:36:16 +0200 Subject: alsa/rme96: Add missing inclusion of linux/vmalloc.h Signed-off-by: Knut Petersen Signed-off-by: Takashi Iwai diff --git a/sound/pci/rme96.c b/sound/pci/rme96.c index 9d2a81f..bb9ebc5 100644 --- a/sound/pci/rme96.c +++ b/sound/pci/rme96.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include -- cgit v0.10.2 From c445be35956b0cefe85db75d1e7994af5cecf16a Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Fri, 23 Aug 2013 14:35:17 -0300 Subject: ASoC: simple-card: Provide owner and MODULE_ALIAS() Add .owner field and also MODULE_ALIAS(), so that auto module loading can work. Signed-off-by: Fabio Estevam Signed-off-by: Mark Brown diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index 6cf8355..8c49147 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -105,6 +105,7 @@ static int asoc_simple_card_remove(struct platform_device *pdev) static struct platform_driver asoc_simple_card = { .driver = { .name = "asoc-simple-card", + .owner = THIS_MODULE, }, .probe = asoc_simple_card_probe, .remove = asoc_simple_card_remove, @@ -112,6 +113,7 @@ static struct platform_driver asoc_simple_card = { module_platform_driver(asoc_simple_card); +MODULE_ALIAS("platform:asoc-simple-card"); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("ASoC Simple Sound Card"); MODULE_AUTHOR("Kuninori Morimoto "); -- cgit v0.10.2 From 5af407cd365c8aab8a20e66aa6e4bc4a4983979e Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Fri, 23 Aug 2013 18:14:45 -0300 Subject: ASoC: fsl_spdif: Remove unnecessary dev_set_drvdata() Driver core clears the driver data to NULL after device_release or on probe failure, so just remove it from here. Signed-off-by: Fabio Estevam Reviewed-by: Nicolin Chen Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c index 42a4382..a8ef46a 100644 --- a/sound/soc/fsl/fsl_spdif.c +++ b/sound/soc/fsl/fsl_spdif.c @@ -1184,7 +1184,7 @@ static int fsl_spdif_probe(struct platform_device *pdev) &spdif_priv->cpu_dai_drv, 1); if (ret) { dev_err(&pdev->dev, "failed to register DAI: %d\n", ret); - goto error_dev; + return ret; } ret = imx_pcm_dma_init(pdev); @@ -1197,8 +1197,6 @@ static int fsl_spdif_probe(struct platform_device *pdev) error_component: snd_soc_unregister_component(&pdev->dev); -error_dev: - dev_set_drvdata(&pdev->dev, NULL); return ret; } @@ -1207,7 +1205,6 @@ static int fsl_spdif_remove(struct platform_device *pdev) { imx_pcm_dma_exit(pdev); snd_soc_unregister_component(&pdev->dev); - dev_set_drvdata(&pdev->dev, NULL); return 0; } -- cgit v0.10.2 From 6d22db43cf8b841dae37e7e3ee284c2b6c91a58b Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Fri, 23 Aug 2013 18:14:46 -0300 Subject: ASoC: fsl_spdif: Reduce the noise on comments Remove the "====" pattern to let the comments cleaner and more uniform. Also, do not use multi-line style for a single line comment. Signed-off-by: Fabio Estevam Acked-by: Nicolin Chen Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c index a8ef46a..a9798aa 100644 --- a/sound/soc/fsl/fsl_spdif.c +++ b/sound/soc/fsl/fsl_spdif.c @@ -555,7 +555,6 @@ struct snd_soc_dai_ops fsl_spdif_dai_ops = { /* - * ============================================ * FSL SPDIF IEC958 controller(mixer) functions * * Channel status get/put control @@ -563,7 +562,6 @@ struct snd_soc_dai_ops fsl_spdif_dai_ops = { * Valid bit value get control * DPLL lock status get control * User bit sync mode selection control - * ============================================ */ static int fsl_spdif_info(struct snd_kcontrol *kcontrol, @@ -942,11 +940,7 @@ static const struct snd_soc_component_driver fsl_spdif_component = { .name = "fsl-spdif", }; -/* - * ================ - * FSL SPDIF REGMAP - * ================ - */ +/* FSL SPDIF REGMAP */ static bool fsl_spdif_readable_reg(struct device *dev, unsigned int reg) { -- cgit v0.10.2 From 53110a256a334c5e01db2d94c5306b4880a9180e Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Sun, 25 Aug 2013 23:36:23 -0700 Subject: ASoC: fsi: reserve prefetch period on DMA transferring Current FSI is supporting DMAEngine transfer, but, it needs to use work queue. Therefore, DMA transfer settings might be late if there is heavy task. This patch reserves next period beforehand on DMA transfer function. Android sound will be breaking up without this patch. Tested-by: Tomohito Esaki Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c index 3039026..b33ca7c 100644 --- a/sound/soc/sh/fsi.c +++ b/sound/soc/sh/fsi.c @@ -235,6 +235,8 @@ struct fsi_stream { struct sh_dmae_slave slave; /* see fsi_handler_init() */ struct work_struct work; dma_addr_t dma; + int loop_cnt; + int additional_pos; }; struct fsi_clk { @@ -1289,6 +1291,8 @@ static int fsi_dma_init(struct fsi_priv *fsi, struct fsi_stream *io) io->bus_option = BUSOP_SET(24, PACKAGE_24BITBUS_BACK) | BUSOP_SET(16, PACKAGE_16BITBUS_STREAM); + io->loop_cnt = 2; /* push 1st, 2nd period first, then 3rd, 4th... */ + io->additional_pos = 0; io->dma = dma_map_single(dai->dev, runtime->dma_area, snd_pcm_lib_buffer_bytes(io->substream), dir); return 0; @@ -1305,11 +1309,15 @@ static int fsi_dma_quit(struct fsi_priv *fsi, struct fsi_stream *io) return 0; } -static dma_addr_t fsi_dma_get_area(struct fsi_stream *io) +static dma_addr_t fsi_dma_get_area(struct fsi_stream *io, int additional) { struct snd_pcm_runtime *runtime = io->substream->runtime; + int period = io->period_pos + additional; - return io->dma + samples_to_bytes(runtime, io->buff_sample_pos); + if (period >= runtime->periods) + period = 0; + + return io->dma + samples_to_bytes(runtime, period * io->period_samples); } static void fsi_dma_complete(void *data) @@ -1321,7 +1329,7 @@ static void fsi_dma_complete(void *data) enum dma_data_direction dir = fsi_stream_is_play(fsi, io) ? DMA_TO_DEVICE : DMA_FROM_DEVICE; - dma_sync_single_for_cpu(dai->dev, fsi_dma_get_area(io), + dma_sync_single_for_cpu(dai->dev, fsi_dma_get_area(io, 0), samples_to_bytes(runtime, io->period_samples), dir); io->buff_sample_pos += io->period_samples; @@ -1347,7 +1355,7 @@ static void fsi_dma_do_work(struct work_struct *work) struct snd_pcm_runtime *runtime; enum dma_data_direction dir; int is_play = fsi_stream_is_play(fsi, io); - int len; + int len, i; dma_addr_t buf; if (!fsi_stream_is_working(fsi, io)) @@ -1357,26 +1365,33 @@ static void fsi_dma_do_work(struct work_struct *work) runtime = io->substream->runtime; dir = is_play ? DMA_TO_DEVICE : DMA_FROM_DEVICE; len = samples_to_bytes(runtime, io->period_samples); - buf = fsi_dma_get_area(io); - dma_sync_single_for_device(dai->dev, buf, len, dir); + for (i = 0; i < io->loop_cnt; i++) { + buf = fsi_dma_get_area(io, io->additional_pos); - desc = dmaengine_prep_slave_single(io->chan, buf, len, dir, - DMA_PREP_INTERRUPT | DMA_CTRL_ACK); - if (!desc) { - dev_err(dai->dev, "dmaengine_prep_slave_sg() fail\n"); - return; - } + dma_sync_single_for_device(dai->dev, buf, len, dir); - desc->callback = fsi_dma_complete; - desc->callback_param = io; + desc = dmaengine_prep_slave_single(io->chan, buf, len, dir, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!desc) { + dev_err(dai->dev, "dmaengine_prep_slave_sg() fail\n"); + return; + } - if (dmaengine_submit(desc) < 0) { - dev_err(dai->dev, "tx_submit() fail\n"); - return; + desc->callback = fsi_dma_complete; + desc->callback_param = io; + + if (dmaengine_submit(desc) < 0) { + dev_err(dai->dev, "tx_submit() fail\n"); + return; + } + + dma_async_issue_pending(io->chan); + + io->additional_pos = 1; } - dma_async_issue_pending(io->chan); + io->loop_cnt = 1; /* * FIXME -- cgit v0.10.2 From f61df384282dfd1ca845e73ca8b8a187b87eb38a Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 26 Aug 2013 09:25:13 -0300 Subject: ASoC: fsl_ssi: Remove unnecessary dev_set_drvdata() Driver core clears the driver data to NULL after device_release or on probe failure, so just remove it from here. Signed-off-by: Fabio Estevam Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 5cf626c..c6b7439 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -1114,7 +1114,6 @@ error_dai: snd_soc_unregister_component(&pdev->dev); error_dev: - dev_set_drvdata(&pdev->dev, NULL); device_remove_file(&pdev->dev, dev_attr); error_clk: -- cgit v0.10.2 From f1aa06847506d5b88f5eb41fae6a24a7128097e7 Mon Sep 17 00:00:00 2001 From: Mengdong Lin Date: Mon, 26 Aug 2013 21:35:21 -0400 Subject: ALSA: hda - add flags and routines to get devices selection info for DP1.2 MST This patch adds flags and routines to get device list & selection info on a pin. To support Display Port 1.2 multi-stream transport (MST) over single DP port, a pin can support multiple devices. Please refer to HD-A spec Document Change Notificaton HDA040-A. A display audio codec can set flag "dp_mst" in its patch, indicating its pins can support MST. But at runtime, a pin may not be multi-streaming capable and report the device list is empty, depending on Gfx driver configuration. Signed-off-by: Mengdong Lin Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index fdbb09a..5b6c4e3 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -666,6 +666,64 @@ int snd_hda_get_conn_index(struct hda_codec *codec, hda_nid_t mux, } EXPORT_SYMBOL_HDA(snd_hda_get_conn_index); + +/* return DEVLIST_LEN parameter of the given widget */ +static unsigned int get_num_devices(struct hda_codec *codec, hda_nid_t nid) +{ + unsigned int wcaps = get_wcaps(codec, nid); + unsigned int parm; + + if (!codec->dp_mst || !(wcaps & AC_WCAP_DIGITAL) || + get_wcaps_type(wcaps) != AC_WID_PIN) + return 0; + + parm = snd_hda_param_read(codec, nid, AC_PAR_DEVLIST_LEN); + if (parm == -1 && codec->bus->rirb_error) + parm = 0; + return parm & AC_DEV_LIST_LEN_MASK; +} + +/** + * snd_hda_get_devices - copy device list without cache + * @codec: the HDA codec + * @nid: NID of the pin to parse + * @dev_list: device list array + * @max_devices: max. number of devices to store + * + * Copy the device list. This info is dynamic and so not cached. + * Currently called only from hda_proc.c, so not exported. + */ +int snd_hda_get_devices(struct hda_codec *codec, hda_nid_t nid, + u8 *dev_list, int max_devices) +{ + unsigned int parm; + int i, dev_len, devices; + + parm = get_num_devices(codec, nid); + if (!parm) /* not multi-stream capable */ + return 0; + + dev_len = parm + 1; + dev_len = dev_len < max_devices ? dev_len : max_devices; + + devices = 0; + while (devices < dev_len) { + parm = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_DEVICE_LIST, devices); + if (parm == -1 && codec->bus->rirb_error) + break; + + for (i = 0; i < 8; i++) { + dev_list[devices] = (u8)parm; + parm >>= 4; + devices++; + if (devices >= dev_len) + break; + } + } + return devices; +} + /** * snd_hda_queue_unsol_event - add an unsolicited event to queue * @bus: the BUS diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 701c2e0..b838c70 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -94,6 +94,8 @@ enum { #define AC_VERB_GET_HDMI_DIP_XMIT 0x0f32 #define AC_VERB_GET_HDMI_CP_CTRL 0x0f33 #define AC_VERB_GET_HDMI_CHAN_SLOT 0x0f34 +#define AC_VERB_GET_DEVICE_SEL 0xf35 +#define AC_VERB_GET_DEVICE_LIST 0xf36 /* * SET verbs @@ -133,6 +135,7 @@ enum { #define AC_VERB_SET_HDMI_DIP_XMIT 0x732 #define AC_VERB_SET_HDMI_CP_CTRL 0x733 #define AC_VERB_SET_HDMI_CHAN_SLOT 0x734 +#define AC_VERB_SET_DEVICE_SEL 0x735 /* * Parameter IDs @@ -154,6 +157,7 @@ enum { #define AC_PAR_GPIO_CAP 0x11 #define AC_PAR_AMP_OUT_CAP 0x12 #define AC_PAR_VOL_KNB_CAP 0x13 +#define AC_PAR_DEVLIST_LEN 0x15 #define AC_PAR_HDMI_LPCM_CAP 0x20 /* @@ -352,6 +356,10 @@ enum { #define AC_LPCMCAP_44K (1<<30) /* 44.1kHz support */ #define AC_LPCMCAP_44K_MS (1<<31) /* 44.1kHz-multiplies support */ +/* Display pin's device list length */ +#define AC_DEV_LIST_LEN_MASK 0x3f +#define AC_MAX_DEV_LIST_LEN 64 + /* * Control Parameters */ @@ -460,6 +468,11 @@ enum { #define AC_DEFCFG_PORT_CONN (0x3<<30) #define AC_DEFCFG_PORT_CONN_SHIFT 30 +/* Display pin's device list entry */ +#define AC_DE_PD (1<<0) +#define AC_DE_ELDV (1<<1) +#define AC_DE_IA (1<<2) + /* device device types (0x0-0xf) */ enum { AC_JACK_LINE_OUT, @@ -885,6 +898,7 @@ struct hda_codec { unsigned int pcm_format_first:1; /* PCM format must be set first */ unsigned int epss:1; /* supporting EPSS? */ unsigned int cached_write:1; /* write only to caches */ + unsigned int dp_mst:1; /* support DP1.2 Multi-stream transport */ #ifdef CONFIG_PM unsigned int power_on :1; /* current (global) power-state */ unsigned int d3_stop_clk:1; /* support D3 operation without BCLK */ @@ -972,6 +986,8 @@ int snd_hda_override_conn_list(struct hda_codec *codec, hda_nid_t nid, int nums, const hda_nid_t *list); int snd_hda_get_conn_index(struct hda_codec *codec, hda_nid_t mux, hda_nid_t nid, int recursive); +int snd_hda_get_devices(struct hda_codec *codec, hda_nid_t nid, + u8 *dev_list, int max_devices); int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid, u32 *ratesp, u64 *formatsp, unsigned int *bpsp); -- cgit v0.10.2 From 7a624ea56222fc6f6e3ccd135efedc195ba0b28d Mon Sep 17 00:00:00 2001 From: Mengdong Lin Date: Mon, 26 Aug 2013 21:35:31 -0400 Subject: ALSA: hda - add device list & select info of display pins to codec proc file If a display codec supports multi-stream transport on the pins, the pin's device list length and device entries will be exposed to codec proc file. Signed-off-by: Mengdong Lin Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/hda_proc.c b/sound/pci/hda/hda_proc.c index 9760f00..a8cb22e 100644 --- a/sound/pci/hda/hda_proc.c +++ b/sound/pci/hda/hda_proc.c @@ -582,6 +582,36 @@ static void print_gpio(struct snd_info_buffer *buffer, print_nid_array(buffer, codec, nid, &codec->nids); } +static void print_device_list(struct snd_info_buffer *buffer, + struct hda_codec *codec, hda_nid_t nid) +{ + int i, curr = -1; + u8 dev_list[AC_MAX_DEV_LIST_LEN]; + int devlist_len; + + devlist_len = snd_hda_get_devices(codec, nid, dev_list, + AC_MAX_DEV_LIST_LEN); + snd_iprintf(buffer, " Devices: %d\n", devlist_len); + if (devlist_len <= 0) + return; + + curr = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_DEVICE_SEL, 0); + + for (i = 0; i < devlist_len; i++) { + if (i == curr) + snd_iprintf(buffer, " *"); + else + snd_iprintf(buffer, " "); + + snd_iprintf(buffer, + "Dev %02d: PD = %d, ELDV = %d, IA = %d\n", i, + !!(dev_list[i] & AC_DE_PD), + !!(dev_list[i] & AC_DE_ELDV), + !!(dev_list[i] & AC_DE_IA)); + } +} + static void print_codec_info(struct snd_info_entry *entry, struct snd_info_buffer *buffer) { @@ -751,6 +781,9 @@ static void print_codec_info(struct snd_info_entry *entry, (wid_caps & AC_WCAP_DELAY) >> AC_WCAP_DELAY_SHIFT); + if (wid_type == AC_WID_PIN && codec->dp_mst) + print_device_list(buffer, codec, nid); + if (wid_caps & AC_WCAP_CONN_LIST) print_conn_list(buffer, codec, nid, wid_type, conn, conn_len); -- cgit v0.10.2 From 5dc989bdd968f369fec47d25343868ff9702953a Mon Sep 17 00:00:00 2001 From: Mengdong Lin Date: Mon, 26 Aug 2013 21:35:41 -0400 Subject: ALSA: hda - Haswell codec exposes device list/select info on pins This patch is only to allow codec proc file to expose devices list/select info for Haswell codec pins. Since Haswell Gfx driver cannot support DP1.2 MST now, so all pins' device list is empty, meaning no pin is multi-streaming capaple. Signed-off-by: Mengdong Lin Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 030ca86..87ca984 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -1989,8 +1989,10 @@ static int patch_generic_hdmi(struct hda_codec *codec) return -EINVAL; } codec->patch_ops = generic_hdmi_patch_ops; - if (codec->vendor_id == 0x80862807) + if (codec->vendor_id == 0x80862807) { codec->patch_ops.set_power_state = haswell_set_power_state; + codec->dp_mst = true; + } generic_hdmi_init_per_pins(codec); -- cgit v0.10.2 From 2e59e5ab1c24489c5581b83e56b0435432c54dfe Mon Sep 17 00:00:00 2001 From: Mengdong Lin Date: Mon, 26 Aug 2013 21:35:49 -0400 Subject: ALSA: hda - add device entry and inactive flag to unsolicited response This patch adds two fields to unsolicited response, according to spec HDA040-A: - Device Entry (bit 20:15) - Inactive (bit 2) and show the info in debug message. Signed-off-by: Mengdong Lin Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index b838c70..7aa9870 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -255,6 +255,11 @@ enum { #define AC_UNSOL_RES_TAG_SHIFT 26 #define AC_UNSOL_RES_SUBTAG (0x1f<<21) #define AC_UNSOL_RES_SUBTAG_SHIFT 21 +#define AC_UNSOL_RES_DE (0x3f<<15) /* Device Entry + * (for DP1.2 MST) + */ +#define AC_UNSOL_RES_DE_SHIFT 15 +#define AC_UNSOL_RES_IA (1<<2) /* Inactive (for DP1.2 MST) */ #define AC_UNSOL_RES_ELDV (1<<1) /* ELD Data valid (for HDMI) */ #define AC_UNSOL_RES_PD (1<<0) /* pinsense detect */ #define AC_UNSOL_RES_CP_STATE (1<<1) /* content protection */ diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 87ca984..895a0d3 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -959,6 +959,7 @@ static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res) int pin_nid; int pin_idx; struct hda_jack_tbl *jack; + int dev_entry = (res & AC_UNSOL_RES_DE) >> AC_UNSOL_RES_DE_SHIFT; jack = snd_hda_jack_tbl_get_from_tag(codec, tag); if (!jack) @@ -967,8 +968,8 @@ static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res) jack->jack_dirty = 1; _snd_printd(SND_PR_VERBOSE, - "HDMI hot plug event: Codec=%d Pin=%d Presence_Detect=%d ELD_Valid=%d\n", - codec->addr, pin_nid, + "HDMI hot plug event: Codec=%d Pin=%d Device=%d Inactive=%d Presence_Detect=%d ELD_Valid=%d\n", + codec->addr, pin_nid, dev_entry, !!(res & AC_UNSOL_RES_IA), !!(res & AC_UNSOL_RES_PD), !!(res & AC_UNSOL_RES_ELDV)); pin_idx = pin_nid_to_pin_index(spec, pin_nid); -- cgit v0.10.2 From 1c9a341bbdc14051a4d8c74ea67269786c7d3736 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 27 Aug 2013 14:49:59 +0200 Subject: ALSA: hda - Simplify CONFIG_SND_HDA_I915 condition CONFIG_SND_HDA_I915 doesn't have to be user-selectable as this is almost mandatory when i915 driver is available. Let's enable it always when CONFIG_DRM_I915 is set, so that user won't be bothered by useless questions. Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index 59c5e9c..8de66cc 100644 --- a/sound/pci/hda/Kconfig +++ b/sound/pci/hda/Kconfig @@ -152,14 +152,9 @@ config SND_HDA_CODEC_HDMI This module is automatically loaded at probing. config SND_HDA_I915 - bool "Build Display HD-audio controller/codec power well support for i915 cards" + bool + default y depends on DRM_I915 - help - Say Y here to include full HDMI and DisplayPort HD-audio controller/codec - power-well support for Intel Haswell graphics cards based on the i915 driver. - - Note that this option must be enabled for Intel Haswell C+ stepping machines, otherwise - the GPU audio controller/codecs will not be initialized or damaged when exit from S3 mode. config SND_HDA_CODEC_CIRRUS bool "Build Cirrus Logic codec support" -- cgit v0.10.2 From a85f9da707366e856c0aad9e329db0cc59475290 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 27 Aug 2013 15:50:55 +0200 Subject: ASoC: dmic: Convert table based DAPM setup Let the core take care of instantiating the DAPM widgets and routes, this makes the code a bit shorter. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/dmic.c b/sound/soc/codecs/dmic.c index 66967ba..b2090b2 100644 --- a/sound/soc/codecs/dmic.c +++ b/sound/soc/codecs/dmic.c @@ -50,20 +50,11 @@ static const struct snd_soc_dapm_route intercon[] = { {"DMIC AIF", NULL, "DMic"}, }; -static int dmic_probe(struct snd_soc_codec *codec) -{ - struct snd_soc_dapm_context *dapm = &codec->dapm; - - snd_soc_dapm_new_controls(dapm, dmic_dapm_widgets, - ARRAY_SIZE(dmic_dapm_widgets)); - snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon)); - snd_soc_dapm_new_widgets(dapm); - - return 0; -} - static struct snd_soc_codec_driver soc_dmic = { - .probe = dmic_probe, + .dapm_widgets = dmic_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(dmic_dapm_widgets), + .dapm_routes = intercon, + .num_dapm_routes = ARRAY_SIZE(intercon), }; static int dmic_dev_probe(struct platform_device *pdev) -- cgit v0.10.2 From 34742cb02bd368c1af3349c041d3e4446f7ac6ef Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 27 Aug 2013 15:50:54 +0200 Subject: ASoC: dapm: Fix marking widgets dirty when a route is added The current calls to dapm_mark_dirty() in snd_soc_dapm_add_path() are on a path that is only reached if the sink widget is either a mixer or a mux. Move the calls further up so they are called for all widget types. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index d84bd0f..7e9afbc4 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -2374,6 +2374,9 @@ static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm, wsource->ext = 1; } + dapm_mark_dirty(wsource, "Route added"); + dapm_mark_dirty(wsink, "Route added"); + /* connect static paths */ if (control == NULL) { list_add(&path->list, &dapm->card->paths); @@ -2436,9 +2439,6 @@ static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm, return 0; } - dapm_mark_dirty(wsource, "Route added"); - dapm_mark_dirty(wsink, "Route added"); - return 0; err: kfree(path); -- cgit v0.10.2 From aac97b5fd9537b62a68830d189509297cdac5ad9 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 27 Aug 2013 15:50:56 +0200 Subject: ASoC: tlv320aic32x4: Convert table based control and DAPM setup Let the core take care of instantiating the controls and DAPM widgets and routes, this makes the code a bit shorter. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/tlv320aic32x4.c b/sound/soc/codecs/tlv320aic32x4.c index 17df4e3..2ed57d4 100644 --- a/sound/soc/codecs/tlv320aic32x4.c +++ b/sound/soc/codecs/tlv320aic32x4.c @@ -338,18 +338,6 @@ static inline int aic32x4_get_divs(int mclk, int rate) return -EINVAL; } -static int aic32x4_add_widgets(struct snd_soc_codec *codec) -{ - snd_soc_dapm_new_controls(&codec->dapm, aic32x4_dapm_widgets, - ARRAY_SIZE(aic32x4_dapm_widgets)); - - snd_soc_dapm_add_routes(&codec->dapm, aic32x4_dapm_routes, - ARRAY_SIZE(aic32x4_dapm_routes)); - - snd_soc_dapm_new_widgets(&codec->dapm); - return 0; -} - static int aic32x4_set_dai_sysclk(struct snd_soc_dai *codec_dai, int clk_id, unsigned int freq, int dir) { @@ -683,9 +671,6 @@ static int aic32x4_probe(struct snd_soc_codec *codec) } aic32x4_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - snd_soc_add_codec_controls(codec, aic32x4_snd_controls, - ARRAY_SIZE(aic32x4_snd_controls)); - aic32x4_add_widgets(codec); /* * Workaround: for an unknown reason, the ADC needs to be powered up @@ -714,6 +699,13 @@ static struct snd_soc_codec_driver soc_codec_dev_aic32x4 = { .suspend = aic32x4_suspend, .resume = aic32x4_resume, .set_bias_level = aic32x4_set_bias_level, + + .controls = aic32x4_snd_controls, + .num_controls = ARRAY_SIZE(aic32x4_snd_controls), + .dapm_widgets = aic32x4_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(aic32x4_dapm_widgets), + .dapm_routes = aic32x4_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(aic32x4_dapm_routes), }; static int aic32x4_i2c_probe(struct i2c_client *i2c, -- cgit v0.10.2 From 318ee162c882526685be4f44d7b519cdcc45cbfe Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 27 Aug 2013 15:50:57 +0200 Subject: ASoC: wm8904: Remove unnecessary call to snd_soc_dapm_new_widgets() The core will call snd_soc_dapm_new_widgets() once all components of the card have been initialized, so there is no need to do this manually in the driver. Calling it earlier also might result in a partially instantiated system being powered up which cause undesired side effects. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/wm8904.c b/sound/soc/codecs/wm8904.c index 91dfbfe..4dfa8dc 100644 --- a/sound/soc/codecs/wm8904.c +++ b/sound/soc/codecs/wm8904.c @@ -1202,7 +1202,6 @@ static int wm8904_add_widgets(struct snd_soc_codec *codec) break; } - snd_soc_dapm_new_widgets(dapm); return 0; } -- cgit v0.10.2 From 148663074c1778d88c9e9c5f5cc66493ed30fa25 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 27 Aug 2013 15:50:58 +0200 Subject: ASoC: jack: Remove unnecessary call to snd_soc_dapm_new_widgets() snd_soc_jack_add_pins() does not create any new DAPM widgets, so there is no need to call snd_soc_dapm_new_widgets(). Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/soc-jack.c b/sound/soc/soc-jack.c index 7aa26b5..71358e3 100644 --- a/sound/soc/soc-jack.c +++ b/sound/soc/soc-jack.c @@ -183,8 +183,6 @@ int snd_soc_jack_add_pins(struct snd_soc_jack *jack, int count, list_add(&(pins[i].list), &jack->pins); } - snd_soc_dapm_new_widgets(&jack->codec->card->dapm); - /* Update to reflect the last reported status; canned jack * implementations are likely to set their state before the * card has an opportunity to associate pins. -- cgit v0.10.2 From 4b52fa211a7c65eab78acf3f434361d40de87688 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 27 Aug 2013 15:50:59 +0200 Subject: ASoC: Call snd_soc_dapm_new_widgets() only once during card initialization Each time snd_soc_dapm_new_widgets() is called it will instantiate all the widgets and routes that have been added so far and then power them. Doing this multiple times before the card is fully initialized and all widgets have been added can cause unnecessary and even invalid power state transitions which can result in extra register writes and and also might cause clicks and pops. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index f46472d..85e2a8b 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1230,9 +1230,6 @@ static int soc_post_component_init(struct snd_soc_card *card, } rtd->card = card; - /* Make sure all DAPM widgets are instantiated */ - snd_soc_dapm_new_widgets(&codec->dapm); - /* machine controls, routes and widgets are not prefixed */ temp = codec->name_prefix; codec->name_prefix = NULL; @@ -1728,8 +1725,6 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card) snd_soc_dapm_add_routes(&card->dapm, card->dapm_routes, card->num_dapm_routes); - snd_soc_dapm_new_widgets(&card->dapm); - for (i = 0; i < card->num_links; i++) { dai_link = &card->dai_link[i]; dai_fmt = dai_link->dai_fmt; -- cgit v0.10.2 From 8c193b8dce4f2a2474dc2bc39ec972454df9d439 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 27 Aug 2013 15:51:00 +0200 Subject: ASoC: Move call to snd_soc_dapm_new_widgets() after snd_soc_dapm_auto_nc_codec_pins() Call snd_soc_dapm_new_widgets() before the auto non-connected pins have been marked as not connected will power the system under the assumption that those pins are connected. Once the pins have been marked as disconnected the system there will be an additional power run. This can cause unnecessary power transitions. Calling snd_soc_dapm_new_widgets() only after the pins have been marked as non-connected avoids this. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 85e2a8b..9375012 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1803,12 +1803,12 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card) } } - snd_soc_dapm_new_widgets(&card->dapm); - if (card->fully_routed) list_for_each_entry(codec, &card->codec_dev_list, card_list) snd_soc_dapm_auto_nc_codec_pins(codec); + snd_soc_dapm_new_widgets(&card->dapm); + ret = snd_card_register(card->snd_card); if (ret < 0) { dev_err(card->dev, "ASoC: failed to register soundcard %d\n", -- cgit v0.10.2 From 824ef826f3c4d83d1925a5e351313bfd3e5ca6cb Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 27 Aug 2013 15:51:01 +0200 Subject: ASoC: Pass card instead of dapm context to snd_soc_dapm_new_widgets() snd_soc_dapm_new_widgets() works on the ASoC card as a whole not on a specific DAPM context. The DAPM context that is passed as the parameter is only used to look up the pointer to the card. This patch updates the signature of snd_soc_dapm_new_widgets() to take the card directly. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index c728d28..27a72d5 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -413,7 +413,7 @@ int snd_soc_dapm_new_pcm(struct snd_soc_card *card, struct snd_soc_dapm_widget *sink); /* dapm path setup */ -int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm); +int snd_soc_dapm_new_widgets(struct snd_soc_card *card); 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); diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index d476f75..ed3c253 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1807,7 +1807,7 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card) list_for_each_entry(codec, &card->codec_dev_list, card_list) snd_soc_dapm_auto_nc_codec_pins(codec); - snd_soc_dapm_new_widgets(&card->dapm); + snd_soc_dapm_new_widgets(card); ret = snd_card_register(card->snd_card); if (ret < 0) { diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 7e9afbc4..548b1c9 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -2712,9 +2712,8 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_weak_routes); * * Returns 0 for success. */ -int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm) +int snd_soc_dapm_new_widgets(struct snd_soc_card *card) { - struct snd_soc_card *card = dapm->card; struct snd_soc_dapm_widget *w; unsigned int val; -- cgit v0.10.2 From 446a3bd4329bcaf95d71c6717c2c424a0f97ff18 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Tue, 27 Aug 2013 20:27:11 +0200 Subject: ASoc: kirkwood: Use the Kirkwood audio driver in Dove boards This patch permits the generation of the Kirkwood audio driver which may be used in the Dove boards. Signed-off-by: Jean-Francois Moine Signed-off-by: Mark Brown diff --git a/sound/soc/kirkwood/Kconfig b/sound/soc/kirkwood/Kconfig index 9e1970c..78ed4a4 100644 --- a/sound/soc/kirkwood/Kconfig +++ b/sound/soc/kirkwood/Kconfig @@ -1,6 +1,6 @@ config SND_KIRKWOOD_SOC - tristate "SoC Audio for the Marvell Kirkwood chip" - depends on ARCH_KIRKWOOD || COMPILE_TEST + tristate "SoC Audio for the Marvell Kirkwood and Dove chips" + depends on ARCH_KIRKWOOD || ARCH_DOVE || COMPILE_TEST help Say Y or M if you want to add support for codecs attached to the Kirkwood I2S interface. You will also need to select the -- cgit v0.10.2 From 2a956ec04b3703809b6cf500dbee450e44f3a70c Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Wed, 28 Aug 2013 12:04:46 +0800 Subject: ASoC: fsl: Add S/PDIF machine driver This patch implements a device-tree-only machine driver for Freescale i.MX series Soc. It works with spdif_transmitter/spdif_receiver and fsl_spdif.c drivers. Signed-off-by: Nicolin Chen Acked-by: Stephen Warren Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/sound/imx-audio-spdif.txt b/Documentation/devicetree/bindings/sound/imx-audio-spdif.txt new file mode 100644 index 0000000..7d13479 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/imx-audio-spdif.txt @@ -0,0 +1,34 @@ +Freescale i.MX audio complex with S/PDIF transceiver + +Required properties: + + - compatible : "fsl,imx-audio-spdif" + + - model : The user-visible name of this sound complex + + - spdif-controller : The phandle of the i.MX S/PDIF controller + + +Optional properties: + + - spdif-out : This is a boolean property. If present, the transmitting + function of S/PDIF will be enabled, indicating there's a physical + S/PDIF out connector/jack on the board or it's connecting to some + other IP block, such as an HDMI encoder/display-controller. + + - spdif-in : This is a boolean property. If present, the receiving + function of S/PDIF will be enabled, indicating there's a physical + S/PDIF in connector/jack on the board. + +* Note: At least one of these two properties should be set in the DT binding. + + +Example: + +sound-spdif { + compatible = "fsl,imx-audio-spdif"; + model = "imx-spdif"; + spdif-controller = <&spdif>; + spdif-out; + spdif-in; +}; diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index cd088cc..a708380 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig @@ -193,6 +193,17 @@ config SND_SOC_IMX_SGTL5000 Say Y if you want to add support for SoC audio on an i.MX board with a sgtl5000 codec. +config SND_SOC_IMX_SPDIF + tristate "SoC Audio support for i.MX boards with S/PDIF" + select SND_SOC_IMX_PCM_DMA + select SND_SOC_FSL_SPDIF + select SND_SOC_FSL_UTILS + select SND_SOC_SPDIF + help + SoC Audio support for i.MX boards with S/PDIF + Say Y if you want to add support for SoC audio on an i.MX board with + a S/DPDIF. + config SND_SOC_IMX_MC13783 tristate "SoC Audio support for I.MX boards with mc13783" depends on MFD_MC13783 && ARM diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile index 4b5970e..e2aaff7 100644 --- a/sound/soc/fsl/Makefile +++ b/sound/soc/fsl/Makefile @@ -45,6 +45,7 @@ snd-soc-mx27vis-aic32x4-objs := mx27vis-aic32x4.o snd-soc-wm1133-ev1-objs := wm1133-ev1.o snd-soc-imx-sgtl5000-objs := imx-sgtl5000.o snd-soc-imx-wm8962-objs := imx-wm8962.o +snd-soc-imx-spdif-objs :=imx-spdif.o snd-soc-imx-mc13783-objs := imx-mc13783.o obj-$(CONFIG_SND_SOC_EUKREA_TLV320) += snd-soc-eukrea-tlv320.o @@ -53,4 +54,5 @@ obj-$(CONFIG_SND_SOC_MX27VIS_AIC32X4) += snd-soc-mx27vis-aic32x4.o obj-$(CONFIG_SND_MXC_SOC_WM1133_EV1) += snd-soc-wm1133-ev1.o obj-$(CONFIG_SND_SOC_IMX_SGTL5000) += snd-soc-imx-sgtl5000.o obj-$(CONFIG_SND_SOC_IMX_WM8962) += snd-soc-imx-wm8962.o +obj-$(CONFIG_SND_SOC_IMX_SPDIF) += snd-soc-imx-spdif.o obj-$(CONFIG_SND_SOC_IMX_MC13783) += snd-soc-imx-mc13783.o diff --git a/sound/soc/fsl/imx-spdif.c b/sound/soc/fsl/imx-spdif.c new file mode 100644 index 0000000..816013b --- /dev/null +++ b/sound/soc/fsl/imx-spdif.c @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2013 Freescale Semiconductor, Inc. + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include +#include +#include + +struct imx_spdif_data { + struct snd_soc_dai_link dai[2]; + struct snd_soc_card card; + struct platform_device *txdev; + struct platform_device *rxdev; +}; + +static int imx_spdif_audio_probe(struct platform_device *pdev) +{ + struct device_node *spdif_np, *np = pdev->dev.of_node; + struct imx_spdif_data *data; + int ret = 0, num_links = 0; + + spdif_np = of_parse_phandle(np, "spdif-controller", 0); + if (!spdif_np) { + dev_err(&pdev->dev, "failed to find spdif-controller\n"); + ret = -EINVAL; + goto end; + } + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) { + dev_err(&pdev->dev, "failed to allocate memory\n"); + ret = -ENOMEM; + goto end; + } + + if (of_property_read_bool(np, "spdif-out")) { + data->dai[num_links].name = "S/PDIF TX"; + data->dai[num_links].stream_name = "S/PDIF PCM Playback"; + data->dai[num_links].codec_dai_name = "dit-hifi"; + data->dai[num_links].codec_name = "spdif-dit"; + data->dai[num_links].cpu_of_node = spdif_np; + data->dai[num_links].platform_of_node = spdif_np; + num_links++; + + data->txdev = platform_device_register_simple("spdif-dit", -1, NULL, 0); + if (IS_ERR(data->txdev)) { + ret = PTR_ERR(data->txdev); + dev_err(&pdev->dev, "register dit failed: %d\n", ret); + goto end; + } + } + + if (of_property_read_bool(np, "spdif-in")) { + data->dai[num_links].name = "S/PDIF RX"; + data->dai[num_links].stream_name = "S/PDIF PCM Capture"; + data->dai[num_links].codec_dai_name = "dir-hifi"; + data->dai[num_links].codec_name = "spdif-dir"; + data->dai[num_links].cpu_of_node = spdif_np; + data->dai[num_links].platform_of_node = spdif_np; + num_links++; + + data->rxdev = platform_device_register_simple("spdif-dir", -1, NULL, 0); + if (IS_ERR(data->rxdev)) { + ret = PTR_ERR(data->rxdev); + dev_err(&pdev->dev, "register dir failed: %d\n", ret); + goto error_dit; + } + } + + if (!num_links) { + dev_err(&pdev->dev, "no enabled S/PDIF DAI link\n"); + goto error_dir; + } + + data->card.dev = &pdev->dev; + data->card.num_links = num_links; + data->card.dai_link = data->dai; + + ret = snd_soc_of_parse_card_name(&data->card, "model"); + if (ret) + goto error_dir; + + ret = snd_soc_register_card(&data->card); + if (ret) { + dev_err(&pdev->dev, "snd_soc_register_card failed: %d\n", ret); + goto error_dir; + } + + platform_set_drvdata(pdev, data); + + goto end; + +error_dir: + if (data->rxdev) + platform_device_unregister(data->rxdev); +error_dit: + if (data->txdev) + platform_device_unregister(data->txdev); +end: + if (spdif_np) + of_node_put(spdif_np); + + return ret; +} + +static int imx_spdif_audio_remove(struct platform_device *pdev) +{ + struct imx_spdif_data *data = platform_get_drvdata(pdev); + + if (data->rxdev) + platform_device_unregister(data->rxdev); + if (data->txdev) + platform_device_unregister(data->txdev); + + snd_soc_unregister_card(&data->card); + + return 0; +} + +static const struct of_device_id imx_spdif_dt_ids[] = { + { .compatible = "fsl,imx-audio-spdif", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, imx_spdif_dt_ids); + +static struct platform_driver imx_spdif_driver = { + .driver = { + .name = "imx-spdif", + .owner = THIS_MODULE, + .of_match_table = imx_spdif_dt_ids, + }, + .probe = imx_spdif_audio_probe, + .remove = imx_spdif_audio_remove, +}; + +module_platform_driver(imx_spdif_driver); + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("Freescale i.MX S/PDIF machine driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:imx-spdif"); -- cgit v0.10.2 From bfd7d1aa3b603cf43e6545f873de714b991d6a8a Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Thu, 29 Aug 2013 08:00:05 +0800 Subject: ASoC: fsl_spdif: remove redundant dev_err call in fsl_spdif_probe() There is a error message within devm_ioremap_resource already, so remove the dev_err call to avoid redundant error message. Signed-off-by: Wei Yongjun Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c index a9798aa..e93dc0d 100644 --- a/sound/soc/fsl/fsl_spdif.c +++ b/sound/soc/fsl/fsl_spdif.c @@ -1113,10 +1113,8 @@ static int fsl_spdif_probe(struct platform_device *pdev) } regs = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(regs)) { - dev_err(&pdev->dev, "could not map device resources\n"); + if (IS_ERR(regs)) return PTR_ERR(regs); - } spdif_priv->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "core", regs, &fsl_spdif_regmap_config); -- cgit v0.10.2 From e925a6b1b6e7ddb43a71b31c0afa12ca9a6ec118 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 26 Aug 2013 09:25:15 -0300 Subject: ASoC: designware_i2s: Remove unnecessary dev_set_drvdata() Driver core clears the driver data to NULL after device_release or on probe failure, so just remove it from here. Signed-off-by: Fabio Estevam Signed-off-by: Mark Brown diff --git a/sound/soc/dwc/designware_i2s.c b/sound/soc/dwc/designware_i2s.c index 70eb37a..25c31f1 100644 --- a/sound/soc/dwc/designware_i2s.c +++ b/sound/soc/dwc/designware_i2s.c @@ -421,13 +421,11 @@ static int dw_i2s_probe(struct platform_device *pdev) dw_i2s_dai, 1); if (ret != 0) { dev_err(&pdev->dev, "not able to register dai\n"); - goto err_set_drvdata; + goto err_clk_disable; } return 0; -err_set_drvdata: - dev_set_drvdata(&pdev->dev, NULL); err_clk_disable: clk_disable(dev->clk); err_clk_put: @@ -440,7 +438,6 @@ static int dw_i2s_remove(struct platform_device *pdev) struct dw_i2s_dev *dev = dev_get_drvdata(&pdev->dev); snd_soc_unregister_component(&pdev->dev); - dev_set_drvdata(&pdev->dev, NULL); clk_put(dev->clk); -- cgit v0.10.2 From ba1fb69508615011eba225de1ed2615fa205be9a Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 26 Aug 2013 09:25:14 -0300 Subject: ASoC: ep93xx-i2s: Remove unnecessary dev_set_drvdata() Driver core clears the driver data to NULL after device_release or on probe failure, so just remove it from here. Signed-off-by: Fabio Estevam Signed-off-by: Mark Brown diff --git a/sound/soc/cirrus/ep93xx-i2s.c b/sound/soc/cirrus/ep93xx-i2s.c index f23f331..a57643d 100644 --- a/sound/soc/cirrus/ep93xx-i2s.c +++ b/sound/soc/cirrus/ep93xx-i2s.c @@ -408,7 +408,6 @@ static int ep93xx_i2s_probe(struct platform_device *pdev) return 0; fail_put_lrclk: - dev_set_drvdata(&pdev->dev, NULL); clk_put(info->lrclk); fail_put_sclk: clk_put(info->sclk); @@ -423,7 +422,6 @@ static int ep93xx_i2s_remove(struct platform_device *pdev) struct ep93xx_i2s_info *info = dev_get_drvdata(&pdev->dev); snd_soc_unregister_component(&pdev->dev); - dev_set_drvdata(&pdev->dev, NULL); clk_put(info->lrclk); clk_put(info->sclk); clk_put(info->mclk); -- cgit v0.10.2 From 9b9ae16a97e08bdc4fd5e726a4d17119dbae5d8a Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Sun, 11 Aug 2013 19:59:21 +0200 Subject: ASoC: Samsung: Do not queue cyclic buffers multiple times The legacy S3C-DMA API required every period of a cyclic buffer to be queued separately. After conversion of Samsung ASoC to Samsung DMA wrappers somebody made an assumption that the same is needed for DMA engine API, which is not true. In effect, Samsung ASoC DMA code was queuing the whole cyclic buffer multiple times with a shift of one period per iteration, leading to: a) severe memory waste - up to 13x times more DMA transfer descriptors are allocated than needed, b) possible memory corruption, because further cyclic buffers were out of the original buffers, due to the offset. This patch fixes this problem by making the legacy S3C-DMA API use the same semantics as DMA engine (the whole cyclic buffer is enqueued at once) and modifying users of Samsung DMA wrappers in cyclic mode to behave appropriately. Signed-off-by: Tomasz Figa Acked-by: Linus Walleij Signed-off-by: Mark Brown diff --git a/arch/arm/plat-samsung/s3c-dma-ops.c b/arch/arm/plat-samsung/s3c-dma-ops.c index 0cc40ae..98b10ba 100644 --- a/arch/arm/plat-samsung/s3c-dma-ops.c +++ b/arch/arm/plat-samsung/s3c-dma-ops.c @@ -82,7 +82,8 @@ static int s3c_dma_config(unsigned ch, struct samsung_dma_config *param) static int s3c_dma_prepare(unsigned ch, struct samsung_dma_prep *param) { struct cb_data *data; - int len = (param->cap == DMA_CYCLIC) ? param->period : param->len; + dma_addr_t pos = param->buf; + dma_addr_t end = param->buf + param->len; list_for_each_entry(data, &dma_list, node) if (data->ch == ch) @@ -94,7 +95,15 @@ static int s3c_dma_prepare(unsigned ch, struct samsung_dma_prep *param) data->fp_param = param->fp_param; } - s3c2410_dma_enqueue(ch, (void *)data, param->buf, len); + if (param->cap != DMA_CYCLIC) { + s3c2410_dma_enqueue(ch, (void *)data, param->buf, param->len); + return 0; + } + + while (pos < end) { + s3c2410_dma_enqueue(ch, (void *)data, pos, param->period); + pos += param->period; + } return 0; } diff --git a/sound/soc/samsung/dma.c b/sound/soc/samsung/dma.c index a0c67f6..9338d11 100644 --- a/sound/soc/samsung/dma.c +++ b/sound/soc/samsung/dma.c @@ -90,6 +90,13 @@ static void dma_enqueue(struct snd_pcm_substream *substream) dma_info.period = prtd->dma_period; dma_info.len = prtd->dma_period*limit; + if (dma_info.cap == DMA_CYCLIC) { + dma_info.buf = pos; + prtd->params->ops->prepare(prtd->params->ch, &dma_info); + prtd->dma_loaded += limit; + return; + } + while (prtd->dma_loaded < limit) { pr_debug("dma_loaded: %d\n", prtd->dma_loaded); -- cgit v0.10.2 From 2f82cdbafd53a01e3a3995a618b650653eed9c1a Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Thu, 29 Aug 2013 17:31:41 -0300 Subject: ASoC: fsl: Drop SND_SOC_FSL_UTILS from SND_SOC_IMX_SPDIF SND_SOC_FSL_UTILS is only used by PowerPC machines, so let's drop it in the i.mx case. Signed-off-by: Fabio Estevam Acked-by: Nicolin Chen Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index a708380..704e246 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig @@ -197,7 +197,6 @@ config SND_SOC_IMX_SPDIF tristate "SoC Audio support for i.MX boards with S/PDIF" select SND_SOC_IMX_PCM_DMA select SND_SOC_FSL_SPDIF - select SND_SOC_FSL_UTILS select SND_SOC_SPDIF help SoC Audio support for i.MX boards with S/PDIF -- cgit v0.10.2 From 2daabd7848b89afddd93be616f1be5639ea78822 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 30 Aug 2013 17:39:33 +0200 Subject: ASoC: dapm: Fix auto-disable for inverted controls We need to make sure that the control's cached value is initialized to the same value as the control's widget->on_val. Otherwise updates might be lost. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 7e9afbc4..13fcb61 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -229,6 +229,8 @@ static int dapm_kcontrol_data_alloc(struct snd_soc_dapm_widget *widget, template.id = snd_soc_dapm_kcontrol; template.name = kcontrol->id.name; + data->value = template.on_val; + data->widget = snd_soc_dapm_new_control(widget->dapm, &template); if (!data->widget) { -- cgit v0.10.2 From 246693ba7b0b824a970f9431486ad88c18e0ce2d Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Fri, 23 Aug 2013 10:29:26 +0800 Subject: ASoC: rt5640: change widget sequence for depop Signed-off-by: Bard Liao Tested-by: Stephen Warren Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c index 4db7314..c26a8f8 100644 --- a/sound/soc/codecs/rt5640.c +++ b/sound/soc/codecs/rt5640.c @@ -50,8 +50,6 @@ static const struct regmap_range_cfg rt5640_ranges[] = { static struct reg_default init_list[] = { {RT5640_PR_BASE + 0x3d, 0x3600}, - {RT5640_PR_BASE + 0x1c, 0x0D21}, - {RT5640_PR_BASE + 0x1b, 0x0000}, {RT5640_PR_BASE + 0x12, 0x0aa8}, {RT5640_PR_BASE + 0x14, 0x0aaa}, {RT5640_PR_BASE + 0x20, 0x6110}, @@ -384,15 +382,11 @@ static const SOC_ENUM_SINGLE_DECL( static const struct snd_kcontrol_new rt5640_snd_controls[] = { /* Speaker Output Volume */ - SOC_DOUBLE("Speaker Playback Switch", RT5640_SPK_VOL, - RT5640_L_MUTE_SFT, RT5640_R_MUTE_SFT, 1, 1), SOC_DOUBLE("Speaker Channel Switch", RT5640_SPK_VOL, RT5640_VOL_L_SFT, RT5640_VOL_R_SFT, 1, 1), SOC_DOUBLE_TLV("Speaker Playback Volume", RT5640_SPK_VOL, RT5640_L_VOL_SFT, RT5640_R_VOL_SFT, 39, 1, out_vol_tlv), /* Headphone Output Volume */ - SOC_DOUBLE("HP Playback Switch", RT5640_HP_VOL, - RT5640_L_MUTE_SFT, RT5640_R_MUTE_SFT, 1, 1), SOC_DOUBLE("HP Channel Switch", RT5640_HP_VOL, RT5640_VOL_L_SFT, RT5640_VOL_R_SFT, 1, 1), SOC_DOUBLE_TLV("HP Playback Volume", RT5640_HP_VOL, @@ -737,6 +731,22 @@ static const struct snd_kcontrol_new rt5640_mono_mix[] = { RT5640_M_BST1_MM_SFT, 1, 1), }; +static const struct snd_kcontrol_new spk_l_enable_control = + SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5640_SPK_VOL, + RT5640_L_MUTE_SFT, 1, 1); + +static const struct snd_kcontrol_new spk_r_enable_control = + SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5640_SPK_VOL, + RT5640_R_MUTE_SFT, 1, 1); + +static const struct snd_kcontrol_new hp_l_enable_control = + SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5640_HP_VOL, + RT5640_L_MUTE_SFT, 1, 1); + +static const struct snd_kcontrol_new hp_r_enable_control = + SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5640_HP_VOL, + RT5640_R_MUTE_SFT, 1, 1); + /* Stereo ADC source */ static const char * const rt5640_stereo_adc1_src[] = { "DIG MIX", "ADC" @@ -868,33 +878,6 @@ static const SOC_ENUM_SINGLE_DECL( static const struct snd_kcontrol_new rt5640_sdi_mux = SOC_DAPM_ENUM("SDI select", rt5640_sdi_sel_enum); -static int spk_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) -{ - struct snd_soc_codec *codec = w->codec; - struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec); - - switch (event) { - case SND_SOC_DAPM_POST_PMU: - regmap_update_bits(rt5640->regmap, RT5640_PWR_DIG1, - 0x0001, 0x0001); - regmap_update_bits(rt5640->regmap, RT5640_PR_BASE + 0x1c, - 0xf000, 0xf000); - break; - - case SND_SOC_DAPM_PRE_PMD: - regmap_update_bits(rt5640->regmap, RT5640_PR_BASE + 0x1c, - 0xf000, 0x0000); - regmap_update_bits(rt5640->regmap, RT5640_PWR_DIG1, - 0x0001, 0x0000); - break; - - default: - return 0; - } - return 0; -} - static int rt5640_set_dmic1_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { @@ -943,6 +926,117 @@ static int rt5640_set_dmic2_event(struct snd_soc_dapm_widget *w, return 0; } +void hp_amp_power_on(struct snd_soc_codec *codec) +{ + struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec); + + /* depop parameters */ + regmap_update_bits(rt5640->regmap, RT5640_PR_BASE + + RT5640_CHPUMP_INT_REG1, 0x0700, 0x0200); + regmap_update_bits(rt5640->regmap, RT5640_DEPOP_M2, + RT5640_DEPOP_MASK, RT5640_DEPOP_MAN); + regmap_update_bits(rt5640->regmap, RT5640_DEPOP_M1, + RT5640_HP_CP_MASK | RT5640_HP_SG_MASK | RT5640_HP_CB_MASK, + RT5640_HP_CP_PU | RT5640_HP_SG_DIS | RT5640_HP_CB_PU); + regmap_write(rt5640->regmap, RT5640_PR_BASE + RT5640_HP_DCC_INT1, + 0x9f00); + /* headphone amp power on */ + regmap_update_bits(rt5640->regmap, RT5640_PWR_ANLG1, + RT5640_PWR_FV1 | RT5640_PWR_FV2, 0); + regmap_update_bits(rt5640->regmap, RT5640_PWR_ANLG1, + RT5640_PWR_HA, + RT5640_PWR_HA); + usleep_range(10000, 15000); + regmap_update_bits(rt5640->regmap, RT5640_PWR_ANLG1, + RT5640_PWR_FV1 | RT5640_PWR_FV2 , + RT5640_PWR_FV1 | RT5640_PWR_FV2); +} + +static void rt5640_pmu_depop(struct snd_soc_codec *codec) +{ + struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec); + + regmap_update_bits(rt5640->regmap, RT5640_DEPOP_M2, + RT5640_DEPOP_MASK | RT5640_DIG_DP_MASK, + RT5640_DEPOP_AUTO | RT5640_DIG_DP_EN); + regmap_update_bits(rt5640->regmap, RT5640_CHARGE_PUMP, + RT5640_PM_HP_MASK, RT5640_PM_HP_HV); + + regmap_update_bits(rt5640->regmap, RT5640_DEPOP_M3, + RT5640_CP_FQ1_MASK | RT5640_CP_FQ2_MASK | RT5640_CP_FQ3_MASK, + (RT5640_CP_FQ_192_KHZ << RT5640_CP_FQ1_SFT) | + (RT5640_CP_FQ_12_KHZ << RT5640_CP_FQ2_SFT) | + (RT5640_CP_FQ_192_KHZ << RT5640_CP_FQ3_SFT)); + + regmap_write(rt5640->regmap, RT5640_PR_BASE + + RT5640_MAMP_INT_REG2, 0x1c00); + regmap_update_bits(rt5640->regmap, RT5640_DEPOP_M1, + RT5640_HP_CP_MASK | RT5640_HP_SG_MASK, + RT5640_HP_CP_PD | RT5640_HP_SG_EN); + regmap_update_bits(rt5640->regmap, RT5640_PR_BASE + + RT5640_CHPUMP_INT_REG1, 0x0700, 0x0400); +} + +static int rt5640_hp_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = w->codec; + struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + rt5640_pmu_depop(codec); + rt5640->hp_mute = 0; + break; + + case SND_SOC_DAPM_PRE_PMD: + rt5640->hp_mute = 1; + usleep_range(70000, 75000); + break; + + default: + return 0; + } + + return 0; +} + +static int rt5640_hp_power_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = w->codec; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + hp_amp_power_on(codec); + break; + default: + return 0; + } + + return 0; +} + +static int rt5640_hp_post_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = w->codec; + struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + if (!rt5640->hp_mute) + usleep_range(80000, 85000); + + break; + + default: + return 0; + } + + return 0; +} + static const struct snd_soc_dapm_widget rt5640_dapm_widgets[] = { SND_SOC_DAPM_SUPPLY("PLL1", RT5640_PWR_ANLG2, RT5640_PWR_PLL_BIT, 0, NULL, 0), @@ -1132,15 +1226,28 @@ static const struct snd_soc_dapm_widget rt5640_dapm_widgets[] = { rt5640_mono_mix, ARRAY_SIZE(rt5640_mono_mix)), SND_SOC_DAPM_SUPPLY("Improve MONO Amp Drv", RT5640_PWR_ANLG1, RT5640_PWR_MA_BIT, 0, NULL, 0), - SND_SOC_DAPM_SUPPLY("Improve HP Amp Drv", RT5640_PWR_ANLG1, - SND_SOC_NOPM, 0, NULL, 0), - SND_SOC_DAPM_PGA("HP L Amp", RT5640_PWR_ANLG1, + SND_SOC_DAPM_SUPPLY_S("Improve HP Amp Drv", 1, SND_SOC_NOPM, + 0, 0, rt5640_hp_power_event, SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_PGA_S("HP Amp", 1, SND_SOC_NOPM, 0, 0, + rt5640_hp_event, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_SUPPLY("HP L Amp", RT5640_PWR_ANLG1, RT5640_PWR_HP_L_BIT, 0, NULL, 0), - SND_SOC_DAPM_PGA("HP R Amp", RT5640_PWR_ANLG1, + SND_SOC_DAPM_SUPPLY("HP R Amp", RT5640_PWR_ANLG1, RT5640_PWR_HP_R_BIT, 0, NULL, 0), SND_SOC_DAPM_SUPPLY("Improve SPK Amp Drv", RT5640_PWR_DIG1, - SND_SOC_NOPM, 0, spk_event, - SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), + RT5640_PWR_CLS_D_BIT, 0, NULL, 0), + + /* Output Switch */ + SND_SOC_DAPM_SWITCH("Speaker L Playback", SND_SOC_NOPM, 0, 0, + &spk_l_enable_control), + SND_SOC_DAPM_SWITCH("Speaker R Playback", SND_SOC_NOPM, 0, 0, + &spk_r_enable_control), + SND_SOC_DAPM_SWITCH("HP L Playback", SND_SOC_NOPM, 0, 0, + &hp_l_enable_control), + SND_SOC_DAPM_SWITCH("HP R Playback", SND_SOC_NOPM, 0, 0, + &hp_r_enable_control), + SND_SOC_DAPM_POST("HP Post", rt5640_hp_post_event), /* Output Lines */ SND_SOC_DAPM_OUTPUT("SPOLP"), SND_SOC_DAPM_OUTPUT("SPOLN"), @@ -1381,9 +1488,11 @@ static const struct snd_soc_dapm_route rt5640_dapm_routes[] = { {"HPO MIX L", "HPO MIX DAC2 Switch", "DAC L2"}, {"HPO MIX L", "HPO MIX DAC1 Switch", "DAC L1"}, {"HPO MIX L", "HPO MIX HPVOL Switch", "HPOVOL L"}, + {"HPO MIX L", NULL, "HP L Amp"}, {"HPO MIX R", "HPO MIX DAC2 Switch", "DAC R2"}, {"HPO MIX R", "HPO MIX DAC1 Switch", "DAC R1"}, {"HPO MIX R", "HPO MIX HPVOL Switch", "HPOVOL R"}, + {"HPO MIX R", NULL, "HP R Amp"}, {"LOUT MIX", "DAC L1 Switch", "DAC L1"}, {"LOUT MIX", "DAC R1 Switch", "DAC R1"}, @@ -1396,13 +1505,15 @@ static const struct snd_soc_dapm_route rt5640_dapm_routes[] = { {"Mono MIX", "OUTVOL L Switch", "OUTVOL L"}, {"Mono MIX", "BST1 Switch", "BST1"}, - {"HP L Amp", NULL, "HPO MIX L"}, - {"HP R Amp", NULL, "HPO MIX R"}, + {"HP Amp", NULL, "HPO MIX L"}, + {"HP Amp", NULL, "HPO MIX R"}, - {"SPOLP", NULL, "SPOL MIX"}, - {"SPOLN", NULL, "SPOL MIX"}, - {"SPORP", NULL, "SPOR MIX"}, - {"SPORN", NULL, "SPOR MIX"}, + {"Speaker L Playback", "Switch", "SPOL MIX"}, + {"Speaker R Playback", "Switch", "SPOR MIX"}, + {"SPOLP", NULL, "Speaker L Playback"}, + {"SPOLN", NULL, "Speaker L Playback"}, + {"SPORP", NULL, "Speaker R Playback"}, + {"SPORN", NULL, "Speaker R Playback"}, {"SPOLP", NULL, "Improve SPK Amp Drv"}, {"SPOLN", NULL, "Improve SPK Amp Drv"}, @@ -1412,8 +1523,10 @@ static const struct snd_soc_dapm_route rt5640_dapm_routes[] = { {"HPOL", NULL, "Improve HP Amp Drv"}, {"HPOR", NULL, "Improve HP Amp Drv"}, - {"HPOL", NULL, "HP L Amp"}, - {"HPOR", NULL, "HP R Amp"}, + {"HP L Playback", "Switch", "HP Amp"}, + {"HP R Playback", "Switch", "HP Amp"}, + {"HPOL", NULL, "HP L Playback"}, + {"HPOR", NULL, "HP R Playback"}, {"LOUTL", NULL, "LOUT MIX"}, {"LOUTR", NULL, "LOUT MIX"}, {"MONOP", NULL, "Mono MIX"}, @@ -1792,17 +1905,13 @@ static int rt5640_set_bias_level(struct snd_soc_codec *codec, RT5640_PWR_BG | RT5640_PWR_VREF2, RT5640_PWR_VREF1 | RT5640_PWR_MB | RT5640_PWR_BG | RT5640_PWR_VREF2); - mdelay(10); + usleep_range(10000, 15000); snd_soc_update_bits(codec, RT5640_PWR_ANLG1, RT5640_PWR_FV1 | RT5640_PWR_FV2, RT5640_PWR_FV1 | RT5640_PWR_FV2); regcache_sync(rt5640->regmap); snd_soc_update_bits(codec, RT5640_DUMMY1, 0x0301, 0x0301); - snd_soc_update_bits(codec, RT5640_DEPOP_M1, - 0x001d, 0x0019); - snd_soc_update_bits(codec, RT5640_DEPOP_M2, - 0x2000, 0x2000); snd_soc_update_bits(codec, RT5640_MICBIAS, 0x0030, 0x0030); } @@ -1846,8 +1955,6 @@ static int rt5640_probe(struct snd_soc_codec *codec) rt5640_set_bias_level(codec, SND_SOC_BIAS_OFF); snd_soc_update_bits(codec, RT5640_DUMMY1, 0x0301, 0x0301); - snd_soc_update_bits(codec, RT5640_DEPOP_M1, 0x001d, 0x0019); - snd_soc_update_bits(codec, RT5640_DEPOP_M2, 0x2000, 0x2000); snd_soc_update_bits(codec, RT5640_MICBIAS, 0x0030, 0x0030); snd_soc_update_bits(codec, RT5640_DSP_PATH2, 0xfc00, 0x0c00); @@ -2069,6 +2176,8 @@ static int rt5640_i2c_probe(struct i2c_client *i2c, regmap_update_bits(rt5640->regmap, RT5640_IN3_IN4, RT5640_IN_DF2, RT5640_IN_DF2); + rt5640->hp_mute = 1; + ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5640, rt5640_dai, ARRAY_SIZE(rt5640_dai)); if (ret < 0) diff --git a/sound/soc/codecs/rt5640.h b/sound/soc/codecs/rt5640.h index c48286d..5e8df25a 100644 --- a/sound/soc/codecs/rt5640.h +++ b/sound/soc/codecs/rt5640.h @@ -145,6 +145,8 @@ /* Index of Codec Private Register definition */ +#define RT5640_CHPUMP_INT_REG1 0x24 +#define RT5640_MAMP_INT_REG2 0x37 #define RT5640_3D_SPK 0x63 #define RT5640_WND_1 0x6c #define RT5640_WND_2 0x6d @@ -153,6 +155,7 @@ #define RT5640_WND_5 0x70 #define RT5640_WND_8 0x73 #define RT5640_DIP_SPK_INF 0x75 +#define RT5640_HP_DCC_INT1 0x77 #define RT5640_EQ_BW_LOP 0xa0 #define RT5640_EQ_GN_LOP 0xa1 #define RT5640_EQ_FC_BP1 0xa2 @@ -1201,6 +1204,14 @@ #define RT5640_CP_FQ2_SFT 4 #define RT5640_CP_FQ3_MASK (0x7) #define RT5640_CP_FQ3_SFT 0 +#define RT5640_CP_FQ_1_5_KHZ 0 +#define RT5640_CP_FQ_3_KHZ 1 +#define RT5640_CP_FQ_6_KHZ 2 +#define RT5640_CP_FQ_12_KHZ 3 +#define RT5640_CP_FQ_24_KHZ 4 +#define RT5640_CP_FQ_48_KHZ 5 +#define RT5640_CP_FQ_96_KHZ 6 +#define RT5640_CP_FQ_192_KHZ 7 /* HPOUT charge pump (0x91) */ #define RT5640_OSW_L_MASK (0x1 << 11) @@ -2087,6 +2098,7 @@ struct rt5640_priv { int pll_out; int dmic_en; + bool hp_mute; }; #endif -- cgit v0.10.2 From 8a309d71ed9d17ff251b4b891fcef1c72bf625d1 Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Fri, 30 Aug 2013 17:38:08 +0800 Subject: ASoC: fsl: Add wrapping for dev_dbg() in fsl_spdif.c Add wrapping '\n' for dev_dbg() in fsl_spdif.c Signed-off-by: Nicolin Chen Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c index e93dc0d..98741e9 100644 --- a/sound/soc/fsl/fsl_spdif.c +++ b/sound/soc/fsl/fsl_spdif.c @@ -1071,9 +1071,9 @@ static int fsl_spdif_probe_txclk(struct fsl_spdif_priv *spdif_priv, break; } - dev_dbg(&pdev->dev, "use rxtx%d as tx clock source for %dHz sample rate", + dev_dbg(&pdev->dev, "use rxtx%d as tx clock source for %dHz sample rate\n", spdif_priv->txclk_src[index], rate[index]); - dev_dbg(&pdev->dev, "use divisor %d for %dHz sample rate", + dev_dbg(&pdev->dev, "use divisor %d for %dHz sample rate\n", spdif_priv->txclk_div[index], rate[index]); return 0; -- cgit v0.10.2 From 8626bdf05e93ae9d199cb2ad77b58832b98141f5 Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Fri, 30 Aug 2013 17:39:00 +0800 Subject: ASoC: fsl: Add one blank space after ':=' in Makefile There is a blank space missing between ':=' and 'imx-spdif.o', thus add it. Signed-off-by: Nicolin Chen Signed-off-by: Mark Brown diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile index e2aaff7..8db705b 100644 --- a/sound/soc/fsl/Makefile +++ b/sound/soc/fsl/Makefile @@ -45,7 +45,7 @@ snd-soc-mx27vis-aic32x4-objs := mx27vis-aic32x4.o snd-soc-wm1133-ev1-objs := wm1133-ev1.o snd-soc-imx-sgtl5000-objs := imx-sgtl5000.o snd-soc-imx-wm8962-objs := imx-wm8962.o -snd-soc-imx-spdif-objs :=imx-spdif.o +snd-soc-imx-spdif-objs := imx-spdif.o snd-soc-imx-mc13783-objs := imx-mc13783.o obj-$(CONFIG_SND_SOC_EUKREA_TLV320) += snd-soc-eukrea-tlv320.o -- cgit v0.10.2 From 43d92e7d9aa13b91687f671ea7015204bc88fb84 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sat, 31 Aug 2013 11:02:17 +0200 Subject: ASoC: Remove unused control_type field from snd_soc_codec struct The control_type field was used by the core to track which raw IO methods to use, but when switching to regmap this was no longer necessary and so the last user of the field was removed in commit be3ea3b9 ("ASoC: Use new register map API for ASoC generic physical I/O"). The field is now completely unused and can be removed. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/include/sound/soc.h b/include/sound/soc.h index d57a04e..254bcda 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -701,7 +701,6 @@ struct snd_soc_codec { /* codec IO */ void *control_data; /* codec control (i2c/3wire) data */ - enum snd_soc_control_type control_type; hw_write_t hw_write; unsigned int (*hw_read)(struct snd_soc_codec *, unsigned int); unsigned int (*read)(struct snd_soc_codec *, unsigned int); -- cgit v0.10.2 From ad758a67048f58205f2777b0f0a1a02de824d280 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sat, 31 Aug 2013 11:02:18 +0200 Subject: ASoC: Remove unused debugfs_dapm field from snd_soc_{platform,codec} struct The DAPM context struct has its own field where it stores the pointer to the DAPM debugfs entry. The debugfs_dapm field in the snd_soc_platform and snd_soc_codec structs are completely unused and can be removed. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/include/sound/soc.h b/include/sound/soc.h index 254bcda..c65cc7d 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -719,7 +719,6 @@ struct snd_soc_codec { #ifdef CONFIG_DEBUG_FS struct dentry *debugfs_codec_root; struct dentry *debugfs_reg; - struct dentry *debugfs_dapm; #endif }; @@ -844,7 +843,6 @@ struct snd_soc_platform { #ifdef CONFIG_DEBUG_FS struct dentry *debugfs_platform_root; - struct dentry *debugfs_dapm; #endif }; -- cgit v0.10.2 From d7b1538c7c0e395a308d6f4098d0985fe19e4584 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sat, 31 Aug 2013 11:02:19 +0200 Subject: ASoC: Remove unused sysfs_registered field from snd_soc_codec struct The sysfs_registered field was added to the snd_soc_codec struct in commit f0fba2ad ("ASoC: multi-component - ASoC Multi-Component Support"), but has never been used. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/include/sound/soc.h b/include/sound/soc.h index c65cc7d..26e0df0 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -693,7 +693,6 @@ struct snd_soc_codec { unsigned int probed:1; /* Codec has been probed */ unsigned int ac97_registered:1; /* Codec has been AC97 registered */ unsigned int ac97_created:1; /* Codec has been created by SoC */ - unsigned int sysfs_registered:1; /* codec has been sysfs registered */ unsigned int cache_init:1; /* codec cache has been initialized */ unsigned int using_regmap:1; /* using regmap access */ u32 cache_only; /* Suppress writes to hardware */ -- cgit v0.10.2 From 9d863b88ec371491e926e0828dbe3d36ead0f6f9 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sat, 31 Aug 2013 18:15:23 +0200 Subject: ASoC: ssm2602: Fix cache sync The ssm2602 uses regmap for caching not soc-cache, so we need to use regcache_sync() instead of snd_soc_cache_sync(). Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/sound/soc/codecs/ssm2602.c b/sound/soc/codecs/ssm2602.c index f8d30e5..492644e 100644 --- a/sound/soc/codecs/ssm2602.c +++ b/sound/soc/codecs/ssm2602.c @@ -561,8 +561,9 @@ static int ssm2602_suspend(struct snd_soc_codec *codec) static int ssm2602_resume(struct snd_soc_codec *codec) { - snd_soc_cache_sync(codec); + struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec); + regcache_sync(ssm2602->regmap); ssm2602_set_bias_level(codec, SND_SOC_BIAS_STANDBY); return 0; -- cgit v0.10.2 From 6b4c80f947df9d92b97eb62afc29dda6d7220c7d Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sat, 31 Aug 2013 16:40:51 +0100 Subject: ASoC: fsl_spdif: Staticse non-exported symbols Signed-off-by: Mark Brown Acked-by: Nicolin Chen diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c index 98741e9..3920c3e 100644 --- a/sound/soc/fsl/fsl_spdif.c +++ b/sound/soc/fsl/fsl_spdif.c @@ -411,8 +411,8 @@ static int spdif_set_sample_rate(struct snd_pcm_substream *substream, return 0; } -int fsl_spdif_startup(struct snd_pcm_substream *substream, - struct snd_soc_dai *cpu_dai) +static int fsl_spdif_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(rtd->cpu_dai); @@ -546,7 +546,7 @@ static int fsl_spdif_trigger(struct snd_pcm_substream *substream, return 0; } -struct snd_soc_dai_ops fsl_spdif_dai_ops = { +static struct snd_soc_dai_ops fsl_spdif_dai_ops = { .startup = fsl_spdif_startup, .hw_params = fsl_spdif_hw_params, .trigger = fsl_spdif_trigger, @@ -919,7 +919,7 @@ static int fsl_spdif_dai_probe(struct snd_soc_dai *dai) return 0; } -struct snd_soc_dai_driver fsl_spdif_dai = { +static struct snd_soc_dai_driver fsl_spdif_dai = { .probe = &fsl_spdif_dai_probe, .playback = { .channels_min = 2, -- cgit v0.10.2 From d6bead020d8f8bcaca5cdcb035250c44b21c93e7 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Thu, 29 Aug 2013 10:32:13 -0300 Subject: ASoC: soc-pcm: Allow to specify unidirectional dai_link Add 'playback_only' and 'capture_only' fields that can be used for specifying that a dai_link has a unidirectional capability. The motivation for this is for the cases of systems, such as Freescale MX28, that has two unidirectional DAIs. Signed-off-by: Fabio Estevam Signed-off-by: Mark Brown diff --git a/include/sound/soc.h b/include/sound/soc.h index c0ac3bc..65414e8 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -936,6 +936,10 @@ struct snd_soc_dai_link { /* machine stream operations */ const struct snd_soc_ops *ops; const struct snd_soc_compr_ops *compr_ops; + + /* For unidirectional dai links */ + bool playback_only; + bool capture_only; }; struct snd_soc_codec_conf { diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index b6c6403..9abaa52 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -2027,6 +2027,16 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) capture = 1; } + if (rtd->dai_link->playback_only) { + playback = 1; + capture = 0; + } + + if (rtd->dai_link->capture_only) { + playback = 0; + capture = 1; + } + /* create the PCM */ if (rtd->dai_link->no_pcm) { snprintf(new_name, sizeof(new_name), "(%s)", -- cgit v0.10.2 From a90e6053baa61feed8b19a9f4cbec6b56479d1ba Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Thu, 29 Aug 2013 10:32:14 -0300 Subject: ASoC: mxs-sgtl5000: Configure the dai_links as unidirectional On a mx28 board, running "aplay -l" and "arecord -l" results in the following: $ aplay -l **** List of PLAYBACK Hardware Devices **** card 0: mxssgtl5000 [mxs_sgtl5000], device 0: Playback sgtl5000-0 [] Subdevices: 1/1 Subdevice #0: subdevice #0 card 0: mxssgtl5000 [mxs_sgtl5000], device 1: Capture sgtl5000-1 [] Subdevices: 1/1 Subdevice #0: subdevice #0 $ arecord -l **** List of CAPTURE Hardware Devices **** card 0: mxssgtl5000 [mxs_sgtl5000], device 0: Playback sgtl5000-0 [] Subdevices: 1/1 Subdevice #0: subdevice #0 card 0: mxssgtl5000 [mxs_sgtl5000], device 1: Capture sgtl5000-1 [] Subdevices: 1/1 Subdevice #0: subdevice #0 ,which is not correct because we got a capture device listed in aplay and a playback device listed in arecord. On mx28 there are two serial audio interface ports (SAIF0 and SAIF1) and each one of them are unidirectional. Allow to specify a dai link as 'playback_only' or 'capture_only', which suits well for this case. After this change we can correctly report the capabilities as follows: $ aplay -l **** List of PLAYBACK Hardware Devices **** card 0: mxssgtl5000 [mxs_sgtl5000], device 0: HiFi Playback sgtl5000-0 [] Subdevices: 1/1 Subdevice #0: subdevice #0 $ arecord -l **** List of CAPTURE Hardware Devices **** card 0: mxssgtl5000 [mxs_sgtl5000], device 1: HiFi Capture sgtl5000-1 [] Subdevices: 1/1 Subdevice #0: subdevice #0 Also tested playback and capture on the mx28evk board. Signed-off-by: Fabio Estevam Signed-off-by: Mark Brown diff --git a/sound/soc/mxs/mxs-sgtl5000.c b/sound/soc/mxs/mxs-sgtl5000.c index 1b134d7..ed8a519 100644 --- a/sound/soc/mxs/mxs-sgtl5000.c +++ b/sound/soc/mxs/mxs-sgtl5000.c @@ -91,11 +91,13 @@ static struct snd_soc_dai_link mxs_sgtl5000_dai[] = { .stream_name = "HiFi Playback", .codec_dai_name = "sgtl5000", .ops = &mxs_sgtl5000_hifi_ops, + .playback_only = true, }, { .name = "HiFi Rx", .stream_name = "HiFi Capture", .codec_dai_name = "sgtl5000", .ops = &mxs_sgtl5000_hifi_ops, + .capture_only = true, }, }; -- cgit v0.10.2 From 18e391862cceaf43ddb8eb5cca05e1a83abdebaa Mon Sep 17 00:00:00 2001 From: Anssi Hannula Date: Sun, 1 Sep 2013 14:36:47 +0300 Subject: ALSA: hda - hdmi: Fallback to ALSA allocation when selecting CA MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit hdmi_channel_allocation() tries to find a HDMI channel allocation that matches the number channels in the playback stream and contains only speakers that the HDMI sink has reported as available via EDID. If no such allocation is found, 0 (stereo audio) is used. Using CA 0 causes the audio causes the sink to discard everything except the first two channels (front left and front right). However, the sink may be capable of receiving more channels than it has speakers (and then perform downmix or discard the extra channels), in which case it is preferable to use a CA that contains extra channels than to use CA 0 which discards all the non-stereo channels. Additionally, it seems that HBR (HD) passthrough output does not work on Intel HDMI codecs when CA is set to 0 (possibly the codec zeroes channels not present in CA). This happens with all receivers that report a 5.1 speaker mask since a HBR stream is carried on 8 channels to the codec. Add a fallback in the CA selection so that the CA channel count at least matches the stream channel count, even if the stream contains channels not present in the sink speaker descriptor. Thanks to GrimGriefer at OpenELEC forums for discovering that changing the sink speaker mask allowed HBR output. Reported-by: GrimGriefer Reported-by: Ashecrow Reported-by: Frank Zafka Reported-by: Peter Frühberger Signed-off-by: Anssi Hannula Cc: Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 895a0d3..b83b14f 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -551,6 +551,17 @@ static int hdmi_channel_allocation(struct hdmi_eld *eld, int channels) } } + if (!ca) { + /* if there was no match, select the regular ALSA channel + * allocation with the matching number of channels */ + for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) { + if (channels == channel_allocations[i].channels) { + ca = channel_allocations[i].ca_index; + break; + } + } + } + snd_print_channel_allocation(eld->info.spk_alloc, buf, sizeof(buf)); snd_printdd("HDMI: select CA 0x%x for %d-channel allocation: %s\n", ca, channels, buf); -- cgit v0.10.2 From b054087dbacee30a9dddaef2c9a96312146be04e Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 2 Sep 2013 12:33:02 +0200 Subject: ALSA: hda - Re-setup HDMI pin and audio infoframe on stream switches When the transcoder:port mapping on Haswell HDMI/DP audio is changed during the stream playback, the sound gets lost. Typically this problem is seen when the user switches the graphics mode from eDP+DP to DP-only configuration, where CRTC 1 is used for DP in the former while CRTC 0 is used for the latter. The graphics controller notifies the change via the normal ELD update procedure, so we get the intrinsic event. For enabling the sound again, the HDMI audio driver needs to reset the pin and set up the audio infoframe again. This patch achieves it by: - keep the current status of channels and info frame setup in per_pin struct, - check the reconnection in the intrinsic event handler, - reset the pin and the re-invoke hdmi_setup_audio_infoframe() accordingly. The hdmi_setup_audio_infoframe() function has been changed, too, so that it can be invoked without passing the substream instance. The patch is mostly based on the work by Mengdong Lin. Cc: Mengdong Lin Cc: Signed-off-by: Takashi Iwai diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index b83b14f..22b5089 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -67,6 +67,8 @@ struct hdmi_spec_per_pin { struct delayed_work work; struct snd_kcontrol *eld_ctl; int repoll_count; + bool setup; /* the stream has been set up by prepare callback */ + int channels; /* current number of channels */ bool non_pcm; bool chmap_set; /* channel-map override by ALSA API? */ unsigned char chmap[8]; /* ALSA API channel-map */ @@ -879,18 +881,19 @@ static bool hdmi_infoframe_uptodate(struct hda_codec *codec, hda_nid_t pin_nid, return true; } -static void hdmi_setup_audio_infoframe(struct hda_codec *codec, int pin_idx, - bool non_pcm, - struct snd_pcm_substream *substream) +static void hdmi_setup_audio_infoframe(struct hda_codec *codec, + struct hdmi_spec_per_pin *per_pin, + bool non_pcm) { - struct hdmi_spec *spec = codec->spec; - struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx); hda_nid_t pin_nid = per_pin->pin_nid; - int channels = substream->runtime->channels; + int channels = per_pin->channels; struct hdmi_eld *eld; int ca; union audio_infoframe ai; + if (!channels) + return; + eld = &per_pin->sink_eld; if (!eld->monitor_present) return; @@ -1341,6 +1344,7 @@ static void hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll) eld_changed = true; } if (update_eld) { + bool old_eld_valid = pin_eld->eld_valid; pin_eld->eld_valid = eld->eld_valid; eld_changed = pin_eld->eld_size != eld->eld_size || memcmp(pin_eld->eld_buffer, eld->eld_buffer, @@ -1350,6 +1354,18 @@ static void hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll) eld->eld_size); pin_eld->eld_size = eld->eld_size; pin_eld->info = eld->info; + + /* Haswell-specific workaround: re-setup when the transcoder is + * changed during the stream playback + */ + if (codec->vendor_id == 0x80862807 && + eld->eld_valid && !old_eld_valid && per_pin->setup) { + snd_hda_codec_write(codec, pin_nid, 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AMP_OUT_UNMUTE); + hdmi_setup_audio_infoframe(codec, per_pin, + per_pin->non_pcm); + } } mutex_unlock(&pin_eld->lock); @@ -1522,14 +1538,17 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo, hda_nid_t cvt_nid = hinfo->nid; struct hdmi_spec *spec = codec->spec; int pin_idx = hinfo_to_pin_index(spec, hinfo); - hda_nid_t pin_nid = get_pin(spec, pin_idx)->pin_nid; + struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx); + hda_nid_t pin_nid = per_pin->pin_nid; bool non_pcm; non_pcm = check_non_pcm_per_cvt(codec, cvt_nid); + per_pin->channels = substream->runtime->channels; + per_pin->setup = true; hdmi_set_channel_count(codec, cvt_nid, substream->runtime->channels); - hdmi_setup_audio_infoframe(codec, pin_idx, non_pcm, substream); + hdmi_setup_audio_infoframe(codec, per_pin, non_pcm); return hdmi_setup_stream(codec, cvt_nid, pin_nid, stream_tag, format); } @@ -1569,6 +1588,9 @@ static int hdmi_pcm_close(struct hda_pcm_stream *hinfo, snd_hda_spdif_ctls_unassign(codec, pin_idx); per_pin->chmap_set = false; memset(per_pin->chmap, 0, sizeof(per_pin->chmap)); + + per_pin->setup = false; + per_pin->channels = 0; } return 0; @@ -1704,8 +1726,7 @@ static int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol, per_pin->chmap_set = true; memcpy(per_pin->chmap, chmap, sizeof(chmap)); if (prepared) - hdmi_setup_audio_infoframe(codec, pin_idx, per_pin->non_pcm, - substream); + hdmi_setup_audio_infoframe(codec, per_pin, per_pin->non_pcm); return 0; } -- cgit v0.10.2