Jerome Brunet | e37a0c3 | 2020-02-13 16:51:59 +0100 | [diff] [blame] | 1 | // SPDX-License-Identifier: (GPL-2.0 OR MIT) |
| 2 | // |
| 3 | // Copyright (c) 2020 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 "meson-card.h" |
| 12 | |
| 13 | struct gx_dai_link_i2s_data { |
| 14 | unsigned int mclk_fs; |
| 15 | }; |
| 16 | |
| 17 | /* |
| 18 | * Base params for the codec to codec links |
| 19 | * Those will be over-written by the CPU side of the link |
| 20 | */ |
| 21 | static const struct snd_soc_pcm_stream codec_params = { |
| 22 | .formats = SNDRV_PCM_FMTBIT_S24_LE, |
| 23 | .rate_min = 5525, |
| 24 | .rate_max = 192000, |
| 25 | .channels_min = 1, |
| 26 | .channels_max = 8, |
| 27 | }; |
| 28 | |
| 29 | static int gx_card_i2s_be_hw_params(struct snd_pcm_substream *substream, |
| 30 | struct snd_pcm_hw_params *params) |
| 31 | { |
| 32 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
| 33 | struct meson_card *priv = snd_soc_card_get_drvdata(rtd->card); |
| 34 | struct gx_dai_link_i2s_data *be = |
| 35 | (struct gx_dai_link_i2s_data *)priv->link_data[rtd->num]; |
| 36 | |
| 37 | return meson_card_i2s_set_sysclk(substream, params, be->mclk_fs); |
| 38 | } |
| 39 | |
| 40 | static const struct snd_soc_ops gx_card_i2s_be_ops = { |
| 41 | .hw_params = gx_card_i2s_be_hw_params, |
| 42 | }; |
| 43 | |
| 44 | static int gx_card_parse_i2s(struct snd_soc_card *card, |
| 45 | struct device_node *node, |
| 46 | int *index) |
| 47 | { |
| 48 | struct meson_card *priv = snd_soc_card_get_drvdata(card); |
| 49 | struct snd_soc_dai_link *link = &card->dai_link[*index]; |
| 50 | struct gx_dai_link_i2s_data *be; |
| 51 | |
| 52 | /* Allocate i2s link parameters */ |
| 53 | be = devm_kzalloc(card->dev, sizeof(*be), GFP_KERNEL); |
| 54 | if (!be) |
| 55 | return -ENOMEM; |
| 56 | priv->link_data[*index] = be; |
| 57 | |
| 58 | /* Setup i2s link */ |
| 59 | link->ops = &gx_card_i2s_be_ops; |
| 60 | link->dai_fmt = meson_card_parse_daifmt(node, link->cpus->of_node); |
| 61 | |
| 62 | of_property_read_u32(node, "mclk-fs", &be->mclk_fs); |
| 63 | |
| 64 | return 0; |
| 65 | } |
| 66 | |
| 67 | static int gx_card_cpu_identify(struct snd_soc_dai_link_component *c, |
| 68 | char *match) |
| 69 | { |
| 70 | if (of_device_is_compatible(c->of_node, DT_PREFIX "aiu")) { |
| 71 | if (strstr(c->dai_name, match)) |
| 72 | return 1; |
| 73 | } |
| 74 | |
| 75 | /* dai not matched */ |
| 76 | return 0; |
| 77 | } |
| 78 | |
| 79 | static int gx_card_add_link(struct snd_soc_card *card, struct device_node *np, |
| 80 | int *index) |
| 81 | { |
| 82 | struct snd_soc_dai_link *dai_link = &card->dai_link[*index]; |
| 83 | struct snd_soc_dai_link_component *cpu; |
| 84 | int ret; |
| 85 | |
| 86 | cpu = devm_kzalloc(card->dev, sizeof(*cpu), GFP_KERNEL); |
| 87 | if (!cpu) |
| 88 | return -ENOMEM; |
| 89 | |
| 90 | dai_link->cpus = cpu; |
| 91 | dai_link->num_cpus = 1; |
| 92 | |
| 93 | ret = meson_card_parse_dai(card, np, &dai_link->cpus->of_node, |
| 94 | &dai_link->cpus->dai_name); |
| 95 | if (ret) |
| 96 | return ret; |
| 97 | |
| 98 | if (gx_card_cpu_identify(dai_link->cpus, "FIFO")) |
| 99 | ret = meson_card_set_fe_link(card, dai_link, np, true); |
| 100 | else |
| 101 | ret = meson_card_set_be_link(card, dai_link, np); |
| 102 | |
| 103 | if (ret) |
| 104 | return ret; |
| 105 | |
| 106 | /* Check if the cpu is the i2s encoder and parse i2s data */ |
| 107 | if (gx_card_cpu_identify(dai_link->cpus, "I2S Encoder")) |
| 108 | ret = gx_card_parse_i2s(card, np, index); |
| 109 | |
| 110 | /* Or apply codec to codec params if necessary */ |
Jerome Brunet | de911b4 | 2020-04-20 13:45:11 +0200 | [diff] [blame] | 111 | else if (gx_card_cpu_identify(dai_link->cpus, "CODEC CTRL")) { |
Jerome Brunet | e37a0c3 | 2020-02-13 16:51:59 +0100 | [diff] [blame] | 112 | dai_link->params = &codec_params; |
Jerome Brunet | de911b4 | 2020-04-20 13:45:11 +0200 | [diff] [blame] | 113 | dai_link->no_pcm = 0; /* link is not a DPCM BE */ |
| 114 | } |
Jerome Brunet | e37a0c3 | 2020-02-13 16:51:59 +0100 | [diff] [blame] | 115 | |
| 116 | return ret; |
| 117 | } |
| 118 | |
| 119 | static const struct meson_card_match_data gx_card_match_data = { |
| 120 | .add_link = gx_card_add_link, |
| 121 | }; |
| 122 | |
| 123 | static const struct of_device_id gx_card_of_match[] = { |
| 124 | { |
| 125 | .compatible = "amlogic,gx-sound-card", |
| 126 | .data = &gx_card_match_data, |
| 127 | }, {} |
| 128 | }; |
| 129 | MODULE_DEVICE_TABLE(of, gx_card_of_match); |
| 130 | |
| 131 | static struct platform_driver gx_card_pdrv = { |
| 132 | .probe = meson_card_probe, |
| 133 | .remove = meson_card_remove, |
| 134 | .driver = { |
| 135 | .name = "gx-sound-card", |
| 136 | .of_match_table = gx_card_of_match, |
| 137 | }, |
| 138 | }; |
| 139 | module_platform_driver(gx_card_pdrv); |
| 140 | |
| 141 | MODULE_DESCRIPTION("Amlogic GX ALSA machine driver"); |
| 142 | MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>"); |
| 143 | MODULE_LICENSE("GPL v2"); |