blob: 5bbe6487d1508ef9e1c3809d7e6725f2e86fcf96 [file] [log] [blame]
/*
* sound/soc/PJ203.c
*
* Author: Diao Chengdong <cddiao@ambarella.com>
*
* History:
* 2012/05/17 - [Diao Chengdong] Created file
*
* 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/module.h>
#include <linux/moduleparam.h>
#include <linux/io.h>
#include <linux/platform_device.h>
#include <sound/soc.h>
#include <sound/jack.h>
#include <asm/mach-types.h>
#include <mach/gpio.h>
#include <plat/audio.h>
#include "ambarella_i2s.h"
#include "../codecs/tlv320dac31.h"
#define debug_pj203 (0)
#if debug_pj203
#define PRINTK(arg...) printk(arg)
#else
#define PRINTK(arg...)
#endif
/* havn't test the headphone ,so set gpio_jack = 0*/
#define GPIO_JACK 0
#if GPIO_JACK
module_param(gpio_jack,uint,S_IRUGO);
MODULE_PARM_DESC(gpio_jack,"Whether or not use a head phone.");
/* Headset jack */
static struct snd_soc_jack hs_jack;
/* Headset jack detection DAPM pins */
static struct snd_soc_jack_pin hs_jack_pins[]={
{
.pin = "Headset Mic",
.mask = SND_JACK_MICROPHONE,
},
{
.pin = "Headset Stereophone",
.mask = SND_JACK_HEADPHONE,
},
};
/* Headset jack detection gpios */
static struct snd_soc_jack_gpio hs_jack_gpios[]={
{
.gpio = GPIO(82),
.name = "hsdet-gpio",
.report = SND_JACK_HEADSET,
.debounce_time = 200,
},
};
#endif
static const struct snd_soc_dapm_widget PJ203_dapm_widgets[] = {
/*Output*/
SND_SOC_DAPM_SPK("Main Spk", NULL),
#if GPIO_JACK
SND_SOC_DAPM_HP("Headphone Jack",NULL),
#endif
};
static const struct snd_soc_dapm_route PJ203_dapm_routes[] = {
#if GPIO_JACK
{"Headphone Jack",NULL,"HPL"},
{"Headphone Jack",NULL,"HPR"},
#endif
{"Main Spk",NULL,"LOL"},
{"Main Spk",NULL,"LOL"},
};
static int PJ203_tlv320_init(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_codec *codec = rtd->codec;
struct snd_soc_dapm_context *dapm = &codec->dapm;
PRINTK("in tlv320_init\n");
snd_soc_dapm_new_controls(dapm,PJ203_dapm_widgets,
ARRAY_SIZE(PJ203_dapm_widgets));
snd_soc_dapm_add_routes(dapm,PJ203_dapm_routes,
ARRAY_SIZE(PJ203_dapm_routes));
snd_soc_dapm_sync(dapm);
ambarella_i2s_add_controls(codec);
#if GPIO_JACK
{
int errcode=0;
errcode = snd_soc_jack_new(codec,"Headset Jack",SND_JACK_HEADSET,&hs_jack);
if (errcode)
return errcode;
errcode = snd_soc_jack_add_pins(&hs_jack,ARRAY_SIZE(hs_jack_pins),hs_jack_pins);
if (errcode)
return errcode;
errcode = snd_soc_jack_add_gpios(&hs_jack,ARRAY_SIZE(hs_jack_gpios),hs_jack_gpios);
if (errcode)
return errcode;
}
#endif
return 0;
}
static int PJ203_board_startup(struct snd_pcm_substream *substream)
{
PRINTK("in PJ203_board-statup\n");
return 0;
}
static int PJ203_hifi_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;
PRINTK("in PJ203_hifi_hw_params\n");
switch (params_rate(params)) {
case 8000:
amb_mclk = AudioCodec_4_096M;
mclk = 4096000;
oversample = AudioCodec_512xfs;
break;
case 11025:
amb_mclk = AudioCodec_5_6448M;
mclk = 5644800;
oversample = AudioCodec_512xfs;
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 hifi_hw_params_exit;
}
errorCode = snd_soc_dai_set_fmt(codec_dai,
SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
if (errorCode < 0) {
pr_err("can't set codec DAI configuration\n");
goto hifi_hw_params_exit;
}
errorCode = snd_soc_dai_set_fmt(cpu_dai,
SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
if (errorCode < 0) {
pr_err("can't set cpu DAI configuration\n");
goto hifi_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 hifi_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 hifi_hw_params_exit;
}
errorCode = snd_soc_dai_set_sysclk(codec_dai, 1, mclk, 0);
if (errorCode < 0) {
pr_err("can't set cpu MCLK configuration\n");
goto hifi_hw_params_exit;
}
hifi_hw_params_exit:
return errorCode;
}
static struct snd_soc_ops PJ203_hifi_ops = {
.startup = PJ203_board_startup,
.hw_params = PJ203_hifi_hw_params,
};
static struct snd_soc_dai_link PJ203_dai_link[] = {
{
.name = "dac31amb",
.stream_name = "TLV320 HiFi",
.cpu_dai_name = "ambarella-i2s.0",
.codec_dai_name = "tlv320dac31",
.platform_name = "ambarella-pcm-audio",
.codec_name = "tlv320dac31-codec.0-0018",
.init = PJ203_tlv320_init,
.ops = &PJ203_hifi_ops,
}
};
static struct snd_soc_card snd_soc_card_PJ203 = {
.name = "PJ203",
.dai_link = PJ203_dai_link,
.num_links = 1,
};
static struct platform_device *PJ203_snd_device;
static int __init PJ203_board_init(void)
{
int errCode = 0;
PJ203_snd_device = platform_device_alloc("soc-audio",-1);
if (!PJ203_snd_device) {
pr_err("failed in device alloc \n");
return -ENOMEM;
}
platform_set_drvdata(PJ203_snd_device,&snd_soc_card_PJ203);
errCode = platform_device_add(PJ203_snd_device);
if (errCode)
goto PJ203_board_init_exit;
PRINTK("success in PJ203_board_init\n");
return 0;
PJ203_board_init_exit:
platform_device_put(PJ203_snd_device);
return errCode;
}
static void __exit PJ203_board_exit(void)
{
#if GPIO_JACK
snd_soc_jack_free_gpios(&hs_jack,ARRAY_SIZE(hs_jack_gpios),hs_jack_gpios);
#endif
platform_device_unregister(PJ203_snd_device);
}
module_init(PJ203_board_init);
module_exit(PJ203_board_exit);
MODULE_AUTHOR("Diao Chengdong <cddiao@ambarella.com>");
MODULE_DESCRIPTION("Ambarella PJ203 Board with tlv320DAC3100 Codec for ALSA");
MODULE_LICENSE("GPL");