linux/sound/soc/sof/sof-pci-dev.c
<<
>>
Prefs
   1// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
   2//
   3// This file is provided under a dual BSD/GPLv2 license.  When using or
   4// redistributing this file, you may do so under either license.
   5//
   6// Copyright(c) 2018 Intel Corporation. All rights reserved.
   7//
   8// Author: Liam Girdwood <liam.r.girdwood@linux.intel.com>
   9//
  10
  11#include <linux/firmware.h>
  12#include <linux/dmi.h>
  13#include <linux/module.h>
  14#include <linux/pci.h>
  15#include <linux/pm_runtime.h>
  16#include <sound/soc-acpi.h>
  17#include <sound/soc-acpi-intel-match.h>
  18#include <sound/sof.h>
  19#include "ops.h"
  20#include "sof-pci-dev.h"
  21
  22static char *fw_path;
  23module_param(fw_path, charp, 0444);
  24MODULE_PARM_DESC(fw_path, "alternate path for SOF firmware.");
  25
  26static char *tplg_path;
  27module_param(tplg_path, charp, 0444);
  28MODULE_PARM_DESC(tplg_path, "alternate path for SOF topology.");
  29
  30static int sof_pci_debug;
  31module_param_named(sof_pci_debug, sof_pci_debug, int, 0444);
  32MODULE_PARM_DESC(sof_pci_debug, "SOF PCI debug options (0x0 all off)");
  33
  34static const char *sof_override_tplg_name;
  35
  36#define SOF_PCI_DISABLE_PM_RUNTIME BIT(0)
  37
  38static int sof_tplg_cb(const struct dmi_system_id *id)
  39{
  40        sof_override_tplg_name = id->driver_data;
  41        return 1;
  42}
  43
  44static const struct dmi_system_id sof_tplg_table[] = {
  45        {
  46                .callback = sof_tplg_cb,
  47                .matches = {
  48                        DMI_MATCH(DMI_PRODUCT_FAMILY, "Google_Volteer"),
  49                        DMI_MATCH(DMI_OEM_STRING, "AUDIO-MAX98373_ALC5682I_I2S_UP4"),
  50                },
  51                .driver_data = "sof-tgl-rt5682-ssp0-max98373-ssp2.tplg",
  52        },
  53        {}
  54};
  55
  56static const struct dmi_system_id community_key_platforms[] = {
  57        {
  58                .ident = "Up Squared",
  59                .matches = {
  60                        DMI_MATCH(DMI_SYS_VENDOR, "AAEON"),
  61                        DMI_MATCH(DMI_BOARD_NAME, "UP-APL01"),
  62                }
  63        },
  64        {
  65                .ident = "Up Extreme",
  66                .matches = {
  67                        DMI_MATCH(DMI_SYS_VENDOR, "AAEON"),
  68                        DMI_MATCH(DMI_BOARD_NAME, "UP-WHL01"),
  69                }
  70        },
  71        {
  72                .ident = "Google Chromebooks",
  73                .matches = {
  74                        DMI_MATCH(DMI_SYS_VENDOR, "Google"),
  75                }
  76        },
  77        {},
  78};
  79
  80const struct dev_pm_ops sof_pci_pm = {
  81        .prepare = snd_sof_prepare,
  82        .complete = snd_sof_complete,
  83        SET_SYSTEM_SLEEP_PM_OPS(snd_sof_suspend, snd_sof_resume)
  84        SET_RUNTIME_PM_OPS(snd_sof_runtime_suspend, snd_sof_runtime_resume,
  85                           snd_sof_runtime_idle)
  86};
  87EXPORT_SYMBOL_NS(sof_pci_pm, SND_SOC_SOF_PCI_DEV);
  88
  89static void sof_pci_probe_complete(struct device *dev)
  90{
  91        dev_dbg(dev, "Completing SOF PCI probe");
  92
  93        if (sof_pci_debug & SOF_PCI_DISABLE_PM_RUNTIME)
  94                return;
  95
  96        /* allow runtime_pm */
  97        pm_runtime_set_autosuspend_delay(dev, SND_SOF_SUSPEND_DELAY_MS);
  98        pm_runtime_use_autosuspend(dev);
  99
 100        /*
 101         * runtime pm for pci device is "forbidden" by default.
 102         * so call pm_runtime_allow() to enable it.
 103         */
 104        pm_runtime_allow(dev);
 105
 106        /* mark last_busy for pm_runtime to make sure not suspend immediately */
 107        pm_runtime_mark_last_busy(dev);
 108
 109        /* follow recommendation in pci-driver.c to decrement usage counter */
 110        pm_runtime_put_noidle(dev);
 111}
 112
 113int sof_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
 114{
 115        struct device *dev = &pci->dev;
 116        const struct sof_dev_desc *desc =
 117                (const struct sof_dev_desc *)pci_id->driver_data;
 118        struct snd_sof_pdata *sof_pdata;
 119        int ret;
 120
 121        dev_dbg(&pci->dev, "PCI DSP detected");
 122
 123        if (!desc->ops) {
 124                dev_err(dev, "error: no matching PCI descriptor ops\n");
 125                return -ENODEV;
 126        }
 127
 128        sof_pdata = devm_kzalloc(dev, sizeof(*sof_pdata), GFP_KERNEL);
 129        if (!sof_pdata)
 130                return -ENOMEM;
 131
 132        ret = pcim_enable_device(pci);
 133        if (ret < 0)
 134                return ret;
 135
 136        ret = pci_request_regions(pci, "Audio DSP");
 137        if (ret < 0)
 138                return ret;
 139
 140        sof_pdata->name = pci_name(pci);
 141        sof_pdata->desc = desc;
 142        sof_pdata->dev = dev;
 143        sof_pdata->fw_filename = desc->default_fw_filename;
 144
 145        /*
 146         * for platforms using the SOF community key, change the
 147         * default path automatically to pick the right files from the
 148         * linux-firmware tree. This can be overridden with the
 149         * fw_path kernel parameter, e.g. for developers.
 150         */
 151
 152        /* alternate fw and tplg filenames ? */
 153        if (fw_path) {
 154                sof_pdata->fw_filename_prefix = fw_path;
 155
 156                dev_dbg(dev,
 157                        "Module parameter used, changed fw path to %s\n",
 158                        sof_pdata->fw_filename_prefix);
 159
 160        } else if (dmi_check_system(community_key_platforms)) {
 161                sof_pdata->fw_filename_prefix =
 162                        devm_kasprintf(dev, GFP_KERNEL, "%s/%s",
 163                                       sof_pdata->desc->default_fw_path,
 164                                       "community");
 165
 166                dev_dbg(dev,
 167                        "Platform uses community key, changed fw path to %s\n",
 168                        sof_pdata->fw_filename_prefix);
 169        } else {
 170                sof_pdata->fw_filename_prefix =
 171                        sof_pdata->desc->default_fw_path;
 172        }
 173
 174        if (tplg_path)
 175                sof_pdata->tplg_filename_prefix = tplg_path;
 176        else
 177                sof_pdata->tplg_filename_prefix =
 178                        sof_pdata->desc->default_tplg_path;
 179
 180        dmi_check_system(sof_tplg_table);
 181        if (sof_override_tplg_name)
 182                sof_pdata->tplg_filename = sof_override_tplg_name;
 183
 184        /* set callback to be called on successful device probe to enable runtime_pm */
 185        sof_pdata->sof_probe_complete = sof_pci_probe_complete;
 186
 187        /* call sof helper for DSP hardware probe */
 188        ret = snd_sof_device_probe(dev, sof_pdata);
 189        if (ret)
 190                pci_release_regions(pci);
 191
 192        return ret;
 193}
 194EXPORT_SYMBOL_NS(sof_pci_probe, SND_SOC_SOF_PCI_DEV);
 195
 196void sof_pci_remove(struct pci_dev *pci)
 197{
 198        /* call sof helper for DSP hardware remove */
 199        snd_sof_device_remove(&pci->dev);
 200
 201        /* follow recommendation in pci-driver.c to increment usage counter */
 202        if (snd_sof_device_probe_completed(&pci->dev) &&
 203            !(sof_pci_debug & SOF_PCI_DISABLE_PM_RUNTIME))
 204                pm_runtime_get_noresume(&pci->dev);
 205
 206        /* release pci regions and disable device */
 207        pci_release_regions(pci);
 208}
 209EXPORT_SYMBOL_NS(sof_pci_remove, SND_SOC_SOF_PCI_DEV);
 210
 211void sof_pci_shutdown(struct pci_dev *pci)
 212{
 213        snd_sof_device_shutdown(&pci->dev);
 214}
 215EXPORT_SYMBOL_NS(sof_pci_shutdown, SND_SOC_SOF_PCI_DEV);
 216
 217MODULE_LICENSE("Dual BSD/GPL");
 218