茂昇電子 MS6335 是一顆AB類16位元電壓輸出數位類比轉換器, 常應用於傳統 MP3. 本人之前曾經成功地驅動/整合MS6335 於 RK Android OS. 剛剛找到之前寫的 Source Code, 因此把它分享於部落格中. 它的特點是簡單容易應用, 高品質的音頻訊號.
方塊圖 / Demo Board
Data bus: I2S , Control bus: I2C
茂昇電子 MS6335 Demo Board 我覺得設計的不錯; 它的設計讓程式設計師學習門檻降低不少. 如果有人要向我學寫驅動程式/線路設計. 我會拿它的 Demo Board 當教材.
Ms6335.h
#ifndef __MS6335_H_INCLUDED
#define __MS6335_H_INCLUDED
//#include <type.h>
#define MS6335_SLAVE_ADDR_DAC 0x46
#define MS6335_CLK 169344000
/*
* Register mapping
* */
#define MS6335_FUNC_VOL_CTRL 0x0
#define MS6335_FUNC_VOL_L_CTRL (0x1 << 5)
#define MS6335_FUNC_VOL_R_CTRL (0x2 << 5)
#define MS6335_FUNC_PWR_MODE (0x3 << 5)
/*
* MS6335_FUNC_VOL CTRL
* */
/*
* MS6335_FUNC_PWR_DWN_MODE
* */
#define MS6335_PWR_DWN_CAPGD (0x1 << 4)
#define MS6335_PWR_DWN_OPAPD (0x1 << 3)
#define MS6335_PWR_DWN_DACPD (0x1 << 2)
#define MS6335_PWR_DWN_HPPD (0x1 << 1)
#define MS6335_PWR_DWN_DACM 0x1
/*
* MS6335 Audio PWR
* */
#define MS6335_AUDIO_I2S_MODE 0x81
struct ms6335_priv {
int master;
int sysclk;
};
extern int ms6335_dac_initialize(void);
extern int ms6335_dac_power_switch(int sw);
extern int ms6335_dac_master_volume(int val);
extern int ms6335_dac_master_volume(int val);
extern int ms6335_dac_mute(bool val);
/*1: enable mute , 0:disable mute*/
extern struct snd_soc_dai_driver ms6335_dai;
extern struct snd_soc_codec_driver soc_codec_dev_ms6335;
#endif //__MS6335_H_INCLUDED
Ms6335.c
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/i2c.h>
#include <linux/platform_device.h>
#include <linux/spi/spi.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include <sound/initval.h>
#include "ms6335.h"
#ifndef BIT0
#define BIT0 0x00000001
#define BIT1 0x00000002
#define BIT2 0x00000004
#define BIT3 0x00000008
#define BIT4 0x00000010
#define BIT5 0x00000020
#define BIT6 0x00000040
#define BIT7 0x00000080
#define BIT8 0x00000100
#define BIT9 0x00000200
#define BIT10 0x00000400
#define BIT11 0x00000800
#define BIT12 0x00001000
#define BIT13 0x00002000
#define BIT14 0x00004000
#define BIT15 0x00008000
#define BIT16 0x00010000
#define BIT17 0x00020000
#define BIT18 0x00040000
#define BIT19 0x00080000
#define BIT20 0x00100000
#define BIT21 0x00200000
#define BIT22 0x00400000
#define BIT23 0x00800000
#define BIT24 0x01000000
#define BIT25 0x02000000
#define BIT26 0x04000000
#define BIT27 0x08000000
#define BIT28 0x10000000
#define BIT29 0x20000000
#define BIT30 0x40000000
#define BIT31 0x80000000
#endif // BIT0
static const u8 ms6335_reg[0x80];
static unsigned int BLCK_FREQ=16;
module_param(BLCK_FREQ, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
MODULE_PARM_DESC(BLCK_FREQ, "relationship between bclk and fs");
static struct snd_soc_codec *ms6335_codec;
static struct timer_list spk_timer;
struct work_struct spk_work;
//static bool last_is_spk = false;
static void ms6335_work(struct work_struct *work);
static struct workqueue_struct *ms6335_workq;
static DECLARE_DELAYED_WORK(delayed_work, ms6335_work);
static int ms6335_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int val) {
int ret =-1;
u8 data = reg;
ret = codec->hw_write(codec->control_data, &data, 1);
//ret = codec->bulk_write_raw(codec, reg, &data, 0);
if(ret <0) printk("wirte error-----> reg == %d, val == %d\n", reg, val);
return ret;
}
int ms6335_dac_master_volume(int val) {
int ret;
val = val & 0x1f;
ret = ms6335_write(ms6335_codec, val, 0);
return ret;
}
int ms6335_dac_mute(bool val) {
int ret;
if (val == 1) ret = ms6335_write(ms6335_codec, MS6335_FUNC_PWR_MODE | MS6335_PWR_DWN_DACM, 0);
else ret = ms6335_write(ms6335_codec, MS6335_FUNC_PWR_MODE, 0);
return ret;
}
static int ms6335_mute(struct snd_soc_dai *dai, int mute) {
//struct snd_soc_codec *codec = dai->codec;
return ms6335_dac_mute(mute);
}
static unsigned int ms6335_read(struct snd_soc_codec *codec, unsigned int reg) {
unsigned int val;
val = codec->hw_read(codec, reg);
return val;
}
static inline unsigned int ms6335_read_reg_cache(struct snd_soc_codec *codec, unsigned int reg) {
u16 *cache = codec->reg_cache;
//if (reg >= AK4535_CACHEREGNUM) return -1;
return cache[reg];
}
static int ms6335_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) {
int res;
unsigned int val;
if (0 == codec)
return -1;
switch (level) {
case SND_SOC_BIAS_ON:
case SND_SOC_BIAS_PREPARE:
val = 0X60;
val &= ~MS6335_PWR_DWN_CAPGD;
val &= ~MS6335_PWR_DWN_OPAPD;
val &= ~MS6335_PWR_DWN_DACPD;
val |= MS6335_PWR_DWN_HPPD;
val &= ~MS6335_PWR_DWN_DACM;
res = ms6335_write(codec, val, 0);
if (-1 == res) return res;
break;
case SND_SOC_BIAS_STANDBY:
case SND_SOC_BIAS_OFF:
val = 0X60;
val &= ~MS6335_PWR_DWN_CAPGD;
val &= ~MS6335_PWR_DWN_OPAPD;
val &= ~MS6335_PWR_DWN_DACPD;
val &= ~MS6335_PWR_DWN_HPPD;
val |= MS6335_PWR_DWN_DACM;
res = ms6335_write(codec, val, 0);
if (-1 == res) return res;
break;
}
codec->dapm.bias_level = level;
return 0;
}
static void ms6335_work(struct work_struct *work) {
struct snd_soc_codec *codec = ms6335_codec;
ms6335_set_bias_level(codec, codec->dapm.bias_level);
}
static int ms6335_reg_init(struct snd_soc_codec *codec) {
int res;
u8 val;
val = 0X60;
val &= ~MS6335_PWR_DWN_CAPGD;
val &= ~MS6335_PWR_DWN_OPAPD;
val &= ~MS6335_PWR_DWN_DACPD;
val &= ~MS6335_PWR_DWN_HPPD;
val |= MS6335_PWR_DWN_DACM;
res = ms6335_write(codec, val, 0);
if (-1 == res) return res;
return 0;
}
static void spk_work_handler(struct work_struct *work) {
}
void spk_timer_callback(unsigned long data ) {
int ret = 0;
schedule_work(&spk_work);
ret = mod_timer(&spk_timer, jiffies + msecs_to_jiffies(1000));
if (ret) printk("Error in mod_timer\n");
}
static int ms6335_probe(struct snd_soc_codec *codec) {
int ret;
ret = snd_soc_codec_set_cache_io(codec, 8, 9, SND_SOC_I2C);
if (ret != 0) {
printk("%s() Failed to set cache I/O: %d\n", __FUNCTION__, ret);
return ret;
}
codec->cache_bypass = 1;
schedule_timeout_uninterruptible(msecs_to_jiffies(110));
ms6335_set_bias_level(codec, SND_SOC_BIAS_PREPARE);
codec->dapm.bias_level = SND_SOC_BIAS_STANDBY;
ms6335_reg_init(codec);
setup_timer(&spk_timer, spk_timer_callback, 0 );
//ret = mod_timer( &spk_timer, jiffies + msecs_to_jiffies(5000) );
ret = mod_timer( &spk_timer, jiffies + msecs_to_jiffies(3000));
if (ret) printk("Error in mod_timer\n");
INIT_WORK(&spk_work, spk_work_handler);
ms6335_codec = codec;
return 0;
}
static int ms6335_remove(struct snd_soc_codec *codec) {
ms6335_set_bias_level(codec, SND_SOC_BIAS_OFF);
cancel_delayed_work_sync(&delayed_work);
return 0;
}
struct snd_soc_codec_driver soc_codec_dev_ms6335 = {
.probe = ms6335_probe,
.remove = ms6335_remove,
//.suspend = rt5633_suspend,
//.resume = rt5633_resume,
//.read = ms6335_read_reg_cache,
.set_bias_level = ms6335_set_bias_level,
.reg_cache_size = ARRAY_SIZE(ms6335_reg),
.reg_word_size = sizeof(u8),
.reg_cache_default = ms6335_reg,
.reg_cache_step = 1,
};
static const struct i2c_device_id ms6335_id[] = {
{ "ms6335", 0 },
{ }
};
static void ms6335_hifi_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *codec_dai) {
}
static int ms6335_codec_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) {
struct snd_soc_codec *codec = codec_dai->codec;
struct ms6335_priv *ms6335 = snd_soc_codec_get_drvdata(codec);
u16 iface = 0X80;
iface &= ~BIT1;
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBM_CFM:
ms6335->master = 1;
break;
case SND_SOC_DAIFMT_CBS_CFS:
//iface |= (0x0001 << 15);
ms6335->master = 0;
break;
default:
return -EINVAL;
}
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
iface |= BIT0;
break;
case SND_SOC_DAIFMT_RIGHT_J:
iface &= ~BIT0;
break;
default:
return -EINVAL;
}
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_NB_NF:
break;
case SND_SOC_DAIFMT_IB_NF:
break;
default:
return -EINVAL;
}
ms6335_write(codec, iface, 0);
return 0;
}
static int ms6335_codec_set_dai_sysclk(struct snd_soc_dai *codec_dai, int clk_id, unsigned int freq, int dir) {
struct snd_soc_codec *codec = codec_dai->codec;
struct ms6335_priv *ms6335 = snd_soc_codec_get_drvdata(codec);
printk(KERN_DEBUG "enter %s()\n", __func__);
if ((freq >= (256 * 8000)) && (freq <= (512 * 48000))) {
ms6335->sysclk = freq;
return 0;
}
printk(KERN_ERR "unsupported sysclk freq %u for audio i2s\n", freq);
return 0;
}
static int ms6335_codec_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, int source, unsigned int freq_in, unsigned int freq_out) {
struct snd_soc_codec *codec = codec_dai->codec;
struct ms6335_priv *ms6335 = snd_soc_codec_get_drvdata(codec);
if (!freq_in || !freq_out)
return 0;
if (ms6335->master) {
} else {
}
return 0;
}
static int ms6335_pcm_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) {
struct snd_soc_codec *codec = dai->codec;
struct ms6335_priv *ms6335 = snd_soc_codec_get_drvdata(codec);
int coeff = 0;
int rate;// = params_rate(params);
rate = params_rate(params);
printk("%s(%d) get_coeff err!\n", __func__, rate);
if (!ms6335->master) { }
else { }
if (coeff < 0) {
printk(KERN_ERR "%s() get_coeff err!\n", __func__);
return -EINVAL;
}
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE:
break;
case SNDRV_PCM_FORMAT_S20_3LE:
break;
case SNDRV_PCM_FORMAT_S24_LE:
break;
case SNDRV_PCM_FORMAT_S8:
break;
default:
return -EINVAL;
}
return 0;
}
#define MS6335_STEREO_RATES (SNDRV_PCM_RATE_44100)
#define MS6335_FORMAT (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8)
struct snd_soc_dai_ops ms6335_ops = {
.hw_params = ms6335_pcm_params,
.set_fmt = ms6335_codec_set_dai_fmt,
.set_sysclk = ms6335_codec_set_dai_sysclk,
.set_pll = ms6335_codec_set_dai_pll,
.shutdown = ms6335_hifi_shutdown,
};
struct snd_soc_dai_driver ms6335_dai = {
.name = "MS6335",
.id = 1,
.playback = {
.stream_name = "MS6335 Playback",
.channels_min = 1,
.channels_max = 2,
.rates = MS6335_STEREO_RATES,
.formats = MS6335_FORMAT,
} ,
.capture = {
.stream_name = "MS6335 Capture",
.channels_min = 1,
.channels_max = 2,
.rates = MS6335_STEREO_RATES,
.formats = MS6335_FORMAT,
},
.ops =&ms6335_ops,
};
Rk29_ms6335.c
/*
* rk29_ms6335.c -- SoC audio for rockchip
*
* Driver for rockchip rt5633 audio
*
* 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.
*
*
*/
#include <linux/module.h>
#include <linux/device.h>
#include <linux/clk.h>
#include <linux/i2c.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include <asm/io.h>
#include <mach/hardware.h>
#include <linux/delay.h>
//#include <mach/rk29_iomap.h>
#include "../codecs/ms6335.h"
#include "rk29_pcm.h"
#include "rk29_i2s.h"
#if 0
#define DBG(x...) printk(KERN_INFO x)
#else
#define DBG(x...)
#endif
#ifndef MS6335_PWR_DWN_CAPGD
#define MS6335_PWR_DWN_CAPGD (0x1 << 4)
#define MS6335_PWR_DWN_OPAPD (0x1 << 3)
#define MS6335_PWR_DWN_DACPD (0x1 << 2)
#define MS6335_PWR_DWN_HPPD (0x1 << 1)
#define MS6335_PWR_DWN_DACM 0x1
#define MS6335_FUNC_VOL_CTRL 0x0
#define MS6335_FUNC_VOL_L_CTRL (0x1 << 5) // 0X20
#define MS6335_FUNC_VOL_R_CTRL (0x2 << 5) // 0X40
#define MS6335_FUNC_PWR_MODE (0x3 << 5) // 0X60
#endif // MS6335_PWR_DWN_CAPGD
static int DAC_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) {
struct snd_soc_codec *codec = w->codec;
struct rk2928_codec_data *priv = snd_soc_codec_get_drvdata(codec);
switch (event) {
//before widget power down
case SND_SOC_DAPM_PRE_PMD:
break;
//after widget power up
case SND_SOC_DAPM_POST_PMU:
break;
}
return 0;
}
static int rk29_audio_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) {
return 0;
}
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->codec_dai;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
unsigned int pll_out = 0;
//unsigned int lrclk = 0;
int ret;
printk("%s %s(%d)\n", __FILE__, __FUNCTION__, __LINE__);
#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 // CONFIG_SND_RK29_CODEC_SOC_SLAVE
#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 // CONFIG_SND_RK29_CODEC_SOC_MASTER
if (ret < 0) {
printk("%s %s(%d), ERR ON snd_soc_dai_set_fmt()\n", __FILE__, __FUNCTION__, __LINE__);
return ret;
}
#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 // CONFIG_SND_RK29_CODEC_SOC_SLAVE
#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 // CONFIG_SND_RK29_CODEC_SOC_MASTER
if (ret < 0) {
printk("%s %s(%d), ERR ON snd_soc_dai_set_fmt()\n", __FILE__, __FUNCTION__, __LINE__);
return ret;
}
switch(params_rate(params)) {
case 8000:
case 16000:
case 24000:
case 32000:
case 48000:
pll_out = 12288000;
break;
case 11025:
case 22050:
case 44100:
pll_out = 11289600;
break;
default:
printk("%s %s(%d), Error rate=%d\n", __FILE__,__FUNCTION__, __LINE__, params_rate(params));
return -EINVAL;
break;
}
printk("Enter:%s, %d, rate=%d\n",__FUNCTION__, __LINE__, params_rate(params));
#if defined (CONFIG_SND_RK29_CODEC_SOC_SLAVE)
ret = snd_soc_dai_set_sysclk(codec_dai, 0, pll_out, SND_SOC_CLOCK_IN);
if (ret < 0) {
printk("%s %s(%d), ERR ON snd_soc_dai_set_sysclk()\n", __FILE__, __FUNCTION__, __LINE__);
return ret;
}
#endif // CONFIG_SND_RK29_CODEC_SOC_SLAVE
#if defined (CONFIG_SND_RK29_CODEC_SOC_SLAVE)
snd_soc_dai_set_sysclk(cpu_dai, 0, pll_out, 0);
snd_soc_dai_set_clkdiv(cpu_dai, ROCKCHIP_DIV_BCLK, (pll_out/4) / params_rate(params)-1);
snd_soc_dai_set_clkdiv(cpu_dai, ROCKCHIP_DIV_MCLK, 3);
#endif // CONFIG_SND_RK29_CODEC_SOC_SLAVE
printk("%s %s(%d), LRCK=%d\n", __FILE__,__FUNCTION__, __LINE__,(pll_out/4)/params_rate(params));
return 0;
}
static const struct snd_soc_dapm_route audio_map[] = {
{ "Headphone Jack", NULL, "LOUT1" },
{ "Headphone Jack", NULL, "ROUT1" },
{ "Internal Speaker", NULL, "LOUT2" },
{ "Internal Speaker", NULL, "ROUT2" },
{ "LINPUT1", NULL, "Line Input" },
{ "RINPUT1", NULL, "Line Input" },
};
static const struct snd_soc_dapm_widget ms6335_dapm_widgets[] = {
SND_SOC_DAPM_HP("Headphone Jack", NULL),
SND_SOC_DAPM_SPK("Internal Speaker", NULL),
SND_SOC_DAPM_LINE("Line In", NULL),
};
static int rk29_ms6335_init(struct snd_soc_pcm_runtime *rtd) {
struct snd_soc_codec *codec = rtd->codec;
struct snd_soc_dapm_context *dapm = &codec->dapm;
printk("Enter::%s(%d)---\n", __FUNCTION__, __LINE__);
snd_soc_dapm_new_controls(dapm, ms6335_dapm_widgets, ARRAY_SIZE(ms6335_dapm_widgets));
DBG("Enter::%s(%d)----\n", __FUNCTION__, __LINE__);
snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map));
DBG("Enter::%s(%d)----\n", __FUNCTION__, __LINE__);
snd_soc_dapm_nc_pin(dapm, "LINPUT1");
DBG("Enter::%s(%d)----\n", __FUNCTION__, __LINE__);
snd_soc_dapm_nc_pin(dapm, "RINPUT1");
DBG("Enter::%s(%d)----\n", __FUNCTION__, __LINE__);
snd_soc_dapm_sync(dapm);
DBG("Enter::%s(%d)----\n", __FUNCTION__, __LINE__);
return 0;
}
static struct snd_soc_ops rk29_ops = {
.hw_params = rk29_hw_params,
};
static struct snd_soc_dai_link rk29_dai = {
.name = "MS6335",
.stream_name = "MS6335 PCM",
.codec_name = "MS6335.0-001c",
.platform_name = "rockchip-audio",
.cpu_dai_name = "rk29_i2s.0",
.codec_dai_name = "MS6335 HiFi",
//.codec_dai_name = "MS6335",
//.init = rk29_ms6335_init,
.ops = &rk29_ops,
};
static struct snd_soc_card snd_soc_card_rk29 = {
.name = "RK29_MS6335",
.dai_link = &rk29_dai,
.num_links = 1,
};
static struct platform_device *rk29_snd_device;
static int audio_card_init(void) {
int ret =0;
//rk29_speaker = rk29_speaker_init(RK29_PIN6_PB6, GPIO_HIGH, 2, (200*1000*1000));
printk("%s %s(%d), ---\n", __FILE__, __FUNCTION__, __LINE__);
rk29_snd_device = platform_device_alloc("soc-audio", -1);
if (!rk29_snd_device) {
printk("%s %s(%d) platform device allocation failed\n", __FILE__, __FUNCTION__, __LINE__);
ret = -ENOMEM;
return ret;
}
platform_set_drvdata(rk29_snd_device, &snd_soc_card_rk29);
ret = platform_device_add(rk29_snd_device);
if (ret) {
printk("%s %s(%d), ERR ON platform_device_add()\n", __FILE__, __FUNCTION__, __LINE__);
platform_device_put(rk29_snd_device);
return ret;
}
return ret;
}
static void audio_card_exit(void) {
platform_device_unregister(rk29_snd_device);
}
static int rk29_ms6335_detect_device(struct i2c_client *i2c) {
u8 data[2]={0};
unsigned int val = MS6335_FUNC_PWR_MODE;
int ret;
ret = i2c_master_reg8_send(i2c, 0X81, 0, 0, 100 * 1000);
if(ret < 0) {
printk("%s %s(%d): ERR ON i2c_master_reg8_send !\n", __FILE__, __FUNCTION__, __LINE__);
return -ENODEV;
}
val &= ~MS6335_PWR_DWN_CAPGD; // Set the voltage of CAP to middle of supply voltage
val |= MS6335_PWR_DWN_OPAPD; // Enable OPAmp power down
val |= MS6335_PWR_DWN_DACPD; // Enable DAC power down
val &= ~MS6335_PWR_DWN_HPPD; // Disable HP power down
val |= MS6335_PWR_DWN_DACM; // Enable DAC mute
ret = i2c_master_reg8_send(i2c, val, 0, 0, 100 * 1000);
if(ret < 0) {
printk("%s %s(%d): MS6335_0 err!\n", __FILE__, __FUNCTION__, __LINE__);
return -ENODEV;
}
mdelay(1051);
val &= ~MS6335_PWR_DWN_CAPGD; // Set the voltage of CAP to middle of supply voltage
val &= ~MS6335_PWR_DWN_OPAPD; // Disable OPAmp power down
val &= ~MS6335_PWR_DWN_DACPD; // Disable DAC power down
val &= ~MS6335_PWR_DWN_HPPD; // Disable HP power down
val |= MS6335_PWR_DWN_DACM; // Enable DAC mute
ret = i2c_master_reg8_send(i2c, val, 0, 0, 100 * 1000);
if(ret < 0) printk("%s %s(%d): SET MS6335 ERR!\n", __FILE__, __FUNCTION__, __LINE__);
//val &= ~MS6335_PWR_DWN_DACM; // Disable DAC mute
//ret = i2c_master_reg8_send(i2c, val, 0, 0, 100 * 1000);
//if(ret < 0) printk("%s(%d): SET MS6335 ERR!\n", __FUNCTION__, __LINE__);
ret = i2c_master_reg8_send(i2c, 0X1F, 0, 0, 100 * 1000);
if(ret < 0) printk("%s(%d): SET MS6335 VOL(0X1F) ERR!\n", __FUNCTION__, __LINE__);
printk("%s %s(%d) OK\n", __FILE__, __FUNCTION__, __LINE__);
printk("############################################################\n");
return 0;
}
static const struct i2c_device_id ms6335_i2c_id[] = {
{ "ms6335", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, rt5633_i2c_id);
extern struct i2c_client *ms6335_i2c_client;
static int ms6335_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) {
struct ms6335_priv *ms6335;
int ret;
ret = rk29_ms6335_detect_device(i2c);
if(ret < 0) {
printk("%s %s(%d), detect ms6335 error!\n", __FILE__, __FUNCTION__, __LINE__);
printk("############################################################\n");
printk("############################################################\n");
return ret;
}
audio_card_init();
ms6335 = kzalloc(sizeof(struct ms6335_priv), GFP_KERNEL);
if (NULL == ms6335)
return -ENOMEM;
i2c_set_clientdata(i2c, ms6335);
// jason
ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_ms6335, &ms6335_dai, 1);
if (ret < 0) {
printk("%s %s(%d), ERR ON snd_soc_register_codec()\n", __FILE__, __FUNCTION__, __LINE__);
printk("############################################################\n");
printk("############################################################\n");
kfree(ms6335);
return ret;
}
ms6335_i2c_client = i2c;
printk("%s %s(%d), snd_soc_register_codec() OK\n", __FILE__, __FUNCTION__, __LINE__);
printk("############################################################\n");
printk("############################################################\n");
#if 0
snd_soc_dapm_new_controls(dapm, ms6335_dapm_widgets, ARRAY_SIZE(ms6335_dapm_widgets));
snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map));
snd_soc_dapm_nc_pin(dapm, "LINPUT1");
snd_soc_dapm_nc_pin(dapm, "RINPUT1");
snd_soc_dapm_sync(dapm);
#endif // 0
return ret;
}
static __devexit int ms6335_i2c_remove(struct i2c_client *i2c) {
audio_card_exit();
snd_soc_unregister_codec(&i2c->dev);
kfree(i2c_get_clientdata(i2c));
ms6335_i2c_client = 0;
return 0;
}
struct i2c_driver ms6335_i2c_driver = {
.driver = {
.name = "MS6335",
.owner = THIS_MODULE,
},
.probe = ms6335_i2c_probe,
.remove = __devexit_p(ms6335_i2c_remove),
.id_table = ms6335_i2c_id,
};
static int __init rt5633_modinit(void) {
return i2c_add_driver(&ms6335_i2c_driver);
}
module_init(rt5633_modinit);
static void __exit ms6335_modexit(void) {
i2c_del_driver(&ms6335_i2c_driver);
}
module_exit(ms6335_modexit);
module_init(audio_card_init);
module_exit(audio_card_exit);
MODULE_AUTHOR("rockchip");
MODULE_DESCRIPTION("ROCKCHIP i2s ASoC Interface");
MODULE_LICENSE("GPL");
文章標籤
全站熱搜
