wm8960驱动代码

/* machine tiny_wm8960.c */
platform_set_drvdata(&asoc_dev, &myalsa_card);   //设置平台设备的私有数据
        static struct snd_soc_card myalsa_card = {
            .name = "S3C2440_UDA1341",
            .owner = THIS_MODULE,
            .dai_link = &s3c2440_uda1341_dai_link,
                static struct snd_soc_dai_link s3c2440_uda1341_dai_link = {
                    .name = "100ask_UDA1341",
                    .stream_name = "100ask_UDA1341",
                    .codec_name = "wm8960-codec.0-001a",      // codec
                    .codec_dai_name = "wm8960-hifi",          //codec dai
                    .cpu_dai_name = "samsung-i2s.0",          //cpu dai
                    .ops = &s3c2440_uda1341_ops,              //??
                        static struct snd_soc_ops s3c2440_uda1341_ops = {
                            .hw_params = smdk_hw_params,
                        };
                    .platform_name    = "samsung-audio",        //dma
                    .init = tiny4412_wm8960_machine_init,     //??
                };
            .num_links = 1,
        };
platform_device_register(&asoc_dev);            //注册平台设备
    static struct platform_device asoc_dev = {
        .name         = "soc-audio",            //这名字是固定的,对应的平台驱动会被调用
        .id       = -1,
        .dev = {
            .release = asoc_release,
        },
    };
    
    
    
/* codec : wm8960.c */
ret = snd_soc_register_codec(&i2c->dev,&soc_codec_dev_wm8960, &wm8960_dai, 1);
    static struct snd_soc_codec_driver soc_codec_dev_wm8960 = {
        .probe                 =    wm8960_probe,                    //根据machine的dai_link的codec_name匹配,匹配成功调用到此
        .remove             =    wm8960_remove,                
        .suspend             =    wm8960_suspend,
        .resume             =    wm8960_resume,
        .set_bias_level     =     wm8960_set_bias_level,
        .reg_cache_size     =     ARRAY_SIZE(wm8960_reg),
        .reg_word_size         =     sizeof(u16),
        .reg_cache_default     =     wm8960_reg,
    };


/* codec dai : wm8960.c */
ret = snd_soc_register_codec(&i2c->dev,&soc_codec_dev_wm8960, &wm8960_dai, 1);    
    static struct snd_soc_dai_driver wm8960_dai = {
    .name = "wm8960-hifi",
    .playback = {
        .stream_name = "Playback",
        .channels_min = 1,
        .channels_max = 2,
        .rates = WM8960_RATES,
        .formats = WM8960_FORMATS,},
    .capture = {
        .stream_name = "Capture",
        .channels_min = 1,
        .channels_max = 2,
        .rates = WM8960_RATES,
        .formats = WM8960_FORMATS,},
    .ops = &wm8960_dai_ops,
        static struct snd_soc_dai_ops wm8960_dai_ops = {
            .hw_params = wm8960_hw_params,
            .digital_mute = wm8960_mute,
            .set_fmt = wm8960_set_dai_fmt,
            .set_clkdiv = wm8960_set_dai_clkdiv,
            .set_pll = wm8960_set_dai_pll,
        };
    .symmetric_rates = 1,
};


/* cpu dai : i2s.c*/
    pri_dai = i2s_alloc_dai(pdev, false);
    if (!pri_dai) {
        dev_err(&pdev->dev, "Unable to alloc I2S_pri\n");
        ret = -ENOMEM;
        goto err;
    }

    pri_dai->dma_playback.dma_addr = regs_base + I2STXD;
    pri_dai->dma_capture.dma_addr = regs_base + I2SRXD;
    pri_dai->dma_playback.client =
        (struct s3c2410_dma_client *)&pri_dai->dma_playback;
    pri_dai->dma_capture.client =
        (struct s3c2410_dma_client *)&pri_dai->dma_capture;
    pri_dai->dma_playback.channel = dma_pl_chan;
    pri_dai->dma_capture.channel = dma_cp_chan;
    pri_dai->src_clk = i2s_cfg->src_clk;
    pri_dai->dma_playback.dma_size = 4;
    pri_dai->dma_capture.dma_size = 4;
    pri_dai->base = regs_base;
    pri_dai->quirks = quirks;
    if (pdev->id == 0) {
        pri_dai->audss_clk_enable = audss_clk_enable;
        pri_dai->audss_suspend = audss_suspend;
        pri_dai->audss_resume = audss_resume;
    }

    if (quirks & QUIRK_PRI_6CHAN)
        pri_dai->i2s_dai_drv.playback.channels_max = 6;

    if (quirks & QUIRK_SEC_DAI) {
        sec_dai = i2s_alloc_dai(pdev, true);
        if (!sec_dai) {
            dev_err(&pdev->dev, "Unable to alloc I2S_sec\n");
            ret = -ENOMEM;
            goto err;
        }
        sec_dai->dma_playback.dma_addr = regs_base + I2STXDS;
        sec_dai->dma_playback.client =
            (struct s3c2410_dma_client *)&sec_dai->dma_playback;
        /* Use iDMA always if SysDMA not provided */
        sec_dai->dma_playback.channel = dma_pl_sec_chan ? : -1;
        sec_dai->src_clk = i2s_cfg->src_clk;
        sec_dai->dma_playback.dma_size = 4;
        sec_dai->base = regs_base;
        sec_dai->quirks = quirks;
        sec_dai->pri_dai = pri_dai;
        pri_dai->sec_dai = sec_dai;
        if (pdev->id == 0) {
            sec_dai->audss_clk_enable = audss_clk_enable;
            sec_dai->audss_suspend = audss_suspend;
            sec_dai->audss_resume = audss_resume;
        }
    }
    snd_soc_register_dai(&pri_dai->pdev->dev, &pri_dai->i2s_dai_drv);

/* dma : dma.c */
snd_soc_register_platform(&pdev->dev, &samsung_asoc_platform);
    static struct snd_soc_platform_driver samsung_asoc_platform = {
        .ops        = &dma_ops,
            static struct snd_pcm_ops dma_ops = {
                .open        = dma_open,
                .close        = dma_close,
                .ioctl        = snd_pcm_lib_ioctl,
                .hw_params    = dma_hw_params,
                .hw_free    = dma_hw_free,
                .prepare    = dma_prepare,
                .trigger    = dma_trigger,
                .pointer    = dma_pointer,
                .mmap        = dma_mmap,
            };
        .pcm_new    = dma_new,
        .pcm_free    = dma_free_dma_buffers,
    };


/**************************************************************************************************************/
/* machine */
/* 注册平台设备后对应的平台驱动的probe会被调用 */
static int soc_probe(struct platform_device *pdev)
    ret = snd_soc_register_card(card);
        snd_soc_initialize_card_lists(card);
        card->rtd = kzalloc(sizeof(struct snd_soc_pcm_runtime) *(card->num_links + card->num_aux_devs),GFP_KERNEL);
        INIT_LIST_HEAD(&card->list);
        list_add(&card->list, &card_list);
        snd_soc_instantiate_cards();
            snd_soc_instantiate_card(card);
                soc_bind_dai_link(card, i);   //绑定cpudai,codec,codec dai,platform
                    /* 1.find CPU DAI */
                    rtd->cpu_dai = cpu_dai;  //&pri_dai->i2s_dai_drv
                    /* 2.find_codec */
                    rtd->codec = codec;      //soc_codec_dev_wm8960
                    /* 3.find CODEC DAI */      
                    rtd->codec_dai = codec_dai;  //wm8960_dai
                    /* 4.find_platform */
                    rtd->platform = platform;    //samsung_asoc_platform
                /* initialize the register cache for each available codec */
                ret = snd_soc_init_codec_cache(codec, compress_type);
                /* 创建snd_card */
                ret = snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,card->owner, 0, &card->snd_card);
                /* 注册widget */
                if (card->dapm_widgets)
                    snd_soc_dapm_new_controls(&card->dapm, card->dapm_widgets,card->num_dapm_widgets);
                /* 调用cpu_dai,codec,codec_dai,platform probe函数 */
                ret = soc_probe_dai_link(card, i, order) {
                    /* probe the cpu_dai */
                    snd_soc_dapm_new_dai_widgets(&cpu_dai->dapm, cpu_dai);       //注册cpu_dai的widget
                    ret = cpu_dai->driver->probe(cpu_dai);                        //如果有probe函数调用probe函数
                    /* mark cpu_dai as probed and add to card dai list */
                    list_add(&cpu_dai->card_list, &card->dai_dev_list);
                    
                    /* probe the CODEC */
                    ret = soc_probe_codec(card, codec);
                        snd_soc_dapm_new_controls(&codec->dapm, driver->dapm_widgets,driver->num_dapm_widgets);   //如果codec的dapm_widgets不为空,注册它们
                        snd_soc_dapm_new_dai_widgets(&codec->dapm, dai);        //在dai_list找到对应codec的codec_dai,注册codec_dai的widget,??问什么codec_dai的widget会在这里注册??
                        ret = driver->probe(codec);            //probe函数不为空,调用它,就是wm8960_probe
                        snd_soc_add_codec_controls(codec, driver->controls,driver->num_controls);    //controls不为空,注册它
                        snd_soc_dapm_add_routes(&codec->dapm, driver->dapm_routes, driver->num_dapm_routes); //route不为空,注册它
                        list_add(&codec->card_list, &card->codec_dev_list);
                        list_add(&codec->dapm.list, &card->dapm_list);

    
                    /* probe the platform */
                    ret = soc_probe_platform(card, platform);
                        snd_soc_dapm_new_controls(&platform->dapm,driver->dapm_widgets, driver->num_dapm_widgets);  //如果platform的dapm_widgets不为空,注册它们
                        snd_soc_dapm_new_dai_widgets(&platform->dapm, dai);        //
                        ret = driver->probe(platform);        //probe函数不为空,调用它,这里为NULL
                        snd_soc_add_platform_controls(platform, driver->controls,driver->num_controls);  //controls不为空,注册它
                        snd_soc_dapm_add_routes(&platform->dapm, driver->dapm_routes,driver->num_dapm_routes);//route不为空,注册它
                        list_add(&platform->card_list, &card->platform_dev_list);
                        list_add(&platform->dapm.list, &card->dapm_list);

                    
                    /* probe the CODEC DAI */
                    ret = codec_dai->driver->probe(codec_dai);                    //如果有probe函数调用probe函数
                    list_add(&codec_dai->card_list, &card->dai_dev_list);

                    
                    
                    ret = soc_post_component_init(card, codec, num, 0);
                        snd_soc_dapm_new_widgets(&codec->dapm); //遍历card的widgets链表,取出kcontrol_new重新创建kcontrol,注册到card的controls链表
                    ret = device_create_file(rtd->dev, &dev_attr_pmdown_time);
                    ret = soc_new_pcm(rtd, num);{
                        ret = snd_pcm_new(rtd->card->snd_card, new_name, num, playback,capture, &pcm);    
                        rtd->ops.open        = soc_pcm_open;
                        rtd->ops.hw_params    = soc_pcm_hw_params;
                        rtd->ops.prepare    = soc_pcm_prepare;
                        rtd->ops.trigger    = soc_pcm_trigger;
                        rtd->ops.hw_free    = soc_pcm_hw_free;
                        rtd->ops.close        = soc_pcm_close;
                        rtd->ops.pointer    = soc_pcm_pointer;
                        rtd->ops.ioctl        = soc_pcm_ioctl;
                        /* 将ops复制给substream */
                        if (playback)
                            snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &rtd->ops);
                        if (capture)
                            snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &rtd->ops);

                    }
                    ret = snd_soc_dapm_new_pcm(card, dai_link->params,capture_w, play_w);
                    ret = snd_soc_dapm_new_pcm(card, dai_link->params,capture_w, play_w);
                }
                
                snd_soc_dapm_link_dai_widgets(card);  //连接card中的dai widget和stream widget
                    if (dai->driver->playback.stream_name &&strstr(w->sname,dai->driver->playback.stream_name)) {
                        r.source = dai->playback_widget->name;            //源
                        r.sink = w->name;                                //目的
                        snd_soc_dapm_add_route(w->dapm, &r);            //添加路线
                    }

                    if (dai->driver->capture.stream_name && strstr(w->sname,dai->driver->capture.stream_name)) {
                        r.source = w->name;                                //源
                        r.sink = dai->capture_widget->name;                //目的
                        snd_soc_dapm_add_route(w->dapm, &r);            //添加路线
                    }
                snd_soc_add_card_controls(card, card->controls, card->num_controls);
                snd_soc_dapm_add_routes(&card->dapm, card->dapm_routes,card->num_dapm_routes);
                snd_soc_dapm_new_widgets(&card->dapm){  //创建dapm control   ??这里为什么会有两个snd_soc_dapm_new_widgets,难道codec->dapm->card不等于card->dapm->card吗??
                    /* 分配内存 */
                    w->kcontrols = kzalloc(w->num_kcontrols *sizeof(struct snd_kcontrol *),GFP_KERNEL);
                    /* 创建并初始化dapm control */
                    switch(w->id) {
                    case snd_soc_dapm_switch:
                    case snd_soc_dapm_mixer:
                    case snd_soc_dapm_mixer_named_ctl:
                        dapm_new_mixer(w);{
                                
                        }
                        break;
                    case snd_soc_dapm_mux:
                    case snd_soc_dapm_virt_mux:
                    case snd_soc_dapm_value_mux:
                        dapm_new_mux(w);
                        break;
                    case snd_soc_dapm_pga:
                    case snd_soc_dapm_out_drv:
                        dapm_new_pga(w);
                        break;
                    default:
                        break;
                    /* 根据widget寄存器是当前值,初始化widget的电源状态 */
                    /* 将widget加入到dapm_dirty链表,表示widget发生了变化 */
                    dapm_mark_dirty(w, "new widget");    //linux 3.0.86没有此函数
                    /* 统一处理dapm_dirty链表 */
                    dapm_power_widgets(dapm, SND_SOC_DAPM_STREAM_NOP);
        }
                }


/* codec , codec dai */
snd_soc_register_codec(&i2c->dev,&soc_codec_dev_wm8960, &wm8960_dai, 1);
    struct snd_soc_codec *codec;
    codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);  //分配一个codec
    codec->name = fmt_single_name(dev, &codec->id);   //codec 的名字用于匹配,[平台总线名字].[第几组i2c]-[i2c地址] , 如:wm8960-codec.0-001a
    /* 将副本codec(soc_codec_dev_wm8960)赋值给刚分配的codec */
    codec->write = codec_drv->write;
    codec->read = codec_drv->read;
    codec->volatile_register = codec_drv->volatile_register;
    codec->readable_register = codec_drv->readable_register;
    codec->writable_register = codec_drv->writable_register;
    codec->ignore_pmdown_time = codec_drv->ignore_pmdown_time;
    codec->dapm.bias_level = SND_SOC_BIAS_OFF;
    codec->dapm.dev = dev;
    codec->dapm.codec = codec;
    codec->dapm.seq_notifier = codec_drv->seq_notifier;
    codec->dapm.stream_event = codec_drv->stream_event;
    codec->dev = dev;
    codec->driver = codec_drv;
    codec->num_dai = num_dai;
    /* 将codec添加到codec->list */
    list_add(&codec->list, &codec_list);
    
    /* 注册codec dai */
    ret = snd_soc_register_dais(dev, dai_drv, num_dai);
        dai = kzalloc(sizeof(struct snd_soc_dai), GFP_KERNEL);  //分配dai
        dai->name = fmt_multiple_name(dev, &dai_drv[i]);        // wm8960-hifi
        
    /* codec匹配成功,调用codec的probe函数 */
    static int wm8960_probe(struct snd_soc_codec *codec)
        snd_soc_add_controls(codec, wm8960_snd_controls,ARRAY_SIZE(wm8960_snd_controls));        //注册kcontrol
        wm8960_add_widgets(codec);            
            snd_soc_dapm_new_controls(dapm, wm8960_dapm_widgets,ARRAY_SIZE(wm8960_dapm_widgets));    //注册codec的widget
                for (i = 0; i < num; i++) {  //为每个widget重新创建,注册
                    w = snd_soc_dapm_new_control(dapm, widget);
                        w = dapm_cnew_widget(widget))  //为widget重新分配内存
                        /* 根据widget类型,设置不同的power_check函数 */
                        switch (w->id) {
                        case snd_soc_dapm_switch:
                        case snd_soc_dapm_mixer:
                        case snd_soc_dapm_mixer_named_ctl:
                            w->power_check = dapm_generic_check_power;
                            break;
                        case snd_soc_dapm_mux:
                        case snd_soc_dapm_virt_mux:
                        case snd_soc_dapm_value_mux:
                            w->power_check = dapm_generic_check_power;
                            break;
                        case snd_soc_dapm_adc:
                        case snd_soc_dapm_aif_out:
                            w->power_check = dapm_adc_check_power;
                            break;
                        case snd_soc_dapm_dac:
                        case snd_soc_dapm_aif_in:
                            w->power_check = dapm_dac_check_power;
                            break;
                        case snd_soc_dapm_pga:
                        case snd_soc_dapm_out_drv:
                        case snd_soc_dapm_input:
                        case snd_soc_dapm_output:
                        case snd_soc_dapm_micbias:
                        case snd_soc_dapm_spk:
                        case snd_soc_dapm_hp:
                        case snd_soc_dapm_mic:
                        case snd_soc_dapm_line:
                        case snd_soc_dapm_dai_link:
                            w->power_check = dapm_generic_check_power;
                            break;
                        case snd_soc_dapm_supply:
                        case snd_soc_dapm_regulator_supply:
                            w->power_check = dapm_supply_check_power;
                            break;
                        case snd_soc_dapm_dai:
                            w->power_check = dapm_dai_check_power;
                            break;
                        default:
                            w->power_check = dapm_always_on_check_power;
                            break;
                        }
                        
                        /* 初始化链表 */
                        INIT_LIST_HEAD(&w->sources);    //所有widget输入端的snd_soc_path
                        INIT_LIST_HEAD(&w->sinks);        //所有widget输出端的snd_soc_path
                        INIT_LIST_HEAD(&w->list);        //链接到声卡的widgets链表
                        INIT_LIST_HEAD(&w->dirty);        //链接到声卡的dapm_dirty链表
                        //将widget加入到snd_soc_card链表
                        list_add(&w->list, &dapm->card->widgets);
                        //设置connect状态
                        w->connected = 1;
                        
            snd_soc_dapm_add_routes(dapm, audio_paths, ARRAY_SIZE(audio_paths));        //注册codec的route

            
====================================================================================================================================
片段
            
/* 创建设备节点:control */
snd_card_create
    snd_ctl_create(card);
        snd_device_new(card, SNDRV_DEV_CONTROL, card, &ops);
            /*会掉到*/
            snd_ctl_dev_register(struct snd_device *device)
                sprintf(name, "controlC%i", cardnum);  //设备名
                snd_register_device(SNDRV_DEVICE_TYPE_CONTROL, card, -1,&snd_ctl_f_ops, card, name))
                    snd_register_device_for_dev(type, card, dev, f_ops,private_data, name,snd_card_get_device_link(card));
                        snd_minors[minor] = preg;
                        preg->dev = device_create(sound_class, device, MKDEV(major, minor),private_data, "%s", name);  //创建设备节点

/* fops */                        
static const struct file_operations snd_ctl_f_ops =
{
    .owner =    THIS_MODULE,
    .read =        snd_ctl_read,
    .open =        snd_ctl_open,
    .release =    snd_ctl_release,
    .llseek =    no_llseek,
    .poll =        snd_ctl_poll,
    .unlocked_ioctl =    snd_ctl_ioctl,{
        switch (cmd) {
        case SNDRV_CTL_IOCTL_ELEM_READ:
            return snd_ctl_elem_read_user(card, argp) {
                result = snd_ctl_elem_read(card, control);            
                kctl = snd_ctl_find_id(card, &control->id);            //找到对应的kcontrol
                result = kctl->get(kctl, control);                    //调用get函数
            }
        case SNDRV_CTL_IOCTL_ELEM_WRITE:
            return snd_ctl_elem_write_user(ctl, argp){
                result = snd_ctl_elem_write(card, file, control);
                    kctl = snd_ctl_find_id(card, &control->id);       //找到对应的kcontrol
                    result = kctl->put(kctl, control);                //调用put函数
            }
        }
    }
    .compat_ioctl =    snd_ctl_ioctl_compat,
    .fasync =    snd_ctl_fasync,
};


                        
/* 创建设备节点:pcm */
int snd_pcm_new(struct snd_card *card, const char *id, int device,int playback_count, int capture_count, struct snd_pcm **rpcm)
    _snd_pcm_new(card, id, device, playback_count, capture_count,false, rpcm);
        /*会掉到*/
        static int snd_pcm_dev_register(struct snd_device *device)
            sprintf(str, "pcmC%iD%ip", pcm->card->number, pcm->device);
            devtype = SNDRV_DEVICE_TYPE_PCM_PLAYBACK;
            或
                sprintf(str, "pcmC%iD%ic", pcm->card->number, pcm->device);
                devtype = SNDRV_DEVICE_TYPE_PCM_CAPTURE;
            err = snd_register_device_for_dev(devtype, pcm->card,pcm->device,&snd_pcm_f_ops[cidx],pcm, str, dev);
                snd_minors[minor] = preg;
                preg->dev = device_create(sound_class, device, MKDEV(major, minor),private_data, "%s", name); //创建pcmC0D0p 或 pcmC0D0

/* fops */
const struct file_operations snd_pcm_f_ops[2] = {
    {
        .owner                 = THIS_MODULE,
        .write                 = snd_pcm_write,
        .aio_write             = snd_pcm_aio_write,
        .open                 = snd_pcm_playback_open,{
            struct snd_pcm *pcm;
            pcm = snd_lookup_minor_data(iminor(inode),SNDRV_DEVICE_TYPE_PCM_PLAYBACK);    //在snd_minors数组中根据次设备号找到对应的snd_minor,这个snd_minor的私有数据就是snd_pcm对象
            err = snd_pcm_open(file, pcm, SNDRV_PCM_STREAM_PLAYBACK);
                err = snd_pcm_open_file(file, pcm, stream); //stream=SNDRV_PCM_STREAM_PLAYBACK
                    struct snd_pcm_file *pcm_file;
                    struct snd_pcm_substream *substream;
                    err = snd_pcm_open_substream(pcm, stream, file, &substream);        //根据stream获取对应的snd_pcm_substream
                        err = snd_pcm_attach_substream(pcm, stream, file, &substream);  //找到substream
                        err = snd_pcm_hw_constraints_init(substream);                    
                        err = substream->ops->open(substream)                            //substream->ops : snd_pcm_ops结构体
                            static int soc_pcm_open(struct snd_pcm_substream *substream)
                                ret = cpu_dai->driver->ops->startup(substream, cpu_dai);        //cpu dai的startup
                                ret = platform->driver->ops->open(substream);                    //dma的open
                                ret = codec_dai->driver->ops->startup(substream, codec_dai);    //codec dai的startup
                                ret = rtd->dai_link->ops->startup(substream);                    //machine的startup
                                /* 检查codec dai与cpu dai是参数否完全相同,比如采样频率,通道数 */
                                /* 设置格式和采样率 */
                                
                        err = snd_pcm_hw_constraints_complete(substream);
                        
                    pcm_file = kzalloc(sizeof(*pcm_file), GFP_KERNEL);                        
                    pcm_file->substream = substream;                                
                    file->private_data = pcm_file;                                    //这样就可以通过file的私有数据找到snd_pcm_substream
                
                    
        }
        .release             = snd_pcm_release,
        .llseek             = no_llseek,
        .poll                 = snd_pcm_playback_poll,
        .unlocked_ioctl     = snd_pcm_playback_ioctl,{
            return snd_pcm_playback_ioctl1(file, pcm_file->substream, cmd,(void __user *)arg);
                case SNDRV_PCM_IOCTL_WRITEI_FRAMES:
                    result = snd_pcm_lib_write(substream, xferi.buf, xferi.frames);
                        snd_pcm_lib_write1(substream, (unsigned long)buf, size, nonblock,snd_pcm_lib_write_transfer);
                            err = transfer(substream, appl_ofs, data, offset, frames);  //就是snd_pcm_lib_write_transfer函数
                                copy_from_user(hwbuf, buf, frames_to_bytes(runtime, frames))
                            err = snd_pcm_start(substream);   //启动传输
                        
                    return
                snd_pcm_common_ioctl1(file, substream, cmd, arg);
                    case SNDRV_PCM_IOCTL_HW_PARAMS:
                        return snd_pcm_hw_params_user(substream, arg);
                            err = snd_pcm_hw_params(substream, params);
                                err = substream->ops->hw_params(substream, params);
                                    static int soc_pcm_hw_params(struct snd_pcm_substream *substream,struct snd_pcm_hw_params *params)
                                        ret = rtd->dai_link->ops->hw_params(substream, params);                        // machine的hw_params回调函数
                                        ret = codec_dai->driver->ops->hw_params(substream, params, codec_dai);        // codec_dai的hw_params回调函数
                                        ret = cpu_dai->driver->ops->hw_params(substream, params, cpu_dai);            // cpu_dai的hw_params回调函数
                                        ret = platform->driver->ops->hw_params(substream, params);                    // platform的hw_params回调函数
                                        cpu_dai->rate = params_rate(params);                                        //保存
                                        codec_dai->rate = params_rate(params);                                        //保存
                                    
                            copy_to_user(_params, params, sizeof(*params))  //给用户层
                            
                    
        }
        .compat_ioctl         = snd_pcm_ioctl_compat,
        .mmap                 = snd_pcm_mmap,
        .fasync             = snd_pcm_fasync,
        .get_unmapped_area     = snd_pcm_get_unmapped_area,
    },
    {
        .owner                 = THIS_MODULE,
        .read                 = snd_pcm_read,
        .aio_read             = snd_pcm_aio_read,
        .open                 = snd_pcm_capture_open,{
            pcm = snd_lookup_minor_data(iminor(inode),SNDRV_DEVICE_TYPE_PCM_CAPTURE);
            err = snd_pcm_open(file, pcm, SNDRV_PCM_STREAM_CAPTURE);    //同上
        }
        .release             = snd_pcm_release,
        .llseek             = no_llseek,
        .poll                 = snd_pcm_capture_poll,
        .unlocked_ioctl     = snd_pcm_capture_ioctl,{
            snd_pcm_capture_ioctl1(file, pcm_file->substream, cmd,(void __user *)arg);
                case SNDRV_PCM_IOCTL_READI_FRAMES:    
                    result = snd_pcm_lib_read(substream, xferi.buf, xferi.frames);
                        snd_pcm_lib_read1(substream, (unsigned long)buf, size, nonblock, snd_pcm_lib_read_transfer);
                            case SNDRV_PCM_STATE_PREPARED:
                                err = snd_pcm_start(substream);   //开始传输
                            err = transfer(substream, appl_ofs, data, offset, frames);   //就是snd_pcm_lib_read_transfer
                                copy_to_user(buf, hwbuf, frames_to_bytes(runtime, frames))  //数据返回给用户层
                    return
                snd_pcm_common_ioctl1(file, substream, cmd, arg);

        }
        .compat_ioctl         = snd_pcm_ioctl_compat,
        .mmap                 = snd_pcm_mmap,
        .fasync             = snd_pcm_fasync,
        .get_unmapped_area     = snd_pcm_get_unmapped_area,
    }
};


/* 注册kontrol */
static const struct snd_kcontrol_new wm8960_snd_controls[] = {
    SOC_DOUBLE_R_TLV("Capture Volume", WM8960_LINVOL, WM8960_RINVOL,
             0, 63, 0, adc_tlv),
    SOC_DOUBLE_R("Capture Volume ZC Switch", WM8960_LINVOL, WM8960_RINVOL,
        6, 1, 0),
    ...
};
snd_soc_add_codec_controls(codec, wm8960_snd_controls,ARRAY_SIZE(wm8960_snd_controls));
    snd_soc_add_controls(card, codec->dev, controls, num_controls,codec->name_prefix, codec);
        for (i = 0; i < num_controls; i++) {   //for循环注册每一个control
            const struct snd_kcontrol_new *control = &controls[i];
            err = snd_ctl_add(card, snd_soc_cnew(control, data,control->name, prefix));   //将kcontrol注册到card的controls链表中,data就是codec,所有kcontrol的private_data=codec
                //snd_soc_cnew(control, data,control->name, prefix)  //将kcontrol_new转换成kcontrol
        }
    

/* widget */
/* route ==> path */
static const struct snd_soc_dapm_route audio_paths[] = {
    { "Left Boost Mixer", "LINPUT1 Switch", "LINPUT1" },
    { "Left Boost Mixer", "LINPUT2 Switch", "LINPUT2" },
    { "Left Boost Mixer", "LINPUT3 Switch", "LINPUT3" },
    ...
};
snd_soc_dapm_add_routes(dapm, audio_paths, ARRAY_SIZE(audio_paths));
    for (i = 0; i < num; i++) {                        //num=ARRAY_SIZE(audio_paths)
        ret = snd_soc_dapm_add_route(dapm, route);  //
            list_for_each_entry(w, &dapm->card->widgets, list) {     //遍历card的widgets链表
                if (!wsink && !(strcmp(w->name, sink))) {             //比较sink
                    wtsink = w;
                    if (w->dapm == dapm)                             //比较dapm
                        wsink = w;
                    continue;
                }
                if (!wsource && !(strcmp(w->name, source))) {         //比较souce
                    wtsource = w;
                    if (w->dapm == dapm)                            //比较dapm
                        wsource = w;
                }
            }
            /* 如果dapm不相同,使用wtsink,wtsource */
            if (!wsink)
                wsink = wtsink;
            if (!wsource)
                wsource = wtsource;
            
            path = kzalloc(sizeof(struct snd_soc_dapm_path), GFP_KERNEL);  //分配一个snd_soc_dapm_path
            /* 连接静态path:就是route的kcontrol为NULL */
            if (control == NULL) {
                list_add(&path->list, &dapm->card->paths);            //将path加入到card的path链表
                list_add(&path->list_sink, &wsink->sources);        //path输出就是widget的输入
                list_add(&path->list_source, &wsource->sinks);        //path输入就是widget的输出
                path->connect = 1;                                    //设置path为已连接状态
                return 0;
            }
            /* 连接动态path:就是route的kcontrol不为NULL */
            switch (wsink->id) {
            case snd_soc_dapm_adc:
            case snd_soc_dapm_dac:
            case snd_soc_dapm_pga:
            case snd_soc_dapm_out_drv:
            case snd_soc_dapm_input:
            case snd_soc_dapm_output:
            case snd_soc_dapm_siggen:
            case snd_soc_dapm_micbias:
            case snd_soc_dapm_vmid:
            case snd_soc_dapm_pre:
            case snd_soc_dapm_post:
            case snd_soc_dapm_supply:
            case snd_soc_dapm_regulator_supply:
            case snd_soc_dapm_aif_in:
            case snd_soc_dapm_aif_out:
            case snd_soc_dapm_dai:
            case snd_soc_dapm_dai_link:
                list_add(&path->list, &dapm->card->paths);
                list_add(&path->list_sink, &wsink->sources);
                list_add(&path->list_source, &wsource->sinks);
                path->connect = 1;
                return 0;
            case snd_soc_dapm_mux:
            case snd_soc_dapm_virt_mux:
            case snd_soc_dapm_value_mux:
                ret = dapm_connect_mux(dapm, wsource, wsink, path, control,&wsink->kcontrol_news[0]);      //连接mux根据寄存器值,设置connect状态
                if (ret != 0)
                    goto err;
                break;
            case snd_soc_dapm_switch:
            case snd_soc_dapm_mixer:
            case snd_soc_dapm_mixer_named_ctl:
                ret = dapm_connect_mixer(dapm, wsource, wsink, path, control);                            //连接mix根据寄存器值,设置connect状态
                if (ret != 0)
                    goto err;
                break;
            case snd_soc_dapm_hp:
            case snd_soc_dapm_mic:
            case snd_soc_dapm_line:
            case snd_soc_dapm_spk:
                list_add(&path->list, &dapm->card->paths);
                list_add(&path->list_sink, &wsink->sources);
                list_add(&path->list_source, &wsource->sinks);
                path->connect = 0;
                return 0;
            }
            

/* dapm kcontrol */
int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm) //遍历card的widgets链表,取出kcontrol_new重新创建kcontrol,注册到card的controls链表
    list_for_each_entry(w, &dapm->card->widgets, list)  //遍历card的widgets链表
        w->kcontrols = kzalloc(w->num_kcontrols *sizeof(struct snd_kcontrol *),GFP_KERNEL); //给widget的kcontrols分配内存
        /* 创建dapm kcontrol */
        switch(w->id) {
        case snd_soc_dapm_switch:
        case snd_soc_dapm_mixer:
        case snd_soc_dapm_mixer_named_ctl:
            dapm_new_mixer(w);        //创建mix类的dapm kcontrol
                for (i = 0; i < w->num_kcontrols; i++) {   //for循环widget的kcontrol_new数组
                    list_for_each_entry(path, &w->sources, list_sink) { //遍历widget的输入path
                        if (path->name != (char *)w->kcontrol_news[i].name)  //path->name 与kcontrol_news的name比较,意思是说:如果没path使用kcontrol_new就没必要创建dapm kcontrol了
                            continue;
                        if (w->kcontrols[i]) {
                            path->kcontrol = w->kcontrols[i];  //如果dapm kcontrol已经有了,就没必要继续了,直接给path->kcontrol
                            continue;
                        }
                        path->long_name = kmalloc(name_len, GFP_KERNEL);    //给dapm kcontrol的name分配空间
                        /* 给dapm kcontrol命名 */
                        switch (w->id) {
                        default:
                            snprintf((char *)path->long_name, name_len,"%s %s", w->name + prefix_len,w->kcontrol_news[i].name);   //widget的名字+kcontrol_new的名字
                            break;
                        case snd_soc_dapm_mixer_named_ctl:
                            snprintf((char *)path->long_name, name_len,"%s", w->kcontrol_news[i].name);
                            break;
                        }
                        
                        path->kcontrol = snd_soc_cnew(&w->kcontrol_news[i],wlist, path->long_name,prefix);  //将kcontrol_new转换成dapm kcontrol
                        ret = snd_ctl_add(card, path->kcontrol);        //将dapm kcontrol添加到card的controls链表
                        w->kcontrols[i] = path->kcontrol;                //把dapm kcontrol的指针给w->kcontrols[i],(path->kcontrol是个指针)
                    }
                }

            break;
        case snd_soc_dapm_mux:
        case snd_soc_dapm_virt_mux:
        case snd_soc_dapm_value_mux:
            dapm_new_mux(w);        //创建mux的dapm kcontrol
                shared = dapm_is_shared_kcontrol(dapm, w, &w->kcontrol_news[0],&kcontrol);  //????
                kcontrol = snd_soc_cnew(&w->kcontrol_news[0], wlist,name + prefix_len, prefix);   //mux只有一个kcontrol,将kcontrol_new转换成dapm kcontrol
                ret = snd_ctl_add(card, kcontrol);  //将dapm kcontrol添加到card的controls链表
                w->kcontrols[0] = kcontrol;            //mux只有一个kcontrol,保存它
                list_for_each_entry(path, &w->sources, list_sink)  //设置所有path的kcontrol
                    path->kcontrol = kcontrol;
                
            break;
        case snd_soc_dapm_pga:
        case snd_soc_dapm_out_drv:
            dapm_new_pga(w);
            break;
        default:
            break;
        }
        dapm_mark_dirty(w, "new widget");                        //加入dirty链表
        dapm_power_widgets(dapm, SND_SOC_DAPM_STREAM_NOP);        //统一处理
    

/* widget上电循序 */
static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event)
    list_for_each_entry(w, &card->dapm_dirty, dirty) {        //遍历card的dapm_dirty链表
        dapm_power_one_widget(w, &up_list, &down_list);        //按照一定的顺序, 把需要上电的widget放到up_list链表,把需要下电的widget放到down_list
            ret = dapm_widget_power_check(w);                        //调用每个widget的power_check函数
            dapm_widget_set_power(w, power, up_list, down_list);     //按照一定循序放到up_list或down_list
    }
    dapm_seq_run(dapm, &down_list, event, false);    {            //先下电
        list_for_each_entry_safe(w, n, list, power_list) {        //遍历list(即up_list或down_list),根据成员power_list找到挂到list上的每一个widget
            list_move(&w->power_list, &pending);        //加入到pending链表
        }
        if (!list_empty(&pending))
            dapm_seq_run_coalesced(cur_dapm, &pending); //统一处理widget的电源
                list_for_each_entry(w, pending, power_list)  {  //遍历pending链表,通过power_list成员找到widget
                    if (w->power != power)return;
                    dapm_seq_check_event(dapm, w, SND_SOC_DAPM_PRE_PMU);
                        ret = w->event(w, NULL, event);        //调用w的event函数,event就是SND_SOC_DAPM_PRE_PMU
                    dapm_seq_check_event(dapm, w, SND_SOC_DAPM_PRE_PMD);
                        ret = w->event(w, NULL, event);        //调用w的event函数,event就是SND_SOC_DAPM_PRE_PMD
                }
                pop_wait(card->pop_time);    //等待一下
                snd_soc_update_bits(dapm->codec, reg, mask, value);                //寄存器操作,改变电源
                list_for_each_entry(w, pending, power_list) {
                    if (w->power != power)return;
                    dapm_seq_check_event(dapm, w, SND_SOC_DAPM_POST_PMU);    
                        ret = w->event(w, NULL, event);        //调用w的event函数,event就是SND_SOC_DAPM_POST_PMU
                    dapm_seq_check_event(dapm, w, SND_SOC_DAPM_POST_PMD);    
                        ret = w->event(w, NULL, event);        //调用w的event函数,event就是SND_SOC_DAPM_POST_PMD    
                }
    }
        
    dapm_widget_update(dapm){                                    //更新dapm kcontrol
        ret = w->event(w, update->kcontrol, SND_SOC_DAPM_PRE_REG);                        //发出SND_SOC_DAPM_PRE_REG事件
        ret = snd_soc_update_bits(w->codec, update->reg, update->mask,update->val);        //更新dapm kcontrol电源
        ret = w->event(w, update->kcontrol, SND_SOC_DAPM_POST_REG);                        //发出SND_SOC_DAPM_POST_REG
    }
    dapm_seq_run(dapm, &up_list, event, true);                    //再上电,同上
    
    list_for_each_entry(d, &card->dapm_list, list)
        d->stream_event(d, event);        //linux3.0.86没有这个
    pop_wait(card->pop_time);                                    //等待一下

static int dapm_generic_check_power(struct snd_soc_dapm_widget *w)
    in = is_connected_input_ep(w, NULL);
    dapm_clear_walk_input(w->dapm, &w->sources);
    out = is_connected_output_ep(w, NULL);
    dapm_clear_walk_output(w->dapm, &w->sinks);


               

  • 14
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

seiyaaa

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值