blob: 6f45b5966d8073be4a830507069efd0908a39242 [file] [log] [blame]
/*
*
* Marvell Orion Alsa SOC Sound driver
*
* Author: Yuval Elmaliah
* Author: Ethan Ku
* Copyright (C) 2008 Marvell Ltd.
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/device.h>
#include <linux/ioport.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/clk.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include <sound/asoundef.h>
#include <asm/mach-types.h>
//#include <asm/arch/hardware.h>
#include "../sound/soc/codecs/rt5623.h"
#include <linux/mbus.h>
#include <asm/setup.h>
#include <asm/mach/arch.h>
#include <../arch/arm/mach-dove/common.h>
//#include <asm/plat-orion/i2s-orion.h>
#include "mv88fx-pcm.h"
#include "mv88fx-i2s.h"
#include "ctrlEnv/mvCtrlEnvSpec.h"
#include "audio/mvAudioRegs.h"
extern int mvmpp_gpio_get_value(unsigned int);
extern int mvmpp_gpio_set_value(unsigned int,int);
static struct mv88fx_snd_machine_data mv88fx_machine_data;
static struct rt5623_setup_data rt5623_setup;
static int mv88fx_machine_startup(struct snd_pcm_substream *substream)
{
// struct snd_soc_pcm_runtime *rtd = substream->private_data;
// struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
// struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
// int ret;
#if 0
int phoneInDetected;
// printk("%s\n",__func__);
/* check the jack status at stream startup */
phoneInDetected = mvmpp_gpio_get_value(MPP_PhLine_IN);
if (phoneInDetected < 0) {
snd_printk("Failed to detect phone-in.\n");
} else {
if (! phoneInDetected) {
rt5623_setup.mic2_input = 0;
} else {
rt5623_setup.mic2_input = 1;
}
}
#endif
return 0;
}
static void mv88fx_machine_shutdown(struct snd_pcm_substream *substream)
{
// printk("%s\n",__func__);
}
static int mv88fx_machine_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 clk = 0;
int ret = 0;
unsigned int format;
// printk("%s\n",__func__);
/* set codec DAI configuration */
switch (params_rate(params)) {
case 44100:
clk = 11289600;
break;
case 48000:
clk = 12288000;
break;
case 96000:
clk = 24576000;
break;
}
format = SND_SOC_DAIFMT_CBS_CFS | SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF;
ret = codec_dai->ops->set_fmt(codec_dai, format);
if (ret < 0)
return ret;
/* set cpu DAI configuration */
ret = cpu_dai->ops->set_fmt(cpu_dai, format);
if (ret < 0)
return ret;
/* cpu clock is the mv88fx master clock sent to codec */
ret = cpu_dai->ops->set_sysclk(cpu_dai, 0, 0, SND_SOC_CLOCK_IN);
if (ret < 0)
return ret;
/* codec system clock is supplied by mv88fx*/
ret = codec_dai->ops->set_sysclk(codec_dai, 0, clk, SND_SOC_CLOCK_IN);
if (ret < 0)
return ret;
return 0;
}
static int mv88fx_machine_trigger(struct snd_pcm_substream *substream,int cmd)
{
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
if(substream->stream==SNDRV_PCM_STREAM_PLAYBACK)
{
// mvmpp_gpio_set_value(MPP_Amp_PwrDn,1);
}
break;
case SNDRV_PCM_TRIGGER_STOP:
if(substream->stream==SNDRV_PCM_STREAM_PLAYBACK)
{
// mvmpp_gpio_set_value(MPP_Amp_PwrDn,0);
}
break;
}
return 0;
}
static int mv88fx_rt5623_init(struct snd_soc_codec *codec)
{
mv88fx_snd_debug("");
// mvmpp_gpio_set_value(MPP_Amp_PwrDn,0);
return 0;
}
/* machine stream operations */
static struct snd_soc_ops mv88fx_rt5623_machine_ops =
{
.startup = mv88fx_machine_startup,
.shutdown = mv88fx_machine_shutdown,
.hw_params = mv88fx_machine_hw_params,
.trigger = mv88fx_machine_trigger,
};
static struct snd_soc_dai_link mv88fx_dai = {
.name = "RT5623",
.stream_name = "RT5623",
.cpu_dai = &mv88fx_i2s_dai,
.codec_dai = &rt5623_dai,
.ops = &mv88fx_rt5623_machine_ops,
.init = mv88fx_rt5623_init,
};
static int mv88fx_probe(struct platform_device *pdev)
{
return 0;
}
static int mv88fx_remove(struct platform_device *pdev)
{
return 0;
}
static struct snd_soc_card dove = {
.name = "Dove",
.platform = &mv88fx_soc_platform,
.probe = mv88fx_probe,
.remove = mv88fx_remove,
/* CPU <--> Codec DAI links */
.dai_link = &mv88fx_dai,
.num_links = 1,
};
static struct rt5623_setup_data rt5623_setup = {
.i2c_address = 0x1a,
.mic2_input = 1,
};
static struct snd_soc_device mv88fx_snd_devdata = {
.card = &dove,
.codec_dev = &soc_codec_dev_rt5623,
.codec_data = &rt5623_setup,
};
static int mv88fx_initalize_machine_data(struct platform_device *pdev)
{
struct resource *r = NULL;
int err = 0;
mv88fx_snd_debug("");
mv88fx_machine_data.port = pdev->id;
mv88fx_machine_data.pdata =
(struct orion_i2s_platform_data *)pdev->dev.platform_data;
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!r) {
err = -ENXIO;
goto error;
}
r = request_mem_region(r->start, SZ_16K, DRIVER_NAME);
if (!r) {
err = -EBUSY;
goto error;
}
mv88fx_machine_data.res = r;
mv88fx_machine_data.base = ioremap(r->start, SZ_16K);
if (!mv88fx_machine_data.base) {
mv88fx_snd_error("ioremap failed");
err = -ENOMEM;
goto error;
}
mv88fx_machine_data.base -= MV_AUDIO_REGS_OFFSET(mv88fx_machine_data.port);
mv88fx_machine_data.irq = platform_get_irq(pdev, 0);
if (mv88fx_machine_data.irq == NO_IRQ) {
err = -ENXIO;
goto error;
}
#if defined(CONFIG_HAVE_CLK)
mv88fx_machine_data.clk = clk_get(&pdev->dev, NULL);
if (IS_ERR(mv88fx_machine_data.clk))
dev_notice(&pdev->dev, "cannot get clkdev\n");
else
clk_enable(mv88fx_machine_data.clk);
#endif
return 0;
error:
if (mv88fx_machine_data.base) {
iounmap(mv88fx_machine_data.base);
mv88fx_machine_data.base = NULL;
}
release_mem_region(mv88fx_machine_data.res->start, SZ_16K);
return err;
}
static int mv88fx_snd_probe(struct platform_device *pdev)
{
int ret = 0;
mv88fx_snd_debug("");
if (mv88fx_initalize_machine_data(pdev) != 0)
goto error;
mv88fx_machine_data.snd_dev = platform_device_alloc("soc-audio", -1);
if (!mv88fx_machine_data.snd_dev) {
ret = -ENOMEM;
goto error;
}
platform_set_drvdata(mv88fx_machine_data.snd_dev, &mv88fx_snd_devdata);
mv88fx_snd_devdata.dev = &mv88fx_machine_data.snd_dev->dev;
mv88fx_machine_data.snd_dev->dev.platform_data = &mv88fx_machine_data;
ret = platform_device_add(mv88fx_machine_data.snd_dev);
if(ret)
{
platform_device_put(mv88fx_machine_data.snd_dev);
}
return ret;
error:
mv88fx_snd_error("");
#if defined(CONFIG_HAVE_CLK)
if (!IS_ERR(mv88fx_machine_data.clk)) {
clk_disable(mv88fx_machine_data.clk);
clk_put(mv88fx_machine_data.clk);
}
#endif
if (mv88fx_machine_data.snd_dev)
platform_device_unregister(mv88fx_machine_data.snd_dev);
return ret;
}
static int mv88fx_snd_remove(struct platform_device *dev)
{
mv88fx_snd_debug("");
#if defined(CONFIG_HAVE_CLK)
if (!IS_ERR(mv88fx_machine_data.clk)) {
clk_disable(mv88fx_machine_data.clk);
clk_put(mv88fx_machine_data.clk);
}
#endif
mv88fx_machine_data.snd_dev->dev.platform_data = NULL;
platform_device_unregister(mv88fx_machine_data.snd_dev);
release_mem_region(mv88fx_machine_data.res->start, SZ_16K);
return 0;
}
static struct platform_driver mv88fx_snd_driver = {
.probe = mv88fx_snd_probe,
.remove = mv88fx_snd_remove,
.suspend = NULL,
.resume = NULL,
.driver = {
.name = DRIVER_NAME,
},
};
static int __init mv88fx_snd_init(void)
{
if (!machine_is_dove_rd_avng())
return -ENODEV;
mv88fx_snd_debug("");
return platform_driver_register(&mv88fx_snd_driver);
}
static void __exit mv88fx_snd_exit(void)
{
mv88fx_snd_debug("");
platform_driver_unregister(&mv88fx_snd_driver);
}
module_init(mv88fx_snd_init);
module_exit(mv88fx_snd_exit);
/* Module information */
MODULE_AUTHOR("Yuval Elmaliah <eyuval@marvell.com>");
MODULE_AUTHOR("Ethan Ku <eku@marvell.com>");
MODULE_DESCRIPTION("ALSA SoC Dove");
MODULE_LICENSE("GPL");