Path: blob/master/sound/soc/atmel/sam9g20_wm8731.c
10817 views
/*1* sam9g20_wm8731 -- SoC audio for AT91SAM9G20-based2* ATMEL AT91SAM9G20ek board.3*4* Copyright (C) 2005 SAN People5* Copyright (C) 2008 Atmel6*7* Authors: Sedji Gaouaou <[email protected]>8*9* Based on ati_b1_wm8731.c by:10* Frank Mandarino <[email protected]>11* Copyright 2006 Endrelia Technologies Inc.12* Based on corgi.c by:13* Copyright 2005 Wolfson Microelectronics PLC.14* Copyright 2005 Openedhand Ltd.15*16* This program is free software; you can redistribute it and/or modify17* it under the terms of the GNU General Public License as published by18* the Free Software Foundation; either version 2 of the License, or19* (at your option) any later version.20*21* This program is distributed in the hope that it will be useful,22* but WITHOUT ANY WARRANTY; without even the implied warranty of23* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the24* GNU General Public License for more details.25*26* You should have received a copy of the GNU General Public License27* along with this program; if not, write to the Free Software28* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA29*/3031#include <linux/module.h>32#include <linux/moduleparam.h>33#include <linux/kernel.h>34#include <linux/clk.h>35#include <linux/timer.h>36#include <linux/interrupt.h>37#include <linux/platform_device.h>38#include <linux/i2c.h>3940#include <linux/atmel-ssc.h>4142#include <sound/core.h>43#include <sound/pcm.h>44#include <sound/pcm_params.h>45#include <sound/soc.h>4647#include <asm/mach-types.h>48#include <mach/hardware.h>49#include <mach/gpio.h>5051#include "../codecs/wm8731.h"52#include "atmel-pcm.h"53#include "atmel_ssc_dai.h"5455#define MCLK_RATE 120000005657/*58* As shipped the board does not have inputs. However, it is relatively59* straightforward to modify the board to hook them up so support is left60* in the driver.61*/62#undef ENABLE_MIC_INPUT6364static struct clk *mclk;6566static int at91sam9g20ek_hw_params(struct snd_pcm_substream *substream,67struct snd_pcm_hw_params *params)68{69struct snd_soc_pcm_runtime *rtd = substream->private_data;70struct snd_soc_dai *codec_dai = rtd->codec_dai;71struct snd_soc_dai *cpu_dai = rtd->cpu_dai;72int ret;7374/* set codec DAI configuration */75ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |76SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);77if (ret < 0)78return ret;7980/* set cpu DAI configuration */81ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |82SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);83if (ret < 0)84return ret;8586return 0;87}8889static struct snd_soc_ops at91sam9g20ek_ops = {90.hw_params = at91sam9g20ek_hw_params,91};9293static int at91sam9g20ek_set_bias_level(struct snd_soc_card *card,94enum snd_soc_bias_level level)95{96static int mclk_on;97int ret = 0;9899switch (level) {100case SND_SOC_BIAS_ON:101case SND_SOC_BIAS_PREPARE:102if (!mclk_on)103ret = clk_enable(mclk);104if (ret == 0)105mclk_on = 1;106break;107108case SND_SOC_BIAS_OFF:109case SND_SOC_BIAS_STANDBY:110if (mclk_on)111clk_disable(mclk);112mclk_on = 0;113break;114}115116return ret;117}118119static const struct snd_soc_dapm_widget at91sam9g20ek_dapm_widgets[] = {120SND_SOC_DAPM_MIC("Int Mic", NULL),121SND_SOC_DAPM_SPK("Ext Spk", NULL),122};123124static const struct snd_soc_dapm_route intercon[] = {125126/* speaker connected to LHPOUT */127{"Ext Spk", NULL, "LHPOUT"},128129/* mic is connected to Mic Jack, with WM8731 Mic Bias */130{"MICIN", NULL, "Mic Bias"},131{"Mic Bias", NULL, "Int Mic"},132};133134/*135* Logic for a wm8731 as connected on a at91sam9g20ek board.136*/137static int at91sam9g20ek_wm8731_init(struct snd_soc_pcm_runtime *rtd)138{139struct snd_soc_codec *codec = rtd->codec;140struct snd_soc_dai *codec_dai = rtd->codec_dai;141struct snd_soc_dapm_context *dapm = &codec->dapm;142int ret;143144printk(KERN_DEBUG145"at91sam9g20ek_wm8731 "146": at91sam9g20ek_wm8731_init() called\n");147148ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK_MCLK,149MCLK_RATE, SND_SOC_CLOCK_IN);150if (ret < 0) {151printk(KERN_ERR "Failed to set WM8731 SYSCLK: %d\n", ret);152return ret;153}154155/* Add specific widgets */156snd_soc_dapm_new_controls(dapm, at91sam9g20ek_dapm_widgets,157ARRAY_SIZE(at91sam9g20ek_dapm_widgets));158/* Set up specific audio path interconnects */159snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon));160161/* not connected */162snd_soc_dapm_nc_pin(dapm, "RLINEIN");163snd_soc_dapm_nc_pin(dapm, "LLINEIN");164165#ifdef ENABLE_MIC_INPUT166snd_soc_dapm_enable_pin(dapm, "Int Mic");167#else168snd_soc_dapm_nc_pin(dapm, "Int Mic");169#endif170171/* always connected */172snd_soc_dapm_enable_pin(dapm, "Ext Spk");173174snd_soc_dapm_sync(dapm);175176return 0;177}178179static struct snd_soc_dai_link at91sam9g20ek_dai = {180.name = "WM8731",181.stream_name = "WM8731 PCM",182.cpu_dai_name = "atmel-ssc-dai.0",183.codec_dai_name = "wm8731-hifi",184.init = at91sam9g20ek_wm8731_init,185.platform_name = "atmel-pcm-audio",186.codec_name = "wm8731.0-001b",187.ops = &at91sam9g20ek_ops,188};189190static struct snd_soc_card snd_soc_at91sam9g20ek = {191.name = "AT91SAMG20-EK",192.dai_link = &at91sam9g20ek_dai,193.num_links = 1,194.set_bias_level = at91sam9g20ek_set_bias_level,195};196197static struct platform_device *at91sam9g20ek_snd_device;198199static int __init at91sam9g20ek_init(void)200{201struct clk *pllb;202int ret;203204if (!(machine_is_at91sam9g20ek() || machine_is_at91sam9g20ek_2mmc()))205return -ENODEV;206207ret = atmel_ssc_set_audio(0);208if (ret != 0) {209pr_err("Failed to set SSC 0 for audio: %d\n", ret);210return ret;211}212213/*214* Codec MCLK is supplied by PCK0 - set it up.215*/216mclk = clk_get(NULL, "pck0");217if (IS_ERR(mclk)) {218printk(KERN_ERR "ASoC: Failed to get MCLK\n");219ret = PTR_ERR(mclk);220goto err;221}222223pllb = clk_get(NULL, "pllb");224if (IS_ERR(pllb)) {225printk(KERN_ERR "ASoC: Failed to get PLLB\n");226ret = PTR_ERR(pllb);227goto err_mclk;228}229ret = clk_set_parent(mclk, pllb);230clk_put(pllb);231if (ret != 0) {232printk(KERN_ERR "ASoC: Failed to set MCLK parent\n");233goto err_mclk;234}235236clk_set_rate(mclk, MCLK_RATE);237238at91sam9g20ek_snd_device = platform_device_alloc("soc-audio", -1);239if (!at91sam9g20ek_snd_device) {240printk(KERN_ERR "ASoC: Platform device allocation failed\n");241ret = -ENOMEM;242goto err_mclk;243}244245platform_set_drvdata(at91sam9g20ek_snd_device,246&snd_soc_at91sam9g20ek);247248ret = platform_device_add(at91sam9g20ek_snd_device);249if (ret) {250printk(KERN_ERR "ASoC: Platform device allocation failed\n");251goto err_device_add;252}253254return ret;255256err_device_add:257platform_device_put(at91sam9g20ek_snd_device);258err_mclk:259clk_put(mclk);260mclk = NULL;261err:262return ret;263}264265static void __exit at91sam9g20ek_exit(void)266{267platform_device_unregister(at91sam9g20ek_snd_device);268at91sam9g20ek_snd_device = NULL;269clk_put(mclk);270mclk = NULL;271}272273module_init(at91sam9g20ek_init);274module_exit(at91sam9g20ek_exit);275276/* Module information */277MODULE_AUTHOR("Sedji Gaouaou <[email protected]>");278MODULE_DESCRIPTION("ALSA SoC AT91SAM9G20EK_WM8731");279MODULE_LICENSE("GPL");280281282