Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 //
0003 // Copyright(c) 2020 Intel Corporation. All rights reserved.
0004 //
0005 // Author: Cezary Rojewski <cezary.rojewski@intel.com>
0006 //
0007 // Special thanks to:
0008 //    Marcin Barlik <marcin.barlik@intel.com>
0009 //    Piotr Papierkowski <piotr.papierkowski@intel.com>
0010 //
0011 // for sharing LPT-LP and WTP-LP AudioDSP architecture expertise and
0012 // helping backtrack its historical background
0013 //
0014 
0015 #include <linux/acpi.h>
0016 #include <linux/dma-mapping.h>
0017 #include <linux/interrupt.h>
0018 #include <linux/module.h>
0019 #include <linux/pci.h>
0020 #include <linux/platform_device.h>
0021 #include <linux/pm_runtime.h>
0022 #include <sound/intel-dsp-config.h>
0023 #include <sound/soc.h>
0024 #include <sound/soc-acpi.h>
0025 #include <sound/soc-acpi-intel-match.h>
0026 #include "core.h"
0027 #include "registers.h"
0028 
0029 #define CREATE_TRACE_POINTS
0030 #include "trace.h"
0031 
0032 static int __maybe_unused catpt_suspend(struct device *dev)
0033 {
0034     struct catpt_dev *cdev = dev_get_drvdata(dev);
0035     struct dma_chan *chan;
0036     int ret;
0037 
0038     chan = catpt_dma_request_config_chan(cdev);
0039     if (IS_ERR(chan))
0040         return PTR_ERR(chan);
0041 
0042     memset(&cdev->dx_ctx, 0, sizeof(cdev->dx_ctx));
0043     ret = catpt_ipc_enter_dxstate(cdev, CATPT_DX_STATE_D3, &cdev->dx_ctx);
0044     if (ret) {
0045         ret = CATPT_IPC_ERROR(ret);
0046         goto release_dma_chan;
0047     }
0048 
0049     ret = catpt_dsp_stall(cdev, true);
0050     if (ret)
0051         goto release_dma_chan;
0052 
0053     ret = catpt_store_memdumps(cdev, chan);
0054     if (ret) {
0055         dev_err(cdev->dev, "store memdumps failed: %d\n", ret);
0056         goto release_dma_chan;
0057     }
0058 
0059     ret = catpt_store_module_states(cdev, chan);
0060     if (ret) {
0061         dev_err(cdev->dev, "store module states failed: %d\n", ret);
0062         goto release_dma_chan;
0063     }
0064 
0065     ret = catpt_store_streams_context(cdev, chan);
0066     if (ret)
0067         dev_err(cdev->dev, "store streams ctx failed: %d\n", ret);
0068 
0069 release_dma_chan:
0070     dma_release_channel(chan);
0071     if (ret)
0072         return ret;
0073     return catpt_dsp_power_down(cdev);
0074 }
0075 
0076 static int __maybe_unused catpt_resume(struct device *dev)
0077 {
0078     struct catpt_dev *cdev = dev_get_drvdata(dev);
0079     int ret, i;
0080 
0081     ret = catpt_dsp_power_up(cdev);
0082     if (ret)
0083         return ret;
0084 
0085     if (!try_module_get(dev->driver->owner)) {
0086         dev_info(dev, "module unloading, skipping fw boot\n");
0087         return 0;
0088     }
0089     module_put(dev->driver->owner);
0090 
0091     ret = catpt_boot_firmware(cdev, true);
0092     if (ret) {
0093         dev_err(cdev->dev, "boot firmware failed: %d\n", ret);
0094         return ret;
0095     }
0096 
0097     /* reconfigure SSP devices after Dx transition */
0098     for (i = 0; i < CATPT_SSP_COUNT; i++) {
0099         if (cdev->devfmt[i].iface == UINT_MAX)
0100             continue;
0101 
0102         ret = catpt_ipc_set_device_format(cdev, &cdev->devfmt[i]);
0103         if (ret)
0104             return CATPT_IPC_ERROR(ret);
0105     }
0106 
0107     return 0;
0108 }
0109 
0110 static int __maybe_unused catpt_runtime_suspend(struct device *dev)
0111 {
0112     if (!try_module_get(dev->driver->owner)) {
0113         dev_info(dev, "module unloading, skipping suspend\n");
0114         return 0;
0115     }
0116     module_put(dev->driver->owner);
0117 
0118     return catpt_suspend(dev);
0119 }
0120 
0121 static int __maybe_unused catpt_runtime_resume(struct device *dev)
0122 {
0123     return catpt_resume(dev);
0124 }
0125 
0126 static const struct dev_pm_ops catpt_dev_pm = {
0127     SET_SYSTEM_SLEEP_PM_OPS(catpt_suspend, catpt_resume)
0128     SET_RUNTIME_PM_OPS(catpt_runtime_suspend, catpt_runtime_resume, NULL)
0129 };
0130 
0131 /* machine board owned by CATPT is removed with this hook */
0132 static void board_pdev_unregister(void *data)
0133 {
0134     platform_device_unregister(data);
0135 }
0136 
0137 static int catpt_register_board(struct catpt_dev *cdev)
0138 {
0139     const struct catpt_spec *spec = cdev->spec;
0140     struct snd_soc_acpi_mach *mach;
0141     struct platform_device *board;
0142 
0143     mach = snd_soc_acpi_find_machine(spec->machines);
0144     if (!mach) {
0145         dev_info(cdev->dev, "no machines present\n");
0146         return 0;
0147     }
0148 
0149     mach->mach_params.platform = "catpt-platform";
0150     board = platform_device_register_data(NULL, mach->drv_name,
0151                     PLATFORM_DEVID_NONE,
0152                     (const void *)mach, sizeof(*mach));
0153     if (IS_ERR(board)) {
0154         dev_err(cdev->dev, "board register failed\n");
0155         return PTR_ERR(board);
0156     }
0157 
0158     return devm_add_action_or_reset(cdev->dev, board_pdev_unregister,
0159                     board);
0160 }
0161 
0162 static int catpt_probe_components(struct catpt_dev *cdev)
0163 {
0164     int ret;
0165 
0166     ret = catpt_dsp_power_up(cdev);
0167     if (ret)
0168         return ret;
0169 
0170     ret = catpt_dmac_probe(cdev);
0171     if (ret) {
0172         dev_err(cdev->dev, "DMAC probe failed: %d\n", ret);
0173         goto err_dmac_probe;
0174     }
0175 
0176     ret = catpt_first_boot_firmware(cdev);
0177     if (ret) {
0178         dev_err(cdev->dev, "first fw boot failed: %d\n", ret);
0179         goto err_boot_fw;
0180     }
0181 
0182     ret = catpt_register_plat_component(cdev);
0183     if (ret) {
0184         dev_err(cdev->dev, "register plat comp failed: %d\n", ret);
0185         goto err_boot_fw;
0186     }
0187 
0188     ret = catpt_register_board(cdev);
0189     if (ret) {
0190         dev_err(cdev->dev, "register board failed: %d\n", ret);
0191         goto err_reg_board;
0192     }
0193 
0194     /* reflect actual ADSP state in pm_runtime */
0195     pm_runtime_set_active(cdev->dev);
0196 
0197     pm_runtime_set_autosuspend_delay(cdev->dev, 2000);
0198     pm_runtime_use_autosuspend(cdev->dev);
0199     pm_runtime_mark_last_busy(cdev->dev);
0200     pm_runtime_enable(cdev->dev);
0201     return 0;
0202 
0203 err_reg_board:
0204     snd_soc_unregister_component(cdev->dev);
0205 err_boot_fw:
0206     catpt_dmac_remove(cdev);
0207 err_dmac_probe:
0208     catpt_dsp_power_down(cdev);
0209 
0210     return ret;
0211 }
0212 
0213 static void catpt_dev_init(struct catpt_dev *cdev, struct device *dev,
0214                const struct catpt_spec *spec)
0215 {
0216     cdev->dev = dev;
0217     cdev->spec = spec;
0218     init_completion(&cdev->fw_ready);
0219     INIT_LIST_HEAD(&cdev->stream_list);
0220     spin_lock_init(&cdev->list_lock);
0221     mutex_init(&cdev->clk_mutex);
0222 
0223     /*
0224      * Mark both device formats as uninitialized. Once corresponding
0225      * cpu_dai's pcm is created, proper values are assigned.
0226      */
0227     cdev->devfmt[CATPT_SSP_IFACE_0].iface = UINT_MAX;
0228     cdev->devfmt[CATPT_SSP_IFACE_1].iface = UINT_MAX;
0229 
0230     catpt_ipc_init(&cdev->ipc, dev);
0231 
0232     catpt_sram_init(&cdev->dram, spec->host_dram_offset,
0233             catpt_dram_size(cdev));
0234     catpt_sram_init(&cdev->iram, spec->host_iram_offset,
0235             catpt_iram_size(cdev));
0236 }
0237 
0238 static int catpt_acpi_probe(struct platform_device *pdev)
0239 {
0240     const struct catpt_spec *spec;
0241     struct catpt_dev *cdev;
0242     struct device *dev = &pdev->dev;
0243     const struct acpi_device_id *id;
0244     struct resource *res;
0245     int ret;
0246 
0247     id = acpi_match_device(dev->driver->acpi_match_table, dev);
0248     if (!id)
0249         return -ENODEV;
0250 
0251     ret = snd_intel_acpi_dsp_driver_probe(dev, id->id);
0252     if (ret != SND_INTEL_DSP_DRIVER_ANY && ret != SND_INTEL_DSP_DRIVER_SST) {
0253         dev_dbg(dev, "CATPT ACPI driver not selected, aborting probe\n");
0254         return -ENODEV;
0255     }
0256 
0257     cdev = devm_kzalloc(dev, sizeof(*cdev), GFP_KERNEL);
0258     if (!cdev)
0259         return -ENOMEM;
0260 
0261     spec = (const struct catpt_spec *)id->driver_data;
0262     catpt_dev_init(cdev, dev, spec);
0263 
0264     /* map DSP bar address */
0265     cdev->lpe_ba = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
0266     if (IS_ERR(cdev->lpe_ba))
0267         return PTR_ERR(cdev->lpe_ba);
0268     cdev->lpe_base = res->start;
0269 
0270     /* map PCI bar address */
0271     cdev->pci_ba = devm_platform_ioremap_resource(pdev, 1);
0272     if (IS_ERR(cdev->pci_ba))
0273         return PTR_ERR(cdev->pci_ba);
0274 
0275     /* alloc buffer for storing DRAM context during dx transitions */
0276     cdev->dxbuf_vaddr = dmam_alloc_coherent(dev, catpt_dram_size(cdev),
0277                         &cdev->dxbuf_paddr, GFP_KERNEL);
0278     if (!cdev->dxbuf_vaddr)
0279         return -ENOMEM;
0280 
0281     ret = platform_get_irq(pdev, 0);
0282     if (ret < 0)
0283         return ret;
0284     cdev->irq = ret;
0285 
0286     platform_set_drvdata(pdev, cdev);
0287 
0288     ret = devm_request_threaded_irq(dev, cdev->irq, catpt_dsp_irq_handler,
0289                     catpt_dsp_irq_thread,
0290                     IRQF_SHARED, "AudioDSP", cdev);
0291     if (ret)
0292         return ret;
0293 
0294     return catpt_probe_components(cdev);
0295 }
0296 
0297 static int catpt_acpi_remove(struct platform_device *pdev)
0298 {
0299     struct catpt_dev *cdev = platform_get_drvdata(pdev);
0300 
0301     pm_runtime_disable(cdev->dev);
0302 
0303     snd_soc_unregister_component(cdev->dev);
0304     catpt_dmac_remove(cdev);
0305     catpt_dsp_power_down(cdev);
0306 
0307     catpt_sram_free(&cdev->iram);
0308     catpt_sram_free(&cdev->dram);
0309 
0310     return 0;
0311 }
0312 
0313 static struct catpt_spec lpt_desc = {
0314     .machines = snd_soc_acpi_intel_haswell_machines,
0315     .core_id = 0x01,
0316     .host_dram_offset = 0x000000,
0317     .host_iram_offset = 0x080000,
0318     .host_shim_offset = 0x0E7000,
0319     .host_dma_offset = { 0x0F0000, 0x0F8000 },
0320     .host_ssp_offset = { 0x0E8000, 0x0E9000 },
0321     .dram_mask = LPT_VDRTCTL0_DSRAMPGE_MASK,
0322     .iram_mask = LPT_VDRTCTL0_ISRAMPGE_MASK,
0323     .d3srampgd_bit = LPT_VDRTCTL0_D3SRAMPGD,
0324     .d3pgd_bit = LPT_VDRTCTL0_D3PGD,
0325     .pll_shutdown = lpt_dsp_pll_shutdown,
0326 };
0327 
0328 static struct catpt_spec wpt_desc = {
0329     .machines = snd_soc_acpi_intel_broadwell_machines,
0330     .core_id = 0x02,
0331     .host_dram_offset = 0x000000,
0332     .host_iram_offset = 0x0A0000,
0333     .host_shim_offset = 0x0FB000,
0334     .host_dma_offset = { 0x0FE000, 0x0FF000 },
0335     .host_ssp_offset = { 0x0FC000, 0x0FD000 },
0336     .dram_mask = WPT_VDRTCTL0_DSRAMPGE_MASK,
0337     .iram_mask = WPT_VDRTCTL0_ISRAMPGE_MASK,
0338     .d3srampgd_bit = WPT_VDRTCTL0_D3SRAMPGD,
0339     .d3pgd_bit = WPT_VDRTCTL0_D3PGD,
0340     .pll_shutdown = wpt_dsp_pll_shutdown,
0341 };
0342 
0343 static const struct acpi_device_id catpt_ids[] = {
0344     { "INT33C8", (unsigned long)&lpt_desc },
0345     { "INT3438", (unsigned long)&wpt_desc },
0346     { }
0347 };
0348 MODULE_DEVICE_TABLE(acpi, catpt_ids);
0349 
0350 static struct platform_driver catpt_acpi_driver = {
0351     .probe = catpt_acpi_probe,
0352     .remove = catpt_acpi_remove,
0353     .driver = {
0354         .name = "intel_catpt",
0355         .acpi_match_table = catpt_ids,
0356         .pm = &catpt_dev_pm,
0357         .dev_groups = catpt_attr_groups,
0358     },
0359 };
0360 module_platform_driver(catpt_acpi_driver);
0361 
0362 MODULE_AUTHOR("Cezary Rojewski <cezary.rojewski@intel.com>");
0363 MODULE_DESCRIPTION("Intel LPT/WPT AudioDSP driver");
0364 MODULE_LICENSE("GPL v2");