blob: aecaf37ddafd7fdc068c31e5f073170a81d3748a [file] [log] [blame]
/*
* sound/soc/dropcam.c
*
* Author: Cao Rongrong <rrcao@ambarella.com>
*
* History:
* 2009/08/20 - [Cao Rongrong] Created file
* 2011/03/20 - [Cao Rongrong] Port to 2.6.38,
* merge coconut and durian into dropcam
*
* Copyright (C) 2004-2009, Ambarella, Inc.
*
* 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.
*
* 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
*
*/
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include <mach/hardware.h>
#include <plat/audio.h>
#include "../codecs/wm8974_amb.h"
static unsigned int dai_fmt = 0;
module_param(dai_fmt, uint, 0644);
MODULE_PARM_DESC(dai_fmt, "DAI format.");
static int dropcam_board_startup(struct snd_pcm_substream *substream)
{
return 0;
}
static int dropcam_board_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;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
int errorCode = 0, amb_mclk, mclk, oversample, i2s_mode;
switch (params_rate(params)) {
case 8000:
amb_mclk = AudioCodec_2_048M;
mclk = 2048000;
oversample = AudioCodec_256xfs;
break;
case 11025:
amb_mclk = AudioCodec_2_8224M;
mclk = 2822400;
oversample = AudioCodec_256xfs;
break;
case 12000:
amb_mclk = AudioCodec_3_072M;
mclk = 3072000;
oversample = AudioCodec_256xfs;
break;
case 16000:
amb_mclk = AudioCodec_4_096M;
mclk = 4096000;
oversample = AudioCodec_256xfs;
break;
case 22050:
amb_mclk = AudioCodec_5_6448M;
mclk = 5644800;
oversample = AudioCodec_256xfs;
break;
case 24000:
amb_mclk = AudioCodec_6_144;
mclk = 6144000;
oversample = AudioCodec_256xfs;
break;
case 32000:
amb_mclk = AudioCodec_8_192M;
mclk = 8192000;
oversample = AudioCodec_256xfs;
break;
case 44100:
amb_mclk = AudioCodec_11_2896M;
mclk = 11289600;
oversample = AudioCodec_256xfs;
break;
case 48000:
amb_mclk = AudioCodec_12_288M;
mclk = 12288000;
oversample = AudioCodec_256xfs;
break;
default:
errorCode = -EINVAL;
goto hw_params_exit;
}
if (dai_fmt == 0)
i2s_mode = SND_SOC_DAIFMT_I2S;
else
i2s_mode = SND_SOC_DAIFMT_DSP_A;
/* set the I2S system data format*/
errorCode = snd_soc_dai_set_fmt(codec_dai,
i2s_mode | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
if (errorCode < 0) {
pr_err("can't set codec DAI configuration\n");
goto hw_params_exit;
}
errorCode = snd_soc_dai_set_fmt(cpu_dai,
i2s_mode | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
if (errorCode < 0) {
pr_err("can't set cpu DAI configuration\n");
goto hw_params_exit;
}
/* set the I2S system clock*/
errorCode = snd_soc_dai_set_sysclk(codec_dai, WM8974_SYSCLK, mclk, 0);
if (errorCode < 0) {
pr_err("can't set cpu MCLK configuration\n");
goto hw_params_exit;
}
errorCode = snd_soc_dai_set_sysclk(cpu_dai, AMBARELLA_CLKSRC_ONCHIP, amb_mclk, 0);
if (errorCode < 0) {
pr_err("can't set cpu MCLK configuration\n");
goto hw_params_exit;
}
errorCode = snd_soc_dai_set_clkdiv(cpu_dai, AMBARELLA_CLKDIV_LRCLK, oversample);
if (errorCode < 0) {
pr_err("can't set cpu MCLK/SF ratio\n");
goto hw_params_exit;
}
hw_params_exit:
return errorCode;
}
static struct snd_soc_ops dropcam_board_ops = {
.startup = dropcam_board_startup,
.hw_params = dropcam_board_hw_params,
};
/* dropcam machine dapm widgets */
static const struct snd_soc_dapm_widget dropcam_dapm_widgets[] = {
SND_SOC_DAPM_MIC("Mic External", NULL),
SND_SOC_DAPM_SPK("Speaker", NULL),
};
/* dropcam machine audio map (connections to wm8974 pins) */
static const struct snd_soc_dapm_route dropcam_audio_map[] = {
/*Mic External is connected to MICN , MICP, AUX*/
{"MICN", NULL, "Mic External"},
{"MICP", NULL, "Mic External"},
{"AUX", NULL, "Mic External"},
/* Speaker is connected to SPP, SPN */
{"Speaker", NULL, "SPKOUTP"},
{"Speaker", NULL, "SPKOUTN"},
{"Speaker", NULL, "MONOOUT"},
};
static int dropcam_wm8974_init(struct snd_soc_pcm_runtime *rtd)
{
int errorCode = 0;
struct snd_soc_codec *codec = rtd->codec;
struct snd_soc_dapm_context *dapm = &codec->dapm;
/* not connected */
//snd_soc_dapm_nc_pin(dapm, "MONOOUT");
//snd_soc_dapm_nc_pin(dapm, "AUX");
/* Add dropcam specific widgets */
errorCode = snd_soc_dapm_new_controls(dapm,
dropcam_dapm_widgets,
ARRAY_SIZE(dropcam_dapm_widgets));
if (errorCode) {
goto init_exit;
}
/* Set up dropcam specific audio path dropcam_audio_map */
errorCode = snd_soc_dapm_add_routes(dapm,
dropcam_audio_map,
ARRAY_SIZE(dropcam_audio_map));
if (errorCode) {
goto init_exit;
}
errorCode = snd_soc_dapm_sync(dapm);
init_exit:
return errorCode;
}
/* dropcam digital audio interface glue - connects codec <--> A2S */
static struct snd_soc_dai_link dropcam_dai_link = {
.name = "WM8974",
.stream_name = "WM8974-STREAM",
.cpu_dai_name = "ambarella-i2s.0",
.platform_name = "ambarella-pcm-audio",
.codec_dai_name = "wm8974-hifi",
.codec_name = "wm8974-codec.0-001a",
.init = dropcam_wm8974_init,
.ops = &dropcam_board_ops,
};
/* dropcam audio machine driver */
static struct snd_soc_card snd_soc_card_dropcam = {
.name = "DROPCAM",
.dai_link = &dropcam_dai_link,
.num_links = 1,
};
static struct platform_device *dropcam_snd_device;
static int __init dropcam_board_init(void)
{
int errorCode = 0;
dropcam_snd_device = platform_device_alloc("soc-audio", -1);
if (!dropcam_snd_device)
return -ENOMEM;
platform_set_drvdata(dropcam_snd_device, &snd_soc_card_dropcam);
errorCode = platform_device_add(dropcam_snd_device);
if (errorCode) {
goto dropcam_board_init_exit;
}
return 0;
dropcam_board_init_exit:
platform_device_put(dropcam_snd_device);
return errorCode;
}
static void __exit dropcam_board_exit(void)
{
platform_device_unregister(dropcam_snd_device);
}
module_init(dropcam_board_init);
module_exit(dropcam_board_exit);
MODULE_AUTHOR("Cao Rongrong <rrcao@ambarella.com>");
MODULE_DESCRIPTION("Dropcam Crown Royal Board with WM8974 Codec for ALSA");
MODULE_LICENSE("GPL");