1 // SPDX-License-Identifier: GPL-2.0-only
3 // Copyright(c) 2020 Intel Corporation. All rights reserved.
5 // Author: Cezary Rojewski <cezary.rojewski@intel.com>
8 // Marcin Barlik <marcin.barlik@intel.com>
9 // Piotr Papierkowski <piotr.papierkowski@intel.com>
11 // for sharing LPT-LP and WTP-LP AudioDSP architecture expertise and
12 // helping backtrack its historical background
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 <sound/soc-acpi-intel-match.h>
27 #include "registers.h"
29 #define CREATE_TRACE_POINTS
32 static int __maybe_unused
catpt_suspend(struct device
*dev
)
34 struct catpt_dev
*cdev
= dev_get_drvdata(dev
);
35 struct dma_chan
*chan
;
38 chan
= catpt_dma_request_config_chan(cdev
);
42 memset(&cdev
->dx_ctx
, 0, sizeof(cdev
->dx_ctx
));
43 ret
= catpt_ipc_enter_dxstate(cdev
, CATPT_DX_STATE_D3
, &cdev
->dx_ctx
);
45 ret
= CATPT_IPC_ERROR(ret
);
46 goto release_dma_chan
;
49 ret
= catpt_dsp_stall(cdev
, true);
51 goto release_dma_chan
;
53 ret
= catpt_store_memdumps(cdev
, chan
);
55 dev_err(cdev
->dev
, "store memdumps failed: %d\n", ret
);
56 goto release_dma_chan
;
59 ret
= catpt_store_module_states(cdev
, chan
);
61 dev_err(cdev
->dev
, "store module states failed: %d\n", ret
);
62 goto release_dma_chan
;
65 ret
= catpt_store_streams_context(cdev
, chan
);
67 dev_err(cdev
->dev
, "store streams ctx failed: %d\n", ret
);
70 dma_release_channel(chan
);
73 return catpt_dsp_power_down(cdev
);
76 static int __maybe_unused
catpt_resume(struct device
*dev
)
78 struct catpt_dev
*cdev
= dev_get_drvdata(dev
);
81 ret
= catpt_dsp_power_up(cdev
);
85 if (!try_module_get(dev
->driver
->owner
)) {
86 dev_info(dev
, "module unloading, skipping fw boot\n");
89 module_put(dev
->driver
->owner
);
91 ret
= catpt_boot_firmware(cdev
, true);
93 dev_err(cdev
->dev
, "boot firmware failed: %d\n", ret
);
97 /* reconfigure SSP devices after Dx transition */
98 for (i
= 0; i
< CATPT_SSP_COUNT
; i
++) {
99 if (cdev
->devfmt
[i
].iface
== UINT_MAX
)
102 ret
= catpt_ipc_set_device_format(cdev
, &cdev
->devfmt
[i
]);
104 return CATPT_IPC_ERROR(ret
);
110 static int __maybe_unused
catpt_runtime_suspend(struct device
*dev
)
112 if (!try_module_get(dev
->driver
->owner
)) {
113 dev_info(dev
, "module unloading, skipping suspend\n");
116 module_put(dev
->driver
->owner
);
118 return catpt_suspend(dev
);
121 static int __maybe_unused
catpt_runtime_resume(struct device
*dev
)
123 return catpt_resume(dev
);
126 static const struct dev_pm_ops catpt_dev_pm
= {
127 SET_SYSTEM_SLEEP_PM_OPS(catpt_suspend
, catpt_resume
)
128 SET_RUNTIME_PM_OPS(catpt_runtime_suspend
, catpt_runtime_resume
, NULL
)
131 /* machine board owned by CATPT is removed with this hook */
132 static void board_pdev_unregister(void *data
)
134 platform_device_unregister(data
);
137 static int catpt_register_board(struct catpt_dev
*cdev
)
139 const struct catpt_spec
*spec
= cdev
->spec
;
140 struct snd_soc_acpi_mach
*mach
;
141 struct platform_device
*board
;
143 mach
= snd_soc_acpi_find_machine(spec
->machines
);
145 dev_info(cdev
->dev
, "no machines present\n");
149 mach
->mach_params
.platform
= "catpt-platform";
150 board
= platform_device_register_data(NULL
, mach
->drv_name
,
152 (const void *)mach
, sizeof(*mach
));
154 dev_err(cdev
->dev
, "board register failed\n");
155 return PTR_ERR(board
);
158 return devm_add_action_or_reset(cdev
->dev
, board_pdev_unregister
,
162 static int catpt_probe_components(struct catpt_dev
*cdev
)
166 ret
= catpt_dsp_power_up(cdev
);
170 ret
= catpt_dmac_probe(cdev
);
172 dev_err(cdev
->dev
, "DMAC probe failed: %d\n", ret
);
176 ret
= catpt_first_boot_firmware(cdev
);
178 dev_err(cdev
->dev
, "first fw boot failed: %d\n", ret
);
182 ret
= catpt_register_plat_component(cdev
);
184 dev_err(cdev
->dev
, "register plat comp failed: %d\n", ret
);
188 ret
= catpt_register_board(cdev
);
190 dev_err(cdev
->dev
, "register board failed: %d\n", ret
);
194 /* reflect actual ADSP state in pm_runtime */
195 pm_runtime_set_active(cdev
->dev
);
197 pm_runtime_set_autosuspend_delay(cdev
->dev
, 2000);
198 pm_runtime_use_autosuspend(cdev
->dev
);
199 pm_runtime_mark_last_busy(cdev
->dev
);
200 pm_runtime_enable(cdev
->dev
);
204 snd_soc_unregister_component(cdev
->dev
);
206 catpt_dmac_remove(cdev
);
208 catpt_dsp_power_down(cdev
);
213 static void catpt_dev_init(struct catpt_dev
*cdev
, struct device
*dev
,
214 const struct catpt_spec
*spec
)
218 init_completion(&cdev
->fw_ready
);
219 INIT_LIST_HEAD(&cdev
->stream_list
);
220 spin_lock_init(&cdev
->list_lock
);
221 mutex_init(&cdev
->clk_mutex
);
224 * Mark both device formats as uninitialized. Once corresponding
225 * cpu_dai's pcm is created, proper values are assigned.
227 cdev
->devfmt
[CATPT_SSP_IFACE_0
].iface
= UINT_MAX
;
228 cdev
->devfmt
[CATPT_SSP_IFACE_1
].iface
= UINT_MAX
;
230 catpt_ipc_init(&cdev
->ipc
, dev
);
232 catpt_sram_init(&cdev
->dram
, spec
->host_dram_offset
,
233 catpt_dram_size(cdev
));
234 catpt_sram_init(&cdev
->iram
, spec
->host_iram_offset
,
235 catpt_iram_size(cdev
));
238 static int catpt_acpi_probe(struct platform_device
*pdev
)
240 const struct catpt_spec
*spec
;
241 struct catpt_dev
*cdev
;
242 struct device
*dev
= &pdev
->dev
;
243 const struct acpi_device_id
*id
;
244 struct resource
*res
;
247 id
= acpi_match_device(dev
->driver
->acpi_match_table
, dev
);
251 ret
= snd_intel_acpi_dsp_driver_probe(dev
, id
->id
);
252 if (ret
!= SND_INTEL_DSP_DRIVER_ANY
&& ret
!= SND_INTEL_DSP_DRIVER_SST
) {
253 dev_dbg(dev
, "CATPT ACPI driver not selected, aborting probe\n");
257 spec
= device_get_match_data(dev
);
261 cdev
= devm_kzalloc(dev
, sizeof(*cdev
), GFP_KERNEL
);
265 catpt_dev_init(cdev
, dev
, spec
);
267 /* map DSP bar address */
268 cdev
->lpe_ba
= devm_platform_get_and_ioremap_resource(pdev
, 0, &res
);
269 if (IS_ERR(cdev
->lpe_ba
))
270 return PTR_ERR(cdev
->lpe_ba
);
271 cdev
->lpe_base
= res
->start
;
273 /* map PCI bar address */
274 cdev
->pci_ba
= devm_platform_ioremap_resource(pdev
, 1);
275 if (IS_ERR(cdev
->pci_ba
))
276 return PTR_ERR(cdev
->pci_ba
);
278 /* alloc buffer for storing DRAM context during dx transitions */
279 cdev
->dxbuf_vaddr
= dmam_alloc_coherent(dev
, catpt_dram_size(cdev
),
280 &cdev
->dxbuf_paddr
, GFP_KERNEL
);
281 if (!cdev
->dxbuf_vaddr
)
284 ret
= platform_get_irq(pdev
, 0);
289 platform_set_drvdata(pdev
, cdev
);
291 ret
= devm_request_threaded_irq(dev
, cdev
->irq
, catpt_dsp_irq_handler
,
292 catpt_dsp_irq_thread
,
293 IRQF_SHARED
, "AudioDSP", cdev
);
297 return catpt_probe_components(cdev
);
300 static int catpt_acpi_remove(struct platform_device
*pdev
)
302 struct catpt_dev
*cdev
= platform_get_drvdata(pdev
);
304 pm_runtime_disable(cdev
->dev
);
306 snd_soc_unregister_component(cdev
->dev
);
307 catpt_dmac_remove(cdev
);
308 catpt_dsp_power_down(cdev
);
310 catpt_sram_free(&cdev
->iram
);
311 catpt_sram_free(&cdev
->dram
);
316 static struct catpt_spec lpt_desc
= {
317 .machines
= snd_soc_acpi_intel_haswell_machines
,
319 .host_dram_offset
= 0x000000,
320 .host_iram_offset
= 0x080000,
321 .host_shim_offset
= 0x0E7000,
322 .host_dma_offset
= { 0x0F0000, 0x0F8000 },
323 .host_ssp_offset
= { 0x0E8000, 0x0E9000 },
324 .dram_mask
= LPT_VDRTCTL0_DSRAMPGE_MASK
,
325 .iram_mask
= LPT_VDRTCTL0_ISRAMPGE_MASK
,
326 .d3srampgd_bit
= LPT_VDRTCTL0_D3SRAMPGD
,
327 .d3pgd_bit
= LPT_VDRTCTL0_D3PGD
,
328 .pll_shutdown
= lpt_dsp_pll_shutdown
,
331 static struct catpt_spec wpt_desc
= {
332 .machines
= snd_soc_acpi_intel_broadwell_machines
,
334 .host_dram_offset
= 0x000000,
335 .host_iram_offset
= 0x0A0000,
336 .host_shim_offset
= 0x0FB000,
337 .host_dma_offset
= { 0x0FE000, 0x0FF000 },
338 .host_ssp_offset
= { 0x0FC000, 0x0FD000 },
339 .dram_mask
= WPT_VDRTCTL0_DSRAMPGE_MASK
,
340 .iram_mask
= WPT_VDRTCTL0_ISRAMPGE_MASK
,
341 .d3srampgd_bit
= WPT_VDRTCTL0_D3SRAMPGD
,
342 .d3pgd_bit
= WPT_VDRTCTL0_D3PGD
,
343 .pll_shutdown
= wpt_dsp_pll_shutdown
,
346 static const struct acpi_device_id catpt_ids
[] = {
347 { "INT33C8", (unsigned long)&lpt_desc
},
348 { "INT3438", (unsigned long)&wpt_desc
},
351 MODULE_DEVICE_TABLE(acpi
, catpt_ids
);
353 static struct platform_driver catpt_acpi_driver
= {
354 .probe
= catpt_acpi_probe
,
355 .remove
= catpt_acpi_remove
,
357 .name
= "intel_catpt",
358 .acpi_match_table
= catpt_ids
,
360 .dev_groups
= catpt_attr_groups
,
363 module_platform_driver(catpt_acpi_driver
);
365 MODULE_AUTHOR("Cezary Rojewski <cezary.rojewski@intel.com>");
366 MODULE_DESCRIPTION("Intel LPT/WPT AudioDSP driver");
367 MODULE_LICENSE("GPL v2");