0001
0002
0003
0004
0005
0006 #include <linux/module.h>
0007 #include <linux/of_platform.h>
0008 #include <sound/soc.h>
0009 #include <sound/soc-dai.h>
0010
0011 #include "axg-tdm.h"
0012 #include "meson-card.h"
0013
0014 struct axg_dai_link_tdm_mask {
0015 u32 tx;
0016 u32 rx;
0017 };
0018
0019 struct axg_dai_link_tdm_data {
0020 unsigned int mclk_fs;
0021 unsigned int slots;
0022 unsigned int slot_width;
0023 u32 *tx_mask;
0024 u32 *rx_mask;
0025 struct axg_dai_link_tdm_mask *codec_masks;
0026 };
0027
0028
0029
0030
0031
0032 static const struct snd_soc_pcm_stream codec_params = {
0033 .formats = SNDRV_PCM_FMTBIT_S24_LE,
0034 .rate_min = 5525,
0035 .rate_max = 192000,
0036 .channels_min = 1,
0037 .channels_max = 8,
0038 };
0039
0040 static int axg_card_tdm_be_hw_params(struct snd_pcm_substream *substream,
0041 struct snd_pcm_hw_params *params)
0042 {
0043 struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
0044 struct meson_card *priv = snd_soc_card_get_drvdata(rtd->card);
0045 struct axg_dai_link_tdm_data *be =
0046 (struct axg_dai_link_tdm_data *)priv->link_data[rtd->num];
0047
0048 return meson_card_i2s_set_sysclk(substream, params, be->mclk_fs);
0049 }
0050
0051 static const struct snd_soc_ops axg_card_tdm_be_ops = {
0052 .hw_params = axg_card_tdm_be_hw_params,
0053 };
0054
0055 static int axg_card_tdm_dai_init(struct snd_soc_pcm_runtime *rtd)
0056 {
0057 struct meson_card *priv = snd_soc_card_get_drvdata(rtd->card);
0058 struct axg_dai_link_tdm_data *be =
0059 (struct axg_dai_link_tdm_data *)priv->link_data[rtd->num];
0060 struct snd_soc_dai *codec_dai;
0061 int ret, i;
0062
0063 for_each_rtd_codec_dais(rtd, i, codec_dai) {
0064 ret = snd_soc_dai_set_tdm_slot(codec_dai,
0065 be->codec_masks[i].tx,
0066 be->codec_masks[i].rx,
0067 be->slots, be->slot_width);
0068 if (ret && ret != -ENOTSUPP) {
0069 dev_err(codec_dai->dev,
0070 "setting tdm link slots failed\n");
0071 return ret;
0072 }
0073 }
0074
0075 ret = axg_tdm_set_tdm_slots(asoc_rtd_to_cpu(rtd, 0), be->tx_mask, be->rx_mask,
0076 be->slots, be->slot_width);
0077 if (ret) {
0078 dev_err(asoc_rtd_to_cpu(rtd, 0)->dev, "setting tdm link slots failed\n");
0079 return ret;
0080 }
0081
0082 return 0;
0083 }
0084
0085 static int axg_card_tdm_dai_lb_init(struct snd_soc_pcm_runtime *rtd)
0086 {
0087 struct meson_card *priv = snd_soc_card_get_drvdata(rtd->card);
0088 struct axg_dai_link_tdm_data *be =
0089 (struct axg_dai_link_tdm_data *)priv->link_data[rtd->num];
0090 int ret;
0091
0092
0093 ret = axg_tdm_set_tdm_slots(asoc_rtd_to_cpu(rtd, 0), NULL, be->tx_mask,
0094 be->slots, be->slot_width);
0095 if (ret) {
0096 dev_err(asoc_rtd_to_cpu(rtd, 0)->dev, "setting tdm link slots failed\n");
0097 return ret;
0098 }
0099
0100 return 0;
0101 }
0102
0103 static int axg_card_add_tdm_loopback(struct snd_soc_card *card,
0104 int *index)
0105 {
0106 struct meson_card *priv = snd_soc_card_get_drvdata(card);
0107 struct snd_soc_dai_link *pad = &card->dai_link[*index];
0108 struct snd_soc_dai_link *lb;
0109 struct snd_soc_dai_link_component *dlc;
0110 int ret;
0111
0112
0113 ret = meson_card_reallocate_links(card, card->num_links + 1);
0114 if (ret)
0115 return ret;
0116
0117 lb = &card->dai_link[*index + 1];
0118
0119 lb->name = devm_kasprintf(card->dev, GFP_KERNEL, "%s-lb", pad->name);
0120 if (!lb->name)
0121 return -ENOMEM;
0122
0123 dlc = devm_kzalloc(card->dev, 2 * sizeof(*dlc), GFP_KERNEL);
0124 if (!dlc)
0125 return -ENOMEM;
0126
0127 lb->cpus = &dlc[0];
0128 lb->codecs = &dlc[1];
0129 lb->num_cpus = 1;
0130 lb->num_codecs = 1;
0131
0132 lb->stream_name = lb->name;
0133 lb->cpus->of_node = pad->cpus->of_node;
0134 lb->cpus->dai_name = "TDM Loopback";
0135 lb->codecs->name = "snd-soc-dummy";
0136 lb->codecs->dai_name = "snd-soc-dummy-dai";
0137 lb->dpcm_capture = 1;
0138 lb->no_pcm = 1;
0139 lb->ops = &axg_card_tdm_be_ops;
0140 lb->init = axg_card_tdm_dai_lb_init;
0141
0142
0143 priv->link_data[*index + 1] = priv->link_data[*index];
0144
0145
0146
0147
0148
0149 of_node_get(lb->cpus->of_node);
0150
0151
0152 *index += 1;
0153
0154 return 0;
0155 }
0156
0157 static int axg_card_parse_cpu_tdm_slots(struct snd_soc_card *card,
0158 struct snd_soc_dai_link *link,
0159 struct device_node *node,
0160 struct axg_dai_link_tdm_data *be)
0161 {
0162 char propname[32];
0163 u32 tx, rx;
0164 int i;
0165
0166 be->tx_mask = devm_kcalloc(card->dev, AXG_TDM_NUM_LANES,
0167 sizeof(*be->tx_mask), GFP_KERNEL);
0168 be->rx_mask = devm_kcalloc(card->dev, AXG_TDM_NUM_LANES,
0169 sizeof(*be->rx_mask), GFP_KERNEL);
0170 if (!be->tx_mask || !be->rx_mask)
0171 return -ENOMEM;
0172
0173 for (i = 0, tx = 0; i < AXG_TDM_NUM_LANES; i++) {
0174 snprintf(propname, 32, "dai-tdm-slot-tx-mask-%d", i);
0175 snd_soc_of_get_slot_mask(node, propname, &be->tx_mask[i]);
0176 tx = max(tx, be->tx_mask[i]);
0177 }
0178
0179
0180 if (!tx)
0181 link->dpcm_playback = 0;
0182
0183 for (i = 0, rx = 0; i < AXG_TDM_NUM_LANES; i++) {
0184 snprintf(propname, 32, "dai-tdm-slot-rx-mask-%d", i);
0185 snd_soc_of_get_slot_mask(node, propname, &be->rx_mask[i]);
0186 rx = max(rx, be->rx_mask[i]);
0187 }
0188
0189
0190 if (!rx)
0191 link->dpcm_capture = 0;
0192
0193
0194 if (!tx && !rx) {
0195 dev_err(card->dev, "tdm link has no cpu slots\n");
0196 return -EINVAL;
0197 }
0198
0199 of_property_read_u32(node, "dai-tdm-slot-num", &be->slots);
0200 if (!be->slots) {
0201
0202
0203
0204
0205 be->slots = fls(max(tx, rx));
0206 } else if (be->slots < fls(max(tx, rx)) || be->slots > 32) {
0207
0208
0209
0210
0211 dev_err(card->dev, "bad slot number\n");
0212 return -EINVAL;
0213 }
0214
0215 of_property_read_u32(node, "dai-tdm-slot-width", &be->slot_width);
0216
0217 return 0;
0218 }
0219
0220 static int axg_card_parse_codecs_masks(struct snd_soc_card *card,
0221 struct snd_soc_dai_link *link,
0222 struct device_node *node,
0223 struct axg_dai_link_tdm_data *be)
0224 {
0225 struct axg_dai_link_tdm_mask *codec_mask;
0226 struct device_node *np;
0227
0228 codec_mask = devm_kcalloc(card->dev, link->num_codecs,
0229 sizeof(*codec_mask), GFP_KERNEL);
0230 if (!codec_mask)
0231 return -ENOMEM;
0232
0233 be->codec_masks = codec_mask;
0234
0235 for_each_child_of_node(node, np) {
0236 snd_soc_of_get_slot_mask(np, "dai-tdm-slot-rx-mask",
0237 &codec_mask->rx);
0238 snd_soc_of_get_slot_mask(np, "dai-tdm-slot-tx-mask",
0239 &codec_mask->tx);
0240
0241 codec_mask++;
0242 }
0243
0244 return 0;
0245 }
0246
0247 static int axg_card_parse_tdm(struct snd_soc_card *card,
0248 struct device_node *node,
0249 int *index)
0250 {
0251 struct meson_card *priv = snd_soc_card_get_drvdata(card);
0252 struct snd_soc_dai_link *link = &card->dai_link[*index];
0253 struct axg_dai_link_tdm_data *be;
0254 int ret;
0255
0256
0257 be = devm_kzalloc(card->dev, sizeof(*be), GFP_KERNEL);
0258 if (!be)
0259 return -ENOMEM;
0260 priv->link_data[*index] = be;
0261
0262
0263 link->ops = &axg_card_tdm_be_ops;
0264 link->init = axg_card_tdm_dai_init;
0265 link->dai_fmt = meson_card_parse_daifmt(node, link->cpus->of_node);
0266
0267 of_property_read_u32(node, "mclk-fs", &be->mclk_fs);
0268
0269 ret = axg_card_parse_cpu_tdm_slots(card, link, node, be);
0270 if (ret) {
0271 dev_err(card->dev, "error parsing tdm link slots\n");
0272 return ret;
0273 }
0274
0275 ret = axg_card_parse_codecs_masks(card, link, node, be);
0276 if (ret)
0277 return ret;
0278
0279
0280 if (link->dpcm_playback) {
0281 ret = axg_card_add_tdm_loopback(card, index);
0282 if (ret)
0283 return ret;
0284 }
0285
0286 return 0;
0287 }
0288
0289 static int axg_card_cpu_is_capture_fe(struct device_node *np)
0290 {
0291 return of_device_is_compatible(np, DT_PREFIX "axg-toddr");
0292 }
0293
0294 static int axg_card_cpu_is_playback_fe(struct device_node *np)
0295 {
0296 return of_device_is_compatible(np, DT_PREFIX "axg-frddr");
0297 }
0298
0299 static int axg_card_cpu_is_tdm_iface(struct device_node *np)
0300 {
0301 return of_device_is_compatible(np, DT_PREFIX "axg-tdm-iface");
0302 }
0303
0304 static int axg_card_cpu_is_codec(struct device_node *np)
0305 {
0306 return of_device_is_compatible(np, DT_PREFIX "g12a-tohdmitx") ||
0307 of_device_is_compatible(np, DT_PREFIX "g12a-toacodec");
0308 }
0309
0310 static int axg_card_add_link(struct snd_soc_card *card, struct device_node *np,
0311 int *index)
0312 {
0313 struct snd_soc_dai_link *dai_link = &card->dai_link[*index];
0314 struct snd_soc_dai_link_component *cpu;
0315 int ret;
0316
0317 cpu = devm_kzalloc(card->dev, sizeof(*cpu), GFP_KERNEL);
0318 if (!cpu)
0319 return -ENOMEM;
0320
0321 dai_link->cpus = cpu;
0322 dai_link->num_cpus = 1;
0323
0324 ret = meson_card_parse_dai(card, np, &dai_link->cpus->of_node,
0325 &dai_link->cpus->dai_name);
0326 if (ret)
0327 return ret;
0328
0329 if (axg_card_cpu_is_playback_fe(dai_link->cpus->of_node))
0330 return meson_card_set_fe_link(card, dai_link, np, true);
0331 else if (axg_card_cpu_is_capture_fe(dai_link->cpus->of_node))
0332 return meson_card_set_fe_link(card, dai_link, np, false);
0333
0334
0335 ret = meson_card_set_be_link(card, dai_link, np);
0336 if (ret)
0337 return ret;
0338
0339 if (axg_card_cpu_is_codec(dai_link->cpus->of_node)) {
0340 dai_link->params = &codec_params;
0341 } else {
0342 dai_link->no_pcm = 1;
0343 snd_soc_dai_link_set_capabilities(dai_link);
0344 if (axg_card_cpu_is_tdm_iface(dai_link->cpus->of_node))
0345 ret = axg_card_parse_tdm(card, np, index);
0346 }
0347
0348 return ret;
0349 }
0350
0351 static const struct meson_card_match_data axg_card_match_data = {
0352 .add_link = axg_card_add_link,
0353 };
0354
0355 static const struct of_device_id axg_card_of_match[] = {
0356 {
0357 .compatible = "amlogic,axg-sound-card",
0358 .data = &axg_card_match_data,
0359 }, {}
0360 };
0361 MODULE_DEVICE_TABLE(of, axg_card_of_match);
0362
0363 static struct platform_driver axg_card_pdrv = {
0364 .probe = meson_card_probe,
0365 .remove = meson_card_remove,
0366 .driver = {
0367 .name = "axg-sound-card",
0368 .of_match_table = axg_card_of_match,
0369 },
0370 };
0371 module_platform_driver(axg_card_pdrv);
0372
0373 MODULE_DESCRIPTION("Amlogic AXG ALSA machine driver");
0374 MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
0375 MODULE_LICENSE("GPL v2");