diff options
author | Russell King <rmk@dyn-67.arm.linux.org.uk> | 2009-03-28 20:29:51 +0000 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2009-03-28 20:29:51 +0000 |
commit | ed40d0c472b136682b2fcba05f89762859c7374f (patch) | |
tree | 076b83a26bcd63d6158463735dd34c10bbc591dc /sound/soc/pxa | |
parent | 9e495834e59ca9b29f1a1f63b9f5533bb022ac49 (diff) | |
parent | 5d80f8e5a9dc9c9a94d4aeaa567e219a808b8a4a (diff) | |
download | linux-ed40d0c472b136682b2fcba05f89762859c7374f.tar.bz2 |
Merge branch 'origin' into devel
Conflicts:
sound/soc/pxa/pxa2xx-i2s.c
Diffstat (limited to 'sound/soc/pxa')
-rw-r--r-- | sound/soc/pxa/Kconfig | 27 | ||||
-rw-r--r-- | sound/soc/pxa/Makefile | 6 | ||||
-rw-r--r-- | sound/soc/pxa/corgi.c | 58 | ||||
-rw-r--r-- | sound/soc/pxa/e740_wm9705.c | 211 | ||||
-rw-r--r-- | sound/soc/pxa/e750_wm9705.c | 187 | ||||
-rw-r--r-- | sound/soc/pxa/e800_wm9712.c | 113 | ||||
-rw-r--r-- | sound/soc/pxa/mioa701_wm9713.c | 250 | ||||
-rw-r--r-- | sound/soc/pxa/palm27x.c | 15 | ||||
-rw-r--r-- | sound/soc/pxa/poodle.c | 56 | ||||
-rw-r--r-- | sound/soc/pxa/pxa-ssp.c | 150 | ||||
-rw-r--r-- | sound/soc/pxa/pxa2xx-ac97.c | 59 | ||||
-rw-r--r-- | sound/soc/pxa/pxa2xx-i2s.c | 54 | ||||
-rw-r--r-- | sound/soc/pxa/spitz.c | 14 | ||||
-rw-r--r-- | sound/soc/pxa/tosa.c | 14 | ||||
-rw-r--r-- | sound/soc/pxa/zylonite.c | 132 |
15 files changed, 1126 insertions, 220 deletions
diff --git a/sound/soc/pxa/Kconfig b/sound/soc/pxa/Kconfig index f82e10699471..5998ab366e83 100644 --- a/sound/soc/pxa/Kconfig +++ b/sound/soc/pxa/Kconfig @@ -61,6 +61,24 @@ config SND_PXA2XX_SOC_TOSA Say Y if you want to add support for SoC audio on Sharp Zaurus SL-C6000x models (Tosa). +config SND_PXA2XX_SOC_E740 + tristate "SoC AC97 Audio support for e740" + depends on SND_PXA2XX_SOC && MACH_E740 + select SND_SOC_WM9705 + select SND_PXA2XX_SOC_AC97 + help + Say Y if you want to add support for SoC audio on the + toshiba e740 PDA + +config SND_PXA2XX_SOC_E750 + tristate "SoC AC97 Audio support for e750" + depends on SND_PXA2XX_SOC && MACH_E750 + select SND_SOC_WM9705 + select SND_PXA2XX_SOC_AC97 + help + Say Y if you want to add support for SoC audio on the + toshiba e750 PDA + config SND_PXA2XX_SOC_E800 tristate "SoC AC97 Audio support for e800" depends on SND_PXA2XX_SOC && MACH_E800 @@ -97,3 +115,12 @@ config SND_SOC_ZYLONITE help Say Y if you want to add support for SoC audio on the Marvell Zylonite reference platform. + +config SND_PXA2XX_SOC_MIOA701 + tristate "SoC Audio support for MIO A701" + depends on SND_PXA2XX_SOC && MACH_MIOA701 + select SND_PXA2XX_SOC_AC97 + select SND_SOC_WM9713 + help + Say Y if you want to add support for SoC audio on the + MIO A701. diff --git a/sound/soc/pxa/Makefile b/sound/soc/pxa/Makefile index 08a9f2797729..8ed881c5e5cc 100644 --- a/sound/soc/pxa/Makefile +++ b/sound/soc/pxa/Makefile @@ -13,17 +13,23 @@ obj-$(CONFIG_SND_PXA_SOC_SSP) += snd-soc-pxa-ssp.o snd-soc-corgi-objs := corgi.o snd-soc-poodle-objs := poodle.o snd-soc-tosa-objs := tosa.o +snd-soc-e740-objs := e740_wm9705.o +snd-soc-e750-objs := e750_wm9705.o snd-soc-e800-objs := e800_wm9712.o snd-soc-spitz-objs := spitz.o snd-soc-em-x270-objs := em-x270.o snd-soc-palm27x-objs := palm27x.o snd-soc-zylonite-objs := zylonite.o +snd-soc-mioa701-objs := mioa701_wm9713.o obj-$(CONFIG_SND_PXA2XX_SOC_CORGI) += snd-soc-corgi.o obj-$(CONFIG_SND_PXA2XX_SOC_POODLE) += snd-soc-poodle.o obj-$(CONFIG_SND_PXA2XX_SOC_TOSA) += snd-soc-tosa.o +obj-$(CONFIG_SND_PXA2XX_SOC_E740) += snd-soc-e740.o +obj-$(CONFIG_SND_PXA2XX_SOC_E750) += snd-soc-e750.o obj-$(CONFIG_SND_PXA2XX_SOC_E800) += snd-soc-e800.o obj-$(CONFIG_SND_PXA2XX_SOC_SPITZ) += snd-soc-spitz.o obj-$(CONFIG_SND_PXA2XX_SOC_EM_X270) += snd-soc-em-x270.o obj-$(CONFIG_SND_PXA2XX_SOC_PALM27X) += snd-soc-palm27x.o +obj-$(CONFIG_SND_PXA2XX_SOC_MIOA701) += snd-soc-mioa701.o obj-$(CONFIG_SND_SOC_ZYLONITE) += snd-soc-zylonite.o diff --git a/sound/soc/pxa/corgi.c b/sound/soc/pxa/corgi.c index ec930667feff..d5be2b30cda5 100644 --- a/sound/soc/pxa/corgi.c +++ b/sound/soc/pxa/corgi.c @@ -16,6 +16,7 @@ #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/timer.h> +#include <linux/i2c.h> #include <linux/interrupt.h> #include <linux/platform_device.h> #include <linux/gpio.h> @@ -98,7 +99,7 @@ static void corgi_ext_control(struct snd_soc_codec *codec) static int corgi_startup(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->socdev->codec; + struct snd_soc_codec *codec = rtd->socdev->card->codec; /* check the jack status at stream startup */ corgi_ext_control(codec); @@ -273,18 +274,16 @@ static const struct snd_kcontrol_new wm8731_corgi_controls[] = { */ static int corgi_wm8731_init(struct snd_soc_codec *codec) { - int i, err; + int err; snd_soc_dapm_nc_pin(codec, "LLINEIN"); snd_soc_dapm_nc_pin(codec, "RLINEIN"); /* Add corgi specific controls */ - for (i = 0; i < ARRAY_SIZE(wm8731_corgi_controls); i++) { - err = snd_ctl_add(codec->card, - snd_soc_cnew(&wm8731_corgi_controls[i], codec, NULL)); - if (err < 0) - return err; - } + err = snd_soc_add_controls(codec, wm8731_corgi_controls, + ARRAY_SIZE(wm8731_corgi_controls)); + if (err < 0) + return err; /* Add corgi specific widgets */ snd_soc_dapm_new_controls(codec, wm8731_dapm_widgets, @@ -315,19 +314,44 @@ static struct snd_soc_card snd_soc_corgi = { .num_links = 1, }; -/* corgi audio private data */ -static struct wm8731_setup_data corgi_wm8731_setup = { - .i2c_bus = 0, - .i2c_address = 0x1b, -}; - /* corgi audio subsystem */ static struct snd_soc_device corgi_snd_devdata = { .card = &snd_soc_corgi, .codec_dev = &soc_codec_dev_wm8731, - .codec_data = &corgi_wm8731_setup, }; +/* + * FIXME: This is a temporary bodge to avoid cross-tree merge issues. + * New drivers should register the wm8731 I2C device in the machine + * setup code (under arch/arm for ARM systems). + */ +static int wm8731_i2c_register(void) +{ + struct i2c_board_info info; + struct i2c_adapter *adapter; + struct i2c_client *client; + + memset(&info, 0, sizeof(struct i2c_board_info)); + info.addr = 0x1b; + strlcpy(info.type, "wm8731", I2C_NAME_SIZE); + + adapter = i2c_get_adapter(0); + if (!adapter) { + printk(KERN_ERR "can't get i2c adapter 0\n"); + return -ENODEV; + } + + client = i2c_new_device(adapter, &info); + i2c_put_adapter(adapter); + if (!client) { + printk(KERN_ERR "can't add i2c device at 0x%x\n", + (unsigned int)info.addr); + return -ENODEV; + } + + return 0; +} + static struct platform_device *corgi_snd_device; static int __init corgi_init(void) @@ -338,6 +362,10 @@ static int __init corgi_init(void) machine_is_husky())) return -ENODEV; + ret = wm8731_i2c_register(); + if (ret != 0) + return ret; + corgi_snd_device = platform_device_alloc("soc-audio", -1); if (!corgi_snd_device) return -ENOMEM; diff --git a/sound/soc/pxa/e740_wm9705.c b/sound/soc/pxa/e740_wm9705.c new file mode 100644 index 000000000000..7cd2f89d7b10 --- /dev/null +++ b/sound/soc/pxa/e740_wm9705.c @@ -0,0 +1,211 @@ +/* + * e740-wm9705.c -- SoC audio for e740 + * + * Copyright 2007 (c) Ian Molton <spyro@f2s.com> + * + * 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; version 2 ONLY. + * + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/gpio.h> + +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> + +#include <mach/audio.h> +#include <mach/eseries-gpio.h> + +#include <asm/mach-types.h> + +#include "../codecs/wm9705.h" +#include "pxa2xx-pcm.h" +#include "pxa2xx-ac97.h" + + +#define E740_AUDIO_OUT 1 +#define E740_AUDIO_IN 2 + +static int e740_audio_power; + +static void e740_sync_audio_power(int status) +{ + gpio_set_value(GPIO_E740_WM9705_nAVDD2, !status); + gpio_set_value(GPIO_E740_AMP_ON, (status & E740_AUDIO_OUT) ? 1 : 0); + gpio_set_value(GPIO_E740_MIC_ON, (status & E740_AUDIO_IN) ? 1 : 0); +} + +static int e740_mic_amp_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + if (event & SND_SOC_DAPM_PRE_PMU) + e740_audio_power |= E740_AUDIO_IN; + else if (event & SND_SOC_DAPM_POST_PMD) + e740_audio_power &= ~E740_AUDIO_IN; + + e740_sync_audio_power(e740_audio_power); + + return 0; +} + +static int e740_output_amp_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + if (event & SND_SOC_DAPM_PRE_PMU) + e740_audio_power |= E740_AUDIO_OUT; + else if (event & SND_SOC_DAPM_POST_PMD) + e740_audio_power &= ~E740_AUDIO_OUT; + + e740_sync_audio_power(e740_audio_power); + + return 0; +} + +static const struct snd_soc_dapm_widget e740_dapm_widgets[] = { + SND_SOC_DAPM_HP("Headphone Jack", NULL), + SND_SOC_DAPM_SPK("Speaker", NULL), + SND_SOC_DAPM_MIC("Mic (Internal)", NULL), + SND_SOC_DAPM_PGA_E("Output Amp", SND_SOC_NOPM, 0, 0, NULL, 0, + e740_output_amp_event, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("Mic Amp", SND_SOC_NOPM, 0, 0, NULL, 0, + e740_mic_amp_event, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), +}; + +static const struct snd_soc_dapm_route audio_map[] = { + {"Output Amp", NULL, "LOUT"}, + {"Output Amp", NULL, "ROUT"}, + {"Output Amp", NULL, "MONOOUT"}, + + {"Speaker", NULL, "Output Amp"}, + {"Headphone Jack", NULL, "Output Amp"}, + + {"MIC1", NULL, "Mic Amp"}, + {"Mic Amp", NULL, "Mic (Internal)"}, +}; + +static int e740_ac97_init(struct snd_soc_codec *codec) +{ + snd_soc_dapm_nc_pin(codec, "HPOUTL"); + snd_soc_dapm_nc_pin(codec, "HPOUTR"); + snd_soc_dapm_nc_pin(codec, "PHONE"); + snd_soc_dapm_nc_pin(codec, "LINEINL"); + snd_soc_dapm_nc_pin(codec, "LINEINR"); + snd_soc_dapm_nc_pin(codec, "CDINL"); + snd_soc_dapm_nc_pin(codec, "CDINR"); + snd_soc_dapm_nc_pin(codec, "PCBEEP"); + snd_soc_dapm_nc_pin(codec, "MIC2"); + + snd_soc_dapm_new_controls(codec, e740_dapm_widgets, + ARRAY_SIZE(e740_dapm_widgets)); + + snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); + + snd_soc_dapm_sync(codec); + + return 0; +} + +static struct snd_soc_dai_link e740_dai[] = { + { + .name = "AC97", + .stream_name = "AC97 HiFi", + .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_HIFI], + .codec_dai = &wm9705_dai[WM9705_DAI_AC97_HIFI], + .init = e740_ac97_init, + }, + { + .name = "AC97 Aux", + .stream_name = "AC97 Aux", + .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_AUX], + .codec_dai = &wm9705_dai[WM9705_DAI_AC97_AUX], + }, +}; + +static struct snd_soc_card e740 = { + .name = "Toshiba e740", + .platform = &pxa2xx_soc_platform, + .dai_link = e740_dai, + .num_links = ARRAY_SIZE(e740_dai), +}; + +static struct snd_soc_device e740_snd_devdata = { + .card = &e740, + .codec_dev = &soc_codec_dev_wm9705, +}; + +static struct platform_device *e740_snd_device; + +static int __init e740_init(void) +{ + int ret; + + if (!machine_is_e740()) + return -ENODEV; + + ret = gpio_request(GPIO_E740_MIC_ON, "Mic amp"); + if (ret) + return ret; + + ret = gpio_request(GPIO_E740_AMP_ON, "Output amp"); + if (ret) + goto free_mic_amp_gpio; + + ret = gpio_request(GPIO_E740_WM9705_nAVDD2, "Audio power"); + if (ret) + goto free_op_amp_gpio; + + /* Disable audio */ + ret = gpio_direction_output(GPIO_E740_MIC_ON, 0); + if (ret) + goto free_apwr_gpio; + ret = gpio_direction_output(GPIO_E740_AMP_ON, 0); + if (ret) + goto free_apwr_gpio; + ret = gpio_direction_output(GPIO_E740_WM9705_nAVDD2, 1); + if (ret) + goto free_apwr_gpio; + + e740_snd_device = platform_device_alloc("soc-audio", -1); + if (!e740_snd_device) { + ret = -ENOMEM; + goto free_apwr_gpio; + } + + platform_set_drvdata(e740_snd_device, &e740_snd_devdata); + e740_snd_devdata.dev = &e740_snd_device->dev; + ret = platform_device_add(e740_snd_device); + + if (!ret) + return 0; + +/* Fail gracefully */ + platform_device_put(e740_snd_device); +free_apwr_gpio: + gpio_free(GPIO_E740_WM9705_nAVDD2); +free_op_amp_gpio: + gpio_free(GPIO_E740_AMP_ON); +free_mic_amp_gpio: + gpio_free(GPIO_E740_MIC_ON); + + return ret; +} + +static void __exit e740_exit(void) +{ + platform_device_unregister(e740_snd_device); +} + +module_init(e740_init); +module_exit(e740_exit); + +/* Module information */ +MODULE_AUTHOR("Ian Molton <spyro@f2s.com>"); +MODULE_DESCRIPTION("ALSA SoC driver for e740"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/pxa/e750_wm9705.c b/sound/soc/pxa/e750_wm9705.c new file mode 100644 index 000000000000..8dceccc5e059 --- /dev/null +++ b/sound/soc/pxa/e750_wm9705.c @@ -0,0 +1,187 @@ +/* + * e750-wm9705.c -- SoC audio for e750 + * + * Copyright 2007 (c) Ian Molton <spyro@f2s.com> + * + * 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; version 2 ONLY. + * + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/gpio.h> + +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> + +#include <mach/audio.h> +#include <mach/eseries-gpio.h> + +#include <asm/mach-types.h> + +#include "../codecs/wm9705.h" +#include "pxa2xx-pcm.h" +#include "pxa2xx-ac97.h" + +static int e750_spk_amp_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + if (event & SND_SOC_DAPM_PRE_PMU) + gpio_set_value(GPIO_E750_SPK_AMP_OFF, 0); + else if (event & SND_SOC_DAPM_POST_PMD) + gpio_set_value(GPIO_E750_SPK_AMP_OFF, 1); + + return 0; +} + +static int e750_hp_amp_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + if (event & SND_SOC_DAPM_PRE_PMU) + gpio_set_value(GPIO_E750_HP_AMP_OFF, 0); + else if (event & SND_SOC_DAPM_POST_PMD) + gpio_set_value(GPIO_E750_HP_AMP_OFF, 1); + + return 0; +} + +static const struct snd_soc_dapm_widget e750_dapm_widgets[] = { + SND_SOC_DAPM_HP("Headphone Jack", NULL), + SND_SOC_DAPM_SPK("Speaker", NULL), + SND_SOC_DAPM_MIC("Mic (Internal)", NULL), + SND_SOC_DAPM_PGA_E("Headphone Amp", SND_SOC_NOPM, 0, 0, NULL, 0, + e750_hp_amp_event, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("Speaker Amp", SND_SOC_NOPM, 0, 0, NULL, 0, + e750_spk_amp_event, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), +}; + +static const struct snd_soc_dapm_route audio_map[] = { + {"Headphone Amp", NULL, "HPOUTL"}, + {"Headphone Amp", NULL, "HPOUTR"}, + {"Headphone Jack", NULL, "Headphone Amp"}, + + {"Speaker Amp", NULL, "MONOOUT"}, + {"Speaker", NULL, "Speaker Amp"}, + + {"MIC1", NULL, "Mic (Internal)"}, +}; + +static int e750_ac97_init(struct snd_soc_codec *codec) +{ + snd_soc_dapm_nc_pin(codec, "LOUT"); + snd_soc_dapm_nc_pin(codec, "ROUT"); + snd_soc_dapm_nc_pin(codec, "PHONE"); + snd_soc_dapm_nc_pin(codec, "LINEINL"); + snd_soc_dapm_nc_pin(codec, "LINEINR"); + snd_soc_dapm_nc_pin(codec, "CDINL"); + snd_soc_dapm_nc_pin(codec, "CDINR"); + snd_soc_dapm_nc_pin(codec, "PCBEEP"); + snd_soc_dapm_nc_pin(codec, "MIC2"); + + snd_soc_dapm_new_controls(codec, e750_dapm_widgets, + ARRAY_SIZE(e750_dapm_widgets)); + + snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); + + snd_soc_dapm_sync(codec); + + return 0; +} + +static struct snd_soc_dai_link e750_dai[] = { + { + .name = "AC97", + .stream_name = "AC97 HiFi", + .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_HIFI], + .codec_dai = &wm9705_dai[WM9705_DAI_AC97_HIFI], + .init = e750_ac97_init, + /* use ops to check startup state */ + }, + { + .name = "AC97 Aux", + .stream_name = "AC97 Aux", + .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_AUX], + .codec_dai = &wm9705_dai[WM9705_DAI_AC97_AUX], + }, +}; + +static struct snd_soc_card e750 = { + .name = "Toshiba e750", + .platform = &pxa2xx_soc_platform, + .dai_link = e750_dai, + .num_links = ARRAY_SIZE(e750_dai), +}; + +static struct snd_soc_device e750_snd_devdata = { + .card = &e750, + .codec_dev = &soc_codec_dev_wm9705, +}; + +static struct platform_device *e750_snd_device; + +static int __init e750_init(void) +{ + int ret; + + if (!machine_is_e750()) + return -ENODEV; + + ret = gpio_request(GPIO_E750_HP_AMP_OFF, "Headphone amp"); + if (ret) + return ret; + + ret = gpio_request(GPIO_E750_SPK_AMP_OFF, "Speaker amp"); + if (ret) + goto free_hp_amp_gpio; + + ret = gpio_direction_output(GPIO_E750_HP_AMP_OFF, 1); + if (ret) + goto free_spk_amp_gpio; + + ret = gpio_direction_output(GPIO_E750_SPK_AMP_OFF, 1); + if (ret) + goto free_spk_amp_gpio; + + e750_snd_device = platform_device_alloc("soc-audio", -1); + if (!e750_snd_device) { + ret = -ENOMEM; + goto free_spk_amp_gpio; + } + + platform_set_drvdata(e750_snd_device, &e750_snd_devdata); + e750_snd_devdata.dev = &e750_snd_device->dev; + ret = platform_device_add(e750_snd_device); + + if (!ret) + return 0; + +/* Fail gracefully */ + platform_device_put(e750_snd_device); +free_spk_amp_gpio: + gpio_free(GPIO_E750_SPK_AMP_OFF); +free_hp_amp_gpio: + gpio_free(GPIO_E750_HP_AMP_OFF); + + return ret; +} + +static void __exit e750_exit(void) +{ + platform_device_unregister(e750_snd_device); + gpio_free(GPIO_E750_SPK_AMP_OFF); + gpio_free(GPIO_E750_HP_AMP_OFF); +} + +module_init(e750_init); +module_exit(e750_exit); + +/* Module information */ +MODULE_AUTHOR("Ian Molton <spyro@f2s.com>"); +MODULE_DESCRIPTION("ALSA SoC driver for e750"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/pxa/e800_wm9712.c b/sound/soc/pxa/e800_wm9712.c index ac294c797b7d..bc019cdce429 100644 --- a/sound/soc/pxa/e800_wm9712.c +++ b/sound/soc/pxa/e800_wm9712.c @@ -1,8 +1,6 @@ /* * e800-wm9712.c -- SoC audio for e800 * - * Based on tosa.c - * * Copyright 2007 (c) Ian Molton <spyro@f2s.com> * * This program is free software; you can redistribute it and/or modify it @@ -13,7 +11,7 @@ #include <linux/module.h> #include <linux/moduleparam.h> -#include <linux/device.h> +#include <linux/gpio.h> #include <sound/core.h> #include <sound/pcm.h> @@ -22,20 +20,84 @@ #include <asm/mach-types.h> #include <mach/audio.h> +#include <mach/eseries-gpio.h> #include "../codecs/wm9712.h" #include "pxa2xx-pcm.h" #include "pxa2xx-ac97.h" -static struct snd_soc_card e800; +static int e800_spk_amp_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + if (event & SND_SOC_DAPM_PRE_PMU) + gpio_set_value(GPIO_E800_SPK_AMP_ON, 1); + else if (event & SND_SOC_DAPM_POST_PMD) + gpio_set_value(GPIO_E800_SPK_AMP_ON, 0); -static struct snd_soc_dai_link e800_dai[] = { + return 0; +} + +static int e800_hp_amp_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + if (event & SND_SOC_DAPM_PRE_PMU) + gpio_set_value(GPIO_E800_HP_AMP_OFF, 0); + else if (event & SND_SOC_DAPM_POST_PMD) + gpio_set_value(GPIO_E800_HP_AMP_OFF, 1); + + return 0; +} + +static const struct snd_soc_dapm_widget e800_dapm_widgets[] = { + SND_SOC_DAPM_HP("Headphone Jack", NULL), + SND_SOC_DAPM_MIC("Mic (Internal1)", NULL), + SND_SOC_DAPM_MIC("Mic (Internal2)", NULL), + SND_SOC_DAPM_SPK("Speaker", NULL), + SND_SOC_DAPM_PGA_E("Headphone Amp", SND_SOC_NOPM, 0, 0, NULL, 0, + e800_hp_amp_event, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("Speaker Amp", SND_SOC_NOPM, 0, 0, NULL, 0, + e800_spk_amp_event, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), +}; + +static const struct snd_soc_dapm_route audio_map[] = { + {"Headphone Jack", NULL, "HPOUTL"}, + {"Headphone Jack", NULL, "HPOUTR"}, + {"Headphone Jack", NULL, "Headphone Amp"}, + + {"Speaker Amp", NULL, "MONOOUT"}, + {"Speaker", NULL, "Speaker Amp"}, + + {"MIC1", NULL, "Mic (Internal1)"}, + {"MIC2", NULL, "Mic (Internal2)"}, +}; + +static int e800_ac97_init(struct snd_soc_codec *codec) { - .name = "AC97 Aux", - .stream_name = "AC97 Aux", - .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_AUX], - .codec_dai = &wm9712_dai[WM9712_DAI_AC97_AUX], -}, + snd_soc_dapm_new_controls(codec, e800_dapm_widgets, + ARRAY_SIZE(e800_dapm_widgets)); + + snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); + snd_soc_dapm_sync(codec); + + return 0; +} + +static struct snd_soc_dai_link e800_dai[] = { + { + .name = "AC97", + .stream_name = "AC97 HiFi", + .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_HIFI], + .codec_dai = &wm9712_dai[WM9712_DAI_AC97_HIFI], + .init = e800_ac97_init, + }, + { + .name = "AC97 Aux", + .stream_name = "AC97 Aux", + .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_AUX], + .codec_dai = &wm9712_dai[WM9712_DAI_AC97_AUX], + }, }; static struct snd_soc_card e800 = { @@ -59,6 +121,22 @@ static int __init e800_init(void) if (!machine_is_e800()) return -ENODEV; + ret = gpio_request(GPIO_E800_HP_AMP_OFF, "Headphone amp"); + if (ret) + return ret; + + ret = gpio_request(GPIO_E800_SPK_AMP_ON, "Speaker amp"); + if (ret) + goto free_hp_amp_gpio; + + ret = gpio_direction_output(GPIO_E800_HP_AMP_OFF, 1); + if (ret) + goto free_spk_amp_gpio; + + ret = gpio_direction_output(GPIO_E800_SPK_AMP_ON, 1); + if (ret) + goto free_spk_amp_gpio; + e800_snd_device = platform_device_alloc("soc-audio", -1); if (!e800_snd_device) return -ENOMEM; @@ -67,8 +145,15 @@ static int __init e800_init(void) e800_snd_devdata.dev = &e800_snd_device->dev; ret = platform_device_add(e800_snd_device); - if (ret) - platform_device_put(e800_snd_device); + if (!ret) + return 0; + +/* Fail gracefully */ + platform_device_put(e800_snd_device); +free_spk_amp_gpio: + gpio_free(GPIO_E800_SPK_AMP_ON); +free_hp_amp_gpio: + gpio_free(GPIO_E800_HP_AMP_OFF); return ret; } @@ -76,6 +161,8 @@ static int __init e800_init(void) static void __exit e800_exit(void) { platform_device_unregister(e800_snd_device); + gpio_free(GPIO_E800_SPK_AMP_ON); + gpio_free(GPIO_E800_HP_AMP_OFF); } module_init(e800_init); @@ -84,4 +171,4 @@ module_exit(e800_exit); /* Module information */ MODULE_AUTHOR("Ian Molton <spyro@f2s.com>"); MODULE_DESCRIPTION("ALSA SoC driver for e800"); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/pxa/mioa701_wm9713.c b/sound/soc/pxa/mioa701_wm9713.c new file mode 100644 index 000000000000..19eda8bbfdaf --- /dev/null +++ b/sound/soc/pxa/mioa701_wm9713.c @@ -0,0 +1,250 @@ +/* + * Handles the Mitac mioa701 SoC system + * + * Copyright (C) 2008 Robert Jarzmik + * + * 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 in version 2 of the License. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * This is a little schema of the sound interconnections : + * + * Sagem X200 Wolfson WM9713 + * +--------+ +-------------------+ Rear Speaker + * | | | | /-+ + * | +--->----->---+MONOIN SPKL+--->----+-+ | + * | GSM | | | | | | + * | +--->----->---+PCBEEP SPKR+--->----+-+ | + * | CHIP | | | \-+ + * | +---<-----<---+MONO | + * | | | | Front Speaker + * +--------+ | | /-+ + * | HPL+--->----+-+ | + * | | | | | + * | OUT3+--->----+-+ | + * | | \-+ + * | | + * | | Front Micro + * | | + + * | MIC1+-----<--+o+ + * | | + + * +-------------------+ --- + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/platform_device.h> + +#include <asm/mach-types.h> +#include <mach/audio.h> + +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <sound/initval.h> +#include <sound/ac97_codec.h> + +#include "pxa2xx-pcm.h" +#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 */ +static int rear_amp_power(struct snd_soc_codec *codec, int power) +{ + unsigned short reg; + + if (power) { + reg = snd_soc_read(codec, AC97_GPIO_CFG); + snd_soc_write(codec, AC97_GPIO_CFG, reg | 0x0100); + reg = snd_soc_read(codec, AC97_GPIO_PULL); + snd_soc_write(codec, AC97_GPIO_PULL, reg | (1<<15)); + } else { + reg = snd_soc_read(codec, AC97_GPIO_CFG); + snd_soc_write(codec, AC97_GPIO_CFG, reg & ~0x0100); + reg = snd_soc_read(codec, AC97_GPIO_PULL); + snd_soc_write(codec, AC97_GPIO_PULL, reg & ~(1<<15)); + } + + return 0; +} + +static int rear_amp_event(struct snd_soc_dapm_widget *widget, + struct snd_kcontrol *kctl, int event) +{ + struct snd_soc_codec *codec = widget->codec; + + return rear_amp_power(codec, SND_SOC_DAPM_EVENT_ON(event)); +} + +/* mioa701 machine dapm widgets */ +static const struct snd_soc_dapm_widget mioa701_dapm_widgets[] = { + SND_SOC_DAPM_SPK("Front Speaker", NULL), + SND_SOC_DAPM_SPK("Rear Speaker", rear_amp_event), + SND_SOC_DAPM_MIC("Headset", NULL), + SND_SOC_DAPM_LINE("GSM Line Out", NULL), + SND_SOC_DAPM_LINE("GSM Line In", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), + SND_SOC_DAPM_MIC("Front Mic", NULL), +}; + +static const struct snd_soc_dapm_route audio_map[] = { + /* Call Mic */ + {"Mic Bias", NULL, "Front Mic"}, + {"MIC1", NULL, "Mic Bias"}, + + /* Headset Mic */ + {"LINEL", NULL, "Headset Mic"}, + {"LINER", NULL, "Headset Mic"}, + + /* GSM Module */ + {"MONOIN", NULL, "GSM Line Out"}, + {"PCBEEP", NULL, "GSM Line Out"}, + {"GSM Line In", NULL, "MONO"}, + + /* headphone connected to HPL, HPR */ + {"Headset", NULL, "HPL"}, + {"Headset", NULL, "HPR"}, + + /* front speaker connected to HPL, OUT3 */ + {"Front Speaker", NULL, "HPL"}, + {"Front Speaker", NULL, "OUT3"}, + + /* rear speaker connected to SPKL, SPKR */ + {"Rear Speaker", NULL, "SPKL"}, + {"Rear Speaker", NULL, "SPKR"}, +}; + +static int mioa701_wm9713_init(struct snd_soc_codec *codec) +{ + unsigned short reg; + + /* Add mioa701 specific widgets */ + snd_soc_dapm_new_controls(codec, ARRAY_AND_SIZE(mioa701_dapm_widgets)); + + /* Set up mioa701 specific audio path audio_mapnects */ + snd_soc_dapm_add_routes(codec, ARRAY_AND_SIZE(audio_map)); + + /* Prepare GPIO8 for rear speaker amplifier */ + reg = codec->read(codec, AC97_GPIO_CFG); + codec->write(codec, AC97_GPIO_CFG, reg | 0x0100); + + /* Prepare MIC input */ + reg = codec->read(codec, AC97_3D_CONTROL); + codec->write(codec, AC97_3D_CONTROL, reg | 0xc000); + + snd_soc_dapm_enable_pin(codec, "Front Speaker"); + snd_soc_dapm_enable_pin(codec, "Rear Speaker"); + snd_soc_dapm_enable_pin(codec, "Front Mic"); + snd_soc_dapm_enable_pin(codec, "GSM Line In"); + snd_soc_dapm_enable_pin(codec, "GSM Line Out"); + snd_soc_dapm_sync(codec); + + return 0; +} + +static struct snd_soc_ops mioa701_ops; + +static struct snd_soc_dai_link mioa701_dai[] = { + { + .name = "AC97", + .stream_name = "AC97 HiFi", + .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_HIFI], + .codec_dai = &wm9713_dai[WM9713_DAI_AC97_HIFI], + .init = mioa701_wm9713_init, + .ops = &mioa701_ops, + }, + { + .name = "AC97 Aux", + .stream_name = "AC97 Aux", + .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_AUX], + .codec_dai = &wm9713_dai[WM9713_DAI_AC97_AUX], + .ops = &mioa701_ops, + }, +}; + +static struct snd_soc_card mioa701 = { + .name = "MioA701", + .platform = &pxa2xx_soc_platform, + .dai_link = mioa701_dai, + .num_links = ARRAY_SIZE(mioa701_dai), +}; + +static struct snd_soc_device mioa701_snd_devdata = { + .card = &mioa701, + .codec_dev = &soc_codec_dev_wm9713, +}; + +static struct platform_device *mioa701_snd_device; + +static int mioa701_wm9713_probe(struct platform_device *pdev) +{ + int ret; + + if (!machine_is_mioa701()) + return -ENODEV; + + dev_warn(&pdev->dev, "Be warned that incorrect mixers/muxes setup will" + "lead to overheating and possible destruction of your device." + "Do not use without a good knowledge of mio's board design!\n"); + + mioa701_snd_device = platform_device_alloc("soc-audio", -1); + if (!mioa701_snd_device) + return -ENOMEM; + + platform_set_drvdata(mioa701_snd_device, &mioa701_snd_devdata); + mioa701_snd_devdata.dev = &mioa701_snd_device->dev; + + ret = platform_device_add(mioa701_snd_device); + if (!ret) + return 0; + + platform_device_put(mioa701_snd_device); + return ret; +} + +static int __devexit mioa701_wm9713_remove(struct platform_device *pdev) +{ + platform_device_unregister(mioa701_snd_device); + return 0; +} + +static struct platform_driver mioa701_wm9713_driver = { + .probe = mioa701_wm9713_probe, + .remove = __devexit_p(mioa701_wm9713_remove), + .driver = { + .name = "mioa701-wm9713", + .owner = THIS_MODULE, + }, +}; + +static int __init mioa701_asoc_init(void) +{ + return platform_driver_register(&mioa701_wm9713_driver); +} + +static void __exit mioa701_asoc_exit(void) +{ + platform_driver_unregister(&mioa701_wm9713_driver); +} + +module_init(mioa701_asoc_init); +module_exit(mioa701_asoc_exit); + +/* Module information */ +MODULE_AUTHOR("Robert Jarzmik (rjarzmik@free.fr)"); +MODULE_DESCRIPTION("ALSA SoC WM9713 MIO A701"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/pxa/palm27x.c b/sound/soc/pxa/palm27x.c index 4a9cf3083af0..48a73f64500b 100644 --- a/sound/soc/pxa/palm27x.c +++ b/sound/soc/pxa/palm27x.c @@ -55,7 +55,7 @@ static void palm27x_ext_control(struct snd_soc_codec *codec) static int palm27x_startup(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->socdev->codec; + struct snd_soc_codec *codec = rtd->socdev->card->codec; /* check the jack status at stream startup */ palm27x_ext_control(codec); @@ -146,19 +146,16 @@ static const struct snd_kcontrol_new palm27x_controls[] = { static int palm27x_ac97_init(struct snd_soc_codec *codec) { - int i, err; + int err; snd_soc_dapm_nc_pin(codec, "OUT3"); snd_soc_dapm_nc_pin(codec, "MONOOUT"); /* add palm27x specific controls */ - for (i = 0; i < ARRAY_SIZE(palm27x_controls); i++) { - err = snd_ctl_add(codec->card, - snd_soc_cnew(&palm27x_controls[i], - codec, NULL)); - if (err < 0) - return err; - } + err = snd_soc_add_controls(codec, palm27x_controls, + ARRAY_SIZE(palm27x_controls)); + if (err < 0) + return err; /* add palm27x specific widgets */ snd_soc_dapm_new_controls(codec, palm27x_dapm_widgets, diff --git a/sound/soc/pxa/poodle.c b/sound/soc/pxa/poodle.c index cad2c4c0ac95..a51058f66747 100644 --- a/sound/soc/pxa/poodle.c +++ b/sound/soc/pxa/poodle.c @@ -17,6 +17,7 @@ #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/timer.h> +#include <linux/i2c.h> #include <linux/interrupt.h> #include <linux/platform_device.h> #include <sound/core.h> @@ -75,7 +76,7 @@ static void poodle_ext_control(struct snd_soc_codec *codec) static int poodle_startup(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->socdev->codec; + struct snd_soc_codec *codec = rtd->socdev->card->codec; /* check the jack status at stream startup */ poodle_ext_control(codec); @@ -238,19 +239,17 @@ static const struct snd_kcontrol_new wm8731_poodle_controls[] = { */ static int poodle_wm8731_init(struct snd_soc_codec *codec) { - int i, err; + int err; snd_soc_dapm_nc_pin(codec, "LLINEIN"); snd_soc_dapm_nc_pin(codec, "RLINEIN"); snd_soc_dapm_enable_pin(codec, "MICIN"); /* Add poodle specific controls */ - for (i = 0; i < ARRAY_SIZE(wm8731_poodle_controls); i++) { - err = snd_ctl_add(codec->card, - snd_soc_cnew(&wm8731_poodle_controls[i], codec, NULL)); - if (err < 0) - return err; - } + err = snd_soc_add_controls(codec, wm8731_poodle_controls, + ARRAY_SIZE(wm8731_poodle_controls)); + if (err < 0) + return err; /* Add poodle specific widgets */ snd_soc_dapm_new_controls(codec, wm8731_dapm_widgets, @@ -281,17 +280,42 @@ static struct snd_soc_card snd_soc_poodle = { .num_links = 1, }; -/* poodle audio private data */ -static struct wm8731_setup_data poodle_wm8731_setup = { - .i2c_bus = 0, - .i2c_address = 0x1b, -}; +/* + * FIXME: This is a temporary bodge to avoid cross-tree merge issues. + * New drivers should register the wm8731 I2C device in the machine + * setup code (under arch/arm for ARM systems). + */ +static int wm8731_i2c_register(void) +{ + struct i2c_board_info info; + struct i2c_adapter *adapter; + struct i2c_client *client; + + memset(&info, 0, sizeof(struct i2c_board_info)); + info.addr = 0x1b; + strlcpy(info.type, "wm8731", I2C_NAME_SIZE); + + adapter = i2c_get_adapter(0); + if (!adapter) { + printk(KERN_ERR "can't get i2c adapter 0\n"); + return -ENODEV; + } + + client = i2c_new_device(adapter, &info); + i2c_put_adapter(adapter); + if (!client) { + printk(KERN_ERR "can't add i2c device at 0x%x\n", + (unsigned int)info.addr); + return -ENODEV; + } + + return 0; +} /* poodle audio subsystem */ static struct snd_soc_device poodle_snd_devdata = { .card = &snd_soc_poodle, .codec_dev = &soc_codec_dev_wm8731, - .codec_data = &poodle_wm8731_setup, }; static struct platform_device *poodle_snd_device; @@ -303,6 +327,10 @@ static int __init poodle_init(void) if (!machine_is_poodle()) return -ENODEV; + ret = wm8731_i2c_register(); + if (ret != 0) + return ret; + locomo_gpio_set_dir(&poodle_locomo_device.dev, POODLE_LOCOMO_GPIO_AMP_ON, 0); /* should we mute HP at startup - burning power ?*/ diff --git a/sound/soc/pxa/pxa-ssp.c b/sound/soc/pxa/pxa-ssp.c index 1dfdf66fb1f3..7acd3febf8b0 100644 --- a/sound/soc/pxa/pxa-ssp.c +++ b/sound/soc/pxa/pxa-ssp.c @@ -1,4 +1,3 @@ -#define DEBUG /* * pxa-ssp.c -- ALSA Soc Audio Layer * @@ -21,6 +20,8 @@ #include <linux/clk.h> #include <linux/io.h> +#include <asm/irq.h> + #include <sound/core.h> #include <sound/pcm.h> #include <sound/initval.h> @@ -221,9 +222,9 @@ static int pxa_ssp_startup(struct snd_pcm_substream *substream, int ret = 0; if (!cpu_dai->active) { - ret = ssp_init(&priv->dev, cpu_dai->id + 1, SSP_NO_IRQ); - if (ret < 0) - return ret; + priv->dev.port = cpu_dai->id + 1; + priv->dev.irq = NO_IRQ; + clk_enable(priv->dev.ssp->clk); ssp_disable(&priv->dev); } return ret; @@ -238,7 +239,7 @@ static void pxa_ssp_shutdown(struct snd_pcm_substream *substream, if (!cpu_dai->active) { ssp_disable(&priv->dev); - ssp_exit(&priv->dev); + clk_disable(priv->dev.ssp->clk); } } @@ -298,7 +299,7 @@ static int pxa_ssp_set_dai_sysclk(struct snd_soc_dai *cpu_dai, int val; u32 sscr0 = ssp_read_reg(ssp, SSCR0) & - ~(SSCR0_ECS | SSCR0_NCS | SSCR0_MOD | SSCR0_ADC); + ~(SSCR0_ECS | SSCR0_NCS | SSCR0_MOD | SSCR0_ACS); dev_dbg(&ssp->pdev->dev, "pxa_ssp_set_dai_sysclk id: %d, clk_id %d, freq %d\n", @@ -326,7 +327,7 @@ static int pxa_ssp_set_dai_sysclk(struct snd_soc_dai *cpu_dai, case PXA_SSP_CLK_AUDIO: priv->sysclk = 0; ssp_set_scr(&priv->dev, 1); - sscr0 |= SSCR0_ADC; + sscr0 |= SSCR0_ACS; break; default: return -ENODEV; @@ -520,9 +521,20 @@ static int pxa_ssp_set_dai_fmt(struct snd_soc_dai *cpu_dai, u32 sscr1; u32 sspsp; + /* check if we need to change anything at all */ + if (priv->dai_fmt == fmt) + return 0; + + /* we can only change the settings if the port is not in use */ + if (ssp_read_reg(ssp, SSCR0) & SSCR0_SSE) { + dev_err(&ssp->pdev->dev, + "can't change hardware dai format: stream is in use"); + return -EINVAL; + } + /* reset port settings */ sscr0 = ssp_read_reg(ssp, SSCR0) & - (SSCR0_ECS | SSCR0_NCS | SSCR0_MOD | SSCR0_ADC); + (SSCR0_ECS | SSCR0_NCS | SSCR0_MOD | SSCR0_ACS); sscr1 = SSCR1_RxTresh(8) | SSCR1_TxTresh(7); sspsp = 0; @@ -545,18 +557,18 @@ static int pxa_ssp_set_dai_fmt(struct snd_soc_dai *cpu_dai, switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_I2S: - sscr0 |= SSCR0_MOD | SSCR0_PSP; + sscr0 |= SSCR0_PSP; sscr1 |= SSCR1_RWOT | SSCR1_TRAIL; + /* See hw_params() */ switch (fmt & SND_SOC_DAIFMT_INV_MASK) { case SND_SOC_DAIFMT_NB_NF: - sspsp |= SSPSP_FSRT; + sspsp |= SSPSP_SFRMP; break; case SND_SOC_DAIFMT_NB_IF: - sspsp |= SSPSP_SFRMP | SSPSP_FSRT; break; case SND_SOC_DAIFMT_IB_IF: - sspsp |= SSPSP_SFRMP; + sspsp |= SSPSP_SCMODE(3); break; default: return -EINVAL; @@ -642,34 +654,65 @@ static int pxa_ssp_hw_params(struct snd_pcm_substream *substream, sscr0 |= SSCR0_FPCKE; #endif sscr0 |= SSCR0_DataSize(16); - if (params_channels(params) > 1) - sscr0 |= SSCR0_EDSS; break; case SNDRV_PCM_FORMAT_S24_LE: sscr0 |= (SSCR0_EDSS | SSCR0_DataSize(8)); - /* we must be in network mode (2 slots) for 24 bit stereo */ break; case SNDRV_PCM_FORMAT_S32_LE: sscr0 |= (SSCR0_EDSS | SSCR0_DataSize(16)); - /* we must be in network mode (2 slots) for 32 bit stereo */ break; } ssp_write_reg(ssp, SSCR0, sscr0); switch (priv->dai_fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_I2S: - /* Cleared when the DAI format is set */ - sspsp = ssp_read_reg(ssp, SSPSP) | SSPSP_SFRMWDTH(width); + sspsp = ssp_read_reg(ssp, SSPSP); + + if (((sscr0 & SSCR0_SCR) == SSCR0_SerClkDiv(4)) && + (width == 16)) { + /* This is a special case where the bitclk is 64fs + * and we're not dealing with 2*32 bits of audio + * samples. + * + * The SSP values used for that are all found out by + * trying and failing a lot; some of the registers + * needed for that mode are only available on PXA3xx. + */ + +#ifdef CONFIG_PXA3xx + if (!cpu_is_pxa3xx()) + return -EINVAL; + + sspsp |= SSPSP_SFRMWDTH(width * 2); + sspsp |= SSPSP_SFRMDLY(width * 4); + sspsp |= SSPSP_EDMYSTOP(3); + sspsp |= SSPSP_DMYSTOP(3); + sspsp |= SSPSP_DMYSTRT(1); +#else + return -EINVAL; +#endif + } else { + /* The frame width is the width the LRCLK is + * asserted for; the delay is expressed in + * half cycle units. We need the extra cycle + * because the data starts clocking out one BCLK + * after LRCLK changes polarity. + */ + sspsp |= SSPSP_SFRMWDTH(width + 1); + sspsp |= SSPSP_SFRMDLY((width + 1) * 2); + sspsp |= SSPSP_DMYSTRT(1); + } + ssp_write_reg(ssp, SSPSP, sspsp); break; default: break; } - /* We always use a network mode so we always require TDM slots + /* When we use a network mode, we always require TDM slots * - complain loudly and fail if they've not been set up yet. */ - if (!(ssp_read_reg(ssp, SSTSA) & 0xf)) { + if ((sscr0 & SSCR0_MOD) && !(ssp_read_reg(ssp, SSTSA) & 0xf)) { dev_err(&ssp->pdev->dev, "No TDM timeslot configured\n"); return -EINVAL; } @@ -751,7 +794,7 @@ static int pxa_ssp_probe(struct platform_device *pdev, if (!priv) return -ENOMEM; - priv->dev.ssp = ssp_request(dai->id, "SoC audio"); + priv->dev.ssp = ssp_request(dai->id + 1, "SoC audio"); if (priv->dev.ssp == NULL) { ret = -ENODEV; goto err_priv; @@ -782,6 +825,19 @@ static void pxa_ssp_remove(struct platform_device *pdev, SNDRV_PCM_FMTBIT_S24_LE | \ SNDRV_PCM_FMTBIT_S32_LE) +static struct snd_soc_dai_ops pxa_ssp_dai_ops = { + .startup = pxa_ssp_startup, + .shutdown = pxa_ssp_shutdown, + .trigger = pxa_ssp_trigger, + .hw_params = pxa_ssp_hw_params, + .set_sysclk = pxa_ssp_set_dai_sysclk, + .set_clkdiv = pxa_ssp_set_dai_clkdiv, + .set_pll = pxa_ssp_set_dai_pll, + .set_fmt = pxa_ssp_set_dai_fmt, + .set_tdm_slot = pxa_ssp_set_dai_tdm_slot, + .set_tristate = pxa_ssp_set_dai_tristate, +}; + struct snd_soc_dai pxa_ssp_dai[] = { { .name = "pxa2xx-ssp1", @@ -802,18 +858,7 @@ struct snd_soc_dai pxa_ssp_dai[] = { .rates = PXA_SSP_RATES, .formats = PXA_SSP_FORMATS, }, - .ops = { - .startup = pxa_ssp_startup, - .shutdown = pxa_ssp_shutdown, - .trigger = pxa_ssp_trigger, - .hw_params = pxa_ssp_hw_params, - .set_sysclk = pxa_ssp_set_dai_sysclk, - .set_clkdiv = pxa_ssp_set_dai_clkdiv, - .set_pll = pxa_ssp_set_dai_pll, - .set_fmt = pxa_ssp_set_dai_fmt, - .set_tdm_slot = pxa_ssp_set_dai_tdm_slot, - .set_tristate = pxa_ssp_set_dai_tristate, - }, + .ops = &pxa_ssp_dai_ops, }, { .name = "pxa2xx-ssp2", .id = 1, @@ -833,18 +878,7 @@ struct snd_soc_dai pxa_ssp_dai[] = { .rates = PXA_SSP_RATES, .formats = PXA_SSP_FORMATS, }, - .ops = { - .startup = pxa_ssp_startup, - .shutdown = pxa_ssp_shutdown, - .trigger = pxa_ssp_trigger, - .hw_params = pxa_ssp_hw_params, - .set_sysclk = pxa_ssp_set_dai_sysclk, - .set_clkdiv = pxa_ssp_set_dai_clkdiv, - .set_pll = pxa_ssp_set_dai_pll, - .set_fmt = pxa_ssp_set_dai_fmt, - .set_tdm_slot = pxa_ssp_set_dai_tdm_slot, - .set_tristate = pxa_ssp_set_dai_tristate, - }, + .ops = &pxa_ssp_dai_ops, }, { .name = "pxa2xx-ssp3", @@ -865,18 +899,7 @@ struct snd_soc_dai pxa_ssp_dai[] = { .rates = PXA_SSP_RATES, .formats = PXA_SSP_FORMATS, }, - .ops = { - .startup = pxa_ssp_startup, - .shutdown = pxa_ssp_shutdown, - .trigger = pxa_ssp_trigger, - .hw_params = pxa_ssp_hw_params, - .set_sysclk = pxa_ssp_set_dai_sysclk, - .set_clkdiv = pxa_ssp_set_dai_clkdiv, - .set_pll = pxa_ssp_set_dai_pll, - .set_fmt = pxa_ssp_set_dai_fmt, - .set_tdm_slot = pxa_ssp_set_dai_tdm_slot, - .set_tristate = pxa_ssp_set_dai_tristate, - }, + .ops = &pxa_ssp_dai_ops, }, { .name = "pxa2xx-ssp4", @@ -897,18 +920,7 @@ struct snd_soc_dai pxa_ssp_dai[] = { .rates = PXA_SSP_RATES, .formats = PXA_SSP_FORMATS, }, - .ops = { - .startup = pxa_ssp_startup, - .shutdown = pxa_ssp_shutdown, - .trigger = pxa_ssp_trigger, - .hw_params = pxa_ssp_hw_params, - .set_sysclk = pxa_ssp_set_dai_sysclk, - .set_clkdiv = pxa_ssp_set_dai_clkdiv, - .set_pll = pxa_ssp_set_dai_pll, - .set_fmt = pxa_ssp_set_dai_fmt, - .set_tdm_slot = pxa_ssp_set_dai_tdm_slot, - .set_tristate = pxa_ssp_set_dai_tristate, - }, + .ops = &pxa_ssp_dai_ops, }, }; EXPORT_SYMBOL_GPL(pxa_ssp_dai); diff --git a/sound/soc/pxa/pxa2xx-ac97.c b/sound/soc/pxa/pxa2xx-ac97.c index a4a655f7e304..d9c94d71fa61 100644 --- a/sound/soc/pxa/pxa2xx-ac97.c +++ b/sound/soc/pxa/pxa2xx-ac97.c @@ -106,13 +106,13 @@ static int pxa2xx_ac97_resume(struct snd_soc_dai *dai) static int pxa2xx_ac97_probe(struct platform_device *pdev, struct snd_soc_dai *dai) { - return pxa2xx_ac97_hw_probe(pdev); + return pxa2xx_ac97_hw_probe(to_platform_device(dai->dev)); } static void pxa2xx_ac97_remove(struct platform_device *pdev, struct snd_soc_dai *dai) { - pxa2xx_ac97_hw_remove(pdev); + pxa2xx_ac97_hw_remove(to_platform_device(dai->dev)); } static int pxa2xx_ac97_hw_params(struct snd_pcm_substream *substream, @@ -164,6 +164,18 @@ static int pxa2xx_ac97_hw_mic_params(struct snd_pcm_substream *substream, SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | \ SNDRV_PCM_RATE_48000) +static struct snd_soc_dai_ops pxa_ac97_hifi_dai_ops = { + .hw_params = pxa2xx_ac97_hw_params, +}; + +static struct snd_soc_dai_ops pxa_ac97_aux_dai_ops = { + .hw_params = pxa2xx_ac97_hw_aux_params, +}; + +static struct snd_soc_dai_ops pxa_ac97_mic_dai_ops = { + .hw_params = pxa2xx_ac97_hw_mic_params, +}; + /* * There is only 1 physical AC97 interface for pxa2xx, but it * has extra fifo's that can be used for aux DACs and ADCs. @@ -189,8 +201,7 @@ struct snd_soc_dai pxa_ac97_dai[] = { .channels_max = 2, .rates = PXA2XX_AC97_RATES, .formats = SNDRV_PCM_FMTBIT_S16_LE,}, - .ops = { - .hw_params = pxa2xx_ac97_hw_params,}, + .ops = &pxa_ac97_hifi_dai_ops, }, { .name = "pxa2xx-ac97-aux", @@ -208,8 +219,7 @@ struct snd_soc_dai pxa_ac97_dai[] = { .channels_max = 1, .rates = PXA2XX_AC97_RATES, .formats = SNDRV_PCM_FMTBIT_S16_LE,}, - .ops = { - .hw_params = pxa2xx_ac97_hw_aux_params,}, + .ops = &pxa_ac97_aux_dai_ops, }, { .name = "pxa2xx-ac97-mic", @@ -221,23 +231,52 @@ struct snd_soc_dai pxa_ac97_dai[] = { .channels_max = 1, .rates = PXA2XX_AC97_RATES, .formats = SNDRV_PCM_FMTBIT_S16_LE,}, - .ops = { - .hw_params = pxa2xx_ac97_hw_mic_params,}, + .ops = &pxa_ac97_mic_dai_ops, }, }; EXPORT_SYMBOL_GPL(pxa_ac97_dai); EXPORT_SYMBOL_GPL(soc_ac97_ops); -static int __init pxa_ac97_init(void) +static int __devinit pxa2xx_ac97_dev_probe(struct platform_device *pdev) { + int i; + + for (i = 0; i < ARRAY_SIZE(pxa_ac97_dai); i++) + pxa_ac97_dai[i].dev = &pdev->dev; + + /* Punt most of the init to the SoC probe; we may need the machine + * driver to do interesting things with the clocking to get us up + * and running. + */ return snd_soc_register_dais(pxa_ac97_dai, ARRAY_SIZE(pxa_ac97_dai)); } + +static int __devexit pxa2xx_ac97_dev_remove(struct platform_device *pdev) +{ + snd_soc_unregister_dais(pxa_ac97_dai, ARRAY_SIZE(pxa_ac97_dai)); + + return 0; +} + +static struct platform_driver pxa2xx_ac97_driver = { + .probe = pxa2xx_ac97_dev_probe, + .remove = __devexit_p(pxa2xx_ac97_dev_remove), + .driver = { + .name = "pxa2xx-ac97", + .owner = THIS_MODULE, + }, +}; + +static int __init pxa_ac97_init(void) +{ + return platform_driver_register(&pxa2xx_ac97_driver); +} module_init(pxa_ac97_init); static void __exit pxa_ac97_exit(void) { - snd_soc_unregister_dais(pxa_ac97_dai, ARRAY_SIZE(pxa_ac97_dai)); + platform_driver_unregister(&pxa2xx_ac97_driver); } module_exit(pxa_ac97_exit); diff --git a/sound/soc/pxa/pxa2xx-i2s.c b/sound/soc/pxa/pxa2xx-i2s.c index 223de890259e..2f4b6e489b78 100644 --- a/sound/soc/pxa/pxa2xx-i2s.c +++ b/sound/soc/pxa/pxa2xx-i2s.c @@ -25,20 +25,11 @@ #include <mach/hardware.h> #include <mach/dma.h> -#include <mach/pxa2xx-gpio.h> #include <mach/audio.h> #include "pxa2xx-pcm.h" #include "pxa2xx-i2s.h" -struct pxa2xx_gpio { - u32 sys; - u32 rx; - u32 tx; - u32 clk; - u32 frm; -}; - /* * I2S Controller Register and Bit Definitions */ @@ -106,21 +97,6 @@ static struct pxa2xx_pcm_dma_params pxa2xx_i2s_pcm_stereo_in = { DCMD_BURST32 | DCMD_WIDTH4, }; -static struct pxa2xx_gpio gpio_bus[] = { - { /* I2S SoC Slave */ - .rx = GPIO29_SDATA_IN_I2S_MD, - .tx = GPIO30_SDATA_OUT_I2S_MD, - .clk = GPIO28_BITCLK_IN_I2S_MD, - .frm = GPIO31_SYNC_I2S_MD, - }, - { /* I2S SoC Master */ - .rx = GPIO29_SDATA_IN_I2S_MD, - .tx = GPIO30_SDATA_OUT_I2S_MD, - .clk = GPIO28_BITCLK_OUT_I2S_MD, - .frm = GPIO31_SYNC_I2S_MD, - }, -}; - static int pxa2xx_i2s_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { @@ -181,9 +157,6 @@ static int pxa2xx_i2s_set_dai_sysclk(struct snd_soc_dai *cpu_dai, if (clk_id != PXA2XX_I2S_SYSCLK) return -ENODEV; - if (pxa_i2s.master && dir == SND_SOC_CLOCK_OUT) - pxa_gpio_mode(gpio_bus[pxa_i2s.master].sys); - return 0; } @@ -194,10 +167,6 @@ static int pxa2xx_i2s_hw_params(struct snd_pcm_substream *substream, struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; - pxa_gpio_mode(gpio_bus[pxa_i2s.master].rx); - pxa_gpio_mode(gpio_bus[pxa_i2s.master].tx); - pxa_gpio_mode(gpio_bus[pxa_i2s.master].frm); - pxa_gpio_mode(gpio_bus[pxa_i2s.master].clk); BUG_ON(IS_ERR(clk_i2s)); clk_enable(clk_i2s); pxa_i2s_wait(); @@ -335,6 +304,15 @@ static int pxa2xx_i2s_resume(struct snd_soc_dai *dai) SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | \ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000) +static struct snd_soc_dai_ops pxa_i2s_dai_ops = { + .startup = pxa2xx_i2s_startup, + .shutdown = pxa2xx_i2s_shutdown, + .trigger = pxa2xx_i2s_trigger, + .hw_params = pxa2xx_i2s_hw_params, + .set_fmt = pxa2xx_i2s_set_dai_fmt, + .set_sysclk = pxa2xx_i2s_set_dai_sysclk, +}; + struct snd_soc_dai pxa_i2s_dai = { .name = "pxa2xx-i2s", .id = 0, @@ -350,14 +328,7 @@ struct snd_soc_dai pxa_i2s_dai = { .channels_max = 2, .rates = PXA2XX_I2S_RATES, .formats = SNDRV_PCM_FMTBIT_S16_LE,}, - .ops = { - .startup = pxa2xx_i2s_startup, - .shutdown = pxa2xx_i2s_shutdown, - .trigger = pxa2xx_i2s_trigger, - .hw_params = pxa2xx_i2s_hw_params, - .set_fmt = pxa2xx_i2s_set_dai_fmt, - .set_sysclk = pxa2xx_i2s_set_dai_sysclk, - }, + .ops = &pxa_i2s_dai_ops, }; EXPORT_SYMBOL_GPL(pxa_i2s_dai); @@ -398,11 +369,6 @@ static struct platform_driver pxa2xx_i2s_driver = { static int __init pxa2xx_i2s_init(void) { - if (cpu_is_pxa27x()) - gpio_bus[1].sys = GPIO113_I2S_SYSCLK_MD; - else - gpio_bus[1].sys = GPIO32_SYSCLK_I2S_MD; - clk_i2s = ERR_PTR(-ENOENT); return platform_driver_register(&pxa2xx_i2s_driver); } diff --git a/sound/soc/pxa/spitz.c b/sound/soc/pxa/spitz.c index de8778fa8729..c4cd2acaacb4 100644 --- a/sound/soc/pxa/spitz.c +++ b/sound/soc/pxa/spitz.c @@ -107,7 +107,7 @@ static void spitz_ext_control(struct snd_soc_codec *codec) static int spitz_startup(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->socdev->codec; + struct snd_soc_codec *codec = rtd->socdev->card->codec; /* check the jack status at stream startup */ spitz_ext_control(codec); @@ -276,7 +276,7 @@ static const struct snd_kcontrol_new wm8750_spitz_controls[] = { */ static int spitz_wm8750_init(struct snd_soc_codec *codec) { - int i, err; + int err; /* NC codec pins */ snd_soc_dapm_nc_pin(codec, "RINPUT1"); @@ -288,12 +288,10 @@ static int spitz_wm8750_init(struct snd_soc_codec *codec) snd_soc_dapm_nc_pin(codec, "MONO1"); /* Add spitz specific controls */ - for (i = 0; i < ARRAY_SIZE(wm8750_spitz_controls); i++) { - err = snd_ctl_add(codec->card, - snd_soc_cnew(&wm8750_spitz_controls[i], codec, NULL)); - if (err < 0) - return err; - } + err = snd_soc_add_controls(codec, wm8750_spitz_controls, + ARRAY_SIZE(wm8750_spitz_controls)); + if (err < 0) + return err; /* Add spitz specific widgets */ snd_soc_dapm_new_controls(codec, wm8750_dapm_widgets, diff --git a/sound/soc/pxa/tosa.c b/sound/soc/pxa/tosa.c index 050223d04e54..dbbd3e9d1637 100644 --- a/sound/soc/pxa/tosa.c +++ b/sound/soc/pxa/tosa.c @@ -80,7 +80,7 @@ static void tosa_ext_control(struct snd_soc_codec *codec) static int tosa_startup(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->socdev->codec; + struct snd_soc_codec *codec = rtd->socdev->card->codec; /* check the jack status at stream startup */ tosa_ext_control(codec); @@ -186,18 +186,16 @@ static const struct snd_kcontrol_new tosa_controls[] = { static int tosa_ac97_init(struct snd_soc_codec *codec) { - int i, err; + int err; snd_soc_dapm_nc_pin(codec, "OUT3"); snd_soc_dapm_nc_pin(codec, "MONOOUT"); /* add tosa specific controls */ - for (i = 0; i < ARRAY_SIZE(tosa_controls); i++) { - err = snd_ctl_add(codec->card, - snd_soc_cnew(&tosa_controls[i],codec, NULL)); - if (err < 0) - return err; - } + err = snd_soc_add_controls(codec, tosa_controls, + ARRAY_SIZE(tosa_controls)); + if (err < 0) + return err; /* add tosa specific widgets */ snd_soc_dapm_new_controls(codec, tosa_dapm_widgets, diff --git a/sound/soc/pxa/zylonite.c b/sound/soc/pxa/zylonite.c index f8e9ecd589d3..9a386b4c4ed1 100644 --- a/sound/soc/pxa/zylonite.c +++ b/sound/soc/pxa/zylonite.c @@ -14,6 +14,7 @@ #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/device.h> +#include <linux/clk.h> #include <linux/i2c.h> #include <sound/core.h> #include <sound/pcm.h> @@ -26,6 +27,17 @@ #include "pxa2xx-ac97.h" #include "pxa-ssp.h" +/* + * There is a physical switch SW15 on the board which changes the MCLK + * for the WM9713 between the standard AC97 master clock and the + * output of the CLK_POUT signal from the PXA. + */ +static int clk_pout; +module_param(clk_pout, int, 0); +MODULE_PARM_DESC(clk_pout, "Use CLK_POUT as WM9713 MCLK (SW15 on board)."); + +static struct clk *pout; + static struct snd_soc_card zylonite; static const struct snd_soc_dapm_widget zylonite_dapm_widgets[] = { @@ -61,10 +73,8 @@ static const struct snd_soc_dapm_route audio_map[] = { static int zylonite_wm9713_init(struct snd_soc_codec *codec) { - /* Currently we only support use of the AC97 clock here. If - * CLK_POUT is selected by SW15 then the clock API will need - * to be used to request and enable it here. - */ + if (clk_pout) + snd_soc_dai_set_pll(&codec->dai[0], 0, clk_get_rate(pout), 0); snd_soc_dapm_new_controls(codec, zylonite_dapm_widgets, ARRAY_SIZE(zylonite_dapm_widgets)); @@ -86,40 +96,35 @@ static int zylonite_voice_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; unsigned int pll_out = 0; - unsigned int acds = 0; unsigned int wm9713_div = 0; int ret = 0; + int rate = params_rate(params); + int width = snd_pcm_format_physical_width(params_format(params)); - switch (params_rate(params)) { + /* Only support ratios that we can generate neatly from the AC97 + * based master clock - in particular, this excludes 44.1kHz. + * In most applications the voice DAC will be used for telephony + * data so multiples of 8kHz will be the common case. + */ + switch (rate) { case 8000: wm9713_div = 12; - pll_out = 2048000; break; case 16000: wm9713_div = 6; - pll_out = 4096000; break; case 48000: - default: wm9713_div = 2; - pll_out = 12288000; - acds = 1; break; + default: + /* Don't support OSS emulation */ + return -EINVAL; } - ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | - SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); - 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_CBS_CFS); - if (ret < 0) - return ret; + /* Add 1 to the width for the leading clock cycle */ + pll_out = rate * (width + 1) * 8; - ret = snd_soc_dai_set_tdm_slot(cpu_dai, - params_channels(params), - params_channels(params)); + ret = snd_soc_dai_set_sysclk(cpu_dai, PXA_SSP_CLK_AUDIO, 0, 1); if (ret < 0) return ret; @@ -127,19 +132,22 @@ static int zylonite_voice_hw_params(struct snd_pcm_substream *substream, if (ret < 0) return ret; - ret = snd_soc_dai_set_clkdiv(cpu_dai, PXA_SSP_AUDIO_DIV_ACDS, acds); + if (clk_pout) + ret = snd_soc_dai_set_clkdiv(codec_dai, WM9713_PCMCLK_PLL_DIV, + WM9713_PCMDIV(wm9713_div)); + else + ret = snd_soc_dai_set_clkdiv(codec_dai, WM9713_PCMCLK_DIV, + WM9713_PCMDIV(wm9713_div)); if (ret < 0) return ret; - ret = snd_soc_dai_set_sysclk(cpu_dai, PXA_SSP_CLK_AUDIO, 0, 1); + ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); if (ret < 0) return ret; - /* Note that if the PLL is in use the WM9713_PCMCLK_PLL_DIV needs - * to be set instead. - */ - ret = snd_soc_dai_set_clkdiv(codec_dai, WM9713_PCMCLK_DIV, - WM9713_PCMDIV(wm9713_div)); + ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); if (ret < 0) return ret; @@ -173,8 +181,72 @@ static struct snd_soc_dai_link zylonite_dai[] = { }, }; +static int zylonite_probe(struct platform_device *pdev) +{ + int ret; + + if (clk_pout) { + pout = clk_get(NULL, "CLK_POUT"); + if (IS_ERR(pout)) { + dev_err(&pdev->dev, "Unable to obtain CLK_POUT: %ld\n", + PTR_ERR(pout)); + return PTR_ERR(pout); + } + + ret = clk_enable(pout); + if (ret != 0) { + dev_err(&pdev->dev, "Unable to enable CLK_POUT: %d\n", + ret); + clk_put(pout); + return ret; + } + + dev_dbg(&pdev->dev, "MCLK enabled at %luHz\n", + clk_get_rate(pout)); + } + + return 0; +} + +static int zylonite_remove(struct platform_device *pdev) +{ + if (clk_pout) { + clk_disable(pout); + clk_put(pout); + } + + return 0; +} + +static int zylonite_suspend_post(struct platform_device *pdev, + pm_message_t state) +{ + if (clk_pout) + clk_disable(pout); + + return 0; +} + +static int zylonite_resume_pre(struct platform_device *pdev) +{ + int ret = 0; + + if (clk_pout) { + ret = clk_enable(pout); + if (ret != 0) + dev_err(&pdev->dev, "Unable to enable CLK_POUT: %d\n", + ret); + } + + return ret; +} + static struct snd_soc_card zylonite = { .name = "Zylonite", + .probe = &zylonite_probe, + .remove = &zylonite_remove, + .suspend_post = &zylonite_suspend_post, + .resume_pre = &zylonite_resume_pre, .platform = &pxa2xx_soc_platform, .dai_link = zylonite_dai, .num_links = ARRAY_SIZE(zylonite_dai), |