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