Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/sound/soc/intel/catpt/device.c
26481 views
1
// SPDX-License-Identifier: GPL-2.0-only
2
//
3
// Copyright(c) 2020 Intel Corporation
4
//
5
// Author: Cezary Rojewski <[email protected]>
6
//
7
// Special thanks to:
8
// Marcin Barlik <[email protected]>
9
// Piotr Papierkowski <[email protected]>
10
//
11
// for sharing LPT-LP and WTP-LP AudioDSP architecture expertise and
12
// helping backtrack its historical background
13
//
14
15
#include <linux/acpi.h>
16
#include <linux/dma-mapping.h>
17
#include <linux/interrupt.h>
18
#include <linux/module.h>
19
#include <linux/pci.h>
20
#include <linux/platform_device.h>
21
#include <linux/pm_runtime.h>
22
#include <sound/intel-dsp-config.h>
23
#include <sound/soc.h>
24
#include <sound/soc-acpi.h>
25
#include "core.h"
26
#include "registers.h"
27
28
#define CREATE_TRACE_POINTS
29
#include "trace.h"
30
31
static int catpt_suspend(struct device *dev)
32
{
33
struct catpt_dev *cdev = dev_get_drvdata(dev);
34
struct dma_chan *chan;
35
int ret;
36
37
chan = catpt_dma_request_config_chan(cdev);
38
if (IS_ERR(chan))
39
return PTR_ERR(chan);
40
41
memset(&cdev->dx_ctx, 0, sizeof(cdev->dx_ctx));
42
ret = catpt_ipc_enter_dxstate(cdev, CATPT_DX_STATE_D3, &cdev->dx_ctx);
43
if (ret) {
44
ret = CATPT_IPC_ERROR(ret);
45
goto release_dma_chan;
46
}
47
48
ret = catpt_dsp_stall(cdev, true);
49
if (ret)
50
goto release_dma_chan;
51
52
ret = catpt_store_memdumps(cdev, chan);
53
if (ret) {
54
dev_err(cdev->dev, "store memdumps failed: %d\n", ret);
55
goto release_dma_chan;
56
}
57
58
ret = catpt_store_module_states(cdev, chan);
59
if (ret) {
60
dev_err(cdev->dev, "store module states failed: %d\n", ret);
61
goto release_dma_chan;
62
}
63
64
ret = catpt_store_streams_context(cdev, chan);
65
if (ret)
66
dev_err(cdev->dev, "store streams ctx failed: %d\n", ret);
67
68
release_dma_chan:
69
dma_release_channel(chan);
70
if (ret)
71
return ret;
72
return catpt_dsp_power_down(cdev);
73
}
74
75
static int catpt_resume(struct device *dev)
76
{
77
struct catpt_dev *cdev = dev_get_drvdata(dev);
78
int ret, i;
79
80
ret = catpt_dsp_power_up(cdev);
81
if (ret)
82
return ret;
83
84
if (!try_module_get(dev->driver->owner)) {
85
dev_info(dev, "module unloading, skipping fw boot\n");
86
return 0;
87
}
88
module_put(dev->driver->owner);
89
90
ret = catpt_boot_firmware(cdev, true);
91
if (ret) {
92
dev_err(cdev->dev, "boot firmware failed: %d\n", ret);
93
return ret;
94
}
95
96
/* reconfigure SSP devices after Dx transition */
97
for (i = 0; i < CATPT_SSP_COUNT; i++) {
98
if (cdev->devfmt[i].iface == UINT_MAX)
99
continue;
100
101
ret = catpt_ipc_set_device_format(cdev, &cdev->devfmt[i]);
102
if (ret)
103
return CATPT_IPC_ERROR(ret);
104
}
105
106
return 0;
107
}
108
109
static int catpt_runtime_suspend(struct device *dev)
110
{
111
if (!try_module_get(dev->driver->owner)) {
112
dev_info(dev, "module unloading, skipping suspend\n");
113
return 0;
114
}
115
module_put(dev->driver->owner);
116
117
return catpt_suspend(dev);
118
}
119
120
static int catpt_runtime_resume(struct device *dev)
121
{
122
return catpt_resume(dev);
123
}
124
125
static const struct dev_pm_ops catpt_dev_pm = {
126
SYSTEM_SLEEP_PM_OPS(catpt_suspend, catpt_resume)
127
RUNTIME_PM_OPS(catpt_runtime_suspend, catpt_runtime_resume, NULL)
128
};
129
130
/* machine board owned by CATPT is removed with this hook */
131
static void board_pdev_unregister(void *data)
132
{
133
platform_device_unregister(data);
134
}
135
136
static int catpt_register_board(struct catpt_dev *cdev)
137
{
138
const struct catpt_spec *spec = cdev->spec;
139
struct snd_soc_acpi_mach *mach;
140
struct platform_device *board;
141
142
mach = snd_soc_acpi_find_machine(spec->machines);
143
if (!mach) {
144
dev_info(cdev->dev, "no machines present\n");
145
return 0;
146
}
147
148
mach->mach_params.platform = "catpt-platform";
149
board = platform_device_register_data(NULL, mach->drv_name,
150
PLATFORM_DEVID_NONE,
151
(const void *)mach, sizeof(*mach));
152
if (IS_ERR(board)) {
153
dev_err(cdev->dev, "board register failed\n");
154
return PTR_ERR(board);
155
}
156
157
return devm_add_action_or_reset(cdev->dev, board_pdev_unregister,
158
board);
159
}
160
161
static int catpt_probe_components(struct catpt_dev *cdev)
162
{
163
int ret;
164
165
ret = catpt_dsp_power_up(cdev);
166
if (ret)
167
return ret;
168
169
ret = catpt_dmac_probe(cdev);
170
if (ret) {
171
dev_err(cdev->dev, "DMAC probe failed: %d\n", ret);
172
goto err_dmac_probe;
173
}
174
175
ret = catpt_first_boot_firmware(cdev);
176
if (ret) {
177
dev_err(cdev->dev, "first fw boot failed: %d\n", ret);
178
goto err_boot_fw;
179
}
180
181
ret = catpt_register_plat_component(cdev);
182
if (ret) {
183
dev_err(cdev->dev, "register plat comp failed: %d\n", ret);
184
goto err_boot_fw;
185
}
186
187
ret = catpt_register_board(cdev);
188
if (ret) {
189
dev_err(cdev->dev, "register board failed: %d\n", ret);
190
goto err_reg_board;
191
}
192
193
/* reflect actual ADSP state in pm_runtime */
194
pm_runtime_set_active(cdev->dev);
195
196
pm_runtime_set_autosuspend_delay(cdev->dev, 2000);
197
pm_runtime_use_autosuspend(cdev->dev);
198
pm_runtime_mark_last_busy(cdev->dev);
199
pm_runtime_enable(cdev->dev);
200
return 0;
201
202
err_reg_board:
203
snd_soc_unregister_component(cdev->dev);
204
err_boot_fw:
205
catpt_dmac_remove(cdev);
206
err_dmac_probe:
207
catpt_dsp_power_down(cdev);
208
209
return ret;
210
}
211
212
static void catpt_dev_init(struct catpt_dev *cdev, struct device *dev,
213
const struct catpt_spec *spec)
214
{
215
cdev->dev = dev;
216
cdev->spec = spec;
217
init_completion(&cdev->fw_ready);
218
INIT_LIST_HEAD(&cdev->stream_list);
219
spin_lock_init(&cdev->list_lock);
220
mutex_init(&cdev->clk_mutex);
221
222
/*
223
* Mark both device formats as uninitialized. Once corresponding
224
* cpu_dai's pcm is created, proper values are assigned.
225
*/
226
cdev->devfmt[CATPT_SSP_IFACE_0].iface = UINT_MAX;
227
cdev->devfmt[CATPT_SSP_IFACE_1].iface = UINT_MAX;
228
229
catpt_ipc_init(&cdev->ipc, dev);
230
231
catpt_sram_init(&cdev->dram, spec->host_dram_offset,
232
catpt_dram_size(cdev));
233
catpt_sram_init(&cdev->iram, spec->host_iram_offset,
234
catpt_iram_size(cdev));
235
}
236
237
static int catpt_acpi_probe(struct platform_device *pdev)
238
{
239
const struct catpt_spec *spec;
240
struct catpt_dev *cdev;
241
struct device *dev = &pdev->dev;
242
const struct acpi_device_id *id;
243
struct resource *res;
244
int ret;
245
246
id = acpi_match_device(dev->driver->acpi_match_table, dev);
247
if (!id)
248
return -ENODEV;
249
250
ret = snd_intel_acpi_dsp_driver_probe(dev, id->id);
251
if (ret != SND_INTEL_DSP_DRIVER_ANY && ret != SND_INTEL_DSP_DRIVER_SST) {
252
dev_dbg(dev, "CATPT ACPI driver not selected, aborting probe\n");
253
return -ENODEV;
254
}
255
256
cdev = devm_kzalloc(dev, sizeof(*cdev), GFP_KERNEL);
257
if (!cdev)
258
return -ENOMEM;
259
260
spec = (const struct catpt_spec *)id->driver_data;
261
catpt_dev_init(cdev, dev, spec);
262
263
/* map DSP bar address */
264
cdev->lpe_ba = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
265
if (IS_ERR(cdev->lpe_ba))
266
return PTR_ERR(cdev->lpe_ba);
267
cdev->lpe_base = res->start;
268
269
/* map PCI bar address */
270
cdev->pci_ba = devm_platform_ioremap_resource(pdev, 1);
271
if (IS_ERR(cdev->pci_ba))
272
return PTR_ERR(cdev->pci_ba);
273
274
/* alloc buffer for storing DRAM context during dx transitions */
275
cdev->dxbuf_vaddr = dmam_alloc_coherent(dev, catpt_dram_size(cdev),
276
&cdev->dxbuf_paddr, GFP_KERNEL);
277
if (!cdev->dxbuf_vaddr)
278
return -ENOMEM;
279
280
ret = platform_get_irq(pdev, 0);
281
if (ret < 0)
282
return ret;
283
cdev->irq = ret;
284
285
platform_set_drvdata(pdev, cdev);
286
287
ret = devm_request_threaded_irq(dev, cdev->irq, catpt_dsp_irq_handler,
288
catpt_dsp_irq_thread,
289
IRQF_SHARED, "AudioDSP", cdev);
290
if (ret)
291
return ret;
292
293
return catpt_probe_components(cdev);
294
}
295
296
static void catpt_acpi_remove(struct platform_device *pdev)
297
{
298
struct catpt_dev *cdev = platform_get_drvdata(pdev);
299
300
pm_runtime_disable(cdev->dev);
301
302
snd_soc_unregister_component(cdev->dev);
303
catpt_dmac_remove(cdev);
304
catpt_dsp_power_down(cdev);
305
306
catpt_sram_free(&cdev->iram);
307
catpt_sram_free(&cdev->dram);
308
}
309
310
static struct snd_soc_acpi_mach lpt_machines[] = {
311
{
312
.id = "INT33CA",
313
.drv_name = "hsw_rt5640",
314
},
315
{}
316
};
317
318
static struct snd_soc_acpi_mach wpt_machines[] = {
319
{
320
.id = "INT33CA",
321
.drv_name = "hsw_rt5640",
322
},
323
{
324
.id = "INT343A",
325
.drv_name = "bdw_rt286",
326
},
327
{
328
.id = "10EC5650",
329
.drv_name = "bdw-rt5650",
330
},
331
{
332
.id = "RT5677CE",
333
.drv_name = "bdw-rt5677",
334
},
335
{}
336
};
337
338
static struct catpt_spec lpt_desc = {
339
.machines = lpt_machines,
340
.core_id = 0x01,
341
.host_dram_offset = 0x000000,
342
.host_iram_offset = 0x080000,
343
.host_shim_offset = 0x0E7000,
344
.host_dma_offset = { 0x0F0000, 0x0F8000 },
345
.host_ssp_offset = { 0x0E8000, 0x0E9000 },
346
.dram_mask = LPT_VDRTCTL0_DSRAMPGE_MASK,
347
.iram_mask = LPT_VDRTCTL0_ISRAMPGE_MASK,
348
.d3srampgd_bit = LPT_VDRTCTL0_D3SRAMPGD,
349
.d3pgd_bit = LPT_VDRTCTL0_D3PGD,
350
.pll_shutdown = lpt_dsp_pll_shutdown,
351
};
352
353
static struct catpt_spec wpt_desc = {
354
.machines = wpt_machines,
355
.core_id = 0x02,
356
.host_dram_offset = 0x000000,
357
.host_iram_offset = 0x0A0000,
358
.host_shim_offset = 0x0FB000,
359
.host_dma_offset = { 0x0FE000, 0x0FF000 },
360
.host_ssp_offset = { 0x0FC000, 0x0FD000 },
361
.dram_mask = WPT_VDRTCTL0_DSRAMPGE_MASK,
362
.iram_mask = WPT_VDRTCTL0_ISRAMPGE_MASK,
363
.d3srampgd_bit = WPT_VDRTCTL0_D3SRAMPGD,
364
.d3pgd_bit = WPT_VDRTCTL0_D3PGD,
365
.pll_shutdown = wpt_dsp_pll_shutdown,
366
};
367
368
static const struct acpi_device_id catpt_ids[] = {
369
{ "INT33C8", (unsigned long)&lpt_desc },
370
{ "INT3438", (unsigned long)&wpt_desc },
371
{ }
372
};
373
MODULE_DEVICE_TABLE(acpi, catpt_ids);
374
375
static struct platform_driver catpt_acpi_driver = {
376
.probe = catpt_acpi_probe,
377
.remove = catpt_acpi_remove,
378
.driver = {
379
.name = "intel_catpt",
380
.acpi_match_table = catpt_ids,
381
.pm = pm_ptr(&catpt_dev_pm),
382
.dev_groups = catpt_attr_groups,
383
},
384
};
385
module_platform_driver(catpt_acpi_driver);
386
387
MODULE_AUTHOR("Cezary Rojewski <[email protected]>");
388
MODULE_DESCRIPTION("Intel LPT/WPT AudioDSP driver");
389
MODULE_LICENSE("GPL v2");
390
391