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