0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
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
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
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
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
0225
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
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
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
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");