1 // SPDX-License-Identifier: (GPL-2.0 OR MIT) 2 // 3 // Copyright (c) 2018 BayLibre, SAS. 4 // Author: Jerome Brunet <jbrunet@baylibre.com> 5 6 #include <linux/module.h> 7 #include <linux/of_platform.h> 8 #include <sound/soc.h> 9 #include <sound/soc-dai.h> 10 11 #include "axg-tdm.h" 12 #include "meson-card.h" 13 14 struct axg_dai_link_tdm_mask { 15 u32 tx; 16 u32 rx; 17 }; 18 19 struct axg_dai_link_tdm_data { 20 unsigned int mclk_fs; 21 unsigned int slots; 22 unsigned int slot_width; 23 u32 *tx_mask; 24 u32 *rx_mask; 25 struct axg_dai_link_tdm_mask *codec_masks; 26 }; 27 28 /* 29 * Base params for the codec to codec links 30 * Those will be over-written by the CPU side of the link 31 */ 32 static const struct snd_soc_pcm_stream codec_params = { 33 .formats = SNDRV_PCM_FMTBIT_S24_LE, 34 .rate_min = 5525, 35 .rate_max = 192000, 36 .channels_min = 1, 37 .channels_max = 8, 38 }; 39 40 static int axg_card_tdm_be_hw_params(struct snd_pcm_substream *substream, 41 struct snd_pcm_hw_params *params) 42 { 43 struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); 44 struct meson_card *priv = snd_soc_card_get_drvdata(rtd->card); 45 struct axg_dai_link_tdm_data *be = 46 (struct axg_dai_link_tdm_data *)priv->link_data[rtd->num]; 47 48 return meson_card_i2s_set_sysclk(substream, params, be->mclk_fs); 49 } 50 51 static const struct snd_soc_ops axg_card_tdm_be_ops = { 52 .hw_params = axg_card_tdm_be_hw_params, 53 }; 54 55 static int axg_card_tdm_dai_init(struct snd_soc_pcm_runtime *rtd) 56 { 57 struct meson_card *priv = snd_soc_card_get_drvdata(rtd->card); 58 struct axg_dai_link_tdm_data *be = 59 (struct axg_dai_link_tdm_data *)priv->link_data[rtd->num]; 60 struct snd_soc_dai *codec_dai; 61 int ret, i; 62 63 for_each_rtd_codec_dais(rtd, i, codec_dai) { 64 ret = snd_soc_dai_set_tdm_slot(codec_dai, 65 be->codec_masks[i].tx, 66 be->codec_masks[i].rx, 67 be->slots, be->slot_width); 68 if (ret && ret != -ENOTSUPP) { 69 dev_err(codec_dai->dev, 70 "setting tdm link slots failed\n"); 71 return ret; 72 } 73 } 74 75 ret = axg_tdm_set_tdm_slots(snd_soc_rtd_to_cpu(rtd, 0), be->tx_mask, be->rx_mask, 76 be->slots, be->slot_width); 77 if (ret) { 78 dev_err(snd_soc_rtd_to_cpu(rtd, 0)->dev, "setting tdm link slots failed\n"); 79 return ret; 80 } 81 82 return 0; 83 } 84 85 static int axg_card_tdm_dai_lb_init(struct snd_soc_pcm_runtime *rtd) 86 { 87 struct meson_card *priv = snd_soc_card_get_drvdata(rtd->card); 88 struct axg_dai_link_tdm_data *be = 89 (struct axg_dai_link_tdm_data *)priv->link_data[rtd->num]; 90 int ret; 91 92 /* The loopback rx_mask is the pad tx_mask */ 93 ret = axg_tdm_set_tdm_slots(snd_soc_rtd_to_cpu(rtd, 0), NULL, be->tx_mask, 94 be->slots, be->slot_width); 95 if (ret) { 96 dev_err(snd_soc_rtd_to_cpu(rtd, 0)->dev, "setting tdm link slots failed\n"); 97 return ret; 98 } 99 100 return 0; 101 } 102 103 static int axg_card_add_tdm_loopback(struct snd_soc_card *card, 104 int *index) 105 { 106 struct meson_card *priv = snd_soc_card_get_drvdata(card); 107 struct snd_soc_dai_link *pad = &card->dai_link[*index]; 108 struct snd_soc_dai_link *lb; 109 struct snd_soc_dai_link_component *dlc; 110 int ret; 111 112 /* extend links */ 113 ret = meson_card_reallocate_links(card, card->num_links + 1); 114 if (ret) 115 return ret; 116 117 lb = &card->dai_link[*index + 1]; 118 119 lb->name = devm_kasprintf(card->dev, GFP_KERNEL, "%s-lb", pad->name); 120 if (!lb->name) 121 return -ENOMEM; 122 123 dlc = devm_kzalloc(card->dev, sizeof(*dlc), GFP_KERNEL); 124 if (!dlc) 125 return -ENOMEM; 126 127 lb->cpus = dlc; 128 lb->codecs = &snd_soc_dummy_dlc; 129 lb->num_cpus = 1; 130 lb->num_codecs = 1; 131 132 lb->stream_name = lb->name; 133 lb->cpus->of_node = pad->cpus->of_node; 134 lb->cpus->dai_name = "TDM Loopback"; 135 lb->dpcm_capture = 1; 136 lb->no_pcm = 1; 137 lb->ops = &axg_card_tdm_be_ops; 138 lb->init = axg_card_tdm_dai_lb_init; 139 140 /* Provide the same link data to the loopback */ 141 priv->link_data[*index + 1] = priv->link_data[*index]; 142 143 /* 144 * axg_card_clean_references() will iterate over this link, 145 * make sure the node count is balanced 146 */ 147 of_node_get(lb->cpus->of_node); 148 149 /* Let add_links continue where it should */ 150 *index += 1; 151 152 return 0; 153 } 154 155 static int axg_card_parse_cpu_tdm_slots(struct snd_soc_card *card, 156 struct snd_soc_dai_link *link, 157 struct device_node *node, 158 struct axg_dai_link_tdm_data *be) 159 { 160 char propname[32]; 161 u32 tx, rx; 162 int i; 163 164 be->tx_mask = devm_kcalloc(card->dev, AXG_TDM_NUM_LANES, 165 sizeof(*be->tx_mask), GFP_KERNEL); 166 be->rx_mask = devm_kcalloc(card->dev, AXG_TDM_NUM_LANES, 167 sizeof(*be->rx_mask), GFP_KERNEL); 168 if (!be->tx_mask || !be->rx_mask) 169 return -ENOMEM; 170 171 for (i = 0, tx = 0; i < AXG_TDM_NUM_LANES; i++) { 172 snprintf(propname, 32, "dai-tdm-slot-tx-mask-%d", i); 173 snd_soc_of_get_slot_mask(node, propname, &be->tx_mask[i]); 174 tx = max(tx, be->tx_mask[i]); 175 } 176 177 /* Disable playback is the interface has no tx slots */ 178 if (!tx) 179 link->dpcm_playback = 0; 180 181 for (i = 0, rx = 0; i < AXG_TDM_NUM_LANES; i++) { 182 snprintf(propname, 32, "dai-tdm-slot-rx-mask-%d", i); 183 snd_soc_of_get_slot_mask(node, propname, &be->rx_mask[i]); 184 rx = max(rx, be->rx_mask[i]); 185 } 186 187 /* Disable capture is the interface has no rx slots */ 188 if (!rx) 189 link->dpcm_capture = 0; 190 191 /* ... but the interface should at least have one of them */ 192 if (!tx && !rx) { 193 dev_err(card->dev, "tdm link has no cpu slots\n"); 194 return -EINVAL; 195 } 196 197 of_property_read_u32(node, "dai-tdm-slot-num", &be->slots); 198 if (!be->slots) { 199 /* 200 * If the slot number is not provided, set it such as it 201 * accommodates the largest mask 202 */ 203 be->slots = fls(max(tx, rx)); 204 } else if (be->slots < fls(max(tx, rx)) || be->slots > 32) { 205 /* 206 * Error if the slots can't accommodate the largest mask or 207 * if it is just too big 208 */ 209 dev_err(card->dev, "bad slot number\n"); 210 return -EINVAL; 211 } 212 213 of_property_read_u32(node, "dai-tdm-slot-width", &be->slot_width); 214 215 return 0; 216 } 217 218 static int axg_card_parse_codecs_masks(struct snd_soc_card *card, 219 struct snd_soc_dai_link *link, 220 struct device_node *node, 221 struct axg_dai_link_tdm_data *be) 222 { 223 struct axg_dai_link_tdm_mask *codec_mask; 224 struct device_node *np; 225 226 codec_mask = devm_kcalloc(card->dev, link->num_codecs, 227 sizeof(*codec_mask), GFP_KERNEL); 228 if (!codec_mask) 229 return -ENOMEM; 230 231 be->codec_masks = codec_mask; 232 233 for_each_child_of_node(node, np) { 234 snd_soc_of_get_slot_mask(np, "dai-tdm-slot-rx-mask", 235 &codec_mask->rx); 236 snd_soc_of_get_slot_mask(np, "dai-tdm-slot-tx-mask", 237 &codec_mask->tx); 238 239 codec_mask++; 240 } 241 242 return 0; 243 } 244 245 static int axg_card_parse_tdm(struct snd_soc_card *card, 246 struct device_node *node, 247 int *index) 248 { 249 struct meson_card *priv = snd_soc_card_get_drvdata(card); 250 struct snd_soc_dai_link *link = &card->dai_link[*index]; 251 struct axg_dai_link_tdm_data *be; 252 int ret; 253 254 /* Allocate tdm link parameters */ 255 be = devm_kzalloc(card->dev, sizeof(*be), GFP_KERNEL); 256 if (!be) 257 return -ENOMEM; 258 priv->link_data[*index] = be; 259 260 /* Setup tdm link */ 261 link->ops = &axg_card_tdm_be_ops; 262 link->init = axg_card_tdm_dai_init; 263 link->dai_fmt = meson_card_parse_daifmt(node, link->cpus->of_node); 264 265 of_property_read_u32(node, "mclk-fs", &be->mclk_fs); 266 267 ret = axg_card_parse_cpu_tdm_slots(card, link, node, be); 268 if (ret) { 269 dev_err(card->dev, "error parsing tdm link slots\n"); 270 return ret; 271 } 272 273 ret = axg_card_parse_codecs_masks(card, link, node, be); 274 if (ret) 275 return ret; 276 277 /* Add loopback if the pad dai has playback */ 278 if (link->dpcm_playback) { 279 ret = axg_card_add_tdm_loopback(card, index); 280 if (ret) 281 return ret; 282 } 283 284 return 0; 285 } 286 287 static int axg_card_cpu_is_capture_fe(struct device_node *np) 288 { 289 return of_device_is_compatible(np, DT_PREFIX "axg-toddr"); 290 } 291 292 static int axg_card_cpu_is_playback_fe(struct device_node *np) 293 { 294 return of_device_is_compatible(np, DT_PREFIX "axg-frddr"); 295 } 296 297 static int axg_card_cpu_is_tdm_iface(struct device_node *np) 298 { 299 return of_device_is_compatible(np, DT_PREFIX "axg-tdm-iface"); 300 } 301 302 static int axg_card_cpu_is_codec(struct device_node *np) 303 { 304 return of_device_is_compatible(np, DT_PREFIX "g12a-tohdmitx") || 305 of_device_is_compatible(np, DT_PREFIX "g12a-toacodec"); 306 } 307 308 static int axg_card_add_link(struct snd_soc_card *card, struct device_node *np, 309 int *index) 310 { 311 struct snd_soc_dai_link *dai_link = &card->dai_link[*index]; 312 struct snd_soc_dai_link_component *cpu; 313 int ret; 314 315 cpu = devm_kzalloc(card->dev, sizeof(*cpu), GFP_KERNEL); 316 if (!cpu) 317 return -ENOMEM; 318 319 dai_link->cpus = cpu; 320 dai_link->num_cpus = 1; 321 322 ret = meson_card_parse_dai(card, np, dai_link->cpus); 323 if (ret) 324 return ret; 325 326 if (axg_card_cpu_is_playback_fe(dai_link->cpus->of_node)) 327 return meson_card_set_fe_link(card, dai_link, np, true); 328 else if (axg_card_cpu_is_capture_fe(dai_link->cpus->of_node)) 329 return meson_card_set_fe_link(card, dai_link, np, false); 330 331 332 ret = meson_card_set_be_link(card, dai_link, np); 333 if (ret) 334 return ret; 335 336 if (axg_card_cpu_is_codec(dai_link->cpus->of_node)) { 337 dai_link->c2c_params = &codec_params; 338 dai_link->num_c2c_params = 1; 339 } else { 340 dai_link->no_pcm = 1; 341 snd_soc_dai_link_set_capabilities(dai_link); 342 if (axg_card_cpu_is_tdm_iface(dai_link->cpus->of_node)) 343 ret = axg_card_parse_tdm(card, np, index); 344 } 345 346 return ret; 347 } 348 349 static const struct meson_card_match_data axg_card_match_data = { 350 .add_link = axg_card_add_link, 351 }; 352 353 static const struct of_device_id axg_card_of_match[] = { 354 { 355 .compatible = "amlogic,axg-sound-card", 356 .data = &axg_card_match_data, 357 }, {} 358 }; 359 MODULE_DEVICE_TABLE(of, axg_card_of_match); 360 361 static struct platform_driver axg_card_pdrv = { 362 .probe = meson_card_probe, 363 .remove = meson_card_remove, 364 .driver = { 365 .name = "axg-sound-card", 366 .of_match_table = axg_card_of_match, 367 }, 368 }; 369 module_platform_driver(axg_card_pdrv); 370 371 MODULE_DESCRIPTION("Amlogic AXG ALSA machine driver"); 372 MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>"); 373 MODULE_LICENSE("GPL v2"); 374