1.匹配, 生成节点
-
上面的操作只能在作者指定的linux版本,或着低版本,比如Linux 4.4可以运行通过,在linux5.10上不能编译通过,alsa的函数调用已经发生变化,需要重新编写
-
vmachine.c
#include <linux/init.h> #include <linux/module.h> #include <linux/platform_device.h> #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h>
static struct snd_soc_dai_link_component cpus = { .name = "vcpus.0", .dai_name = "vcpus.0", };
static struct snd_soc_dai_link_component codecs = { .name = "vcodec.0", .dai_name = "vcodec.0", };
static struct snd_soc_dai_link_component platforms = { .name = "vplat.0", .dai_name = "vplat.0", };
static struct snd_soc_dai_link my_card_dai_link[] = { { .name = "my-codec", .stream_name = "MY-CODEC", .codecs = &codecs, .cpus = &cpus, .platforms = &platforms, // .init = my_card_init, // .ops = &my_card_ops, }, };
static const struct snd_soc_dapm_widget vmachine_widget[] = { SND_SOC_DAPM_MIC("vMachine mic",NULL), SND_SOC_DAPM_HP("Headphone hp",NULL), };
static const struct snd_soc_dapm_route snd_proto_route[] = { /*speaker connected to LHPOUT/RHPOUT */ {"Headphone hp",NULL,"LHPOUT"}, {"Headphone hp",NULL,"RHPOUT"},
/*mic is connected to Mic Jack, with WM8731 Mic Bias*/ {"MICIN", NULL, "Mic Bias"}, {"Mic Bias", NULL,"Microphone hp"},
};
static struct snd_soc_card snd_soc_my_card = { .name = "my-codec", .owner = THIS_MODULE, .dai_link = my_card_dai_link, .num_links = ARRAY_SIZE(my_card_dai_link), // .dapm_widgets = vmachine_widget, // .num_dapm_widgets=ARRAY_SIZE(vmachine_widget), // .dapm_routes = snd_proto_route, // .num_dapm_routes=ARRAY_SIZE(snd_proto_route), };
#define SNDRV_DEFAULT_IDX1 (-1) #define SNDRV_DEFAULT_STR1 NULL #define soc_setup_card_name(name,name1,name2,norm)
__soc_setup_card_name(name, sizeof(name),name1,name2,norm) static int isalnum(int c){ if(c>=0&&c<=9) return 1; if(c>='a'&&c<='z'||c>='A'&&c<='Z') return 1; return 0; } static void __soc_setup_card_name(char *name, int len,const char *name1,const char *name2, int normalization) { int i; snprintf(name, len, "%s", name1?name1:name2); if(!normalization) return;/* * Name normalization * * The driver name is somewhat specia, as it's used as a key for * searches in the user-space. * * ex) * "abcd??efg"->"abcd__efg" */ for(i = 0;i<len;i++){ switch (name[i]) { case '_': case '-': case '\0': break; default: if(!isalnum(name[i])) name[i]='_'; break; }}
}
static int vmachine_probe(struct platform_device *pdev) { int ret = 0; struct snd_soc_card *card = &snd_soc_my_card; struct snd_soc_component *component; printk("vmachine_probe-----%s----\n",func);
printk(KERN_ERR "pdev->name=%s\n",pdev->name); printk(KERN_ERR "pdev->dev=%p",pdev->dev); printk(KERN_ERR "card=%p",card); printk(KERN_ERR "card->dev=%p",card->dev); /* register the soc card */ card->dev = &pdev->dev; printk(KERN_ERR "++++card->dev=%p",card->dev); printk(KERN_ERR "card->widgets=%p",card->widgets); printk(KERN_ERR "card->paths=%p",card->paths); printk(KERN_ERR "card->dapm_list=%p",card->dapm_list); printk(KERN_ERR "card->aux_comp_list=%p",card->aux_comp_list); printk(KERN_ERR "card->component_dev_list=%p",card->component_dev_list); printk(KERN_ERR "card->list=%p",card->list); printk(KERN_ERR "card->dapm_dirty=%p",card->dapm_dirty); printk(KERN_ERR "card->dobj_list=%p",card->dobj_list); printk(KERN_ERR "card->mutex=%p",card->mutex); printk(KERN_ERR "card->dapm_mutex=%p",card->dapm_mutex); printk(KERN_ERR "card->pcm_mutex=%p",card->pcm_mutex);
// INIT_LIST_HEAD(&card->dobj_list); if(!card->name || !card->dev) { printk("card->name || !card->dev"); }
dev_set_drvdata(card->dev,card); INIT_LIST_HEAD(&card->widgets); INIT_LIST_HEAD(&card->paths); INIT_LIST_HEAD(&card->dapm_list); INIT_LIST_HEAD(&card->aux_comp_list); INIT_LIST_HEAD(&card->component_dev_list); INIT_LIST_HEAD(&card->list); INIT_LIST_HEAD(&card->rtd_list); INIT_LIST_HEAD(&card->dapm_dirty); INIT_LIST_HEAD(&card->dobj_list); card->instantiated = 0; mutex_init(&card->mutex); mutex_init(&card->dapm_mutex); mutex_init(&card->pcm_mutex); spin_lock_init(&card->dpcm_lock); // ret = devm_snd_soc_register_card(card->dev,card); //snd_soc_bind_card(card); snd_soc_dapm_init(&card->dapm,card,NULL); //soc_check_tplg_fes(card); //ret = soc_bind_aux_dev(card); card->num_rtd=0; int i; struct snd_soc_dai_link *dai_link; for_each_card_prelinks(card,i,dai_link){ ret=snd_soc_add_pcm_runtime(card,dai_link); if(ret<0) { printk(KERN_ERR "snd_soc_add_pcm_runtime failed"); return -1; } }
ret=snd_card_new(card->dev,SNDRV_DEFAULT_IDX1,SNDRV_DEFAULT_STR1,card->owner,0,&card->snd_card);
//soc_init_card_debugfs(card);
//soc_resumd_init(card);
//ret = snd_soc_dapm_new_controls(&card->dapm,card->dapm_widgets,card->num_dapm_widgets);
//ret = snd_soc_dapm_new_controls(&card->dapm,card->of_dapm_widgets,card->num_of_dapm_widgets);
//ret = snd_soc_card_probe(card);
//ret = soc_probe_link_components(card);
//ret = soc_probe_link_dais(card);
/for_each_card_rtds(card,rtd){ ret=soc_init_pcm_runtime(card,rtd); if(ret<0) goto probe_end; }/
//snd_soc_dapm_link_dai_widgets(card); //snd_soc_dapm_connect_dai_link_widgets(card);
ret=snd_soc_dapm_add_routes(&card->dapm,card->dapm_routes,card->num_dapm_routes);
snd_soc_set_dmi_name(card, NULL); soc_setup_card_name(card->snd_card->shortname, card->name,NULL,0); soc_setup_card_name(card->snd_card->longname,card->long_name,card->name,0); soc_setup_card_name(card->snd_card->driver, card->driver_name,card->name,1);
if(card->components) { ret = snd_component_add(card->snd_card,card->components);
if(ret<0){ goto probe_end; }
}
//ret = snd_soc_card_late_probe(card);
snd_soc_dapm_new_widgets(card); ret = snd_card_register(card->snd_card);
card->instantiated=1; dapm_mark_endpoints_dirty(card); snd_soc_dapm_sync(&card->dapm);
for_each_card_components(card,component) if(!snd_soc_component_active(component)) // pinctrl_pm_select_sleep_state(component->dev); printk(KERN_ERR,"5.15.0-71-generic");
probe_end: printk(KERN_ERR "++++++++++++ ret=%d\n",ret); if (ret < 0) { dev_err(&pdev->dev, "snd_soc_register_card failed %d\n", ret); return -1; }
return ret;
}
static int vmachine_remove(struct platform_device *pdev){ printk("-----%s----\n",func);
return 0;
}
/*static void vmachine_pdev_release(struct device dev) { }/
/static struct platform_device vmachine_pdev = { .name = "vmachine", .dev.release = vmachine_pdev_release, };/
static struct platform_device *vmachine_pdev;
static struct platform_driver vmachine_pdrv = { .probe = vmachine_probe, .remove = vmachine_remove, .driver = { .name = "vmachine", }, };
static int __init vmachine_init(void) { int ret; vmachine_pdev = platform_device_register_simple("vmachine",-1,NULL,0); /if (ret) return ret;/ ret = platform_driver_register(&vmachine_pdrv); if (ret) platform_device_unregister(vmachine_pdev); return ret; }
static void __exit vmachine_exit(void) { platform_driver_unregister(&vmachine_pdrv); platform_device_unregister(vmachine_pdev); }
module_init(vmachine_init); module_exit(vmachine_exit); MODULE_LICENSE("GPL");
-
vplatform.c
#include <linux/init.h> #include <linux/module.h> #include <linux/platform_device.h> #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h>
static const struct snd_soc_component_driver plat_cpudai_component = { .name = "plat-cpudai", };
static struct snd_soc_dai_driver plat_cpudai_dai = { .name = "plat-cpudai", .playback = { .channels_min = 1, .channels_max = 2, .rates = SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT, .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, }, .capture = { .channels_min = 1, .channels_max = 2, .rates = SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_KNOT, .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, }, .ops = NULL, };
static int plat_probe(struct platform_device *pdev) { int ret = 0;
printk("vplatform -----%s----\n",__func__); ret = devm_snd_soc_register_component(&pdev->dev, &plat_cpudai_component, &plat_cpudai_dai, 1); if (ret) { dev_err(&pdev->dev, "Could not register CPU DAI: %d\n", ret); // ret = -EBUSY; return ret; }
/* ret = snd_soc_register_platform(&pdev->dev, &plat_soc_drv); if (ret < 0) { dev_err(&pdev->dev, "Could not register platform: %d\n", ret); ret = -EBUSY; return ret; }*/
// ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL,0); return ret; }
static int plat_remove(struct platform_device *pdev){ printk("-----%s----\n",func);
return 0;
}
static void plat_pdev_release(struct device *dev) { }
static struct platform_device plat_pdev = { .name = "vplat", .dev.release = plat_pdev_release, };
static struct platform_driver plat_pdrv = { .probe = plat_probe, .remove = plat_remove, .driver = { .name = "vplat", }, };
static int __init plat_init(void) { int ret;
ret = platform_device_register(&plat_pdev); if (ret) return ret; ret = platform_driver_register(&plat_pdrv); if (ret) platform_device_unregister(&plat_pdev); return ret;
}
static void __exit plat_exit(void) { platform_driver_unregister(&plat_pdrv); platform_device_unregister(&plat_pdev); }
module_init(plat_init); module_exit(plat_exit); MODULE_LICENSE("GPL");
-
vcodec.c
#include <linux/init.h> #include <linux/module.h> #include <linux/platform_device.h> #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h>
/*static const struct snd_soc_component_driver soc_component_dev = { .probe = codec_soc_probe }
static int vcodec_probe(struct platform_device *codec) { //int ret; printk("-----%s----\n",func);
return 0;
}
static struct snd_soc_codec_driver soc_vcodec_drv = { .probe = vcodec_probe, //.remove = vcodec_remove, //.read = vcodec_read, //.write = vcodec_write, //.ignore_pmdown_time = 1, };*/
static int codec_soc_probe(struct snd_soc_component *component) { printk("-----%s----\n",func); return 0; }
static struct snd_soc_dai_driver vcodec_dai[] = { { .name = "vcodec_dai", .playback = { .stream_name = "Playback", .channels_min = 1, .channels_max = 2, .rates = SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT, .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, }, .capture = { .stream_name = "Capture", .channels_min = 1, .channels_max = 2, .rates = SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_KNOT, .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, }, //.ops = &vcodec_dai_ops, }, };
static const struct snd_soc_dapm_widget vcodec_widget[] = { SND_SOC_DAPM_MIC("vCodec mic",NULL), SND_SOC_DAPM_HP("Headphone hp",NULL), };
static const struct snd_soc_dapm_route snd_proto_route[] = { /*speaker connected to LHPOUT/RHPOUT */ {"Headphone hp",NULL,"LHPOUT"}, {"Headphone hp",NULL,"RHPOUT"},
/*mic is connected to Mic Jack, with WM8731 Mic Bias*/ {"MICIN", NULL, "Mic Bias"}, {"Mic Bias", NULL,"Microphone hp"},
};
static const struct snd_soc_component_driver soc_component_dev = { .name = "vcodec_dev", .dapm_widgets = vcodec_widget, .num_dapm_widgets=ARRAY_SIZE(vcodec_widget), .dapm_routes = snd_proto_route, .num_dapm_routes=ARRAY_SIZE(snd_proto_route), };
/*static int vcodec_probe(struct platform_device *codec) { //int ret; printk("-----%s----\n",func);
return 0;
}*/
static int codec_probe(struct platform_device *pdev) { /*int ret = 0;
printk("-----%s----\n",__func__); ret = snd_soc_register_codec(&pdev->dev, &soc_vcodec_drv, vcodec_dai, ARRAY_SIZE(vcodec_dai)); if (ret < 0) { dev_err(&pdev->dev, "register codec failed\n"); return -1; } return ret;*/ struct device *dev = &pdev->dev; //struct jz_codec *codec; int ret; //codec = devm_kzalloc(dev, sizeof(*codec), GFP_KERNEL); //if (!codec) // return -ENOMEM; //codec->dev = dev; //codec->base = devm_platform_ioremap_resource(pdev, 0);
// if (IS_ERR(codec->base)) { // ret = PTR_ERR(codec->base); // dev_err(dev, "Failed to ioremap mmio memory: %d\n", ret); // return ret; // }
// codec->regmap = devm_regmap_init(dev, NULL, codec, // &jz4770_codec_regmap_config); // if (IS_ERR(codec->regmap)) // return PTR_ERR(codec->regmap);
// codec->clk = devm_clk_get(dev, "aic"); // if (IS_ERR(codec->clk)) // return PTR_ERR(codec->clk);
// platform_set_drvdata(pdev, codec);
ret = devm_snd_soc_register_component(dev, &soc_component_dev, vcodec_dai, ARRAY_SIZE(vcodec_dai)); if (ret) { dev_err(dev, "Failed to register codec: %d\n", ret); return ret; } return 0;
}
static int codec_remove(struct platform_device *pdev){ printk("-----%s----\n",func);
return 0;
}
/*static void codec_pdev_release(struct device dev) { }/
/static struct platform_device codec_pdev = { .name = "vcodec", .dev.release = codec_pdev_release, };/
static struct platform_driver codec_pdrv = { .probe = codec_probe, .remove = codec_remove, .driver = { .name = "vcodec", }, };
//struct platform_device_info pdevinfo;
static struct platform_device *codec_pdev;
static int __init codec_init(void) { int ret;
codec_pdev = platform_device_register_simple("codec", -1, NULL, 0); if (IS_ERR(codec_pdev)) return PTR_ERR(codec_pdev); ret = platform_driver_register(&codec_pdrv); if (ret) platform_device_unregister(codec_pdev); return ret;
}
static void __exit codec_exit(void) { platform_driver_unregister(&codec_pdrv); platform_device_unregister(codec_pdev); }
module_init(codec_init); module_exit(codec_exit); MODULE_LICENSE("GPL");
-
Makefile
#KERN_DIR = /home/vbox/workspace/qemu/100ask_imx6ull-qemu/linux-4.9.88 KERN_DIR = /usr/src/linux-headers-5.15.0-71-generic all: make -C $(KERN_DIR) M=
pwd
modulesclean: make -C $(KERN_DIR) M=
pwd
modules clean rm -rf modules.orderobj-m += vplatform.o obj-m += vcodec.o obj-m += vmachine.o