Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/sound/soc/intel/avs/boards/hdaudio.c
26607 views
1
// SPDX-License-Identifier: GPL-2.0-only
2
//
3
// Copyright(c) 2021-2022 Intel Corporation
4
//
5
// Authors: Cezary Rojewski <[email protected]>
6
// Amadeusz Slawinski <[email protected]>
7
//
8
9
#include <linux/module.h>
10
#include <linux/platform_device.h>
11
#include <sound/hda_codec.h>
12
#include <sound/hda_i915.h>
13
#include <sound/soc.h>
14
#include <sound/soc-acpi.h>
15
#include "../../../codecs/hda.h"
16
#include "../utils.h"
17
18
static int avs_create_dai_links(struct device *dev, struct hda_codec *codec, int pcm_count,
19
const char *platform_name, struct snd_soc_dai_link **links)
20
{
21
struct snd_soc_dai_link_component *platform;
22
struct snd_soc_dai_link *dl;
23
struct hda_pcm *pcm;
24
const char *cname = dev_name(&codec->core.dev);
25
int i;
26
27
dl = devm_kcalloc(dev, pcm_count, sizeof(*dl), GFP_KERNEL);
28
platform = devm_kzalloc(dev, sizeof(*platform), GFP_KERNEL);
29
if (!dl || !platform)
30
return -ENOMEM;
31
32
platform->name = platform_name;
33
pcm = list_first_entry(&codec->pcm_list_head, struct hda_pcm, list);
34
35
for (i = 0; i < pcm_count; i++, pcm = list_next_entry(pcm, list)) {
36
dl[i].name = devm_kasprintf(dev, GFP_KERNEL, "%s link%d", cname, i);
37
if (!dl[i].name)
38
return -ENOMEM;
39
40
dl[i].id = i;
41
dl[i].nonatomic = 1;
42
dl[i].no_pcm = 1;
43
dl[i].platforms = platform;
44
dl[i].num_platforms = 1;
45
dl[i].ignore_pmdown_time = 1;
46
47
dl[i].codecs = devm_kzalloc(dev, sizeof(*dl->codecs), GFP_KERNEL);
48
dl[i].cpus = devm_kzalloc(dev, sizeof(*dl->cpus), GFP_KERNEL);
49
if (!dl[i].codecs || !dl[i].cpus)
50
return -ENOMEM;
51
52
dl[i].cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, "%s-cpu%d", cname, i);
53
if (!dl[i].cpus->dai_name)
54
return -ENOMEM;
55
56
dl[i].codecs->name = devm_kstrdup_const(dev, cname, GFP_KERNEL);
57
if (!dl[i].codecs->name)
58
return -ENOMEM;
59
60
dl[i].codecs->dai_name = pcm->name;
61
dl[i].num_codecs = 1;
62
dl[i].num_cpus = 1;
63
}
64
65
*links = dl;
66
return 0;
67
}
68
69
/* Should be aligned with SectionPCM's name from topology */
70
#define FEDAI_NAME_PREFIX "HDMI"
71
72
static struct snd_pcm *
73
avs_card_hdmi_pcm_at(struct snd_soc_card *card, int hdmi_idx)
74
{
75
struct snd_soc_pcm_runtime *rtd;
76
int dir = SNDRV_PCM_STREAM_PLAYBACK;
77
78
for_each_card_rtds(card, rtd) {
79
struct snd_pcm *spcm;
80
int ret, n;
81
82
spcm = rtd->pcm ? rtd->pcm->streams[dir].pcm : NULL;
83
if (!spcm || !strstr(spcm->id, FEDAI_NAME_PREFIX))
84
continue;
85
86
ret = sscanf(spcm->id, FEDAI_NAME_PREFIX "%d", &n);
87
if (ret != 1)
88
continue;
89
if (n == hdmi_idx)
90
return rtd->pcm;
91
}
92
93
return NULL;
94
}
95
96
static int avs_card_late_probe(struct snd_soc_card *card)
97
{
98
struct snd_soc_acpi_mach *mach = dev_get_platdata(card->dev);
99
struct avs_mach_pdata *pdata = mach->pdata;
100
struct hda_codec *codec = pdata->codec;
101
struct hda_pcm *hpcm;
102
/* Topology pcm indexing is 1-based */
103
int i = 1;
104
105
list_for_each_entry(hpcm, &codec->pcm_list_head, list) {
106
struct snd_pcm *spcm;
107
108
spcm = avs_card_hdmi_pcm_at(card, i);
109
if (spcm) {
110
hpcm->pcm = spcm;
111
hpcm->device = spcm->device;
112
dev_info(card->dev, "%s: mapping HDMI converter %d to PCM %d (%p)\n",
113
__func__, i, hpcm->device, spcm);
114
} else {
115
hpcm->pcm = NULL;
116
hpcm->device = SNDRV_PCM_INVALID_DEVICE;
117
dev_warn(card->dev, "%s: no PCM in topology for HDMI converter %d\n",
118
__func__, i);
119
}
120
i++;
121
}
122
123
return hda_codec_probe_complete(codec);
124
}
125
126
static int avs_probing_link_init(struct snd_soc_pcm_runtime *rtm)
127
{
128
struct snd_soc_acpi_mach *mach;
129
struct avs_mach_pdata *pdata;
130
struct snd_soc_dai_link *links = NULL;
131
struct snd_soc_card *card = rtm->card;
132
struct hda_codec *codec;
133
struct hda_pcm *pcm;
134
int ret, pcm_count = 0;
135
136
mach = dev_get_platdata(card->dev);
137
pdata = mach->pdata;
138
codec = pdata->codec;
139
140
if (list_empty(&codec->pcm_list_head))
141
return -EINVAL;
142
list_for_each_entry(pcm, &codec->pcm_list_head, list)
143
pcm_count++;
144
145
ret = avs_create_dai_links(card->dev, codec, pcm_count, mach->mach_params.platform, &links);
146
if (ret < 0) {
147
dev_err(card->dev, "create links failed: %d\n", ret);
148
return ret;
149
}
150
151
ret = snd_soc_add_pcm_runtimes(card, links, pcm_count);
152
if (ret < 0) {
153
dev_err(card->dev, "add links failed: %d\n", ret);
154
return ret;
155
}
156
157
return 0;
158
}
159
160
static const struct snd_soc_dai_link probing_link = {
161
.name = "probing-LINK",
162
.id = -1,
163
.nonatomic = 1,
164
.no_pcm = 1,
165
.cpus = &snd_soc_dummy_dlc,
166
.num_cpus = 1,
167
.init = avs_probing_link_init,
168
};
169
170
static int avs_hdaudio_probe(struct platform_device *pdev)
171
{
172
struct snd_soc_dai_link *binder;
173
struct snd_soc_acpi_mach *mach;
174
struct avs_mach_pdata *pdata;
175
struct snd_soc_card *card;
176
struct device *dev = &pdev->dev;
177
struct hda_codec *codec;
178
179
mach = dev_get_platdata(dev);
180
pdata = mach->pdata;
181
codec = pdata->codec;
182
183
/* codec may be unloaded before card's probe() fires */
184
if (!device_is_registered(&codec->core.dev))
185
return -ENODEV;
186
187
binder = devm_kmemdup(dev, &probing_link, sizeof(probing_link), GFP_KERNEL);
188
if (!binder)
189
return -ENOMEM;
190
191
binder->platforms = devm_kzalloc(dev, sizeof(*binder->platforms), GFP_KERNEL);
192
binder->codecs = devm_kzalloc(dev, sizeof(*binder->codecs), GFP_KERNEL);
193
if (!binder->platforms || !binder->codecs)
194
return -ENOMEM;
195
196
binder->codecs->name = devm_kstrdup_const(dev, dev_name(&codec->core.dev), GFP_KERNEL);
197
if (!binder->codecs->name)
198
return -ENOMEM;
199
200
binder->platforms->name = mach->mach_params.platform;
201
binder->num_platforms = 1;
202
binder->codecs->dai_name = "codec-probing-DAI";
203
binder->num_codecs = 1;
204
205
card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
206
if (!card)
207
return -ENOMEM;
208
209
if (pdata->obsolete_card_names) {
210
card->name = binder->codecs->name;
211
} else {
212
card->driver_name = "avs_hdaudio";
213
if (hda_codec_is_display(codec))
214
card->long_name = card->name = "AVS HDMI";
215
else
216
card->long_name = card->name = "AVS HD-Audio";
217
}
218
219
card->dev = dev;
220
card->owner = THIS_MODULE;
221
card->dai_link = binder;
222
card->num_links = 1;
223
card->fully_routed = true;
224
if (hda_codec_is_display(codec))
225
card->late_probe = avs_card_late_probe;
226
227
return devm_snd_soc_register_deferrable_card(dev, card);
228
}
229
230
static const struct platform_device_id avs_hdaudio_driver_ids[] = {
231
{
232
.name = "avs_hdaudio",
233
},
234
{},
235
};
236
MODULE_DEVICE_TABLE(platform, avs_hdaudio_driver_ids);
237
238
static struct platform_driver avs_hdaudio_driver = {
239
.probe = avs_hdaudio_probe,
240
.driver = {
241
.name = "avs_hdaudio",
242
.pm = &snd_soc_pm_ops,
243
},
244
.id_table = avs_hdaudio_driver_ids,
245
};
246
247
module_platform_driver(avs_hdaudio_driver)
248
249
MODULE_DESCRIPTION("Intel HD-Audio machine driver");
250
MODULE_AUTHOR("Cezary Rojewski <[email protected]>");
251
MODULE_LICENSE("GPL");
252
253