add wm8988 driver for kernel3.0

This commit is contained in:
陈金泉
2011-11-21 20:46:27 +08:00
parent 28ddf9aca0
commit 13f630d8dd
3 changed files with 193 additions and 63 deletions

View File

@@ -1418,7 +1418,7 @@ static struct i2c_board_info __initdata board_i2c0_devices[] = {
.flags = 0,
},
#endif
#if defined (CONFIG_SND_SOC_alc5631)
#if defined (CONFIG_SND_SOC_RT5631)
{
.type = "rt5631",
.addr = 0x1a,
@@ -1432,6 +1432,13 @@ static struct i2c_board_info __initdata board_i2c0_devices[] = {
.flags = 0,
},
#endif
#if defined (CONFIG_SND_SOC_WM8988)
{
.type = "wm8988",
.addr = 0x1A,
.flags = 0,
},
#endif
#if defined (CONFIG_SND_SOC_WM8900)
{
.type = "wm8900",

View File

@@ -25,10 +25,23 @@
#include <sound/pcm_params.h>
#include <sound/tlv.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include <sound/initval.h>
#include <mach/iomux.h>
#include <mach/gpio.h>
#include "wm8988.h"
#include <linux/proc_fs.h>
#include <linux/gpio.h>
#if 0
#define DBG(x...) printk(KERN_INFO x)
#else
#define DBG(x...) do { } while (0)
#endif
/*
* wm8988 register cache
* We can't read the WM8988 register space when we
@@ -53,6 +66,8 @@ struct wm8988_priv {
unsigned int sysclk;
enum snd_soc_control_type control_type;
struct snd_pcm_hw_constraint_list *sysclk_constraints;
int is_startup; // gModify.Add
int is_biason;
};
@@ -190,6 +205,8 @@ static int wm8988_lrc_control(struct snd_soc_dapm_widget *w,
else
adctl2 |= 0x4;
DBG("Enter::%s----%d, adctl2 = %x\n",__FUNCTION__,__LINE__,adctl2);
return snd_soc_write(codec, WM8988_ADCTL2, adctl2);
}
@@ -290,8 +307,9 @@ static const struct snd_soc_dapm_widget wm8988_dapm_widgets[] = {
SND_SOC_DAPM_ADC("Right ADC", "Right Capture", WM8988_PWR1, 2, 0),
SND_SOC_DAPM_ADC("Left ADC", "Left Capture", WM8988_PWR1, 3, 0),
SND_SOC_DAPM_DAC("Right DAC", "Right Playback", WM8988_PWR2, 7, 0),
SND_SOC_DAPM_DAC("Left DAC", "Left Playback", WM8988_PWR2, 8, 0),
/* gModify.Cmmt Implement when suspend/startup */
/*SND_SOC_DAPM_DAC("Right DAC", "Right Playback", WM8988_PWR2, 7, 0),*/
/*SND_SOC_DAPM_DAC("Left DAC", "Left Playback", WM8988_PWR2, 8, 0),*/
SND_SOC_DAPM_MIXER("Left Mixer", SND_SOC_NOPM, 0, 0,
&wm8988_left_mixer_controls[0],
@@ -477,7 +495,7 @@ static struct snd_pcm_hw_constraint_list constraints_112896 = {
};
static unsigned int rates_12[] = {
8000, 11025, 12000, 16000, 22050, 2400, 32000, 41100, 48000,
8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000,
48000, 88235, 96000,
};
@@ -495,6 +513,8 @@ static int wm8988_set_dai_sysclk(struct snd_soc_dai *codec_dai,
struct snd_soc_codec *codec = codec_dai->codec;
struct wm8988_priv *wm8988 = snd_soc_codec_get_drvdata(codec);
DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
switch (freq) {
case 11289600:
case 18432000:
@@ -575,6 +595,8 @@ static int wm8988_set_dai_fmt(struct snd_soc_dai *codec_dai,
return -EINVAL;
}
DBG("Enter::%s----%d iface=%x\n",__FUNCTION__,__LINE__,iface);
snd_soc_write(codec, WM8988_IFACE, iface);
return 0;
}
@@ -585,6 +607,17 @@ static int wm8988_pcm_startup(struct snd_pcm_substream *substream,
struct snd_soc_codec *codec = dai->codec;
struct wm8988_priv *wm8988 = snd_soc_codec_get_drvdata(codec);
if (!wm8988->is_startup) {
wm8988->is_startup = 1;
snd_soc_write(codec, WM8988_PWR1, 0xfc);
gpio_direction_output(RK29_PIN6_PB5, GPIO_LOW);
mdelay(100); // Discharge C310
snd_soc_write(codec, WM8988_PWR2, 0x1e0);
gpio_direction_output(RK29_PIN6_PB5, GPIO_HIGH);
}
DBG("Enter::%s----%d wm8988->sysclk=%d\n",__FUNCTION__,__LINE__,wm8988->sysclk);
/* The set of sample rates that can be supported depends on the
* MCLK supplied to the CODEC - enforce this.
*/
@@ -639,6 +672,8 @@ static int wm8988_pcm_hw_params(struct snd_pcm_substream *substream,
break;
}
DBG("Enter::%s----%d iface=%x srate =%x rate=%d\n",__FUNCTION__,__LINE__,iface,srate,params_rate(params));
/* set iface & srate */
snd_soc_write(codec, WM8988_IFACE, iface);
if (coeff >= 0)
@@ -653,6 +688,8 @@ static int wm8988_mute(struct snd_soc_dai *dai, int mute)
struct snd_soc_codec *codec = dai->codec;
u16 mute_reg = snd_soc_read(codec, WM8988_ADCDAC) & 0xfff7;
DBG("Enter::%s----%d--mute=%d\n",__FUNCTION__,__LINE__,mute);
if (mute)
snd_soc_write(codec, WM8988_ADCDAC, mute_reg | 0x8);
else
@@ -663,13 +700,22 @@ static int wm8988_mute(struct snd_soc_dai *dai, int mute)
static int wm8988_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
struct wm8988_priv *wm8988 = snd_soc_codec_get_drvdata(codec);
u16 pwr_reg = snd_soc_read(codec, WM8988_PWR1) & ~0x1c1;
DBG("Enter::%s----%d level =%d\n",__FUNCTION__,__LINE__,level);
switch (level) {
case SND_SOC_BIAS_ON:
wm8988->is_biason = 1;
break;
case SND_SOC_BIAS_PREPARE:
if (wm8988->is_startup && wm8988->is_biason) {
snd_soc_write(codec, WM8988_PWR2, 0x0);
wm8988->is_startup = 0;
wm8988->is_biason = 0;
}
/* VREF, VMID=2x50k, digital enabled */
snd_soc_write(codec, WM8988_PWR1, pwr_reg | 0x00c0);
break;
@@ -709,7 +755,7 @@ static struct snd_soc_dai_ops wm8988_ops = {
};
static struct snd_soc_dai_driver wm8988_dai = {
.name = "wm8988-hifi",
.name = "WM8988 HiFi",
.playback = {
.stream_name = "Playback",
.channels_min = 1,
@@ -730,6 +776,8 @@ static struct snd_soc_dai_driver wm8988_dai = {
static int wm8988_suspend(struct snd_soc_codec *codec, pm_message_t state)
{
DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
wm8988_set_bias_level(codec, SND_SOC_BIAS_OFF);
return 0;
}
@@ -740,6 +788,8 @@ static int wm8988_resume(struct snd_soc_codec *codec)
u8 data[2];
u16 *cache = codec->reg_cache;
DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
/* Sync reg_cache with the hardware */
for (i = 0; i < WM8988_NUM_REG; i++) {
if (i == WM8988_RESET)
@@ -748,12 +798,30 @@ static int wm8988_resume(struct snd_soc_codec *codec)
data[1] = cache[i] & 0x00ff;
codec->hw_write(codec->control_data, data, 2);
}
//snd_soc_write(codec, WM8988_PWR1, 0xfc);
//snd_soc_write(codec, WM8988_PWR2, 0x1e0);
wm8988_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
return 0;
}
static struct snd_soc_codec *wm8988_codec;
static int entry_read(char *page, char **start, off_t off,
int count, int *eof, void *data)
{
int len;
snd_soc_write(wm8988_codec, WM8988_PWR1, 0x0000);
snd_soc_write(wm8988_codec, WM8988_PWR2, 0x0000);
len = sprintf(page, "wm8988 suspend...\n");
return len ;
}
static int wm8988_probe(struct snd_soc_codec *codec)
{
struct wm8988_priv *wm8988 = snd_soc_codec_get_drvdata(codec);
@@ -761,6 +829,13 @@ static int wm8988_probe(struct snd_soc_codec *codec)
int ret = 0;
u16 reg;
if (codec == NULL) {
dev_err(codec->dev, "Codec device not registered\n");
return -ENODEV;
}
wm8988_codec = codec;
ret = snd_soc_codec_set_cache_io(codec, 7, 9, wm8988->control_type);
if (ret < 0) {
dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
@@ -773,6 +848,14 @@ static int wm8988_probe(struct snd_soc_codec *codec)
return ret;
}
#if 0
/*disable speaker */
gpio_request(RK2818_PIN_PF7, "WM8988");
rk2818_mux_api_set(GPIOE_SPI1_FLASH_SEL_NAME, IOMUXA_GPIO1_A3B7);
gpio_direction_output(RK2818_PIN_PF7,GPIO_HIGH);
#endif
/* set the update bits (we always update left then right) */
reg = snd_soc_read(codec, WM8988_RADC);
snd_soc_write(codec, WM8988_RADC, reg | 0x100);
@@ -783,7 +866,40 @@ static int wm8988_probe(struct snd_soc_codec *codec)
reg = snd_soc_read(codec, WM8988_ROUT2V);
snd_soc_write(codec, WM8988_ROUT2V, reg | 0x0100);
reg = snd_soc_read(codec, WM8988_RINVOL);
snd_soc_write(codec, WM8988_RINVOL, reg | 0x0100);
snd_soc_write(codec, WM8988_RINVOL, reg | 0x0100);
snd_soc_write(codec, WM8988_LOUTM1, 0x120);
snd_soc_write(codec, WM8988_ROUTM2, 0x120);
snd_soc_write(codec, WM8988_LOUTM2, 0x0070);
snd_soc_write(codec, WM8988_ROUTM1, 0x0070);
//tcl miaozh modify
// snd_soc_write(codec, WM8988_LOUT1V, 0x017f);
// snd_soc_write(codec, WM8988_ROUT1V, 0x017f);
snd_soc_write(codec, WM8988_LOUT1V, 0x017a);
snd_soc_write(codec, WM8988_ROUT1V, 0x017a);
snd_soc_write(codec, WM8988_LDAC, 0xfa/*0xff*/); // Change max by zhansb@110415
snd_soc_write(codec, WM8988_RDAC, 0x1fa/*0x1ff*/);//vol set
//TCL lgw add 20110412
snd_soc_write(codec, WM8988_LINVOL, 0x0117);
snd_soc_write(codec, WM8988_RINVOL, 0x0117);
snd_soc_write(codec, WM8988_ADCTL2, 0x0184);
snd_soc_write(codec, WM8988_LADC, 0x01ec);
snd_soc_write(codec, WM8988_RADC, 0x01ec);
snd_soc_write(codec, WM8988_ADCIN, 0x0140);
snd_soc_write(codec, WM8988_LADCIN, 0x00f0);//0x00e0
snd_soc_write(codec, WM8988_RADCIN, 0x0);//0x00e0
//lgw end
snd_soc_write(codec, WM8988_SRATE,0x100); ///SET MCLK/8
//snd_soc_write(codec, WM8988_PWR1, 0x1cc); ///(0x80|0x40|0x20|0x08|0x04|0x10|0x02));
//TCL lgw modify 20110412
//snd_soc_write(codec, WM8988_PWR1, 0xfc);
//snd_soc_write(codec, WM8988_PWR2, 0x1e0); //power r l out1
wm8988_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
@@ -792,6 +908,7 @@ static int wm8988_probe(struct snd_soc_codec *codec)
snd_soc_dapm_new_controls(dapm, wm8988_dapm_widgets,
ARRAY_SIZE(wm8988_dapm_widgets));
snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map));
create_proc_read_entry("wm8988_suspend", 0644, NULL, entry_read, NULL);
return 0;
}
@@ -842,7 +959,7 @@ static int __devexit wm8988_spi_remove(struct spi_device *spi)
static struct spi_driver wm8988_spi_driver = {
.driver = {
.name = "wm8988-codec",
.name = "WM8988",
.owner = THIS_MODULE,
},
.probe = wm8988_spi_probe,
@@ -886,7 +1003,7 @@ MODULE_DEVICE_TABLE(i2c, wm8988_i2c_id);
static struct i2c_driver wm8988_i2c_driver = {
.driver = {
.name = "wm8988-codec",
.name = "WM8988",
.owner = THIS_MODULE,
},
.probe = wm8988_i2c_probe,

View File

@@ -1,5 +1,5 @@
/*
* rk2818_wm8988.c -- SoC audio for rockchip
* rk29_wm8988.c -- SoC audio for rockchip
*
* Driver for rockchip wm8988 audio
*
@@ -24,18 +24,22 @@
#include "rk29_pcm.h"
#include "rk29_i2s.h"
#include <mach/gpio.h>
#if 0
#define DBG(x...) printk(KERN_INFO x)
#else
#define DBG(x...)
#endif
static int rk2818_hw_params(struct snd_pcm_substream *substream,
//static void *rk29_speaker = NULL;
static int rk29_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;
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 ret;
DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
@@ -44,39 +48,39 @@ static int rk2818_hw_params(struct snd_pcm_substream *substream,
#define HW_PARAMS_FLAG_EQVOL_OFF 0x22
if ((params->flags == HW_PARAMS_FLAG_EQVOL_ON)||(params->flags == HW_PARAMS_FLAG_EQVOL_OFF))
{
ret = codec_dai->ops->hw_params(substream, params, codec_dai); //by Vincent
ret = codec_dai->driver->ops->hw_params(substream, params, codec_dai); //by Vincent
DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
}
else
{
/* set codec DAI configuration */
#if defined (CONFIG_SND_CODEC_SOC_SLAVE)
ret = codec_dai->ops->set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
#if defined (CONFIG_SND_RK29_CODEC_SOC_SLAVE)
ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
#endif
#if defined (CONFIG_SND_CODEC_SOC_MASTER)
ret = codec_dai->ops->set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
#if defined (CONFIG_SND_RK29_CODEC_SOC_MASTER)
ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM );
#endif
if (ret < 0)
return ret;
/* set cpu DAI configuration */
#if defined (CONFIG_SND_CODEC_SOC_SLAVE)
ret = cpu_dai->ops->set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
#if defined (CONFIG_SND_RK29_CODEC_SOC_SLAVE)
ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
#endif
#if defined (CONFIG_SND_CODEC_SOC_MASTER)
ret = cpu_dai->ops->set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
#if defined (CONFIG_SND_RK29_CODEC_SOC_MASTER)
ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
#endif
if (ret < 0)
return ret;
}
return 0;
}
static const struct snd_soc_dapm_widget rk2818_dapm_widgets[] = {
static const struct snd_soc_dapm_widget rk29_dapm_widgets[] = {
SND_SOC_DAPM_LINE("Audio Out", NULL),
SND_SOC_DAPM_LINE("Line in", NULL),
SND_SOC_DAPM_MIC("Micn", NULL),
@@ -96,84 +100,86 @@ static const struct snd_soc_dapm_route audio_map[]= {
/*
* Logic for a wm8988 as connected on a rockchip board.
*/
static int rk2818_wm8988_init(struct snd_soc_codec *codec)
static int rk29_wm8988_init(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_dai *codec_dai = &codec->dai[0];
struct snd_soc_dai *codec_dai = rtd->codec_dai;
struct snd_soc_codec *codec = rtd->codec;
struct snd_soc_dapm_context *dapm = &codec->dapm;
int ret;
DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
ret = snd_soc_dai_set_sysclk(codec_dai, 0,
12000000, SND_SOC_CLOCK_IN);
/*12000000*/11289600, SND_SOC_CLOCK_IN);
if (ret < 0) {
printk(KERN_ERR "Failed to set WM8988 SYSCLK: %d\n", ret);
return ret;
}
/* Add specific widgets */
snd_soc_dapm_new_controls(codec, rk2818_dapm_widgets,
ARRAY_SIZE(rk2818_dapm_widgets));
snd_soc_dapm_nc_pin(codec, "LOUT2");
snd_soc_dapm_nc_pin(codec, "ROUT2");
snd_soc_dapm_new_controls(dapm, rk29_dapm_widgets,
ARRAY_SIZE(rk29_dapm_widgets));
//snd_soc_dapm_nc_pin(codec, "LOUT2");
//snd_soc_dapm_nc_pin(codec, "ROUT2");
/* Set up specific audio path audio_mapnects */
snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map));
snd_soc_dapm_sync(codec);
snd_soc_dapm_sync(dapm);
return 0;
}
static struct snd_soc_ops rk2818_ops = {
.hw_params = rk2818_hw_params,
static struct snd_soc_ops rk29_ops = {
.hw_params = rk29_hw_params,
};
static struct snd_soc_dai_link rk2818_dai = {
.name = "WM8988",
.stream_name = "WM8988 PCM",
.cpu_dai = &rk2818_i2s_dai,
.codec_dai = &wm8988_dai,
.init = rk2818_wm8988_init,
.ops = &rk2818_ops,
static struct snd_soc_dai_link rk29_dai = {
.name = "WM8988",
.stream_name = "WM8988 PCM",
.codec_name = "WM8988.0-001a",
.platform_name = "rockchip-audio",
.cpu_dai_name = "rk29_i2s.0",
.codec_dai_name = "WM8988 HiFi",
.init = rk29_wm8988_init,
.ops = &rk29_ops,
};
static struct snd_soc_card snd_soc_card_rk2818 = {
.name = "RK2818_WM8988",
.platform = &rk2818_soc_platform,
.dai_link = &rk2818_dai,
.num_links = 1,
static struct snd_soc_card snd_soc_card_rk29 = {
.name = "RK29_WM8988",
.dai_link = &rk29_dai,
.num_links = 1,
};
static struct snd_soc_device rk2818_snd_devdata = {
.card = &snd_soc_card_rk2818,
.codec_dev = &soc_codec_dev_wm8988,
};
static struct platform_device *rk2818_snd_device;
static struct platform_device *rk29_snd_device;
static int __init audio_card_init(void)
{
int ret =0;
int ret =0;
//rk29_speaker = rk29_speaker_init(RK29_PIN6_PB6, GPIO_HIGH, 2, (200*1000*1000));
DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__);
rk2818_snd_device = platform_device_alloc("soc-audio", -1);
if (!rk2818_snd_device) {
rk29_snd_device = platform_device_alloc("soc-audio", -1);
if (!rk29_snd_device) {
DBG("platform device allocation failed\n");
ret = -ENOMEM;
return ret;
}
platform_set_drvdata(rk2818_snd_device, &rk2818_snd_devdata);
rk2818_snd_devdata.dev = &rk2818_snd_device->dev;
ret = platform_device_add(rk2818_snd_device);
platform_set_drvdata(rk29_snd_device, &snd_soc_card_rk29);
ret = platform_device_add(rk29_snd_device);
if (ret) {
DBG("platform device add failed\n");
platform_device_put(rk2818_snd_device);
platform_device_put(rk29_snd_device);
return ret;
}
return ret;
return ret;
}
static void __exit audio_card_exit(void)
{
platform_device_unregister(rk2818_snd_device);
platform_device_unregister(rk29_snd_device);
//rk29_speaker_deinit(rk29_speaker);
}
module_init(audio_card_init);