Commit d96e75bb authored by Mark Brown's avatar Mark Brown

Merge series "Add Yellow Carp platform ASoC driver" from Vijendar Mukunda...

Merge series "Add Yellow Carp platform ASoC driver" from Vijendar Mukunda <Vijendar.Mukunda@amd.com>:

Yellow Carp platform is new APU series based on acp6.x design.
This patch set adds an ASoC driver for the ACP (Audio CoProcessor)
block on AMD Yellow Carp APU with DMIC endpoint support.

changes since v1:
  - fixed SPDX commenting style in acp6x pci driver.
  - fixed Copyright commenting style in acp6x pci driver,
    pdm dma driver and machine driver files.
  - fixed local variable check logic in acp6x_power_on() function.
  - fixed Kconfig indentation issue
  - removed irq_flag local variable in irq handler.
  - add dmi info check in machine driver probe

Vijendar Mukunda (13):
  ASoC: amd: add Yellow Carp ACP6x IP register header
  ASoC: amd: add Yellow Carp ACP PCI driver
  ASoC: amd: add acp6x init/de-init functions
  ASoC: amd: add platform devices for acp6x pdm driver and dmic driver
  ASoC: amd: add acp6x pdm platform driver
  ASoC: amd: add acp6x irq handler
  ASoC: amd: add acp6x pdm driver dma ops
  ASoC: amd: add acp6x pci driver pm ops
  ASoC: amd: add acp6x pdm driver pm ops
  ASoC: amd: enable Yellow carp acp6x drivers build
  ASoC: amd: create platform device for acp6x machine driver
  ASoC: amd: add YC machine driver using dmic
  ASoC: amd: enable Yellow Carp platform machine driver build

 sound/soc/amd/Kconfig                     |  21 +
 sound/soc/amd/Makefile                    |   1 +
 sound/soc/amd/yc/Makefile                 |   9 +
 sound/soc/amd/yc/acp6x-mach.c             | 194 ++++++++++
 sound/soc/amd/yc/acp6x-pdm-dma.c          | 448 ++++++++++++++++++++++
 sound/soc/amd/yc/acp6x.h                  | 107 ++++++
 sound/soc/amd/yc/acp6x_chip_offset_byte.h | 444 +++++++++++++++++++++
 sound/soc/amd/yc/pci-acp6x.c              | 338 ++++++++++++++++
 8 files changed, 1562 insertions(+)
 create mode 100644 sound/soc/amd/yc/Makefile
 create mode 100644 sound/soc/amd/yc/acp6x-mach.c
 create mode 100644 sound/soc/amd/yc/acp6x-pdm-dma.c
 create mode 100644 sound/soc/amd/yc/acp6x.h
 create mode 100644 sound/soc/amd/yc/acp6x_chip_offset_byte.h
 create mode 100644 sound/soc/amd/yc/pci-acp6x.c

--
2.25.1
parents 866d7444 a80d7eda
......@@ -74,3 +74,24 @@ config SND_SOC_AMD_VANGOGH_MACH
using NAU8821 and CS35L41 codecs.
Say m if you have such a device.
If unsure select "N".
config SND_SOC_AMD_ACP6x
tristate "AMD Audio Coprocessor-v6.x Yellow Carp support"
depends on X86 && PCI
help
This option enables Audio Coprocessor i.e ACP v6.x support on
AMD Yellow Carp platform. By enabling this flag build will be
triggered for ACP PCI driver, ACP PDM DMA driver.
Say m if you have such a device.
If unsure select "N".
config SND_SOC_AMD_YC_MACH
tristate "AMD YC support for DMIC"
select SND_SOC_DMIC
depends on SND_SOC_AMD_ACP6x
help
This option enables machine driver for Yellow Carp platform
using dmic. ACP IP has PDM Decoder block with DMA controller.
DMIC can be connected directly to ACP IP.
Say m if you have such a device.
If unsure select "N".
......@@ -11,3 +11,4 @@ obj-$(CONFIG_SND_SOC_AMD_ACP3x) += raven/
obj-$(CONFIG_SND_SOC_AMD_RV_RT5682_MACH) += snd-soc-acp-rt5682-mach.o
obj-$(CONFIG_SND_SOC_AMD_RENOIR) += renoir/
obj-$(CONFIG_SND_SOC_AMD_ACP5x) += vangogh/
obj-$(CONFIG_SND_SOC_AMD_ACP6x) += yc/
# SPDX-License-Identifier: GPL-2.0+
# Yellow Carp platform Support
snd-pci-acp6x-objs := pci-acp6x.o
snd-acp6x-pdm-dma-objs := acp6x-pdm-dma.o
snd-soc-acp6x-mach-objs := acp6x-mach.o
obj-$(CONFIG_SND_SOC_AMD_ACP6x) += snd-pci-acp6x.o
obj-$(CONFIG_SND_SOC_AMD_ACP6x) += snd-acp6x-pdm-dma.o
obj-$(CONFIG_SND_SOC_AMD_YC_MACH) += snd-soc-acp6x-mach.o
// SPDX-License-Identifier: GPL-2.0+
/*
* Machine driver for AMD Yellow Carp platform using DMIC
*
* Copyright 2021 Advanced Micro Devices, Inc.
*/
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include <linux/module.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <linux/io.h>
#include <linux/dmi.h>
#include "acp6x.h"
#define DRV_NAME "acp_yc_mach"
SND_SOC_DAILINK_DEF(acp6x_pdm,
DAILINK_COMP_ARRAY(COMP_CPU("acp_yc_pdm_dma.0")));
SND_SOC_DAILINK_DEF(dmic_codec,
DAILINK_COMP_ARRAY(COMP_CODEC("dmic-codec.0",
"dmic-hifi")));
SND_SOC_DAILINK_DEF(pdm_platform,
DAILINK_COMP_ARRAY(COMP_PLATFORM("acp_yc_pdm_dma.0")));
static struct snd_soc_dai_link acp6x_dai_pdm[] = {
{
.name = "acp6x-dmic-capture",
.stream_name = "DMIC capture",
.capture_only = 1,
SND_SOC_DAILINK_REG(acp6x_pdm, dmic_codec, pdm_platform),
},
};
static struct snd_soc_card acp6x_card = {
.name = "acp6x",
.owner = THIS_MODULE,
.dai_link = acp6x_dai_pdm,
.num_links = 1,
};
static const struct dmi_system_id yc_acp_quirk_table[] = {
{
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
DMI_MATCH(DMI_PRODUCT_NAME, "21D2"),
}
},
{
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
DMI_MATCH(DMI_PRODUCT_NAME, "21D3"),
}
},
{
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
DMI_MATCH(DMI_PRODUCT_NAME, "21D4"),
}
},
{
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
DMI_MATCH(DMI_PRODUCT_NAME, "21D5"),
}
},
{
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
DMI_MATCH(DMI_PRODUCT_NAME, "21CF"),
}
},
{
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
DMI_MATCH(DMI_PRODUCT_NAME, "21CG"),
}
},
{
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
DMI_MATCH(DMI_PRODUCT_NAME, "21CQ"),
}
},
{
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
DMI_MATCH(DMI_PRODUCT_NAME, "21CR"),
}
},
{
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
DMI_MATCH(DMI_PRODUCT_NAME, "21AW"),
}
},
{
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
DMI_MATCH(DMI_PRODUCT_NAME, "21AX"),
}
},
{
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
DMI_MATCH(DMI_PRODUCT_NAME, "21BN"),
}
},
{
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
DMI_MATCH(DMI_PRODUCT_NAME, "21BQ"),
}
},
{
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
DMI_MATCH(DMI_PRODUCT_NAME, "21CH"),
}
},
{
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
DMI_MATCH(DMI_PRODUCT_NAME, "21CJ"),
}
},
{
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
DMI_MATCH(DMI_PRODUCT_NAME, "21CK"),
}
},
{
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
DMI_MATCH(DMI_PRODUCT_NAME, "21CL"),
}
},
{
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
DMI_MATCH(DMI_PRODUCT_NAME, "21D8"),
}
},
{
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
DMI_MATCH(DMI_PRODUCT_NAME, "21D9"),
}
},
{}
};
static int acp6x_probe(struct platform_device *pdev)
{
struct acp6x_pdm *machine = NULL;
struct snd_soc_card *card;
int ret;
const struct dmi_system_id *dmi_id;
dmi_id = dmi_first_match(yc_acp_quirk_table);
if (!dmi_id)
return -ENODEV;
card = &acp6x_card;
acp6x_card.dev = &pdev->dev;
platform_set_drvdata(pdev, card);
snd_soc_card_set_drvdata(card, machine);
ret = devm_snd_soc_register_card(&pdev->dev, card);
if (ret) {
return dev_err_probe(&pdev->dev, ret,
"snd_soc_register_card(%s) failed\n",
card->name);
}
return 0;
}
static struct platform_driver acp6x_mach_driver = {
.driver = {
.name = "acp_yc_mach",
.pm = &snd_soc_pm_ops,
},
.probe = acp6x_probe,
};
module_platform_driver(acp6x_mach_driver);
MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:" DRV_NAME);
This diff is collapsed.
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* AMD ALSA SoC PDM Driver
*
* Copyright (C) 2021 Advanced Micro Devices, Inc. All rights reserved.
*/
#include "acp6x_chip_offset_byte.h"
#define ACP_DEVICE_ID 0x15E2
#define ACP6x_PHY_BASE_ADDRESS 0x1240000
#define ACP6x_REG_START 0x1240000
#define ACP6x_REG_END 0x1250200
#define ACP6x_DEVS 3
#define ACP6x_PDM_MODE 1
#define ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK 0x00010001
#define ACP_PGFSM_CNTL_POWER_ON_MASK 1
#define ACP_PGFSM_CNTL_POWER_OFF_MASK 0
#define ACP_PGFSM_STATUS_MASK 3
#define ACP_POWERED_ON 0
#define ACP_POWER_ON_IN_PROGRESS 1
#define ACP_POWERED_OFF 2
#define ACP_POWER_OFF_IN_PROGRESS 3
#define ACP_ERROR_MASK 0x20000000
#define ACP_EXT_INTR_STAT_CLEAR_MASK 0xFFFFFFFF
#define PDM_DMA_STAT 0x10
#define PDM_DMA_INTR_MASK 0x10000
#define ACP_ERROR_STAT 29
#define PDM_DECIMATION_FACTOR 2
#define ACP_PDM_CLK_FREQ_MASK 7
#define ACP_WOV_MISC_CTRL_MASK 0x10
#define ACP_PDM_ENABLE 1
#define ACP_PDM_DISABLE 0
#define ACP_PDM_DMA_EN_STATUS 2
#define TWO_CH 2
#define DELAY_US 5
#define ACP_COUNTER 20000
#define ACP_SRAM_PTE_OFFSET 0x03800000
#define PAGE_SIZE_4K_ENABLE 2
#define PDM_PTE_OFFSET 0
#define PDM_MEM_WINDOW_START 0x4000000
#define CAPTURE_MIN_NUM_PERIODS 4
#define CAPTURE_MAX_NUM_PERIODS 4
#define CAPTURE_MAX_PERIOD_SIZE 8192
#define CAPTURE_MIN_PERIOD_SIZE 4096
#define MAX_BUFFER (CAPTURE_MAX_PERIOD_SIZE * CAPTURE_MAX_NUM_PERIODS)
#define MIN_BUFFER MAX_BUFFER
/* time in ms for runtime suspend delay */
#define ACP_SUSPEND_DELAY_MS 2000
enum acp_config {
ACP_CONFIG_0 = 0,
ACP_CONFIG_1,
ACP_CONFIG_2,
ACP_CONFIG_3,
ACP_CONFIG_4,
ACP_CONFIG_5,
ACP_CONFIG_6,
ACP_CONFIG_7,
ACP_CONFIG_8,
ACP_CONFIG_9,
ACP_CONFIG_10,
ACP_CONFIG_11,
ACP_CONFIG_12,
ACP_CONFIG_13,
ACP_CONFIG_14,
ACP_CONFIG_15,
};
struct pdm_dev_data {
u32 pdm_irq;
void __iomem *acp6x_base;
struct snd_pcm_substream *capture_stream;
};
struct pdm_stream_instance {
u16 num_pages;
u16 channels;
dma_addr_t dma_addr;
u64 bytescount;
void __iomem *acp6x_base;
};
union acp_pdm_dma_count {
struct {
u32 low;
u32 high;
} bcount;
u64 bytescount;
};
static inline u32 acp6x_readl(void __iomem *base_addr)
{
return readl(base_addr - ACP6x_PHY_BASE_ADDRESS);
}
static inline void acp6x_writel(u32 val, void __iomem *base_addr)
{
writel(val, base_addr - ACP6x_PHY_BASE_ADDRESS);
}
This diff is collapsed.
// SPDX-License-Identifier: GPL-2.0+
/*
* AMD Yellow Carp ACP PCI Driver
*
* Copyright 2021 Advanced Micro Devices, Inc.
*/
#include <linux/pci.h>
#include <linux/module.h>
#include <linux/io.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <sound/pcm_params.h>
#include <linux/pm_runtime.h>
#include "acp6x.h"
struct acp6x_dev_data {
void __iomem *acp6x_base;
struct resource *res;
bool acp6x_audio_mode;
struct platform_device *pdev[ACP6x_DEVS];
};
static int acp6x_power_on(void __iomem *acp_base)
{
u32 val;
int timeout;
val = acp6x_readl(acp_base + ACP_PGFSM_STATUS);
if (!val)
return val;
if ((val & ACP_PGFSM_STATUS_MASK) != ACP_POWER_ON_IN_PROGRESS)
acp6x_writel(ACP_PGFSM_CNTL_POWER_ON_MASK, acp_base + ACP_PGFSM_CONTROL);
timeout = 0;
while (++timeout < 500) {
val = acp6x_readl(acp_base + ACP_PGFSM_STATUS);
if (!val)
return 0;
udelay(1);
}
return -ETIMEDOUT;
}
static int acp6x_reset(void __iomem *acp_base)
{
u32 val;
int timeout;
acp6x_writel(1, acp_base + ACP_SOFT_RESET);
timeout = 0;
while (++timeout < 500) {
val = acp6x_readl(acp_base + ACP_SOFT_RESET);
if (val & ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK)
break;
cpu_relax();
}
acp6x_writel(0, acp_base + ACP_SOFT_RESET);
timeout = 0;
while (++timeout < 500) {
val = acp6x_readl(acp_base + ACP_SOFT_RESET);
if (!val)
return 0;
cpu_relax();
}
return -ETIMEDOUT;
}
static void acp6x_enable_interrupts(void __iomem *acp_base)
{
acp6x_writel(0x01, acp_base + ACP_EXTERNAL_INTR_ENB);
}
static void acp6x_disable_interrupts(void __iomem *acp_base)
{
acp6x_writel(ACP_EXT_INTR_STAT_CLEAR_MASK, acp_base +
ACP_EXTERNAL_INTR_STAT);
acp6x_writel(0x00, acp_base + ACP_EXTERNAL_INTR_CNTL);
acp6x_writel(0x00, acp_base + ACP_EXTERNAL_INTR_ENB);
}
static int acp6x_init(void __iomem *acp_base)
{
int ret;
/* power on */
ret = acp6x_power_on(acp_base);
if (ret) {
pr_err("ACP power on failed\n");
return ret;
}
acp6x_writel(0x01, acp_base + ACP_CONTROL);
/* Reset */
ret = acp6x_reset(acp_base);
if (ret) {
pr_err("ACP reset failed\n");
return ret;
}
acp6x_writel(0x03, acp_base + ACP_CLKMUX_SEL);
acp6x_enable_interrupts(acp_base);
return 0;
}
static int acp6x_deinit(void __iomem *acp_base)
{
int ret;
acp6x_disable_interrupts(acp_base);
/* Reset */
ret = acp6x_reset(acp_base);
if (ret) {
pr_err("ACP reset failed\n");
return ret;
}
acp6x_writel(0x00, acp_base + ACP_CLKMUX_SEL);
acp6x_writel(0x00, acp_base + ACP_CONTROL);
return 0;
}
static irqreturn_t acp6x_irq_handler(int irq, void *dev_id)
{
struct acp6x_dev_data *adata;
struct pdm_dev_data *yc_pdm_data;
u32 val;
adata = dev_id;
if (!adata)
return IRQ_NONE;
val = acp6x_readl(adata->acp6x_base + ACP_EXTERNAL_INTR_STAT);
if (val & BIT(PDM_DMA_STAT)) {
yc_pdm_data = dev_get_drvdata(&adata->pdev[0]->dev);
acp6x_writel(BIT(PDM_DMA_STAT), adata->acp6x_base + ACP_EXTERNAL_INTR_STAT);
if (yc_pdm_data->capture_stream)
snd_pcm_period_elapsed(yc_pdm_data->capture_stream);
return IRQ_HANDLED;
}
return IRQ_NONE;
}
static int snd_acp6x_probe(struct pci_dev *pci,
const struct pci_device_id *pci_id)
{
struct acp6x_dev_data *adata;
struct platform_device_info pdevinfo[ACP6x_DEVS];
int ret, index;
int val = 0x00;
u32 addr;
unsigned int irqflags;
irqflags = IRQF_SHARED;
/* Yellow Carp device check */
if (pci->revision != 0x60)
return -ENODEV;
if (pci_enable_device(pci)) {
dev_err(&pci->dev, "pci_enable_device failed\n");
return -ENODEV;
}
ret = pci_request_regions(pci, "AMD ACP3x audio");
if (ret < 0) {
dev_err(&pci->dev, "pci_request_regions failed\n");
goto disable_pci;
}
adata = devm_kzalloc(&pci->dev, sizeof(struct acp6x_dev_data),
GFP_KERNEL);
if (!adata) {
ret = -ENOMEM;
goto release_regions;
}
addr = pci_resource_start(pci, 0);
adata->acp6x_base = devm_ioremap(&pci->dev, addr,
pci_resource_len(pci, 0));
if (!adata->acp6x_base) {
ret = -ENOMEM;
goto release_regions;
}
pci_set_master(pci);
pci_set_drvdata(pci, adata);
ret = acp6x_init(adata->acp6x_base);
if (ret)
goto release_regions;
val = acp6x_readl(adata->acp6x_base + ACP_PIN_CONFIG);
switch (val) {
case ACP_CONFIG_0:
case ACP_CONFIG_1:
case ACP_CONFIG_2:
case ACP_CONFIG_3:
case ACP_CONFIG_9:
case ACP_CONFIG_15:
dev_info(&pci->dev, "Audio Mode %d\n", val);
break;
default:
adata->res = devm_kzalloc(&pci->dev,
sizeof(struct resource),
GFP_KERNEL);
if (!adata->res) {
ret = -ENOMEM;
goto de_init;
}
adata->res->name = "acp_iomem";
adata->res->flags = IORESOURCE_MEM;
adata->res->start = addr;
adata->res->end = addr + (ACP6x_REG_END - ACP6x_REG_START);
adata->acp6x_audio_mode = ACP6x_PDM_MODE;
memset(&pdevinfo, 0, sizeof(pdevinfo));
pdevinfo[0].name = "acp_yc_pdm_dma";
pdevinfo[0].id = 0;
pdevinfo[0].parent = &pci->dev;
pdevinfo[0].num_res = 1;
pdevinfo[0].res = adata->res;
pdevinfo[1].name = "dmic-codec";
pdevinfo[1].id = 0;
pdevinfo[1].parent = &pci->dev;
pdevinfo[2].name = "acp_yc_mach";
pdevinfo[2].id = 0;
pdevinfo[2].parent = &pci->dev;
for (index = 0; index < ACP6x_DEVS; index++) {
adata->pdev[index] =
platform_device_register_full(&pdevinfo[index]);
if (IS_ERR(adata->pdev[index])) {
dev_err(&pci->dev, "cannot register %s device\n",
pdevinfo[index].name);
ret = PTR_ERR(adata->pdev[index]);
goto unregister_devs;
}
}
break;
}
ret = devm_request_irq(&pci->dev, pci->irq, acp6x_irq_handler,
irqflags, "ACP_PCI_IRQ", adata);
if (ret) {
dev_err(&pci->dev, "ACP PCI IRQ request failed\n");
goto unregister_devs;
}
pm_runtime_set_autosuspend_delay(&pci->dev, ACP_SUSPEND_DELAY_MS);
pm_runtime_use_autosuspend(&pci->dev);
pm_runtime_put_noidle(&pci->dev);
pm_runtime_allow(&pci->dev);
return 0;
unregister_devs:
for (--index; index >= 0; index--)
platform_device_unregister(adata->pdev[index]);
de_init:
if (acp6x_deinit(adata->acp6x_base))
dev_err(&pci->dev, "ACP de-init failed\n");
release_regions:
pci_release_regions(pci);
disable_pci:
pci_disable_device(pci);
return ret;
}
static int __maybe_unused snd_acp6x_suspend(struct device *dev)
{
struct acp6x_dev_data *adata;
int ret;
adata = dev_get_drvdata(dev);
ret = acp6x_deinit(adata->acp6x_base);
if (ret)
dev_err(dev, "ACP de-init failed\n");
return ret;
}
static int __maybe_unused snd_acp6x_resume(struct device *dev)
{
struct acp6x_dev_data *adata;
int ret;
adata = dev_get_drvdata(dev);
ret = acp6x_init(adata->acp6x_base);
if (ret)
dev_err(dev, "ACP init failed\n");
return ret;
}
static const struct dev_pm_ops acp6x_pm = {
SET_RUNTIME_PM_OPS(snd_acp6x_suspend, snd_acp6x_resume, NULL)
SET_SYSTEM_SLEEP_PM_OPS(snd_acp6x_suspend, snd_acp6x_resume)
};
static void snd_acp6x_remove(struct pci_dev *pci)
{
struct acp6x_dev_data *adata;
int ret, index;
adata = pci_get_drvdata(pci);
if (adata->acp6x_audio_mode == ACP6x_PDM_MODE) {
for (index = 0; index < ACP6x_DEVS; index++)
platform_device_unregister(adata->pdev[index]);
}
ret = acp6x_deinit(adata->acp6x_base);
if (ret)
dev_err(&pci->dev, "ACP de-init failed\n");
pm_runtime_forbid(&pci->dev);
pm_runtime_get_noresume(&pci->dev);
pci_release_regions(pci);
pci_disable_device(pci);
}
static const struct pci_device_id snd_acp6x_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, ACP_DEVICE_ID),
.class = PCI_CLASS_MULTIMEDIA_OTHER << 8,
.class_mask = 0xffffff },
{ 0, },
};
MODULE_DEVICE_TABLE(pci, snd_acp6x_ids);
static struct pci_driver yc_acp6x_driver = {
.name = KBUILD_MODNAME,
.id_table = snd_acp6x_ids,
.probe = snd_acp6x_probe,
.remove = snd_acp6x_remove,
.driver = {
.pm = &acp6x_pm,
}
};
module_pci_driver(yc_acp6x_driver);
MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
MODULE_DESCRIPTION("AMD ACP Yellow Carp PCI driver");
MODULE_LICENSE("GPL v2");
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