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        const struct snd_sof_dsp_ops *ops;
 120        int ret;
 121
 122        dev_dbg(&pci->dev, "PCI DSP detected");
 123
 124        /* get ops for platform */
 125        ops = desc->ops;
 126        if (!ops) {
 127                dev_err(dev, "error: no matching PCI descriptor ops\n");
 128                return -ENODEV;
 129        }
 130
 131        sof_pdata = devm_kzalloc(dev, sizeof(*sof_pdata), GFP_KERNEL);
 132        if (!sof_pdata)
 133                return -ENOMEM;
 134
 135        ret = pcim_enable_device(pci);
 136        if (ret < 0)
 137                return ret;
 138
 139        ret = pci_request_regions(pci, "Audio DSP");
 140        if (ret < 0)
 141                return ret;
 142
 143        sof_pdata->name = pci_name(pci);
 144        sof_pdata->desc = (struct sof_dev_desc *)pci_id->driver_data;
 145        sof_pdata->dev = dev;
 146        sof_pdata->fw_filename = desc->default_fw_filename;
 147
 148        /*
 149         * for platforms using the SOF community key, change the
 150         * default path automatically to pick the right files from the
 151         * linux-firmware tree. This can be overridden with the
 152         * fw_path kernel parameter, e.g. for developers.
 153         */
 154
 155        /* alternate fw and tplg filenames ? */
 156        if (fw_path) {
 157                sof_pdata->fw_filename_prefix = fw_path;
 158
 159                dev_dbg(dev,
 160                        "Module parameter used, changed fw path to %s\n",
 161                        sof_pdata->fw_filename_prefix);
 162
 163        } else if (dmi_check_system(community_key_platforms)) {
 164                sof_pdata->fw_filename_prefix =
 165                        devm_kasprintf(dev, GFP_KERNEL, "%s/%s",
 166                                       sof_pdata->desc->default_fw_path,
 167                                       "community");
 168
 169                dev_dbg(dev,
 170                        "Platform uses community key, changed fw path to %s\n",
 171                        sof_pdata->fw_filename_prefix);
 172        } else {
 173                sof_pdata->fw_filename_prefix =
 174                        sof_pdata->desc->default_fw_path;
 175        }
 176
 177        if (tplg_path)
 178                sof_pdata->tplg_filename_prefix = tplg_path;
 179        else
 180                sof_pdata->tplg_filename_prefix =
 181                        sof_pdata->desc->default_tplg_path;
 182
 183        dmi_check_system(sof_tplg_table);
 184        if (sof_override_tplg_name)
 185                sof_pdata->tplg_filename = sof_override_tplg_name;
 186
 187        /* set callback to be called on successful device probe to enable runtime_pm */
 188        sof_pdata->sof_probe_complete = sof_pci_probe_complete;
 189
 190        /* call sof helper for DSP hardware probe */
 191        ret = snd_sof_device_probe(dev, sof_pdata);
 192        if (ret)
 193                pci_release_regions(pci);
 194
 195        return ret;
 196}
 197EXPORT_SYMBOL_NS(sof_pci_probe, SND_SOC_SOF_PCI_DEV);
 198
 199void sof_pci_remove(struct pci_dev *pci)
 200{
 201        /* call sof helper for DSP hardware remove */
 202        snd_sof_device_remove(&pci->dev);
 203
 204        /* follow recommendation in pci-driver.c to increment usage counter */
 205        if (snd_sof_device_probe_completed(&pci->dev) &&
 206            !(sof_pci_debug & SOF_PCI_DISABLE_PM_RUNTIME))
 207                pm_runtime_get_noresume(&pci->dev);
 208
 209        /* release pci regions and disable device */
 210        pci_release_regions(pci);
 211}
 212EXPORT_SYMBOL_NS(sof_pci_remove, SND_SOC_SOF_PCI_DEV);
 213
 214void sof_pci_shutdown(struct pci_dev *pci)
 215{
 216        snd_sof_device_shutdown(&pci->dev);
 217}
 218EXPORT_SYMBOL_NS(sof_pci_shutdown, SND_SOC_SOF_PCI_DEV);
 219
 220MODULE_LICENSE("Dual BSD/GPL");
 221