diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2009-12-08 07:47:46 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-12-08 07:47:46 -0800 |
commit | a421018e8c10e5593a1fee076af72a66c3fe8ca3 (patch) | |
tree | 2854511845d0e07d33726a13eda6de1059a5c9df /sound/soc/pxa/raumfeld.c | |
parent | 3ad1f3b35e8309ec93454dbf89beaafcdb5312da (diff) | |
parent | 86e1d57e4f24ca27ce813bdc2afaac4adafcbaf4 (diff) |
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound-2.6
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound-2.6: (294 commits)
S3C64XX: Staticise platform data for PCM devices
ASoC: Rename controls with a / in wm_hubs
snd-fm801: autodetect SF64-PCR (tuner-only) card
ALSA: tea575x-tuner: fix mute
ASoC: au1x: dbdma2: plug memleak in pcm device creation error path
ASoC: au1x: dbdma2: fix oops on soc device removal.
ALSA: hda - Fix memory leaks in the previous patch
ALSA: hda - Add ALC661/259, ALC892/888VD support
ALSA: opti9xx: remove snd_opti9xx fields
ALSA: aaci - Clean up duplicate code
ALSA: usb - Fix mixer map for Hercules Gamesurround Muse Pocket LT
ALSA: hda - Add position_fix quirk for HP dv3
ALSA: hda - Add a pin-fix for FSC Amilo Pi1505
ALSA: hda - Fix Cxt5047 test mode
ASoC: pxa/raumfeld: adopt new snd_soc_dai_set_pll() API
ASoC: sh: fsi: Add runtime PM support
sh: ms7724se: Add runtime PM support for FSI
ALSA: hda - Add a position_fix quirk for MSI Wind U115
ALSA: opti-miro: add PnP detection
ALSA: opti-miro: separate comon probing code
...
Diffstat (limited to 'sound/soc/pxa/raumfeld.c')
-rw-r--r-- | sound/soc/pxa/raumfeld.c | 335 |
1 files changed, 335 insertions, 0 deletions
diff --git a/sound/soc/pxa/raumfeld.c b/sound/soc/pxa/raumfeld.c new file mode 100644 index 000000000000..acfce1c0f1c9 --- /dev/null +++ b/sound/soc/pxa/raumfeld.c @@ -0,0 +1,335 @@ +/* + * raumfeld_audio.c -- SoC audio for Raumfeld audio devices + * + * Copyright (c) 2009 Daniel Mack <daniel@caiaq.de> + * + * based on code from: + * + * Wolfson Microelectronics PLC. + * Openedhand Ltd. + * Liam Girdwood <lrg@slimlogic.co.uk> + * Richard Purdie <richard@openedhand.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; either version 2 of the License, or (at your + * option) any later version. + */ + +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/delay.h> +#include <linux/gpio.h> +#include <sound/pcm.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> + +#include <asm/mach-types.h> + +#include "../codecs/cs4270.h" +#include "../codecs/ak4104.h" +#include "pxa2xx-pcm.h" +#include "pxa-ssp.h" + +#define GPIO_SPDIF_RESET (38) +#define GPIO_MCLK_RESET (111) +#define GPIO_CODEC_RESET (120) + +static struct i2c_client *max9486_client; +static struct i2c_board_info max9486_hwmon_info = { + I2C_BOARD_INFO("max9485", 0x63), +}; + +#define MAX9485_MCLK_FREQ_112896 0x22 +#define MAX9485_MCLK_FREQ_122880 0x23 + +static void set_max9485_clk(char clk) +{ + i2c_master_send(max9486_client, &clk, 1); +} + +static void raumfeld_enable_audio(bool en) +{ + if (en) { + gpio_set_value(GPIO_MCLK_RESET, 1); + + /* wait some time to let the clocks become stable */ + msleep(100); + + gpio_set_value(GPIO_SPDIF_RESET, 1); + gpio_set_value(GPIO_CODEC_RESET, 1); + } else { + gpio_set_value(GPIO_MCLK_RESET, 0); + gpio_set_value(GPIO_SPDIF_RESET, 0); + gpio_set_value(GPIO_CODEC_RESET, 0); + } +} + +/* CS4270 */ +static int raumfeld_cs4270_startup(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; + + set_max9485_clk(MAX9485_MCLK_FREQ_112896); + + return snd_soc_dai_set_sysclk(codec_dai, 0, 11289600, 0); +} + +static int raumfeld_cs4270_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->dai->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; + unsigned int fmt, clk = 0; + int ret = 0; + + switch (params_rate(params)) { + case 8000: + case 16000: + case 48000: + case 96000: + set_max9485_clk(MAX9485_MCLK_FREQ_122880); + clk = 12288000; + break; + case 11025: + case 22050: + case 44100: + case 88200: + set_max9485_clk(MAX9485_MCLK_FREQ_112896); + clk = 11289600; + break; + } + + fmt = SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS; + + /* setup the CODEC DAI */ + ret = snd_soc_dai_set_fmt(codec_dai, fmt); + if (ret < 0) + return ret; + + ret = snd_soc_dai_set_sysclk(codec_dai, 0, clk, 0); + if (ret < 0) + return ret; + + /* setup the CPU DAI */ + ret = snd_soc_dai_set_pll(cpu_dai, 0, 0, 0, clk); + if (ret < 0) + return ret; + + ret = snd_soc_dai_set_fmt(cpu_dai, fmt); + if (ret < 0) + return ret; + + ret = snd_soc_dai_set_clkdiv(cpu_dai, PXA_SSP_DIV_SCR, 4); + if (ret < 0) + return ret; + + ret = snd_soc_dai_set_sysclk(cpu_dai, PXA_SSP_CLK_EXT, 0, 1); + if (ret < 0) + return ret; + + return 0; +} + +static struct snd_soc_ops raumfeld_cs4270_ops = { + .startup = raumfeld_cs4270_startup, + .hw_params = raumfeld_cs4270_hw_params, +}; + +static int raumfeld_line_suspend(struct platform_device *pdev, pm_message_t state) +{ + raumfeld_enable_audio(false); + return 0; +} + +static int raumfeld_line_resume(struct platform_device *pdev) +{ + raumfeld_enable_audio(true); + return 0; +} + +static struct snd_soc_dai_link raumfeld_line_dai = { + .name = "CS4270", + .stream_name = "CS4270", + .cpu_dai = &pxa_ssp_dai[PXA_DAI_SSP1], + .codec_dai = &cs4270_dai, + .ops = &raumfeld_cs4270_ops, +}; + +static struct snd_soc_card snd_soc_line_raumfeld = { + .name = "Raumfeld analog", + .platform = &pxa2xx_soc_platform, + .dai_link = &raumfeld_line_dai, + .suspend_post = raumfeld_line_suspend, + .resume_pre = raumfeld_line_resume, + .num_links = 1, +}; + + +/* AK4104 */ + +static int raumfeld_ak4104_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->dai->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; + int fmt, ret = 0, clk = 0; + + switch (params_rate(params)) { + case 8000: + case 16000: + case 48000: + case 96000: + set_max9485_clk(MAX9485_MCLK_FREQ_122880); + clk = 12288000; + break; + case 11025: + case 22050: + case 44100: + case 88200: + set_max9485_clk(MAX9485_MCLK_FREQ_112896); + clk = 11289600; + break; + } + + fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF; + + /* setup the CODEC DAI */ + ret = snd_soc_dai_set_fmt(codec_dai, fmt | SND_SOC_DAIFMT_CBS_CFS); + if (ret < 0) + return ret; + + /* setup the CPU DAI */ + ret = snd_soc_dai_set_pll(cpu_dai, 0, 0, 0, clk); + if (ret < 0) + return ret; + + ret = snd_soc_dai_set_fmt(cpu_dai, fmt | SND_SOC_DAIFMT_CBS_CFS); + if (ret < 0) + return ret; + + ret = snd_soc_dai_set_clkdiv(cpu_dai, PXA_SSP_DIV_SCR, 4); + if (ret < 0) + return ret; + + ret = snd_soc_dai_set_sysclk(cpu_dai, PXA_SSP_CLK_EXT, 0, 1); + if (ret < 0) + return ret; + + return 0; +} + +static struct snd_soc_ops raumfeld_ak4104_ops = { + .hw_params = raumfeld_ak4104_hw_params, +}; + +static struct snd_soc_dai_link raumfeld_spdif_dai = { + .name = "ak4104", + .stream_name = "Playback", + .cpu_dai = &pxa_ssp_dai[PXA_DAI_SSP2], + .codec_dai = &ak4104_dai, + .ops = &raumfeld_ak4104_ops, +}; + +static struct snd_soc_card snd_soc_spdif_raumfeld = { + .name = "Raumfeld S/PDIF", + .platform = &pxa2xx_soc_platform, + .dai_link = &raumfeld_spdif_dai, + .num_links = 1 +}; + +/* raumfeld_audio audio subsystem */ +static struct snd_soc_device raumfeld_line_devdata = { + .card = &snd_soc_line_raumfeld, + .codec_dev = &soc_codec_device_cs4270, +}; + +static struct snd_soc_device raumfeld_spdif_devdata = { + .card = &snd_soc_spdif_raumfeld, + .codec_dev = &soc_codec_device_ak4104, +}; + +static struct platform_device *raumfeld_audio_line_device; +static struct platform_device *raumfeld_audio_spdif_device; + +static int __init raumfeld_audio_init(void) +{ + int ret; + + if (!machine_is_raumfeld_speaker() && + !machine_is_raumfeld_connector()) + return 0; + + max9486_client = i2c_new_device(i2c_get_adapter(0), + &max9486_hwmon_info); + + if (!max9486_client) + return -ENOMEM; + + set_max9485_clk(MAX9485_MCLK_FREQ_122880); + + /* LINE */ + raumfeld_audio_line_device = platform_device_alloc("soc-audio", 0); + if (!raumfeld_audio_line_device) + return -ENOMEM; + + platform_set_drvdata(raumfeld_audio_line_device, + &raumfeld_line_devdata); + raumfeld_line_devdata.dev = &raumfeld_audio_line_device->dev; + ret = platform_device_add(raumfeld_audio_line_device); + if (ret) + platform_device_put(raumfeld_audio_line_device); + + /* no S/PDIF on Speakers */ + if (machine_is_raumfeld_speaker()) + return ret; + + /* S/PDIF */ + raumfeld_audio_spdif_device = platform_device_alloc("soc-audio", 1); + if (!raumfeld_audio_spdif_device) { + platform_device_put(raumfeld_audio_line_device); + return -ENOMEM; + } + + platform_set_drvdata(raumfeld_audio_spdif_device, + &raumfeld_spdif_devdata); + raumfeld_spdif_devdata.dev = &raumfeld_audio_spdif_device->dev; + ret = platform_device_add(raumfeld_audio_spdif_device); + if (ret) { + platform_device_put(raumfeld_audio_line_device); + platform_device_put(raumfeld_audio_spdif_device); + } + + raumfeld_enable_audio(true); + + return ret; +} + +static void __exit raumfeld_audio_exit(void) +{ + raumfeld_enable_audio(false); + + platform_device_unregister(raumfeld_audio_line_device); + + if (machine_is_raumfeld_connector()) + platform_device_unregister(raumfeld_audio_spdif_device); + + i2c_unregister_device(max9486_client); + + gpio_free(GPIO_MCLK_RESET); + gpio_free(GPIO_CODEC_RESET); + gpio_free(GPIO_SPDIF_RESET); +} + +module_init(raumfeld_audio_init); +module_exit(raumfeld_audio_exit); + +/* Module information */ +MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>"); +MODULE_DESCRIPTION("Raumfeld audio SoC"); +MODULE_LICENSE("GPL"); |