Commit ef6069f3 authored by Mark Brown's avatar Mark Brown

ASoC: SOF: IPC path handling and fallback support

Merge series from Peter Ujfalusi <peter.ujfalusi@linux.intel.com>:

The main aim of the series is to provide a mechanism to fallback to 'older' IPC
versions in case the desired one is missing either a firmware or topology file.
It is going to make the life of users and distributions if we are going to
start transition existing IPC3 platforms to IPC4 (CAVS2.5) and we might have
missed some topology file to convert for example.
In that case the kernel will fallback to IPC3 without audio regression.

To be able to support this we needed to change the probe sequence to know the
topology filename earlier and check if it is present in the filesystem.

No functional changes for now, the default IPC versions have not been changed.
parents 15c7fab0 6c393ebb
......@@ -57,6 +57,18 @@ enum sof_ipc_type {
SOF_IPC_TYPE_COUNT
};
struct sof_loadable_file_profile {
enum sof_ipc_type ipc_type;
const char *fw_path;
const char *fw_path_postfix;
const char *fw_name;
const char *fw_lib_path;
const char *fw_lib_path_postfix;
const char *tplg_path;
const char *tplg_name;
};
/*
* SOF Platform data.
*/
......@@ -86,6 +98,9 @@ struct snd_sof_pdata {
/* descriptor */
const struct sof_dev_desc *desc;
/* platform's preferred IPC type and path overrides */
struct sof_loadable_file_profile ipc_file_profile_base;
/* firmware and topology filenames */
const char *fw_filename_prefix;
const char *fw_filename;
......
......@@ -126,6 +126,17 @@ config SND_SOC_SOF_STRICT_ABI_CHECKS
If you are not involved in SOF releases and CI development,
select "N".
config SND_SOC_SOF_ALLOW_FALLBACK_TO_NEWER_IPC_VERSION
bool "SOF allow fallback to newer IPC version"
help
This option will allow the kernel to try to 'fallback' to a newer IPC
version if there are missing firmware files to satisfy the default IPC
version.
IPC version fallback to older versions is not affected by this option,
it is always available.
Say Y if you are involved in SOF development and need this option.
If not, select N.
config SND_SOC_SOF_DEBUG
bool "SOF debugging features"
help
......
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
snd-sof-objs := core.o ops.o loader.o ipc.o pcm.o pm.o debug.o topology.o\
control.o trace.o iomem-utils.o sof-audio.o stream-ipc.o
control.o trace.o iomem-utils.o sof-audio.o stream-ipc.o\
fw-file-profile.o
# IPC implementations
ifneq ($(CONFIG_SND_SOC_SOF_IPC3),)
......
......@@ -13,6 +13,7 @@
#include <sound/soc.h>
#include <sound/sof.h>
#include "sof-priv.h"
#include "sof-of-dev.h"
#include "ops.h"
#define CREATE_TRACE_POINTS
......@@ -143,6 +144,211 @@ void sof_set_fw_state(struct snd_sof_dev *sdev, enum sof_fw_state new_state)
}
EXPORT_SYMBOL(sof_set_fw_state);
/* SOF Driver enumeration */
static int sof_machine_check(struct snd_sof_dev *sdev)
{
struct snd_sof_pdata *sof_pdata = sdev->pdata;
const struct sof_dev_desc *desc = sof_pdata->desc;
struct snd_soc_acpi_mach *mach;
if (!IS_ENABLED(CONFIG_SND_SOC_SOF_FORCE_NOCODEC_MODE)) {
const struct snd_sof_of_mach *of_mach;
if (IS_ENABLED(CONFIG_SND_SOC_SOF_NOCODEC_DEBUG_SUPPORT) &&
sof_debug_check_flag(SOF_DBG_FORCE_NOCODEC))
goto nocodec;
/* find machine */
mach = snd_sof_machine_select(sdev);
if (mach) {
sof_pdata->machine = mach;
if (sof_pdata->subsystem_id_set) {
mach->mach_params.subsystem_vendor = sof_pdata->subsystem_vendor;
mach->mach_params.subsystem_device = sof_pdata->subsystem_device;
mach->mach_params.subsystem_id_set = true;
}
snd_sof_set_mach_params(mach, sdev);
return 0;
}
of_mach = sof_of_machine_select(sdev);
if (of_mach) {
sof_pdata->of_machine = of_mach;
return 0;
}
if (!IS_ENABLED(CONFIG_SND_SOC_SOF_NOCODEC)) {
dev_err(sdev->dev, "error: no matching ASoC machine driver found - aborting probe\n");
return -ENODEV;
}
} else {
dev_warn(sdev->dev, "Force to use nocodec mode\n");
}
nocodec:
/* select nocodec mode */
dev_warn(sdev->dev, "Using nocodec machine driver\n");
mach = devm_kzalloc(sdev->dev, sizeof(*mach), GFP_KERNEL);
if (!mach)
return -ENOMEM;
mach->drv_name = "sof-nocodec";
if (!sof_pdata->tplg_filename)
sof_pdata->tplg_filename = desc->nocodec_tplg_filename;
sof_pdata->machine = mach;
snd_sof_set_mach_params(mach, sdev);
return 0;
}
static int sof_select_ipc_and_paths(struct snd_sof_dev *sdev)
{
struct snd_sof_pdata *plat_data = sdev->pdata;
struct sof_loadable_file_profile *base_profile = &plat_data->ipc_file_profile_base;
struct sof_loadable_file_profile out_profile;
struct device *dev = sdev->dev;
int ret;
if (base_profile->ipc_type != plat_data->desc->ipc_default)
dev_info(dev,
"Module parameter used, overriding default IPC %d to %d\n",
plat_data->desc->ipc_default, base_profile->ipc_type);
if (base_profile->fw_path)
dev_dbg(dev, "Module parameter used, changed fw path to %s\n",
base_profile->fw_path);
else if (base_profile->fw_path_postfix)
dev_dbg(dev, "Path postfix appended to default fw path: %s\n",
base_profile->fw_path_postfix);
if (base_profile->fw_lib_path)
dev_dbg(dev, "Module parameter used, changed fw_lib path to %s\n",
base_profile->fw_lib_path);
else if (base_profile->fw_lib_path_postfix)
dev_dbg(dev, "Path postfix appended to default fw_lib path: %s\n",
base_profile->fw_lib_path_postfix);
if (base_profile->fw_name)
dev_dbg(dev, "Module parameter used, changed fw filename to %s\n",
base_profile->fw_name);
if (base_profile->tplg_path)
dev_dbg(dev, "Module parameter used, changed tplg path to %s\n",
base_profile->tplg_path);
if (base_profile->tplg_name)
dev_dbg(dev, "Module parameter used, changed tplg name to %s\n",
base_profile->tplg_name);
ret = sof_create_ipc_file_profile(sdev, base_profile, &out_profile);
if (ret)
return ret;
plat_data->ipc_type = out_profile.ipc_type;
plat_data->fw_filename = out_profile.fw_name;
plat_data->fw_filename_prefix = out_profile.fw_path;
plat_data->fw_lib_prefix = out_profile.fw_lib_path;
plat_data->tplg_filename_prefix = out_profile.tplg_path;
return 0;
}
static int validate_sof_ops(struct snd_sof_dev *sdev)
{
int ret;
/* init ops, if necessary */
ret = sof_ops_init(sdev);
if (ret < 0)
return ret;
/* check all mandatory ops */
if (!sof_ops(sdev) || !sof_ops(sdev)->probe) {
dev_err(sdev->dev, "missing mandatory ops\n");
sof_ops_free(sdev);
return -EINVAL;
}
if (!sdev->dspless_mode_selected &&
(!sof_ops(sdev)->run || !sof_ops(sdev)->block_read ||
!sof_ops(sdev)->block_write || !sof_ops(sdev)->send_msg ||
!sof_ops(sdev)->load_firmware || !sof_ops(sdev)->ipc_msg_data)) {
dev_err(sdev->dev, "missing mandatory DSP ops\n");
sof_ops_free(sdev);
return -EINVAL;
}
return 0;
}
static int sof_init_sof_ops(struct snd_sof_dev *sdev)
{
struct snd_sof_pdata *plat_data = sdev->pdata;
struct sof_loadable_file_profile *base_profile = &plat_data->ipc_file_profile_base;
/* check IPC support */
if (!(BIT(base_profile->ipc_type) & plat_data->desc->ipc_supported_mask)) {
dev_err(sdev->dev,
"ipc_type %d is not supported on this platform, mask is %#x\n",
base_profile->ipc_type, plat_data->desc->ipc_supported_mask);
return -EINVAL;
}
/*
* Save the selected IPC type and a topology name override before
* selecting ops since platform code might need this information
*/
plat_data->ipc_type = base_profile->ipc_type;
plat_data->tplg_filename = base_profile->tplg_name;
return validate_sof_ops(sdev);
}
static int sof_init_environment(struct snd_sof_dev *sdev)
{
struct snd_sof_pdata *plat_data = sdev->pdata;
struct sof_loadable_file_profile *base_profile = &plat_data->ipc_file_profile_base;
int ret;
/* probe the DSP hardware */
ret = snd_sof_probe(sdev);
if (ret < 0) {
dev_err(sdev->dev, "failed to probe DSP %d\n", ret);
sof_ops_free(sdev);
return ret;
}
/* check machine info */
ret = sof_machine_check(sdev);
if (ret < 0) {
dev_err(sdev->dev, "failed to get machine info %d\n", ret);
goto err_machine_check;
}
ret = sof_select_ipc_and_paths(sdev);
if (!ret && plat_data->ipc_type != base_profile->ipc_type) {
/* IPC type changed, re-initialize the ops */
sof_ops_free(sdev);
ret = validate_sof_ops(sdev);
if (ret < 0) {
snd_sof_remove(sdev);
return ret;
}
}
err_machine_check:
if (ret) {
snd_sof_remove(sdev);
sof_ops_free(sdev);
}
return ret;
}
/*
* FW Boot State Transition Diagram
*
......@@ -188,23 +394,13 @@ static int sof_probe_continue(struct snd_sof_dev *sdev)
struct snd_sof_pdata *plat_data = sdev->pdata;
int ret;
/* probe the DSP hardware */
ret = snd_sof_probe(sdev);
if (ret < 0) {
dev_err(sdev->dev, "error: failed to probe DSP %d\n", ret);
goto probe_err;
}
/* Initialize loadable file paths and check the environment validity */
ret = sof_init_environment(sdev);
if (ret)
return ret;
sof_set_fw_state(sdev, SOF_FW_BOOT_PREPARE);
/* check machine info */
ret = sof_machine_check(sdev);
if (ret < 0) {
dev_err(sdev->dev, "error: failed to get machine info %d\n",
ret);
goto dsp_err;
}
/* set up platform component driver */
snd_sof_new_platform_drv(sdev);
......@@ -324,9 +520,7 @@ static int sof_probe_continue(struct snd_sof_dev *sdev)
ipc_err:
dbg_err:
snd_sof_free_debug(sdev);
dsp_err:
snd_sof_remove(sdev);
probe_err:
snd_sof_remove_late(sdev);
sof_ops_free(sdev);
......@@ -381,34 +575,11 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data)
}
}
/* check IPC support */
if (!(BIT(plat_data->ipc_type) & plat_data->desc->ipc_supported_mask)) {
dev_err(dev, "ipc_type %d is not supported on this platform, mask is %#x\n",
plat_data->ipc_type, plat_data->desc->ipc_supported_mask);
return -EINVAL;
}
/* init ops, if necessary */
ret = sof_ops_init(sdev);
if (ret < 0)
/* Initialize sof_ops based on the initial selected IPC version */
ret = sof_init_sof_ops(sdev);
if (ret)
return ret;
/* check all mandatory ops */
if (!sof_ops(sdev) || !sof_ops(sdev)->probe) {
sof_ops_free(sdev);
dev_err(dev, "missing mandatory ops\n");
return -EINVAL;
}
if (!sdev->dspless_mode_selected &&
(!sof_ops(sdev)->run || !sof_ops(sdev)->block_read ||
!sof_ops(sdev)->block_write || !sof_ops(sdev)->send_msg ||
!sof_ops(sdev)->load_firmware || !sof_ops(sdev)->ipc_msg_data)) {
sof_ops_free(sdev);
dev_err(dev, "missing mandatory DSP ops\n");
return -EINVAL;
}
INIT_LIST_HEAD(&sdev->pcm_list);
INIT_LIST_HEAD(&sdev->kcontrol_list);
INIT_LIST_HEAD(&sdev->widget_list);
......@@ -527,6 +698,40 @@ int snd_sof_device_shutdown(struct device *dev)
}
EXPORT_SYMBOL(snd_sof_device_shutdown);
/* Machine driver registering and unregistering */
int sof_machine_register(struct snd_sof_dev *sdev, void *pdata)
{
struct snd_sof_pdata *plat_data = pdata;
const char *drv_name;
const void *mach;
int size;
drv_name = plat_data->machine->drv_name;
mach = plat_data->machine;
size = sizeof(*plat_data->machine);
/* register machine driver, pass machine info as pdata */
plat_data->pdev_mach =
platform_device_register_data(sdev->dev, drv_name,
PLATFORM_DEVID_NONE, mach, size);
if (IS_ERR(plat_data->pdev_mach))
return PTR_ERR(plat_data->pdev_mach);
dev_dbg(sdev->dev, "created machine %s\n",
dev_name(&plat_data->pdev_mach->dev));
return 0;
}
EXPORT_SYMBOL(sof_machine_register);
void sof_machine_unregister(struct snd_sof_dev *sdev, void *pdata)
{
struct snd_sof_pdata *plat_data = pdata;
platform_device_unregister(plat_data->pdev_mach);
}
EXPORT_SYMBOL(sof_machine_unregister);
MODULE_AUTHOR("Liam Girdwood");
MODULE_DESCRIPTION("Sound Open Firmware (SOF) Core");
MODULE_LICENSE("Dual BSD/GPL");
......
// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
//
// This file is provided under a dual BSD/GPLv2 license. When using or
// redistributing this file, you may do so under either license.
//
// Copyright(c) 2023 Intel Corporation. All rights reserved.
//
#include <linux/firmware.h>
#include <sound/sof.h>
#include <sound/sof/ext_manifest4.h>
#include "sof-priv.h"
static int sof_test_firmware_file(struct device *dev,
struct sof_loadable_file_profile *profile,
enum sof_ipc_type *ipc_type_to_adjust)
{
enum sof_ipc_type fw_ipc_type;
const struct firmware *fw;
const char *fw_filename;
const u32 *magic;
int ret;
fw_filename = kasprintf(GFP_KERNEL, "%s/%s", profile->fw_path,
profile->fw_name);
if (!fw_filename)
return -ENOMEM;
ret = firmware_request_nowarn(&fw, fw_filename, dev);
if (ret < 0) {
dev_dbg(dev, "Failed to open firmware file: %s\n", fw_filename);
kfree(fw_filename);
return ret;
}
/* firmware file exists, check the magic number */
magic = (const u32 *)fw->data;
switch (*magic) {
case SOF_EXT_MAN_MAGIC_NUMBER:
fw_ipc_type = SOF_IPC_TYPE_3;
break;
case SOF_EXT_MAN4_MAGIC_NUMBER:
fw_ipc_type = SOF_IPC_TYPE_4;
break;
default:
dev_err(dev, "Invalid firmware magic: %#x\n", *magic);
ret = -EINVAL;
goto out;
}
if (ipc_type_to_adjust) {
*ipc_type_to_adjust = fw_ipc_type;
} else if (fw_ipc_type != profile->ipc_type) {
dev_err(dev,
"ipc type mismatch between %s and expected: %d vs %d\n",
fw_filename, fw_ipc_type, profile->ipc_type);
ret = -EINVAL;
}
out:
release_firmware(fw);
kfree(fw_filename);
return ret;
}
static int sof_test_topology_file(struct device *dev,
struct sof_loadable_file_profile *profile)
{
const struct firmware *fw;
const char *tplg_filename;
int ret;
if (!profile->tplg_path || !profile->tplg_name)
return 0;
tplg_filename = kasprintf(GFP_KERNEL, "%s/%s", profile->tplg_path,
profile->tplg_name);
if (!tplg_filename)
return -ENOMEM;
ret = firmware_request_nowarn(&fw, tplg_filename, dev);
if (!ret)
release_firmware(fw);
else
dev_dbg(dev, "Failed to open topology file: %s\n", tplg_filename);
kfree(tplg_filename);
return ret;
}
static int
sof_file_profile_for_ipc_type(struct snd_sof_dev *sdev,
enum sof_ipc_type ipc_type,
const struct sof_dev_desc *desc,
struct sof_loadable_file_profile *base_profile,
struct sof_loadable_file_profile *out_profile)
{
struct snd_sof_pdata *plat_data = sdev->pdata;
bool fw_lib_path_allocated = false;
struct device *dev = sdev->dev;
bool fw_path_allocated = false;
int ret = 0;
/* firmware path */
if (base_profile->fw_path) {
out_profile->fw_path = base_profile->fw_path;
} else if (base_profile->fw_path_postfix) {
out_profile->fw_path = devm_kasprintf(dev, GFP_KERNEL, "%s/%s",
desc->default_fw_path[ipc_type],
base_profile->fw_path_postfix);
if (!out_profile->fw_path)
return -ENOMEM;
fw_path_allocated = true;
} else {
out_profile->fw_path = desc->default_fw_path[ipc_type];
}
/* firmware filename */
if (base_profile->fw_name)
out_profile->fw_name = base_profile->fw_name;
else
out_profile->fw_name = desc->default_fw_filename[ipc_type];
/*
* Check the custom firmware path/filename and adjust the ipc_type to
* match with the existing file for the remaining path configuration.
*
* For default path and firmware name do a verification before
* continuing further.
*/
if (base_profile->fw_path || base_profile->fw_name) {
ret = sof_test_firmware_file(dev, out_profile, &ipc_type);
if (ret)
return ret;
if (!(desc->ipc_supported_mask & BIT(ipc_type))) {
dev_err(dev, "Unsupported IPC type %d needed by %s/%s\n",
ipc_type, out_profile->fw_path,
out_profile->fw_name);
return -EINVAL;
}
}
/* firmware library path */
if (base_profile->fw_lib_path) {
out_profile->fw_lib_path = base_profile->fw_lib_path;
} else if (desc->default_lib_path[ipc_type]) {
if (base_profile->fw_lib_path_postfix) {
out_profile->fw_lib_path = devm_kasprintf(dev,
GFP_KERNEL, "%s/%s",
desc->default_lib_path[ipc_type],
base_profile->fw_lib_path_postfix);
if (!out_profile->fw_lib_path) {
ret = -ENOMEM;
goto out;
}
fw_lib_path_allocated = true;
} else {
out_profile->fw_lib_path = desc->default_lib_path[ipc_type];
}
}
if (base_profile->fw_path_postfix)
out_profile->fw_path_postfix = base_profile->fw_path_postfix;
if (base_profile->fw_lib_path_postfix)
out_profile->fw_lib_path_postfix = base_profile->fw_lib_path_postfix;
/* topology path */
if (base_profile->tplg_path)
out_profile->tplg_path = base_profile->tplg_path;
else
out_profile->tplg_path = desc->default_tplg_path[ipc_type];
/* topology name */
out_profile->tplg_name = plat_data->tplg_filename;
out_profile->ipc_type = ipc_type;
/* Test only default firmware file */
if (!base_profile->fw_path && !base_profile->fw_name)
ret = sof_test_firmware_file(dev, out_profile, NULL);
if (!ret)
ret = sof_test_topology_file(dev, out_profile);
out:
if (ret) {
/* Free up path strings created with devm_kasprintf */
if (fw_path_allocated)
devm_kfree(dev, out_profile->fw_path);
if (fw_lib_path_allocated)
devm_kfree(dev, out_profile->fw_lib_path);
memset(out_profile, 0, sizeof(*out_profile));
}
return ret;
}
static void
sof_print_missing_firmware_info(struct snd_sof_dev *sdev,
enum sof_ipc_type ipc_type,
struct sof_loadable_file_profile *base_profile)
{
struct snd_sof_pdata *plat_data = sdev->pdata;
const struct sof_dev_desc *desc = plat_data->desc;
struct device *dev = sdev->dev;
int ipc_type_count, i;
char *marker;
dev_err(dev, "SOF firmware and/or topology file not found.\n");
dev_info(dev, "Supported default profiles\n");
if (IS_ENABLED(CONFIG_SND_SOC_SOF_ALLOW_FALLBACK_TO_NEWER_IPC_VERSION))
ipc_type_count = SOF_IPC_TYPE_COUNT - 1;
else
ipc_type_count = base_profile->ipc_type;
for (i = 0; i <= ipc_type_count; i++) {
if (!(desc->ipc_supported_mask & BIT(i)))
continue;
if (i == ipc_type)
marker = "Requested";
else
marker = "Fallback";
dev_info(dev, "- ipc type %d (%s):\n", i, marker);
if (base_profile->fw_path_postfix)
dev_info(dev, " Firmware file: %s/%s/%s\n",
desc->default_fw_path[i],
base_profile->fw_path_postfix,
desc->default_fw_filename[i]);
else
dev_info(dev, " Firmware file: %s/%s\n",
desc->default_fw_path[i],
desc->default_fw_filename[i]);
dev_info(dev, " Topology file: %s/%s\n",
desc->default_tplg_path[i],
plat_data->tplg_filename);
}
if (base_profile->fw_path || base_profile->fw_name ||
base_profile->tplg_path || base_profile->tplg_name)
dev_info(dev, "Verify the path/name override module parameters.\n");
dev_info(dev, "Check if you have 'sof-firmware' package installed.\n");
dev_info(dev, "Optionally it can be manually downloaded from:\n");
dev_info(dev, " https://github.com/thesofproject/sof-bin/\n");
}
static void sof_print_profile_info(struct snd_sof_dev *sdev,
enum sof_ipc_type ipc_type,
struct sof_loadable_file_profile *profile)
{
struct device *dev = sdev->dev;
if (ipc_type != profile->ipc_type)
dev_info(dev,
"Using fallback IPC type %d (requested type was %d)\n",
profile->ipc_type, ipc_type);
dev_info(dev, "Firmware paths/files for ipc type %d:\n", profile->ipc_type);
dev_info(dev, " Firmware file: %s/%s\n", profile->fw_path, profile->fw_name);
if (profile->fw_lib_path)
dev_info(dev, " Firmware lib path: %s\n", profile->fw_lib_path);
dev_info(dev, " Topology file: %s/%s\n", profile->tplg_path, profile->tplg_name);
}
int sof_create_ipc_file_profile(struct snd_sof_dev *sdev,
struct sof_loadable_file_profile *base_profile,
struct sof_loadable_file_profile *out_profile)
{
const struct sof_dev_desc *desc = sdev->pdata->desc;
int ipc_fallback_start, ret, i;
memset(out_profile, 0, sizeof(*out_profile));
ret = sof_file_profile_for_ipc_type(sdev, base_profile->ipc_type, desc,
base_profile, out_profile);
if (!ret)
goto out;
/*
* No firmware file was found for the requested IPC type, as fallback
* if SND_SOC_SOF_ALLOW_FALLBACK_TO_NEWER_IPC_VERSION is selected, check
* all IPC versions in a backwards direction (from newer to older)
* if SND_SOC_SOF_ALLOW_FALLBACK_TO_NEWER_IPC_VERSION is not selected,
* check only older IPC versions than the selected/default version
*/
if (IS_ENABLED(CONFIG_SND_SOC_SOF_ALLOW_FALLBACK_TO_NEWER_IPC_VERSION))
ipc_fallback_start = SOF_IPC_TYPE_COUNT - 1;
else
ipc_fallback_start = (int)base_profile->ipc_type - 1;
for (i = ipc_fallback_start; i >= 0 ; i--) {
if (i == base_profile->ipc_type ||
!(desc->ipc_supported_mask & BIT(i)))
continue;
ret = sof_file_profile_for_ipc_type(sdev, i, desc, base_profile,
out_profile);
if (!ret)
break;
}
out:
if (ret)
sof_print_missing_firmware_info(sdev, base_profile->ipc_type,
base_profile);
else
sof_print_profile_info(sdev, base_profile->ipc_type, out_profile);
return ret;
}
EXPORT_SYMBOL(sof_create_ipc_file_profile);
......@@ -55,7 +55,7 @@ int sof_apl_ops_init(struct snd_sof_dev *sdev)
if (sdev->pdata->ipc_type == SOF_IPC_TYPE_4) {
struct sof_ipc4_fw_data *ipc4_data;
sdev->private = devm_kzalloc(sdev->dev, sizeof(*ipc4_data), GFP_KERNEL);
sdev->private = kzalloc(sizeof(*ipc4_data), GFP_KERNEL);
if (!sdev->private)
return -ENOMEM;
......
......@@ -402,7 +402,7 @@ int sof_cnl_ops_init(struct snd_sof_dev *sdev)
if (sdev->pdata->ipc_type == SOF_IPC_TYPE_4) {
struct sof_ipc4_fw_data *ipc4_data;
sdev->private = devm_kzalloc(sdev->dev, sizeof(*ipc4_data), GFP_KERNEL);
sdev->private = kzalloc(sizeof(*ipc4_data), GFP_KERNEL);
if (!sdev->private)
return -ENOMEM;
......
......@@ -621,6 +621,9 @@ void hda_ops_free(struct snd_sof_dev *sdev)
if (!hda_use_tplg_nhlt)
intel_nhlt_free(ipc4_data->nhlt);
kfree(sdev->private);
sdev->private = NULL;
}
}
EXPORT_SYMBOL_NS(hda_ops_free, SND_SOC_SOF_INTEL_HDA_COMMON);
......
......@@ -123,7 +123,7 @@ int sof_icl_ops_init(struct snd_sof_dev *sdev)
if (sdev->pdata->ipc_type == SOF_IPC_TYPE_4) {
struct sof_ipc4_fw_data *ipc4_data;
sdev->private = devm_kzalloc(sdev->dev, sizeof(*ipc4_data), GFP_KERNEL);
sdev->private = kzalloc(sizeof(*ipc4_data), GFP_KERNEL);
if (!sdev->private)
return -ENOMEM;
......
......@@ -120,7 +120,7 @@ int sof_lnl_ops_init(struct snd_sof_dev *sdev)
sof_lnl_ops.get_stream_position = mtl_dsp_get_stream_hda_link_position;
sdev->private = devm_kzalloc(sdev->dev, sizeof(struct sof_ipc4_fw_data), GFP_KERNEL);
sdev->private = kzalloc(sizeof(struct sof_ipc4_fw_data), GFP_KERNEL);
if (!sdev->private)
return -ENOMEM;
......
......@@ -709,7 +709,7 @@ int sof_mtl_ops_init(struct snd_sof_dev *sdev)
sof_mtl_ops.get_stream_position = mtl_dsp_get_stream_hda_link_position;
sdev->private = devm_kzalloc(sdev->dev, sizeof(struct sof_ipc4_fw_data), GFP_KERNEL);
sdev->private = kzalloc(sizeof(struct sof_ipc4_fw_data), GFP_KERNEL);
if (!sdev->private)
return -ENOMEM;
......
......@@ -62,7 +62,7 @@ int sof_skl_ops_init(struct snd_sof_dev *sdev)
/* probe/remove/shutdown */
sof_skl_ops.shutdown = hda_dsp_shutdown;
sdev->private = devm_kzalloc(sdev->dev, sizeof(*ipc4_data), GFP_KERNEL);
sdev->private = kzalloc(sizeof(*ipc4_data), GFP_KERNEL);
if (!sdev->private)
return -ENOMEM;
......
......@@ -82,7 +82,7 @@ int sof_tgl_ops_init(struct snd_sof_dev *sdev)
if (sdev->pdata->ipc_type == SOF_IPC_TYPE_4) {
struct sof_ipc4_fw_data *ipc4_data;
sdev->private = devm_kzalloc(sdev->dev, sizeof(*ipc4_data), GFP_KERNEL);
sdev->private = kzalloc(sizeof(*ipc4_data), GFP_KERNEL);
if (!sdev->private)
return -ENOMEM;
......
......@@ -74,18 +74,10 @@ int sof_acpi_probe(struct platform_device *pdev, const struct sof_dev_desc *desc
sof_pdata->desc = desc;
sof_pdata->dev = &pdev->dev;
sof_pdata->fw_filename = desc->default_fw_filename[SOF_IPC_TYPE_3];
/* alternate fw and tplg filenames ? */
if (fw_path)
sof_pdata->fw_filename_prefix = fw_path;
else
sof_pdata->fw_filename_prefix = desc->default_fw_path[SOF_IPC_TYPE_3];
if (tplg_path)
sof_pdata->tplg_filename_prefix = tplg_path;
else
sof_pdata->tplg_filename_prefix = desc->default_tplg_path[SOF_IPC_TYPE_3];
sof_pdata->ipc_file_profile_base.ipc_type = desc->ipc_default;
sof_pdata->ipc_file_profile_base.fw_path = fw_path;
sof_pdata->ipc_file_profile_base.tplg_path = tplg_path;
/* set callback to be called on successful device probe to enable runtime_pm */
sof_pdata->sof_probe_complete = sof_acpi_probe_complete;
......
......@@ -11,7 +11,6 @@
#include <linux/bitfield.h>
#include <trace/events/sof.h>
#include "sof-audio.h"
#include "sof-of-dev.h"
#include "ops.h"
static bool is_virtual_widget(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget,
......@@ -989,122 +988,3 @@ int sof_dai_get_bclk(struct snd_soc_pcm_runtime *rtd)
return sof_dai_get_clk(rtd, SOF_DAI_CLK_INTEL_SSP_BCLK);
}
EXPORT_SYMBOL(sof_dai_get_bclk);
static struct snd_sof_of_mach *sof_of_machine_select(struct snd_sof_dev *sdev)
{
struct snd_sof_pdata *sof_pdata = sdev->pdata;
const struct sof_dev_desc *desc = sof_pdata->desc;
struct snd_sof_of_mach *mach = desc->of_machines;
if (!mach)
return NULL;
for (; mach->compatible; mach++) {
if (of_machine_is_compatible(mach->compatible)) {
sof_pdata->tplg_filename = mach->sof_tplg_filename;
if (mach->fw_filename)
sof_pdata->fw_filename = mach->fw_filename;
return mach;
}
}
return NULL;
}
/*
* SOF Driver enumeration.
*/
int sof_machine_check(struct snd_sof_dev *sdev)
{
struct snd_sof_pdata *sof_pdata = sdev->pdata;
const struct sof_dev_desc *desc = sof_pdata->desc;
struct snd_soc_acpi_mach *mach;
if (!IS_ENABLED(CONFIG_SND_SOC_SOF_FORCE_NOCODEC_MODE)) {
const struct snd_sof_of_mach *of_mach;
if (IS_ENABLED(CONFIG_SND_SOC_SOF_NOCODEC_DEBUG_SUPPORT) &&
sof_debug_check_flag(SOF_DBG_FORCE_NOCODEC))
goto nocodec;
/* find machine */
mach = snd_sof_machine_select(sdev);
if (mach) {
sof_pdata->machine = mach;
if (sof_pdata->subsystem_id_set) {
mach->mach_params.subsystem_vendor = sof_pdata->subsystem_vendor;
mach->mach_params.subsystem_device = sof_pdata->subsystem_device;
mach->mach_params.subsystem_id_set = true;
}
snd_sof_set_mach_params(mach, sdev);
return 0;
}
of_mach = sof_of_machine_select(sdev);
if (of_mach) {
sof_pdata->of_machine = of_mach;
return 0;
}
if (!IS_ENABLED(CONFIG_SND_SOC_SOF_NOCODEC)) {
dev_err(sdev->dev, "error: no matching ASoC machine driver found - aborting probe\n");
return -ENODEV;
}
} else {
dev_warn(sdev->dev, "Force to use nocodec mode\n");
}
nocodec:
/* select nocodec mode */
dev_warn(sdev->dev, "Using nocodec machine driver\n");
mach = devm_kzalloc(sdev->dev, sizeof(*mach), GFP_KERNEL);
if (!mach)
return -ENOMEM;
mach->drv_name = "sof-nocodec";
if (!sof_pdata->tplg_filename)
sof_pdata->tplg_filename = desc->nocodec_tplg_filename;
sof_pdata->machine = mach;
snd_sof_set_mach_params(mach, sdev);
return 0;
}
EXPORT_SYMBOL(sof_machine_check);
int sof_machine_register(struct snd_sof_dev *sdev, void *pdata)
{
struct snd_sof_pdata *plat_data = pdata;
const char *drv_name;
const void *mach;
int size;
drv_name = plat_data->machine->drv_name;
mach = plat_data->machine;
size = sizeof(*plat_data->machine);
/* register machine driver, pass machine info as pdata */
plat_data->pdev_mach =
platform_device_register_data(sdev->dev, drv_name,
PLATFORM_DEVID_NONE, mach, size);
if (IS_ERR(plat_data->pdev_mach))
return PTR_ERR(plat_data->pdev_mach);
dev_dbg(sdev->dev, "created machine %s\n",
dev_name(&plat_data->pdev_mach->dev));
return 0;
}
EXPORT_SYMBOL(sof_machine_register);
void sof_machine_unregister(struct snd_sof_dev *sdev, void *pdata)
{
struct snd_sof_pdata *plat_data = pdata;
if (!IS_ERR_OR_NULL(plat_data->pdev_mach))
platform_device_unregister(plat_data->pdev_mach);
}
EXPORT_SYMBOL(sof_machine_unregister);
......@@ -41,6 +41,29 @@ static void sof_of_probe_complete(struct device *dev)
pm_runtime_enable(dev);
}
struct snd_sof_of_mach *sof_of_machine_select(struct snd_sof_dev *sdev)
{
struct snd_sof_pdata *sof_pdata = sdev->pdata;
const struct sof_dev_desc *desc = sof_pdata->desc;
struct snd_sof_of_mach *mach = desc->of_machines;
if (!mach)
return NULL;
for (; mach->compatible; mach++) {
if (of_machine_is_compatible(mach->compatible)) {
sof_pdata->tplg_filename = mach->sof_tplg_filename;
if (mach->fw_filename)
sof_pdata->fw_filename = mach->fw_filename;
return mach;
}
}
return NULL;
}
EXPORT_SYMBOL(sof_of_machine_select);
int sof_of_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
......@@ -64,17 +87,10 @@ int sof_of_probe(struct platform_device *pdev)
sof_pdata->desc = desc;
sof_pdata->dev = &pdev->dev;
sof_pdata->fw_filename = desc->default_fw_filename[SOF_IPC_TYPE_3];
if (fw_path)
sof_pdata->fw_filename_prefix = fw_path;
else
sof_pdata->fw_filename_prefix = desc->default_fw_path[SOF_IPC_TYPE_3];
if (tplg_path)
sof_pdata->tplg_filename_prefix = tplg_path;
else
sof_pdata->tplg_filename_prefix = desc->default_tplg_path[SOF_IPC_TYPE_3];
sof_pdata->ipc_file_profile_base.ipc_type = desc->ipc_default;
sof_pdata->ipc_file_profile_base.fw_path = fw_path;
sof_pdata->ipc_file_profile_base.tplg_path = tplg_path;
/* set callback to be called on successful device probe to enable runtime_pm */
sof_pdata->sof_probe_complete = sof_of_probe_complete;
......
......@@ -16,6 +16,15 @@ struct snd_sof_of_mach {
const char *sof_tplg_filename;
};
#if IS_ENABLED(CONFIG_SND_SOC_SOF_OF_DEV)
struct snd_sof_of_mach *sof_of_machine_select(struct snd_sof_dev *sdev);
#else
static inline struct snd_sof_of_mach *sof_of_machine_select(struct snd_sof_dev *sdev)
{
return NULL;
}
#endif
extern const struct dev_pm_ops sof_of_pm;
int sof_of_probe(struct platform_device *pdev);
......
......@@ -190,6 +190,7 @@ static void sof_pci_probe_complete(struct device *dev)
int sof_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
{
struct sof_loadable_file_profile *path_override;
struct device *dev = &pci->dev;
const struct sof_dev_desc *desc =
(const struct sof_dev_desc *)pci_id->driver_data;
......@@ -232,106 +233,39 @@ int sof_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
sof_pdata->desc = desc;
sof_pdata->dev = dev;
sof_pdata->ipc_type = desc->ipc_default;
path_override = &sof_pdata->ipc_file_profile_base;
if (sof_pci_ipc_type < 0) {
sof_pdata->ipc_type = desc->ipc_default;
path_override->ipc_type = desc->ipc_default;
} else if (sof_pci_ipc_type < SOF_IPC_TYPE_COUNT) {
path_override->ipc_type = sof_pci_ipc_type;
} else {
dev_info(dev, "overriding default IPC %d to requested %d\n",
desc->ipc_default, sof_pci_ipc_type);
if (sof_pci_ipc_type >= SOF_IPC_TYPE_COUNT) {
dev_err(dev, "invalid request value %d\n", sof_pci_ipc_type);
ret = -EINVAL;
goto out;
}
if (!(BIT(sof_pci_ipc_type) & desc->ipc_supported_mask)) {
dev_err(dev, "invalid request value %d, supported mask is %#x\n",
sof_pci_ipc_type, desc->ipc_supported_mask);
ret = -EINVAL;
goto out;
}
sof_pdata->ipc_type = sof_pci_ipc_type;
dev_err(dev, "Invalid IPC type requested: %d\n", sof_pci_ipc_type);
ret = -EINVAL;
goto out;
}
if (fw_filename) {
sof_pdata->fw_filename = fw_filename;
path_override->fw_path = fw_path;
path_override->fw_name = fw_filename;
path_override->fw_lib_path = lib_path;
path_override->tplg_path = tplg_path;
dev_dbg(dev, "Module parameter used, changed fw filename to %s\n",
sof_pdata->fw_filename);
} else {
sof_pdata->fw_filename = desc->default_fw_filename[sof_pdata->ipc_type];
if (dmi_check_system(community_key_platforms) &&
sof_dmi_use_community_key) {
path_override->fw_path_postfix = "community";
path_override->fw_lib_path_postfix = "community";
}
/*
* for platforms using the SOF community key, change the
* default path automatically to pick the right files from the
* linux-firmware tree. This can be overridden with the
* fw_path kernel parameter, e.g. for developers.
*/
/* alternate fw and tplg filenames ? */
if (fw_path) {
sof_pdata->fw_filename_prefix = fw_path;
dev_dbg(dev,
"Module parameter used, changed fw path to %s\n",
sof_pdata->fw_filename_prefix);
} else if (dmi_check_system(community_key_platforms) && sof_dmi_use_community_key) {
sof_pdata->fw_filename_prefix =
devm_kasprintf(dev, GFP_KERNEL, "%s/%s",
sof_pdata->desc->default_fw_path[sof_pdata->ipc_type],
"community");
dev_dbg(dev,
"Platform uses community key, changed fw path to %s\n",
sof_pdata->fw_filename_prefix);
} else {
sof_pdata->fw_filename_prefix =
sof_pdata->desc->default_fw_path[sof_pdata->ipc_type];
}
if (lib_path) {
sof_pdata->fw_lib_prefix = lib_path;
dev_dbg(dev, "Module parameter used, changed fw_lib path to %s\n",
sof_pdata->fw_lib_prefix);
} else if (sof_pdata->desc->default_lib_path[sof_pdata->ipc_type]) {
if (dmi_check_system(community_key_platforms) && sof_dmi_use_community_key) {
sof_pdata->fw_lib_prefix =
devm_kasprintf(dev, GFP_KERNEL, "%s/%s",
sof_pdata->desc->default_lib_path[sof_pdata->ipc_type],
"community");
dev_dbg(dev,
"Platform uses community key, changed fw_lib path to %s\n",
sof_pdata->fw_lib_prefix);
} else {
sof_pdata->fw_lib_prefix =
sof_pdata->desc->default_lib_path[sof_pdata->ipc_type];
}
}
if (tplg_path)
sof_pdata->tplg_filename_prefix = tplg_path;
else
sof_pdata->tplg_filename_prefix =
sof_pdata->desc->default_tplg_path[sof_pdata->ipc_type];
/*
* the topology filename will be provided in the machine descriptor, unless
* it is overridden by a module parameter or DMI quirk.
*/
if (tplg_filename) {
sof_pdata->tplg_filename = tplg_filename;
dev_dbg(dev, "Module parameter used, changed tplg filename to %s\n",
sof_pdata->tplg_filename);
path_override->tplg_name = tplg_filename;
} else {
dmi_check_system(sof_tplg_table);
if (sof_dmi_override_tplg_name)
sof_pdata->tplg_filename = sof_dmi_override_tplg_name;
path_override->tplg_name = sof_dmi_override_tplg_name;
}
/* set callback to be called on successful device probe to enable runtime_pm */
......
......@@ -695,6 +695,13 @@ void snd_sof_new_platform_drv(struct snd_sof_dev *sdev);
*/
extern struct snd_compress_ops sof_compressed_ops;
/*
* Firmware (firmware, libraries, topologies) file location
*/
int sof_create_ipc_file_profile(struct snd_sof_dev *sdev,
struct sof_loadable_file_profile *base_profile,
struct sof_loadable_file_profile *out_profile);
/*
* Firmware loading.
*/
......@@ -814,8 +821,6 @@ int sof_stream_pcm_open(struct snd_sof_dev *sdev,
int sof_stream_pcm_close(struct snd_sof_dev *sdev,
struct snd_pcm_substream *substream);
int sof_machine_check(struct snd_sof_dev *sdev);
/* SOF client support */
#if IS_ENABLED(CONFIG_SND_SOC_SOF_CLIENT)
int sof_client_dev_register(struct snd_sof_dev *sdev, const char *name, u32 id,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment