1 // SPDX-License-Identifier: GPL-2.0 2 // 3 // audio-graph-card2-custom-sample.c 4 // 5 // Copyright (C) 2020 Renesas Electronics Corp. 6 // Copyright (C) 2020 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> 7 // 8 #include <linux/device.h> 9 #include <linux/mod_devicetable.h> 10 #include <linux/module.h> 11 #include <linux/platform_device.h> 12 #include <sound/graph_card.h> 13 14 /* 15 * Custom driver can have own priv 16 * which includes simple_util_priv. 17 */ 18 struct custom_priv { 19 struct simple_util_priv simple_priv; 20 21 /* custom driver's own params */ 22 int custom_params; 23 }; 24 25 /* You can get custom_priv from simple_priv */ 26 #define simple_to_custom(simple) container_of((simple), struct custom_priv, simple_priv) 27 28 static int custom_card_probe(struct snd_soc_card *card) 29 { 30 struct simple_util_priv *simple_priv = snd_soc_card_get_drvdata(card); 31 struct custom_priv *custom_priv = simple_to_custom(simple_priv); 32 struct device *dev = simple_priv_to_dev(simple_priv); 33 34 dev_info(dev, "custom probe\n"); 35 36 custom_priv->custom_params = 1; 37 38 /* you can use generic probe function */ 39 return graph_util_card_probe(card); 40 } 41 42 static int custom_hook_pre(struct simple_util_priv *priv) 43 { 44 struct device *dev = simple_priv_to_dev(priv); 45 46 /* You can custom before parsing */ 47 dev_info(dev, "hook : %s\n", __func__); 48 49 return 0; 50 } 51 52 static int custom_hook_post(struct simple_util_priv *priv) 53 { 54 struct device *dev = simple_priv_to_dev(priv); 55 struct snd_soc_card *card; 56 57 /* You can custom after parsing */ 58 dev_info(dev, "hook : %s\n", __func__); 59 60 /* overwrite .probe sample */ 61 card = simple_priv_to_card(priv); 62 card->probe = custom_card_probe; 63 64 return 0; 65 } 66 67 static int custom_normal(struct simple_util_priv *priv, 68 struct device_node *lnk, 69 struct link_info *li) 70 { 71 struct device *dev = simple_priv_to_dev(priv); 72 73 /* 74 * You can custom Normal parsing 75 * before/affter audio_graph2_link_normal() 76 */ 77 dev_info(dev, "hook : %s\n", __func__); 78 79 return audio_graph2_link_normal(priv, lnk, li); 80 } 81 82 static int custom_dpcm(struct simple_util_priv *priv, 83 struct device_node *lnk, 84 struct link_info *li) 85 { 86 struct device *dev = simple_priv_to_dev(priv); 87 88 /* 89 * You can custom DPCM parsing 90 * before/affter audio_graph2_link_dpcm() 91 */ 92 dev_info(dev, "hook : %s\n", __func__); 93 94 return audio_graph2_link_dpcm(priv, lnk, li); 95 } 96 97 static int custom_c2c(struct simple_util_priv *priv, 98 struct device_node *lnk, 99 struct link_info *li) 100 { 101 struct device *dev = simple_priv_to_dev(priv); 102 103 /* 104 * You can custom Codec2Codec parsing 105 * before/affter audio_graph2_link_c2c() 106 */ 107 dev_info(dev, "hook : %s\n", __func__); 108 109 return audio_graph2_link_c2c(priv, lnk, li); 110 } 111 112 /* 113 * audio-graph-card2 has many hooks for your customizing. 114 */ 115 static struct graph2_custom_hooks custom_hooks = { 116 .hook_pre = custom_hook_pre, 117 .hook_post = custom_hook_post, 118 .custom_normal = custom_normal, 119 .custom_dpcm = custom_dpcm, 120 .custom_c2c = custom_c2c, 121 }; 122 123 static int custom_startup(struct snd_pcm_substream *substream) 124 { 125 struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); 126 struct simple_util_priv *priv = snd_soc_card_get_drvdata(rtd->card); 127 struct device *dev = simple_priv_to_dev(priv); 128 129 dev_info(dev, "custom startup\n"); 130 131 return simple_util_startup(substream); 132 } 133 134 /* You can use custom ops */ 135 static const struct snd_soc_ops custom_ops = { 136 .startup = custom_startup, 137 .shutdown = simple_util_shutdown, 138 .hw_params = simple_util_hw_params, 139 }; 140 141 static int custom_probe(struct platform_device *pdev) 142 { 143 struct custom_priv *custom_priv; 144 struct simple_util_priv *simple_priv; 145 struct device *dev = &pdev->dev; 146 int ret; 147 148 custom_priv = devm_kzalloc(dev, sizeof(*custom_priv), GFP_KERNEL); 149 if (!custom_priv) 150 return -ENOMEM; 151 152 simple_priv = &custom_priv->simple_priv; 153 simple_priv->ops = &custom_ops; /* customize dai_link ops */ 154 155 /* "audio-graph-card2-custom-sample" is too long */ 156 simple_priv->snd_card.name = "card2-custom"; 157 158 /* use audio-graph-card2 parsing with own custom hooks */ 159 ret = audio_graph2_parse_of(simple_priv, dev, &custom_hooks); 160 if (ret < 0) 161 return ret; 162 163 /* customize more if needed */ 164 165 return 0; 166 } 167 168 static const struct of_device_id custom_of_match[] = { 169 { .compatible = "audio-graph-card2-custom-sample", }, 170 {}, 171 }; 172 MODULE_DEVICE_TABLE(of, custom_of_match); 173 174 static struct platform_driver custom_card = { 175 .driver = { 176 .name = "audio-graph-card2-custom-sample", 177 .of_match_table = custom_of_match, 178 }, 179 .probe = custom_probe, 180 .remove = simple_util_remove, 181 }; 182 module_platform_driver(custom_card); 183 184 MODULE_ALIAS("platform:asoc-audio-graph-card2-custom-sample"); 185 MODULE_LICENSE("GPL v2"); 186 MODULE_DESCRIPTION("ASoC Audio Graph Card2 Custom Sample"); 187 MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>"); 188