]>
Commit | Line | Data |
---|---|---|
e37a0c31 JB |
1 | // SPDX-License-Identifier: (GPL-2.0 OR MIT) |
2 | // | |
3 | // Copyright (c) 2020 BayLibre, SAS. | |
4 | // Author: Jerome Brunet <[email protected]> | |
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 */ | |
de911b4e | 111 | else if (gx_card_cpu_identify(dai_link->cpus, "CODEC CTRL")) { |
e37a0c31 | 112 | dai_link->params = &codec_params; |
de911b4e JB |
113 | dai_link->no_pcm = 0; /* link is not a DPCM BE */ |
114 | } | |
e37a0c31 JB |
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 <[email protected]>"); | |
143 | MODULE_LICENSE("GPL v2"); |