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->id]; 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->id]; 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->id]; 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; 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 pad = &card->dai_link[*index]; 118 lb = &card->dai_link[*index + 1]; 119 120 lb->name = devm_kasprintf(card->dev, GFP_KERNEL, "%s-lb", pad->name); 121 if (!lb->name) 122 return -ENOMEM; 123 124 dlc = devm_kzalloc(card->dev, sizeof(*dlc), GFP_KERNEL); 125 if (!dlc) 126 return -ENOMEM; 127 128 lb->cpus = dlc; 129 lb->codecs = &snd_soc_dummy_dlc; 130 lb->num_cpus = 1; 131 lb->num_codecs = 1; 132 133 lb->stream_name = lb->name; 134 lb->cpus->of_node = pad->cpus->of_node; 135 lb->cpus->dai_name = "TDM Loopback"; 136 lb->capture_only = 1; 137 lb->no_pcm = 1; 138 lb->ops = &axg_card_tdm_be_ops; 139 lb->init = axg_card_tdm_dai_lb_init; 140 141 /* Provide the same link data to the loopback */ 142 priv->link_data[*index + 1] = priv->link_data[*index]; 143 144 /* 145 * axg_card_clean_references() will iterate over this link, 146 * make sure the node count is balanced 147 */ 148 of_node_get(lb->cpus->of_node); 149 150 /* Let add_links continue where it should */ 151 *index += 1; 152 153 return 0; 154 } 155 156 static int axg_card_parse_cpu_tdm_slots(struct snd_soc_card *card, 157 struct snd_soc_dai_link *link, 158 struct device_node *node, 159 struct axg_dai_link_tdm_data *be) 160 { 161 char propname[32]; 162 u32 tx, rx; 163 int i; 164 165 be->tx_mask = devm_kcalloc(card->dev, AXG_TDM_NUM_LANES, 166 sizeof(*be->tx_mask), GFP_KERNEL); 167 be->rx_mask = devm_kcalloc(card->dev, AXG_TDM_NUM_LANES, 168 sizeof(*be->rx_mask), GFP_KERNEL); 169 if (!be->tx_mask || !be->rx_mask) 170 return -ENOMEM; 171 172 for (i = 0, tx = 0; i < AXG_TDM_NUM_LANES; i++) { 173 snprintf(propname, 32, "dai-tdm-slot-tx-mask-%d", i); 174 snd_soc_of_get_slot_mask(node, propname, &be->tx_mask[i]); 175 tx = max(tx, be->tx_mask[i]); 176 } 177 178 /* Disable playback is the interface has no tx slots */ 179 if (!tx) 180 link->capture_only = 1; 181 182 for (i = 0, rx = 0; i < AXG_TDM_NUM_LANES; i++) { 183 snprintf(propname, 32, "dai-tdm-slot-rx-mask-%d", i); 184 snd_soc_of_get_slot_mask(node, propname, &be->rx_mask[i]); 185 rx = max(rx, be->rx_mask[i]); 186 } 187 188 /* Disable capture is the interface has no rx slots */ 189 if (!rx) 190 link->playback_only = 1; 191 192 /* ... but the interface should at least have one direction */ 193 if (!tx && !rx) { 194 dev_err(card->dev, "tdm link has no cpu slots\n"); 195 return -EINVAL; 196 } 197 198 of_property_read_u32(node, "dai-tdm-slot-num", &be->slots); 199 if (!be->slots) { 200 /* 201 * If the slot number is not provided, set it such as it 202 * accommodates the largest mask 203 */ 204 be->slots = fls(max(tx, rx)); 205 } else if (be->slots < fls(max(tx, rx)) || be->slots > 32) { 206 /* 207 * Error if the slots can't accommodate the largest mask or 208 * if it is just too big 209 */ 210 dev_err(card->dev, "bad slot number\n"); 211 return -EINVAL; 212 } 213 214 of_property_read_u32(node, "dai-tdm-slot-width", &be->slot_width); 215 216 return 0; 217 } 218 219 static int axg_card_parse_codecs_masks(struct snd_soc_card *card, 220 struct snd_soc_dai_link *link, 221 struct device_node *node, 222 struct axg_dai_link_tdm_data *be) 223 { 224 struct axg_dai_link_tdm_mask *codec_mask; 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_scoped(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->capture_only) { 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 dai_link->nonatomic = true; 322 323 ret = meson_card_parse_dai(card, np, dai_link->cpus); 324 if (ret) 325 return ret; 326 327 if (axg_card_cpu_is_playback_fe(dai_link->cpus->of_node)) 328 return meson_card_set_fe_link(card, dai_link, np, true); 329 else if (axg_card_cpu_is_capture_fe(dai_link->cpus->of_node)) 330 return meson_card_set_fe_link(card, dai_link, np, false); 331 332 333 ret = meson_card_set_be_link(card, dai_link, np); 334 if (ret) 335 return ret; 336 337 if (axg_card_cpu_is_codec(dai_link->cpus->of_node)) { 338 dai_link->c2c_params = &codec_params; 339 dai_link->num_c2c_params = 1; 340 } else { 341 dai_link->no_pcm = 1; 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