lc1120 codec driver

一. 在/sound/soc/codec/lc1120.c中的初始化代码:

1.init/exit lc1120_i2c_driver:

static const struct i2c_device_id lc1120_i2c_id[] = {
        { "lc1120", 0 },
        { }
    };
    MODULE_DEVICE_TABLE(i2c, lc1120_i2c_id);
    /* machine i2c codec control layer */
    static struct i2c_driver lc1120_i2c_driver = {
        .driver = {
            .name = "lc1120",
            .owner = THIS_MODULE,
        },
        .probe    = lc1120_i2c_probe,   //lc1120_i2c_driver 添加完毕,就会自动运行probe.
        .remove = lc1120_i2c_remove,
        .id_table = lc1120_i2c_id,
    };
    static int __init lc1120_i2c_init(void)
    {
        int ret;
    D_ENTER;
        ret = i2c_add_driver(&lc1120_i2c_driver);   //向I2C总线,添加一个i2c_driver,lc1120_i2c_driver,就是一个i2c_driver.
        if (ret)
        {
            printk(KERN_ERR "%s: error when regsiter i2c, %d\n",
             __func__, ret);
        }
        return ret;
    }
    static void __exit lc1120_i2c_exit(void)
    {
    D_ENTER;
        i2c_del_driver(&lc1120_i2c_driver);
    }
    module_init(lc1120_i2c_init);
    module_exit(lc1120_i2c_exit);
2.lc1120_i2c_probe and lc1120_i2c_remove:

/* codec private data */
    struct lc1120_priv
    {
        struct snd_soc_codec codec;   //只包含了一个codec 和一个原子变量
        atomic_t count;
    };

    /*
     * If the i2c layer weren't so broken, we could pass this kind of data
     * around
     */
    static int lc1120_i2c_probe(struct i2c_client *client,
                 const struct i2c_device_id *id)
    {
        struct snd_soc_codec *codec;
        struct lc1120_priv *lc1120;     //lc1120_priv 是一个自定义的表示lc1120设备的结构体。
    D_ENTER;
        if (device_create_file(&client->dev, &dev_attr_reg))       //在sys中建立reg和val文件,用来与用户控件交互。
            printk(KERN_ERR "%s create sysfs reg failed\n", __func__);
        if (device_create_file(&client->dev, &dev_attr_val))
            printk(KERN_ERR "%s create sysfs val failed\n", __func__);
        lc1120 = kzalloc(sizeof(struct lc1120_priv), GFP_KERNEL);   //给lc1120_priv 申请内存.
        if (lc1120 == NULL) {
            dev_err(&client->dev, "failed to create private data\n");
            return -ENOMEM;
        }
        atomic_set(&lc1120->count, 0);    //初始化原子变量为0。
        codec = &lc1120->codec;          
        codec->dev = &client->dev;     //client设备 指向 codec 设备。
        snd_soc_codec_set_drvdata(codec, lc1120); //把lc1120 指向 codec->drvdata.
        codec->control_data = client;  //client 指向 codec->control_data
        i2c_set_clientdata(client, lc1120);   //把lc1120 指向 client->dev->device_private->driver_data
        return lc1120_register(codec);   //注册codec。
    }
    static int lc1120_i2c_remove(struct i2c_client *client)
    {
        struct lc1120_priv *lc1120 = i2c_get_clientdata(client);
    D_ENTER;
        lc1120_unregister(lc1120);   注销codec
        kfree(lc1120);
        return 0;
    }
3. lc1120_register(codec) /lc1120_unregister(codec)
static DEVICE_ATTR(reg, S_IRUGO | S_IWUGO, sys_reg_show, sys_reg_store);
    static DEVICE_ATTR(val, S_IRUGO | S_IWUGO, sys_val_show, sys_val_store);  //在sysfs中建立reg和val变量。
    static int lc1120_register(struct snd_soc_codec *codec)
    {
        int ret;
    D_ENTER;
        lc1120_codec = codec;
        codec->name = "lc1120";
        codec->owner = THIS_MODULE;
        codec->read = lc1120_read;
        codec->write = lc1120_write;
    /* TODO:
        codec->set_bias_level = lc1120_set_bias_level;
    */
        codec->dai = &lc1120_dai;   //dai 就是soc与codec通信的链接。
        codec->num_dai = 1;
        ret = snd_soc_register_codec(codec);  //标准注册codec函数。
        if (ret) {
            dev_err(codec->dev, "Failed to register codec\n");
            return ret;
        }
        ret = snd_soc_register_dai(&lc1120_dai);  //标准注册dai函数。
        if (ret) {
            dev_err(codec->dev, "Failed to register dai\n");
            snd_soc_unregister_codec(codec);
            return ret;
        }
    printk(KERN_ERR "yzt %s exited\n", __func__);
        return 0;
    }
    static int lc1120_unregister(struct lc1120_priv *lc1120)
    {
    D_ENTER;
    //    lc1120_set_bias_level(&lc1120->codec, SND_SOC_BIAS_OFF);
        snd_soc_unregister_dai(&lc1120_dai);      //先注销dai
        snd_soc_unregister_codec(&lc1120->codec);  //在注销codec
        lc1120_codec = NULL;
        return 0;
    }
二.在sound/soc/comip/snd_soc_comip_lc1120.c中的初始化代码:

1.comip_lc1120_init()/comip_lc1120_exit()

static struct clk *codec_mclk;
    static struct platform_device *snd_soc_comip_lc1120_device;  //两个全局变量,下面会用到。

    static int __init comip_lc1120_init(void)
    {
        int ret;
    D_ENTER;
        codec_mclk = snd_soc_codec_set_mclk();   //在soc中配置寄存器,使之输出13M I2S main_clk
        snd_soc_comip_lc1120_device = platform_device_alloc("soc-audio", -1);  //申请platform device 空间。
        if (!snd_soc_comip_lc1120_device) {
            printk(KERN_ERR "Platform device allocation failed\n");
            return -ENOMEM;
        }
        //把snd_soc_device 指向 platform -> dev
        //snd_soc_comip_lc1120_devdata 是snd_soc_device,snd_soc_comip_lc1120_device 是platform
        platform_set_drvdata(snd_soc_comip_lc1120_device, &snd_soc_comip_lc1120_devdata);
        snd_soc_comip_lc1120_devdata.dev = &snd_soc_comip_lc1120_device->dev;
        ret = platform_device_add(snd_soc_comip_lc1120_device);  添加一个platform设备。
        if (ret) {
            printk(KERN_ERR "Soc Audio: Unable to add lc1120\n");
            platform_device_put(snd_soc_comip_lc1120_device);
        }
        return ret;
    }
    module_init(comip_lc1120_init);
    static void __exit comip_lc1120_exit(void)
    {
    D_ENTER;
        if (codec_mclk)
            clk_put(codec_mclk);
        codec_mclk = NULL;   //时钟清零
        platform_device_unregister(snd_soc_comip_lc1120_device);
    }
    module_exit(comip_lc1120_exit);
2. 几个结构体之间的关系:

a.一个platform_device 包含一个device 和一个resource。resource很有用。

struct platform_device {
        const char    * name;
        int        id;
        struct device    dev;
        u32        num_resources;
        struct resource    * resource;
        const struct platform_device_id    *id_entry;
        /* arch specific additions */
        struct pdev_archdata    archdata;
    };
b.一个snd_soc_device 包括一个snd_soc_card 和一个 snd_soc_codec_device.
/* SoC Device - the audio subsystem */
    struct snd_soc_device {
        struct device *dev;
        struct snd_soc_card *card;
        struct snd_soc_codec_device *codec_dev;
        void *codec_data;
    };
具体如下:
/* Audio subsystem */
    static struct snd_soc_device snd_soc_comip_lc1120_devdata =  //snd_soc_device包含了两项
    {
        .card = &snd_soc_comip_lc1120_card_data,   //snd_soc_card.
        .codec_dev = &soc_codec_dev_lc1120,        //snd_soc_codec_device.
    };
c.一 snd_soc_card 包含一个 snd_soc_platform 和一个 snd_soc_dai_link :
/* Audio machine driver */
    static struct snd_soc_card snd_soc_comip_lc1120_card_data =
    {
        .name = "comip_lc1120",
        .platform = &comip_soc_platform,         //snd_soc_platform,是指Soc端的音频操作
        .dai_link = snd_soc_comip_lc1120_dai_link,  //snd_soc_dai_link,Soc和codec 两个dai的 链接
        .num_links = ARRAY_SIZE(snd_soc_comip_lc1120_dai_link),
    };
d. 一个 snd_soc_platform 表示Soc端的pcm码流操作,包含:
struct snd_soc_platform comip_soc_platform = {
        .name        = "comip-audio",
        .pcm_ops     = &comip_pcm_ops,
        .pcm_new    = comip_pcm_new,
        .pcm_free    = comip_pcm_free_dma_buffers,
    };
    EXPORT_SYMBOL_GPL(comip_soc_platform);
e. 一个snd_soc_dai_link 包含两个snd_soc_dai,一个cpu_dai和一个codec_dai;还有一个snd_soc_ops
static struct snd_soc_dai_link snd_soc_comip_lc1120_dai_link[] =
    {
        {
            .name = "comip_i2s_lc1120",
            .stream_name = "comip_i2s_lc1120",
            .cpu_dai = &comip_i2s_dai,
            .codec_dai = &lc1120_dai,
            .ops = &snd_soc_comip_lc1120_ops,
        },
    };
f:snd_soc_ops snd_soc_comip_lc1120_ops ,包含如下;音频播放的时候,会调用trigger,startup,hw_param等等。
/* Digital audio interface glue - connects codec <--> CPU */
    static struct snd_soc_ops snd_soc_comip_lc1120_ops =
    {
        .startup = comip_lc1120_startup,  
        .shutdown = comip_lc1120_shutdown,
        .hw_params = comip_lc1120_i2s_hw_params,
        .hw_free = comip_lc1120_hw_free,
        .prepare = comip_lc1120_prepare,
        .trigger = comip_lc1120_trigger,
    };
g. comip_lc1120_i2s_hw_params内容如下,其他基本都是直接return 0;
static int comip_lc1120_i2s_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;
    int ret = 0;
//    unsigned long rate = params_rate(params);



    D_ENTER;
        ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
                            SND_SOC_DAIFMT_CBM_CFM);
        if (ret < 0)
            return ret;
        ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
                            SND_SOC_DAIFMT_CBM_CFM);
        if (ret < 0)
            return ret;
        return ret;
    }
h. struct snd_soc_codec_device soc_codec_dev_lc1120定义如下:soc_codec_dev_lc1120

struct snd_soc_codec_device soc_codec_dev_lc1120 = {
        .probe = lc1120_probe, //在注册了soc_codec_dev_lc1120之后,就自动运行lc1120_probe
        .remove = lc1120_remove,
        .suspend = lc1120_suspend,
        .resume = lc1120_resume,
    };
    EXPORT_SYMBOL_GPL(soc_codec_dev_lc1120);

三. lc1120 功能部分,注册完了之后,就具备了一系列功能。

1. lc1120_probe():

static struct snd_soc_codec *lc1120_codec;    //定义了一个全局变量

    static int lc1120_probe(struct platform_device *pdev)
    {
        struct snd_soc_device *socdev = platform_get_drvdata(pdev);
        struct snd_soc_codec *codec;
        int ret = 0;
    D_ENTER;
        codec = lc1120_codec;
        if (!codec) {
            dev_err(&pdev->dev, "Codec not registered\n");
            return -ENODEV;
        }
        if (lc1120_write_regs(codec, lc1120_init_data,   //初始化一系列寄存器
                ARRAY_SIZE(lc1120_init_data)))
        {
            printk(KERN_ERR "%s: initialize lc1120 failed\n", __func__);
            return -ENODEV;
        }
        socdev->card->codec = codec;
        /* register pcms */
        mutex_init(&codec->mutex);
        ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);   //create new sound card and pcms。
        if (ret < 0) {
            printk(KERN_ERR "%s: snd_soc_new_pcms failed for %d\n",
                __func__, ret);
            goto pcm_err;
        }
        ret = snd_soc_add_controls(codec, lc1120_snd_controls,  //add an array of controls to a codec.
                 ARRAY_SIZE(lc1120_snd_controls));
        if (ret < 0)
        {
            printk(KERN_ERR "%s: snd_soc_add controls failed for %d\n",
                __func__, ret);
            goto pcm_err;
        }
        INIT_LIST_HEAD(&codec->dapm_widgets);
        INIT_LIST_HEAD(&codec->dapm_paths);
        ret = snd_soc_dapm_new_controls(codec, lc1120_dapm_widgets,  //create new dapm controls
                    ARRAY_SIZE(lc1120_dapm_widgets));
        if (ret < 0)
        {
            printk(KERN_ERR "%s: snd_soc_dapm_new_controls failed for %d\n",
                __func__, ret);
            goto pcm_err;
        }
        ret = snd_soc_dapm_add_routes(codec, lc1120_route,     //Add routes between DAPM widgets
                    ARRAY_SIZE(lc1120_route));
        if (ret < 0)
        {
            printk(KERN_ERR "%s: snd_soc_dapm_add_routes failed for %d\n",
                __func__, ret);
            goto pcm_err;
        }
        ret = snd_soc_dapm_new_widgets(codec);       //add new dapm widgets
        if (ret < 0)
        {
            printk(KERN_ERR "%s: snd_soc_dapm_new_widgets failed for %d\n",
                __func__, ret);
            goto pcm_err;
        }
    printk(KERN_ERR "yzt: %s exited\n", __func__);
    pcm_err:
        return ret;
    }


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值