linux/sound/soc/intel/common/sst-acpi.c
<<
>>
Prefs
   1/*
   2 * Intel SST loader on ACPI systems
   3 *
   4 * Copyright (C) 2013, Intel Corporation. All rights reserved.
   5 *
   6 * This program is free software; you can redistribute it and/or
   7 * modify it under the terms of the GNU General Public License version
   8 * 2 as published by the Free Software Foundation.
   9 *
  10 * This program is distributed in the hope that it will be useful,
  11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13 * GNU General Public License for more details.
  14 *
  15 */
  16
  17#include <linux/acpi.h>
  18#include <linux/device.h>
  19#include <linux/firmware.h>
  20#include <linux/module.h>
  21#include <linux/platform_device.h>
  22
  23#include "sst-dsp.h"
  24#include "sst-acpi.h"
  25
  26#define SST_LPT_DSP_DMA_ADDR_OFFSET     0x0F0000
  27#define SST_WPT_DSP_DMA_ADDR_OFFSET     0x0FE000
  28#define SST_LPT_DSP_DMA_SIZE            (1024 - 1)
  29
  30/* Descriptor for setting up SST platform data */
  31struct sst_acpi_desc {
  32        const char *drv_name;
  33        struct sst_acpi_mach *machines;
  34        /* Platform resource indexes. Must set to -1 if not used */
  35        int resindex_lpe_base;
  36        int resindex_pcicfg_base;
  37        int resindex_fw_base;
  38        int irqindex_host_ipc;
  39        int resindex_dma_base;
  40        /* Unique number identifying the SST core on platform */
  41        int sst_id;
  42        /* DMA only valid when resindex_dma_base != -1*/
  43        int dma_engine;
  44        int dma_size;
  45};
  46
  47struct sst_acpi_priv {
  48        struct platform_device *pdev_mach;
  49        struct platform_device *pdev_pcm;
  50        struct sst_pdata sst_pdata;
  51        struct sst_acpi_desc *desc;
  52        struct sst_acpi_mach *mach;
  53};
  54
  55static void sst_acpi_fw_cb(const struct firmware *fw, void *context)
  56{
  57        struct platform_device *pdev = context;
  58        struct device *dev = &pdev->dev;
  59        struct sst_acpi_priv *sst_acpi = platform_get_drvdata(pdev);
  60        struct sst_pdata *sst_pdata = &sst_acpi->sst_pdata;
  61        struct sst_acpi_desc *desc = sst_acpi->desc;
  62        struct sst_acpi_mach *mach = sst_acpi->mach;
  63
  64        sst_pdata->fw = fw;
  65        if (!fw) {
  66                dev_err(dev, "Cannot load firmware %s\n", mach->fw_filename);
  67                return;
  68        }
  69
  70        /* register PCM and DAI driver */
  71        sst_acpi->pdev_pcm =
  72                platform_device_register_data(dev, desc->drv_name, -1,
  73                                              sst_pdata, sizeof(*sst_pdata));
  74        if (IS_ERR(sst_acpi->pdev_pcm)) {
  75                dev_err(dev, "Cannot register device %s. Error %d\n",
  76                        desc->drv_name, (int)PTR_ERR(sst_acpi->pdev_pcm));
  77        }
  78
  79        return;
  80}
  81
  82static int sst_acpi_probe(struct platform_device *pdev)
  83{
  84        const struct acpi_device_id *id;
  85        struct device *dev = &pdev->dev;
  86        struct sst_acpi_priv *sst_acpi;
  87        struct sst_pdata *sst_pdata;
  88        struct sst_acpi_mach *mach;
  89        struct sst_acpi_desc *desc;
  90        struct resource *mmio;
  91        int ret = 0;
  92
  93        sst_acpi = devm_kzalloc(dev, sizeof(*sst_acpi), GFP_KERNEL);
  94        if (sst_acpi == NULL)
  95                return -ENOMEM;
  96
  97        id = acpi_match_device(dev->driver->acpi_match_table, dev);
  98        if (!id)
  99                return -ENODEV;
 100
 101        desc = (struct sst_acpi_desc *)id->driver_data;
 102        mach = sst_acpi_find_machine(desc->machines);
 103        if (mach == NULL) {
 104                dev_err(dev, "No matching ASoC machine driver found\n");
 105                return -ENODEV;
 106        }
 107
 108        sst_pdata = &sst_acpi->sst_pdata;
 109        sst_pdata->id = desc->sst_id;
 110        sst_pdata->dma_dev = dev;
 111        sst_acpi->desc = desc;
 112        sst_acpi->mach = mach;
 113
 114        sst_pdata->resindex_dma_base = desc->resindex_dma_base;
 115        if (desc->resindex_dma_base >= 0) {
 116                sst_pdata->dma_engine = desc->dma_engine;
 117                sst_pdata->dma_base = desc->resindex_dma_base;
 118                sst_pdata->dma_size = desc->dma_size;
 119        }
 120
 121        if (desc->irqindex_host_ipc >= 0)
 122                sst_pdata->irq = platform_get_irq(pdev, desc->irqindex_host_ipc);
 123
 124        if (desc->resindex_lpe_base >= 0) {
 125                mmio = platform_get_resource(pdev, IORESOURCE_MEM,
 126                                             desc->resindex_lpe_base);
 127                if (mmio) {
 128                        sst_pdata->lpe_base = mmio->start;
 129                        sst_pdata->lpe_size = resource_size(mmio);
 130                }
 131        }
 132
 133        if (desc->resindex_pcicfg_base >= 0) {
 134                mmio = platform_get_resource(pdev, IORESOURCE_MEM,
 135                                             desc->resindex_pcicfg_base);
 136                if (mmio) {
 137                        sst_pdata->pcicfg_base = mmio->start;
 138                        sst_pdata->pcicfg_size = resource_size(mmio);
 139                }
 140        }
 141
 142        if (desc->resindex_fw_base >= 0) {
 143                mmio = platform_get_resource(pdev, IORESOURCE_MEM,
 144                                             desc->resindex_fw_base);
 145                if (mmio) {
 146                        sst_pdata->fw_base = mmio->start;
 147                        sst_pdata->fw_size = resource_size(mmio);
 148                }
 149        }
 150
 151        platform_set_drvdata(pdev, sst_acpi);
 152
 153        /* register machine driver */
 154        sst_acpi->pdev_mach =
 155                platform_device_register_data(dev, mach->drv_name, -1,
 156                                              sst_pdata, sizeof(*sst_pdata));
 157        if (IS_ERR(sst_acpi->pdev_mach))
 158                return PTR_ERR(sst_acpi->pdev_mach);
 159
 160        /* continue SST probing after firmware is loaded */
 161        ret = request_firmware_nowait(THIS_MODULE, true, mach->fw_filename,
 162                                      dev, GFP_KERNEL, pdev, sst_acpi_fw_cb);
 163        if (ret)
 164                platform_device_unregister(sst_acpi->pdev_mach);
 165
 166        return ret;
 167}
 168
 169static int sst_acpi_remove(struct platform_device *pdev)
 170{
 171        struct sst_acpi_priv *sst_acpi = platform_get_drvdata(pdev);
 172        struct sst_pdata *sst_pdata = &sst_acpi->sst_pdata;
 173
 174        platform_device_unregister(sst_acpi->pdev_mach);
 175        if (!IS_ERR_OR_NULL(sst_acpi->pdev_pcm))
 176                platform_device_unregister(sst_acpi->pdev_pcm);
 177        release_firmware(sst_pdata->fw);
 178
 179        return 0;
 180}
 181
 182static struct sst_acpi_mach haswell_machines[] = {
 183        { "INT33CA", "haswell-audio", "intel/IntcSST1.bin", NULL, NULL, NULL },
 184        {}
 185};
 186
 187static struct sst_acpi_desc sst_acpi_haswell_desc = {
 188        .drv_name = "haswell-pcm-audio",
 189        .machines = haswell_machines,
 190        .resindex_lpe_base = 0,
 191        .resindex_pcicfg_base = 1,
 192        .resindex_fw_base = -1,
 193        .irqindex_host_ipc = 0,
 194        .sst_id = SST_DEV_ID_LYNX_POINT,
 195        .dma_engine = SST_DMA_TYPE_DW,
 196        .resindex_dma_base = SST_LPT_DSP_DMA_ADDR_OFFSET,
 197        .dma_size = SST_LPT_DSP_DMA_SIZE,
 198};
 199
 200static struct sst_acpi_mach broadwell_machines[] = {
 201        { "INT343A", "broadwell-audio", "intel/IntcSST2.bin", NULL, NULL, NULL },
 202        { "RT5677CE", "bdw-rt5677", "intel/IntcSST2.bin", NULL, NULL, NULL },
 203        {}
 204};
 205
 206static struct sst_acpi_desc sst_acpi_broadwell_desc = {
 207        .drv_name = "haswell-pcm-audio",
 208        .machines = broadwell_machines,
 209        .resindex_lpe_base = 0,
 210        .resindex_pcicfg_base = 1,
 211        .resindex_fw_base = -1,
 212        .irqindex_host_ipc = 0,
 213        .sst_id = SST_DEV_ID_WILDCAT_POINT,
 214        .dma_engine = SST_DMA_TYPE_DW,
 215        .resindex_dma_base = SST_WPT_DSP_DMA_ADDR_OFFSET,
 216        .dma_size = SST_LPT_DSP_DMA_SIZE,
 217};
 218
 219#if !IS_ENABLED(CONFIG_SND_SST_IPC_ACPI)
 220static struct sst_acpi_mach baytrail_machines[] = {
 221        { "10EC5640", "byt-rt5640", "intel/fw_sst_0f28.bin-48kHz_i2s_master", NULL, NULL, NULL },
 222        { "193C9890", "byt-max98090", "intel/fw_sst_0f28.bin-48kHz_i2s_master", NULL, NULL, NULL },
 223        {}
 224};
 225
 226static struct sst_acpi_desc sst_acpi_baytrail_desc = {
 227        .drv_name = "baytrail-pcm-audio",
 228        .machines = baytrail_machines,
 229        .resindex_lpe_base = 0,
 230        .resindex_pcicfg_base = 1,
 231        .resindex_fw_base = 2,
 232        .irqindex_host_ipc = 5,
 233        .sst_id = SST_DEV_ID_BYT,
 234        .resindex_dma_base = -1,
 235};
 236#endif
 237
 238static const struct acpi_device_id sst_acpi_match[] = {
 239        { "INT33C8", (unsigned long)&sst_acpi_haswell_desc },
 240        { "INT3438", (unsigned long)&sst_acpi_broadwell_desc },
 241#if !IS_ENABLED(CONFIG_SND_SST_IPC_ACPI)
 242        { "80860F28", (unsigned long)&sst_acpi_baytrail_desc },
 243#endif
 244        { }
 245};
 246MODULE_DEVICE_TABLE(acpi, sst_acpi_match);
 247
 248static struct platform_driver sst_acpi_driver = {
 249        .probe = sst_acpi_probe,
 250        .remove = sst_acpi_remove,
 251        .driver = {
 252                .name = "sst-acpi",
 253                .acpi_match_table = ACPI_PTR(sst_acpi_match),
 254        },
 255};
 256module_platform_driver(sst_acpi_driver);
 257
 258MODULE_AUTHOR("Jarkko Nikula <jarkko.nikula@linux.intel.com>");
 259MODULE_DESCRIPTION("Intel SST loader on ACPI systems");
 260MODULE_LICENSE("GPL v2");
 261