320 lines
9.2 KiB
C
320 lines
9.2 KiB
C
/*
|
|
* tegra_virt_ref_alt.c - Tegra reference virtual machine driver
|
|
*
|
|
* Copyright (c) 2015-2019, NVIDIA CORPORATION. All rights reserved.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms and conditions of the GNU General Public License,
|
|
* version 2, as published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
* more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/version.h>
|
|
#include <linux/of.h>
|
|
#include <linux/platform_device.h>
|
|
#include <sound/pcm.h>
|
|
#include <sound/soc.h>
|
|
#include <linux/of_platform.h>
|
|
#include <linux/pm_runtime.h>
|
|
#include <linux/tegra_pm_domains.h>
|
|
|
|
#include "tegra_asoc_metadata_util_alt.h"
|
|
#include "tegra210_virt_alt_admaif.h"
|
|
#include "tegra_asoc_machine_virt_alt.h"
|
|
#include "tegra_asoc_util_virt_alt.h"
|
|
#include "tegra_asoc_xbar_virt_alt.h"
|
|
#include "tegra_virt_alt_ivc.h"
|
|
|
|
|
|
static struct tegra_audio_metadata_cntx meta = {
|
|
.metadata_mode = HEADER_MODE,
|
|
.init_metadata_flood = 0,
|
|
.enable_metadata_flood = 0,
|
|
|
|
/* 0 indexed */
|
|
.admaif_id = 3,
|
|
.dma_id = 3,
|
|
.dma_ch_page = 0,
|
|
};
|
|
|
|
static struct snd_soc_card tegra_virt_t210ref_card = {
|
|
.name = "t210ref-virt-card",
|
|
.owner = THIS_MODULE,
|
|
.fully_routed = true,
|
|
};
|
|
|
|
static struct snd_soc_card tegra_virt_t186ref_card = {
|
|
.name = "t186ref-virt-card",
|
|
.owner = THIS_MODULE,
|
|
.fully_routed = true,
|
|
};
|
|
|
|
static void tegra_virt_set_dai_params(
|
|
struct snd_soc_dai_link *dai_link,
|
|
struct snd_soc_pcm_stream *user_params,
|
|
unsigned int dai_id)
|
|
{
|
|
dai_link[dai_id].params = user_params;
|
|
}
|
|
|
|
static struct tegra_virt_admaif_soc_data soc_data_tegra210 = {
|
|
.num_ch = TEGRA210_ADMAIF_CHANNEL_COUNT,
|
|
};
|
|
|
|
static struct tegra_virt_admaif_soc_data soc_data_tegra186 = {
|
|
.num_ch = TEGRA186_ADMAIF_CHANNEL_COUNT,
|
|
};
|
|
|
|
|
|
static const struct of_device_id tegra_virt_machine_of_match[] = {
|
|
{ .compatible = "nvidia,tegra210-virt-pcm",
|
|
.data = &soc_data_tegra210 },
|
|
{ .compatible = "nvidia,tegra186-virt-pcm",
|
|
.data = &soc_data_tegra186},
|
|
{},
|
|
};
|
|
|
|
static int tegra_virt_machine_driver_probe(struct platform_device *pdev)
|
|
{
|
|
struct snd_soc_card *card = NULL;
|
|
int i, ret = 0;
|
|
int admaif_ch_num = 0;
|
|
bool adsp_enabled = false;
|
|
unsigned int admaif_ch_list[MAX_ADMAIF_IDS];
|
|
const struct of_device_id *match;
|
|
struct tegra_virt_admaif_soc_data *soc_data;
|
|
char buffer[30];
|
|
int32_t adsp_admaif_bits, adsp_admaif_format;
|
|
int32_t adsp_admaif_channels;
|
|
struct snd_soc_pcm_stream adsp_admaif_dt_params;
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0)
|
|
struct snd_soc_pcm_runtime *rtd;
|
|
#endif
|
|
match = tegra_virt_machine_of_match;
|
|
if (of_device_is_compatible(pdev->dev.of_node,
|
|
"nvidia,tegra210-virt-pcm")) {
|
|
card = &tegra_virt_t210ref_card;
|
|
match = of_match_device(tegra_virt_machine_of_match,
|
|
&pdev->dev);
|
|
if (!match)
|
|
return -ENODEV;
|
|
soc_data = (struct tegra_virt_admaif_soc_data *)match->data;
|
|
} else if (of_device_is_compatible(pdev->dev.of_node,
|
|
"nvidia,tegra186-virt-pcm")) {
|
|
card = &tegra_virt_t186ref_card;
|
|
match = of_match_device(tegra_virt_machine_of_match,
|
|
&pdev->dev);
|
|
if (!match)
|
|
return -ENODEV;
|
|
soc_data = (struct tegra_virt_admaif_soc_data *)match->data;
|
|
} else {
|
|
dev_err(&pdev->dev, "device match data not found\n");
|
|
return -ENODEV;
|
|
}
|
|
|
|
card->dev = &pdev->dev;
|
|
card->dai_link = tegra_virt_machine_get_dai_link();
|
|
card->num_links = tegra_virt_machine_get_num_dai_links();
|
|
adsp_enabled = of_property_read_bool(pdev->dev.of_node,
|
|
"adsp_enabled");
|
|
|
|
|
|
if (adsp_enabled) {
|
|
dev_info(&pdev->dev, "virt-alt-pcm: adsp config is set\n");
|
|
|
|
/* Get ADSP ADMAIF default param info */
|
|
for (i = 0; i < MAX_ADMAIF_IDS; i++) {
|
|
sprintf(buffer, "adsp-admaif%d-channels", i + 1);
|
|
if (of_property_read_u32(pdev->dev.of_node,
|
|
buffer, &adsp_admaif_channels))
|
|
adsp_admaif_channels = 2;
|
|
|
|
|
|
sprintf(buffer, "adsp-admaif%d-bits", i + 1);
|
|
if (of_property_read_u32(pdev->dev.of_node,
|
|
buffer, &adsp_admaif_bits))
|
|
adsp_admaif_bits = 16;
|
|
|
|
if (adsp_admaif_channels > 16 || adsp_admaif_channels < 1) {
|
|
dev_err(&pdev->dev,
|
|
"unsupported channels %d for adsp admaif %d, setting default 2 channel\n",
|
|
adsp_admaif_channels, i + 1);
|
|
adsp_admaif_channels = 2;
|
|
}
|
|
|
|
switch (adsp_admaif_bits) {
|
|
|
|
case 8:
|
|
adsp_admaif_format = SNDRV_PCM_FMTBIT_S8;
|
|
break;
|
|
case 16:
|
|
adsp_admaif_format = SNDRV_PCM_FMTBIT_S16_LE;
|
|
break;
|
|
case 32:
|
|
adsp_admaif_format = SNDRV_PCM_FMTBIT_S32_LE;
|
|
break;
|
|
default:
|
|
adsp_admaif_format = SNDRV_PCM_FMTBIT_S16_LE;
|
|
dev_err(&pdev->dev,
|
|
"unsupported bits %d for adsp admaif %d, setting default 16 bit\n",
|
|
adsp_admaif_bits, i + 1);
|
|
adsp_admaif_bits = 16;
|
|
break;
|
|
}
|
|
adsp_admaif_dt_params.formats = adsp_admaif_format;
|
|
adsp_admaif_dt_params.rate_min = 48000;
|
|
adsp_admaif_dt_params.rate_max = 48000;
|
|
adsp_admaif_dt_params.channels_min = adsp_admaif_channels;
|
|
adsp_admaif_dt_params.channels_max = adsp_admaif_channels;
|
|
|
|
tegra_virt_machine_set_adsp_admaif_dai_params(
|
|
i, &adsp_admaif_dt_params);
|
|
}
|
|
} else {
|
|
dev_info(&pdev->dev, "virt-alt-pcm: adsp config is not set\n");
|
|
card->num_links = soc_data->num_ch;
|
|
}
|
|
|
|
if (tegra210_virt_admaif_register_component(pdev, soc_data)) {
|
|
dev_err(&pdev->dev, "Failed register admaif component\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (tegra_virt_xbar_register_codec(pdev)) {
|
|
dev_err(&pdev->dev, "Failed register xbar component\n");
|
|
ret = -EINVAL;
|
|
goto undo_register_component;
|
|
}
|
|
|
|
if (of_property_read_u32(pdev->dev.of_node,
|
|
"admaif_ch_num", &admaif_ch_num)) {
|
|
dev_err(&pdev->dev, "number of admaif channels is not set\n");
|
|
ret = -EINVAL;
|
|
goto undo_register_codec;
|
|
}
|
|
|
|
if (of_property_read_string(pdev->dev.of_node, "cardname", &card->name))
|
|
dev_warn(&pdev->dev, "Using default card name %s\n",
|
|
card->name);
|
|
|
|
if (admaif_ch_num > 0) {
|
|
|
|
if (of_property_read_u32_array(pdev->dev.of_node,
|
|
"admaif_ch_list",
|
|
admaif_ch_list,
|
|
admaif_ch_num)) {
|
|
dev_err(&pdev->dev, "admaif_ch_list os not populated\n");
|
|
ret = -EINVAL;
|
|
goto undo_register_codec;
|
|
}
|
|
for (i = 0; i < admaif_ch_num; i++) {
|
|
tegra_virt_set_dai_params(
|
|
tegra_virt_machine_get_dai_link(),
|
|
NULL,
|
|
(admaif_ch_list[i] - 1));
|
|
}
|
|
}
|
|
|
|
ret = snd_soc_register_card(card);
|
|
if (ret) {
|
|
dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n",
|
|
ret);
|
|
ret = -EPROBE_DEFER;
|
|
goto undo_register_codec;
|
|
}
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 5, 0)
|
|
for (i = 0; i < card->num_rtd; i++) {
|
|
struct snd_soc_dai *codec_dai = card->rtd[i].codec_dai;
|
|
struct snd_soc_dai_driver *codec_drv = codec_dai->driver;
|
|
struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai;
|
|
struct snd_soc_dai_driver *cpu_drv = cpu_dai->driver;
|
|
|
|
cpu_drv->playback.rates = SNDRV_PCM_RATE_KNOT;
|
|
cpu_drv->playback.rate_min = 8000;
|
|
cpu_drv->playback.rate_max = 192000;
|
|
cpu_drv->capture.rates = SNDRV_PCM_RATE_KNOT;
|
|
cpu_drv->capture.rate_min = 8000;
|
|
cpu_drv->capture.rate_max = 192000;
|
|
|
|
codec_drv->playback.rates = SNDRV_PCM_RATE_KNOT;
|
|
codec_drv->playback.rate_min = 8000;
|
|
codec_drv->playback.rate_max = 192000;
|
|
codec_drv->capture.rates = SNDRV_PCM_RATE_KNOT;
|
|
codec_drv->capture.rate_min = 8000;
|
|
codec_drv->capture.rate_max = 192000;
|
|
}
|
|
#else
|
|
list_for_each_entry(rtd, &card->rtd_list, list) {
|
|
struct snd_soc_dai *codec_dai = rtd->codec_dai;
|
|
struct snd_soc_dai_driver *codec_drv = codec_dai->driver;
|
|
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
|
|
struct snd_soc_dai_driver *cpu_drv = cpu_dai->driver;
|
|
|
|
cpu_drv->playback.rates = SNDRV_PCM_RATE_KNOT;
|
|
cpu_drv->playback.rate_min = 8000;
|
|
cpu_drv->playback.rate_max = 192000;
|
|
cpu_drv->capture.rates = SNDRV_PCM_RATE_KNOT;
|
|
cpu_drv->capture.rate_min = 8000;
|
|
cpu_drv->capture.rate_max = 192000;
|
|
|
|
codec_drv->playback.rates = SNDRV_PCM_RATE_KNOT;
|
|
codec_drv->playback.rate_min = 8000;
|
|
codec_drv->playback.rate_max = 192000;
|
|
codec_drv->capture.rates = SNDRV_PCM_RATE_KNOT;
|
|
codec_drv->capture.rate_min = 8000;
|
|
codec_drv->capture.rate_max = 192000;
|
|
}
|
|
#endif
|
|
tegra_metadata_setup(pdev, &meta, card);
|
|
tegra_pd_add_device(&pdev->dev);
|
|
pm_runtime_forbid(&pdev->dev);
|
|
|
|
return 0;
|
|
|
|
undo_register_codec:
|
|
snd_soc_unregister_codec(&pdev->dev);
|
|
undo_register_component:
|
|
tegra210_virt_admaif_unregister_component(pdev);
|
|
|
|
nvaudio_ivc_free_ctxt(&pdev->dev);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int tegra_virt_machine_driver_remove(struct platform_device *pdev)
|
|
{
|
|
struct snd_soc_card *card = platform_get_drvdata(pdev);
|
|
|
|
snd_soc_unregister_card(card);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct platform_driver tegra_virt_machine_driver = {
|
|
.driver = {
|
|
.name = DRV_NAME,
|
|
.owner = THIS_MODULE,
|
|
.pm = &snd_soc_pm_ops,
|
|
.of_match_table =
|
|
of_match_ptr(tegra_virt_machine_of_match),
|
|
},
|
|
.probe = tegra_virt_machine_driver_probe,
|
|
.remove = tegra_virt_machine_driver_remove,
|
|
};
|
|
module_platform_driver(tegra_virt_machine_driver);
|
|
|
|
MODULE_AUTHOR("Paresh Anandathirtha <paresha@nvidia.com>");
|
|
MODULE_DESCRIPTION("Tegra virt machine driver");
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_DEVICE_TABLE(of, tegra_virt_machine_of_match);
|
|
MODULE_ALIAS("platform:" DRV_NAME);
|
|
MODULE_SOFTDEP("pre: snd_soc_tegra210_virt_alt_adsp");
|