Path: blob/master/sound/soc/samsung/neo1973_wm8753.c
10817 views
/*1* neo1973_wm8753.c -- SoC audio for Openmoko Neo1973 and Freerunner devices2*3* Copyright 2007 Openmoko Inc4* Author: Graeme Gregory <[email protected]>5* Copyright 2007 Wolfson Microelectronics PLC.6* Author: Graeme Gregory7* [email protected] or [email protected]8* Copyright 2009 Wolfson Microelectronics9*10* This program is free software; you can redistribute it and/or modify it11* under the terms of the GNU General Public License as published by the12* Free Software Foundation; either version 2 of the License, or (at your13* option) any later version.14*/1516#include <linux/module.h>17#include <linux/platform_device.h>18#include <linux/gpio.h>1920#include <sound/soc.h>2122#include <asm/mach-types.h>23#include <plat/regs-iis.h>24#include <mach/gta02.h>2526#include "../codecs/wm8753.h"27#include "s3c24xx-i2s.h"2829static int neo1973_hifi_hw_params(struct snd_pcm_substream *substream,30struct snd_pcm_hw_params *params)31{32struct snd_soc_pcm_runtime *rtd = substream->private_data;33struct snd_soc_dai *codec_dai = rtd->codec_dai;34struct snd_soc_dai *cpu_dai = rtd->cpu_dai;35unsigned int pll_out = 0, bclk = 0;36int ret = 0;37unsigned long iis_clkrate;3839iis_clkrate = s3c24xx_i2s_get_clockrate();4041switch (params_rate(params)) {42case 8000:43case 16000:44pll_out = 12288000;45break;46case 48000:47bclk = WM8753_BCLK_DIV_4;48pll_out = 12288000;49break;50case 96000:51bclk = WM8753_BCLK_DIV_2;52pll_out = 12288000;53break;54case 11025:55bclk = WM8753_BCLK_DIV_16;56pll_out = 11289600;57break;58case 22050:59bclk = WM8753_BCLK_DIV_8;60pll_out = 11289600;61break;62case 44100:63bclk = WM8753_BCLK_DIV_4;64pll_out = 11289600;65break;66case 88200:67bclk = WM8753_BCLK_DIV_2;68pll_out = 11289600;69break;70}7172/* set codec DAI configuration */73ret = snd_soc_dai_set_fmt(codec_dai,74SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |75SND_SOC_DAIFMT_CBM_CFM);76if (ret < 0)77return ret;7879/* set cpu DAI configuration */80ret = snd_soc_dai_set_fmt(cpu_dai,81SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |82SND_SOC_DAIFMT_CBM_CFM);83if (ret < 0)84return ret;8586/* set the codec system clock for DAC and ADC */87ret = snd_soc_dai_set_sysclk(codec_dai, WM8753_MCLK, pll_out,88SND_SOC_CLOCK_IN);89if (ret < 0)90return ret;9192/* set MCLK division for sample rate */93ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_MCLK,94S3C2410_IISMOD_32FS);95if (ret < 0)96return ret;9798/* set codec BCLK division for sample rate */99ret = snd_soc_dai_set_clkdiv(codec_dai, WM8753_BCLKDIV, bclk);100if (ret < 0)101return ret;102103/* set prescaler division for sample rate */104ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER,105S3C24XX_PRESCALE(4, 4));106if (ret < 0)107return ret;108109/* codec PLL input is PCLK/4 */110ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL1, 0,111iis_clkrate / 4, pll_out);112if (ret < 0)113return ret;114115return 0;116}117118static int neo1973_hifi_hw_free(struct snd_pcm_substream *substream)119{120struct snd_soc_pcm_runtime *rtd = substream->private_data;121struct snd_soc_dai *codec_dai = rtd->codec_dai;122123/* disable the PLL */124return snd_soc_dai_set_pll(codec_dai, WM8753_PLL1, 0, 0, 0);125}126127/*128* Neo1973 WM8753 HiFi DAI opserations.129*/130static struct snd_soc_ops neo1973_hifi_ops = {131.hw_params = neo1973_hifi_hw_params,132.hw_free = neo1973_hifi_hw_free,133};134135static int neo1973_voice_hw_params(struct snd_pcm_substream *substream,136struct snd_pcm_hw_params *params)137{138struct snd_soc_pcm_runtime *rtd = substream->private_data;139struct snd_soc_dai *codec_dai = rtd->codec_dai;140unsigned int pcmdiv = 0;141int ret = 0;142unsigned long iis_clkrate;143144iis_clkrate = s3c24xx_i2s_get_clockrate();145146if (params_rate(params) != 8000)147return -EINVAL;148if (params_channels(params) != 1)149return -EINVAL;150151pcmdiv = WM8753_PCM_DIV_6; /* 2.048 MHz */152153/* todo: gg check mode (DSP_B) against CSR datasheet */154/* set codec DAI configuration */155ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_DSP_B |156SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);157if (ret < 0)158return ret;159160/* set the codec system clock for DAC and ADC */161ret = snd_soc_dai_set_sysclk(codec_dai, WM8753_PCMCLK, 12288000,162SND_SOC_CLOCK_IN);163if (ret < 0)164return ret;165166/* set codec PCM division for sample rate */167ret = snd_soc_dai_set_clkdiv(codec_dai, WM8753_PCMDIV, pcmdiv);168if (ret < 0)169return ret;170171/* configure and enable PLL for 12.288MHz output */172ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL2, 0,173iis_clkrate / 4, 12288000);174if (ret < 0)175return ret;176177return 0;178}179180static int neo1973_voice_hw_free(struct snd_pcm_substream *substream)181{182struct snd_soc_pcm_runtime *rtd = substream->private_data;183struct snd_soc_dai *codec_dai = rtd->codec_dai;184185/* disable the PLL */186return snd_soc_dai_set_pll(codec_dai, WM8753_PLL2, 0, 0, 0);187}188189static struct snd_soc_ops neo1973_voice_ops = {190.hw_params = neo1973_voice_hw_params,191.hw_free = neo1973_voice_hw_free,192};193194/* Shared routes and controls */195196static const struct snd_soc_dapm_widget neo1973_wm8753_dapm_widgets[] = {197SND_SOC_DAPM_LINE("GSM Line Out", NULL),198SND_SOC_DAPM_LINE("GSM Line In", NULL),199SND_SOC_DAPM_MIC("Headset Mic", NULL),200SND_SOC_DAPM_MIC("Handset Mic", NULL),201};202203static const struct snd_soc_dapm_route neo1973_wm8753_routes[] = {204/* Connections to the GSM Module */205{"GSM Line Out", NULL, "MONO1"},206{"GSM Line Out", NULL, "MONO2"},207{"RXP", NULL, "GSM Line In"},208{"RXN", NULL, "GSM Line In"},209210/* Connections to Headset */211{"MIC1", NULL, "Mic Bias"},212{"Mic Bias", NULL, "Headset Mic"},213214/* Call Mic */215{"MIC2", NULL, "Mic Bias"},216{"MIC2N", NULL, "Mic Bias"},217{"Mic Bias", NULL, "Handset Mic"},218219/* Connect the ALC pins */220{"ACIN", NULL, "ACOP"},221};222223static const struct snd_kcontrol_new neo1973_wm8753_controls[] = {224SOC_DAPM_PIN_SWITCH("GSM Line Out"),225SOC_DAPM_PIN_SWITCH("GSM Line In"),226SOC_DAPM_PIN_SWITCH("Headset Mic"),227SOC_DAPM_PIN_SWITCH("Handset Mic"),228};229230/* GTA02 specific routes and controls */231232#ifdef CONFIG_MACH_NEO1973_GTA02233234static int gta02_speaker_enabled;235236static int lm4853_set_spk(struct snd_kcontrol *kcontrol,237struct snd_ctl_elem_value *ucontrol)238{239gta02_speaker_enabled = ucontrol->value.integer.value[0];240241gpio_set_value(GTA02_GPIO_HP_IN, !gta02_speaker_enabled);242243return 0;244}245246static int lm4853_get_spk(struct snd_kcontrol *kcontrol,247struct snd_ctl_elem_value *ucontrol)248{249ucontrol->value.integer.value[0] = gta02_speaker_enabled;250return 0;251}252253static int lm4853_event(struct snd_soc_dapm_widget *w,254struct snd_kcontrol *k, int event)255{256gpio_set_value(GTA02_GPIO_AMP_SHUT, SND_SOC_DAPM_EVENT_OFF(event));257258return 0;259}260261static const struct snd_soc_dapm_route neo1973_gta02_routes[] = {262/* Connections to the amp */263{"Stereo Out", NULL, "LOUT1"},264{"Stereo Out", NULL, "ROUT1"},265266/* Call Speaker */267{"Handset Spk", NULL, "LOUT2"},268{"Handset Spk", NULL, "ROUT2"},269};270271static const struct snd_kcontrol_new neo1973_gta02_wm8753_controls[] = {272SOC_DAPM_PIN_SWITCH("Handset Spk"),273SOC_DAPM_PIN_SWITCH("Stereo Out"),274275SOC_SINGLE_BOOL_EXT("Amp Spk Switch", 0,276lm4853_get_spk,277lm4853_set_spk),278};279280static const struct snd_soc_dapm_widget neo1973_gta02_wm8753_dapm_widgets[] = {281SND_SOC_DAPM_SPK("Handset Spk", NULL),282SND_SOC_DAPM_SPK("Stereo Out", lm4853_event),283};284285static int neo1973_gta02_wm8753_init(struct snd_soc_codec *codec)286{287struct snd_soc_dapm_context *dapm = &codec->dapm;288int ret;289290ret = snd_soc_dapm_new_controls(dapm, neo1973_gta02_wm8753_dapm_widgets,291ARRAY_SIZE(neo1973_gta02_wm8753_dapm_widgets));292if (ret)293return ret;294295ret = snd_soc_dapm_add_routes(dapm, neo1973_gta02_routes,296ARRAY_SIZE(neo1973_gta02_routes));297if (ret)298return ret;299300ret = snd_soc_add_controls(codec, neo1973_gta02_wm8753_controls,301ARRAY_SIZE(neo1973_gta02_wm8753_controls));302if (ret)303return ret;304305snd_soc_dapm_disable_pin(dapm, "Stereo Out");306snd_soc_dapm_disable_pin(dapm, "Handset Spk");307snd_soc_dapm_ignore_suspend(dapm, "Stereo Out");308snd_soc_dapm_ignore_suspend(dapm, "Handset Spk");309310return 0;311}312313#else314static int neo1973_gta02_wm8753_init(struct snd_soc_code *codec) { return 0; }315#endif316317static int neo1973_wm8753_init(struct snd_soc_pcm_runtime *rtd)318{319struct snd_soc_codec *codec = rtd->codec;320struct snd_soc_dapm_context *dapm = &codec->dapm;321int ret;322323/* set up NC codec pins */324if (machine_is_neo1973_gta01()) {325snd_soc_dapm_nc_pin(dapm, "LOUT2");326snd_soc_dapm_nc_pin(dapm, "ROUT2");327}328snd_soc_dapm_nc_pin(dapm, "OUT3");329snd_soc_dapm_nc_pin(dapm, "OUT4");330snd_soc_dapm_nc_pin(dapm, "LINE1");331snd_soc_dapm_nc_pin(dapm, "LINE2");332333/* Add neo1973 specific widgets */334ret = snd_soc_dapm_new_controls(dapm, neo1973_wm8753_dapm_widgets,335ARRAY_SIZE(neo1973_wm8753_dapm_widgets));336if (ret)337return ret;338339/* add neo1973 specific controls */340ret = snd_soc_add_controls(codec, neo1973_wm8753_controls,341ARRAY_SIZE(neo1973_wm8753_controls));342if (ret)343return ret;344345/* set up neo1973 specific audio routes */346ret = snd_soc_dapm_add_routes(dapm, neo1973_wm8753_routes,347ARRAY_SIZE(neo1973_wm8753_routes));348if (ret)349return ret;350351/* set endpoints to default off mode */352snd_soc_dapm_disable_pin(dapm, "GSM Line Out");353snd_soc_dapm_disable_pin(dapm, "GSM Line In");354snd_soc_dapm_disable_pin(dapm, "Headset Mic");355snd_soc_dapm_disable_pin(dapm, "Handset Mic");356357/* allow audio paths from the GSM modem to run during suspend */358snd_soc_dapm_ignore_suspend(dapm, "GSM Line Out");359snd_soc_dapm_ignore_suspend(dapm, "GSM Line In");360snd_soc_dapm_ignore_suspend(dapm, "Headset Mic");361snd_soc_dapm_ignore_suspend(dapm, "Handset Mic");362363if (machine_is_neo1973_gta02()) {364ret = neo1973_gta02_wm8753_init(codec);365if (ret)366return ret;367}368369snd_soc_dapm_sync(dapm);370371return 0;372}373374/* GTA01 specific controls */375376#ifdef CONFIG_MACH_NEO1973_GTA01377378static const struct snd_soc_dapm_route neo1973_lm4857_routes[] = {379{"Amp IN", NULL, "ROUT1"},380{"Amp IN", NULL, "LOUT1"},381382{"Handset Spk", NULL, "Amp EP"},383{"Stereo Out", NULL, "Amp LS"},384{"Headphone", NULL, "Amp HP"},385};386387static const struct snd_soc_dapm_widget neo1973_lm4857_dapm_widgets[] = {388SND_SOC_DAPM_SPK("Handset Spk", NULL),389SND_SOC_DAPM_SPK("Stereo Out", NULL),390SND_SOC_DAPM_HP("Headphone", NULL),391};392393static int neo1973_lm4857_init(struct snd_soc_dapm_context *dapm)394{395int ret;396397ret = snd_soc_dapm_new_controls(dapm, neo1973_lm4857_dapm_widgets,398ARRAY_SIZE(neo1973_lm4857_dapm_widgets));399if (ret)400return ret;401402ret = snd_soc_dapm_add_routes(dapm, neo1973_lm4857_routes,403ARRAY_SIZE(neo1973_lm4857_routes));404if (ret)405return ret;406407snd_soc_dapm_ignore_suspend(dapm, "Stereo Out");408snd_soc_dapm_ignore_suspend(dapm, "Handset Spk");409snd_soc_dapm_ignore_suspend(dapm, "Headphone");410411snd_soc_dapm_sync(dapm);412413return 0;414}415416#else417static int neo1973_lm4857_init(struct snd_soc_dapm_context *dapm) { return 0; };418#endif419420static struct snd_soc_dai_link neo1973_dai[] = {421{ /* Hifi Playback - for similatious use with voice below */422.name = "WM8753",423.stream_name = "WM8753 HiFi",424.platform_name = "samsung-audio",425.cpu_dai_name = "s3c24xx-iis",426.codec_dai_name = "wm8753-hifi",427.codec_name = "wm8753-codec.0-001a",428.init = neo1973_wm8753_init,429.ops = &neo1973_hifi_ops,430},431{ /* Voice via BT */432.name = "Bluetooth",433.stream_name = "Voice",434.cpu_dai_name = "dfbmcs320-pcm",435.codec_dai_name = "wm8753-voice",436.codec_name = "wm8753-codec.0-001a",437.ops = &neo1973_voice_ops,438},439};440441static struct snd_soc_aux_dev neo1973_aux_devs[] = {442{443.name = "dfbmcs320",444.codec_name = "dfbmcs320.0",445},446{447.name = "lm4857",448.codec_name = "lm4857.0-007c",449.init = neo1973_lm4857_init,450},451};452453static struct snd_soc_codec_conf neo1973_codec_conf[] = {454{455.dev_name = "lm4857.0-007c",456.name_prefix = "Amp",457},458};459460#ifdef CONFIG_MACH_NEO1973_GTA02461static const struct gpio neo1973_gta02_gpios[] = {462{ GTA02_GPIO_HP_IN, GPIOF_OUT_INIT_HIGH, "GTA02_HP_IN" },463{ GTA02_GPIO_AMP_SHUT, GPIOF_OUT_INIT_HIGH, "GTA02_AMP_SHUT" },464};465#else466static const struct gpio neo1973_gta02_gpios[] = {};467#endif468469static struct snd_soc_card neo1973 = {470.name = "neo1973",471.dai_link = neo1973_dai,472.num_links = ARRAY_SIZE(neo1973_dai),473.aux_dev = neo1973_aux_devs,474.num_aux_devs = ARRAY_SIZE(neo1973_aux_devs),475.codec_conf = neo1973_codec_conf,476.num_configs = ARRAY_SIZE(neo1973_codec_conf),477};478479static struct platform_device *neo1973_snd_device;480481static int __init neo1973_init(void)482{483int ret;484485if (!machine_is_neo1973_gta01() && !machine_is_neo1973_gta02())486return -ENODEV;487488if (machine_is_neo1973_gta02()) {489neo1973.name = "neo1973gta02";490neo1973.num_aux_devs = 1;491492ret = gpio_request_array(neo1973_gta02_gpios,493ARRAY_SIZE(neo1973_gta02_gpios));494if (ret)495return ret;496}497498neo1973_snd_device = platform_device_alloc("soc-audio", -1);499if (!neo1973_snd_device) {500ret = -ENOMEM;501goto err_gpio_free;502}503504platform_set_drvdata(neo1973_snd_device, &neo1973);505ret = platform_device_add(neo1973_snd_device);506507if (ret)508goto err_put_device;509510return 0;511512err_put_device:513platform_device_put(neo1973_snd_device);514err_gpio_free:515if (machine_is_neo1973_gta02()) {516gpio_free_array(neo1973_gta02_gpios,517ARRAY_SIZE(neo1973_gta02_gpios));518}519return ret;520}521module_init(neo1973_init);522523static void __exit neo1973_exit(void)524{525platform_device_unregister(neo1973_snd_device);526527if (machine_is_neo1973_gta02()) {528gpio_free_array(neo1973_gta02_gpios,529ARRAY_SIZE(neo1973_gta02_gpios));530}531}532module_exit(neo1973_exit);533534/* Module information */535MODULE_AUTHOR("Graeme Gregory, [email protected], www.openmoko.org");536MODULE_DESCRIPTION("ALSA SoC WM8753 Neo1973 and Frerunner");537MODULE_LICENSE("GPL");538539540