Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/sound/soc/sdw_utils/soc_sdw_rt_amp.c
26436 views
1
// SPDX-License-Identifier: GPL-2.0-only
2
// This file incorporates work covered by the following copyright notice:
3
// Copyright (c) 2022 Intel Corporation
4
// Copyright (c) 2024 Advanced Micro Devices, Inc.
5
6
/*
7
* soc_sdw_rt_amp - Helpers to handle RT1308/RT1316/RT1318 from generic machine driver
8
*/
9
10
#include <linux/device.h>
11
#include <linux/errno.h>
12
#include <sound/control.h>
13
#include <sound/soc.h>
14
#include <sound/soc-acpi.h>
15
#include <sound/soc-dapm.h>
16
#include <linux/soundwire/sdw.h>
17
#include <linux/soundwire/sdw_type.h>
18
#include <linux/dmi.h>
19
#include <sound/soc_sdw_utils.h>
20
#include "soc_sdw_rt_amp_coeff_tables.h"
21
#include "../codecs/rt1308.h"
22
23
#define CODEC_NAME_SIZE 7
24
25
/* choose a larger value to resolve compatibility issues */
26
#define RT_AMP_MAX_BQ_REG RT1316_MAX_BQ_REG
27
28
struct rt_amp_platform_data {
29
const unsigned char *bq_params;
30
const unsigned int bq_params_cnt;
31
};
32
33
static const struct rt_amp_platform_data dell_0a5d_platform_data = {
34
.bq_params = dell_0a5d_bq_params,
35
.bq_params_cnt = ARRAY_SIZE(dell_0a5d_bq_params),
36
};
37
38
static const struct rt_amp_platform_data dell_0b00_platform_data = {
39
.bq_params = dell_0b00_bq_params,
40
.bq_params_cnt = ARRAY_SIZE(dell_0b00_bq_params),
41
};
42
43
static const struct dmi_system_id dmi_platform_data[] = {
44
/* CometLake devices */
45
{
46
.matches = {
47
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
48
DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0990")
49
},
50
.driver_data = (void *)&dell_0a5d_platform_data,
51
},
52
{
53
.matches = {
54
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
55
DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "098F")
56
},
57
.driver_data = (void *)&dell_0a5d_platform_data,
58
},
59
/* TigerLake devices */
60
{
61
.matches = {
62
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
63
DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0A5D")
64
},
65
.driver_data = (void *)&dell_0a5d_platform_data,
66
},
67
{
68
.matches = {
69
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
70
DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0A5E")
71
},
72
.driver_data = (void *)&dell_0a5d_platform_data,
73
},
74
/* AlderLake devices */
75
{
76
.matches = {
77
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
78
DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0B00")
79
},
80
.driver_data = (void *)&dell_0b00_platform_data,
81
},
82
{
83
.matches = {
84
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
85
DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0B01")
86
},
87
.driver_data = (void *)&dell_0b00_platform_data,
88
},
89
{
90
.matches = {
91
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
92
DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0AFF")
93
},
94
.driver_data = (void *)&dell_0b00_platform_data,
95
},
96
{
97
.matches = {
98
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
99
DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0AFE")
100
},
101
.driver_data = (void *)&dell_0b00_platform_data,
102
},
103
{},
104
};
105
106
static int rt_amp_add_device_props(struct device *sdw_dev)
107
{
108
struct property_entry props[3] = {};
109
struct fwnode_handle *fwnode;
110
const struct dmi_system_id *dmi_data;
111
const struct rt_amp_platform_data *pdata;
112
unsigned char params[RT_AMP_MAX_BQ_REG];
113
int ret;
114
115
dmi_data = dmi_first_match(dmi_platform_data);
116
if (!dmi_data)
117
return 0;
118
119
pdata = dmi_data->driver_data;
120
memcpy(&params, pdata->bq_params, sizeof(unsigned char) * pdata->bq_params_cnt);
121
122
props[0] = PROPERTY_ENTRY_U8_ARRAY("realtek,bq-params", params);
123
props[1] = PROPERTY_ENTRY_U32("realtek,bq-params-cnt", pdata->bq_params_cnt);
124
125
fwnode = fwnode_create_software_node(props, NULL);
126
if (IS_ERR(fwnode))
127
return PTR_ERR(fwnode);
128
129
ret = device_add_software_node(sdw_dev, to_software_node(fwnode));
130
131
fwnode_handle_put(fwnode);
132
133
return ret;
134
}
135
136
/*
137
* dapm routes for rt1308/rt1316/rt1318 will be registered dynamically
138
* according to the number of rt1308/rt1316/rt1318 used. The first two
139
* entries will be registered for one codec case, and the last two entries
140
* are also registered if two 1308s/1316s/1318s are used.
141
*/
142
static const struct snd_soc_dapm_route rt1308_map[] = {
143
{ "Speaker", NULL, "rt1308-1 SPOL" },
144
{ "Speaker", NULL, "rt1308-1 SPOR" },
145
{ "Speaker", NULL, "rt1308-2 SPOL" },
146
{ "Speaker", NULL, "rt1308-2 SPOR" },
147
};
148
149
static const struct snd_soc_dapm_route rt1316_map[] = {
150
{ "Speaker", NULL, "rt1316-1 SPOL" },
151
{ "Speaker", NULL, "rt1316-1 SPOR" },
152
{ "Speaker", NULL, "rt1316-2 SPOL" },
153
{ "Speaker", NULL, "rt1316-2 SPOR" },
154
};
155
156
static const struct snd_soc_dapm_route rt1318_map[] = {
157
{ "Speaker", NULL, "rt1318-1 SPOL" },
158
{ "Speaker", NULL, "rt1318-1 SPOR" },
159
{ "Speaker", NULL, "rt1318-2 SPOL" },
160
{ "Speaker", NULL, "rt1318-2 SPOR" },
161
};
162
163
static const struct snd_soc_dapm_route rt1320_map[] = {
164
{ "Speaker", NULL, "rt1320-1 SPOL" },
165
{ "Speaker", NULL, "rt1320-1 SPOR" },
166
{ "Speaker", NULL, "rt1320-2 SPOL" },
167
{ "Speaker", NULL, "rt1320-2 SPOR" },
168
};
169
170
static const struct snd_soc_dapm_route *get_codec_name_and_route(struct snd_soc_dai *dai,
171
char *codec_name)
172
{
173
/* get the codec name */
174
snprintf(codec_name, CODEC_NAME_SIZE, "%s", dai->name);
175
176
/* choose the right codec's map */
177
if (strcmp(codec_name, "rt1308") == 0)
178
return rt1308_map;
179
else if (strcmp(codec_name, "rt1316") == 0)
180
return rt1316_map;
181
else if (strcmp(codec_name, "rt1318") == 0)
182
return rt1318_map;
183
else
184
return rt1320_map;
185
}
186
187
int asoc_sdw_rt_amp_spk_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai)
188
{
189
struct snd_soc_card *card = rtd->card;
190
const struct snd_soc_dapm_route *rt_amp_map;
191
char codec_name[CODEC_NAME_SIZE];
192
struct snd_soc_dai *codec_dai;
193
int ret = -EINVAL;
194
int i;
195
196
rt_amp_map = get_codec_name_and_route(dai, codec_name);
197
198
for_each_rtd_codec_dais(rtd, i, codec_dai) {
199
if (strstr(codec_dai->component->name_prefix, "-1"))
200
ret = snd_soc_dapm_add_routes(&card->dapm, rt_amp_map, 2);
201
else if (strstr(codec_dai->component->name_prefix, "-2"))
202
ret = snd_soc_dapm_add_routes(&card->dapm, rt_amp_map + 2, 2);
203
}
204
205
return ret;
206
}
207
EXPORT_SYMBOL_NS(asoc_sdw_rt_amp_spk_rtd_init, "SND_SOC_SDW_UTILS");
208
209
static int rt1308_i2s_hw_params(struct snd_pcm_substream *substream,
210
struct snd_pcm_hw_params *params)
211
{
212
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
213
struct snd_soc_card *card = rtd->card;
214
struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
215
int clk_id, clk_freq, pll_out;
216
int err;
217
218
clk_id = RT1308_PLL_S_MCLK;
219
clk_freq = 38400000;
220
221
pll_out = params_rate(params) * 512;
222
223
/* Set rt1308 pll */
224
err = snd_soc_dai_set_pll(codec_dai, 0, clk_id, clk_freq, pll_out);
225
if (err < 0) {
226
dev_err(card->dev, "Failed to set RT1308 PLL: %d\n", err);
227
return err;
228
}
229
230
/* Set rt1308 sysclk */
231
err = snd_soc_dai_set_sysclk(codec_dai, RT1308_FS_SYS_S_PLL, pll_out,
232
SND_SOC_CLOCK_IN);
233
if (err < 0) {
234
dev_err(card->dev, "Failed to set RT1308 SYSCLK: %d\n", err);
235
return err;
236
}
237
238
return 0;
239
}
240
241
/* machine stream operations */
242
const struct snd_soc_ops soc_sdw_rt1308_i2s_ops = {
243
.hw_params = rt1308_i2s_hw_params,
244
};
245
EXPORT_SYMBOL_NS(soc_sdw_rt1308_i2s_ops, "SND_SOC_SDW_UTILS");
246
247
int asoc_sdw_rt_amp_exit(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link)
248
{
249
struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
250
251
if (ctx->amp_dev1) {
252
device_remove_software_node(ctx->amp_dev1);
253
put_device(ctx->amp_dev1);
254
}
255
256
if (ctx->amp_dev2) {
257
device_remove_software_node(ctx->amp_dev2);
258
put_device(ctx->amp_dev2);
259
}
260
261
return 0;
262
}
263
EXPORT_SYMBOL_NS(asoc_sdw_rt_amp_exit, "SND_SOC_SDW_UTILS");
264
265
int asoc_sdw_rt_amp_init(struct snd_soc_card *card,
266
struct snd_soc_dai_link *dai_links,
267
struct asoc_sdw_codec_info *info,
268
bool playback)
269
{
270
struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
271
struct device *sdw_dev1, *sdw_dev2;
272
int ret;
273
274
/* Count amp number and do init on playback link only. */
275
if (!playback)
276
return 0;
277
278
info->amp_num++;
279
280
if (info->amp_num == 2) {
281
sdw_dev1 = bus_find_device_by_name(&sdw_bus_type, NULL, dai_links->codecs[0].name);
282
if (!sdw_dev1)
283
return -EPROBE_DEFER;
284
285
ret = rt_amp_add_device_props(sdw_dev1);
286
if (ret < 0) {
287
put_device(sdw_dev1);
288
return ret;
289
}
290
ctx->amp_dev1 = sdw_dev1;
291
292
sdw_dev2 = bus_find_device_by_name(&sdw_bus_type, NULL, dai_links->codecs[1].name);
293
if (!sdw_dev2)
294
return -EPROBE_DEFER;
295
296
ret = rt_amp_add_device_props(sdw_dev2);
297
if (ret < 0) {
298
put_device(sdw_dev2);
299
return ret;
300
}
301
ctx->amp_dev2 = sdw_dev2;
302
}
303
304
return 0;
305
}
306
EXPORT_SYMBOL_NS(asoc_sdw_rt_amp_init, "SND_SOC_SDW_UTILS");
307
308