茂昇電子 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");

 

 

 

 

 

文章標籤
全站熱搜
創作者介紹
創作者 Lexra 的頭像
Lexra

Lexra Pixnet

Lexra 發表在 痞客邦 留言(0) 人氣(376)