Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  pci-acp5x.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0+
//
// AMD Vangogh ACP PCI Driver
//
// Copyright (C) 2021, 2023 Advanced Micro Devices, Inc. All rights reserved.

#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 <linux/pm_runtime.h>

#include "acp5x.h"
#include "../mach-config.h"

struct acp5x_dev_data {
 void __iomem *acp5x_base;
 bool acp5x_audio_mode;
 struct resource *res;
 struct platform_device *pdev[ACP5x_DEVS];
};

static int acp5x_power_on(void __iomem *acp5x_base)
{
 u32 val;
 int timeout;

 val = acp_readl(acp5x_base + ACP_PGFSM_STATUS);

 if (val == 0)
  return val;

 if ((val & ACP_PGFSM_STATUS_MASK) !=
    ACP_POWER_ON_IN_PROGRESS)
  acp_writel(ACP_PGFSM_CNTL_POWER_ON_MASK,
      acp5x_base + ACP_PGFSM_CONTROL);
 timeout = 0;
 while (++timeout < 500) {
  val = acp_readl(acp5x_base + ACP_PGFSM_STATUS);
  if ((val & ACP_PGFSM_STATUS_MASK) == ACP_POWERED_ON)
   return 0;
  udelay(1);
 }
 return -ETIMEDOUT;
}

static int acp5x_reset(void __iomem *acp5x_base)
{
 u32 val;
 int timeout;

 acp_writel(1, acp5x_base + ACP_SOFT_RESET);
 timeout = 0;
 while (++timeout < 500) {
  val = acp_readl(acp5x_base + ACP_SOFT_RESET);
  if (val & ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK)
   break;
  cpu_relax();
 }
 acp_writel(0, acp5x_base + ACP_SOFT_RESET);
 timeout = 0;
 while (++timeout < 500) {
  val = acp_readl(acp5x_base + ACP_SOFT_RESET);
  if (!val)
   return 0;
  cpu_relax();
 }
 return -ETIMEDOUT;
}

static void acp5x_enable_interrupts(void __iomem *acp5x_base)
{
 acp_writel(0x01, acp5x_base + ACP_EXTERNAL_INTR_ENB);
}

static void acp5x_disable_interrupts(void __iomem *acp5x_base)
{
 acp_writel(ACP_EXT_INTR_STAT_CLEAR_MASK, acp5x_base +
     ACP_EXTERNAL_INTR_STAT);
 acp_writel(0x00, acp5x_base + ACP_EXTERNAL_INTR_CNTL);
 acp_writel(0x00, acp5x_base + ACP_EXTERNAL_INTR_ENB);
}

static int acp5x_init(void __iomem *acp5x_base)
{
 int ret;

 /* power on */
 ret = acp5x_power_on(acp5x_base);
 if (ret) {
  pr_err("ACP5x power on failed\n");
  return ret;
 }
 acp_writel(0x01, acp5x_base + ACP_CONTROL);
 /* Reset */
 ret = acp5x_reset(acp5x_base);
 if (ret) {
  pr_err("ACP5x reset failed\n");
  return ret;
 }
 acp_writel(0x03, acp5x_base + ACP_CLKMUX_SEL);
 acp5x_enable_interrupts(acp5x_base);
 return 0;
}

static int acp5x_deinit(void __iomem *acp5x_base)
{
 int ret;

 acp5x_disable_interrupts(acp5x_base);
 /* Reset */
 ret = acp5x_reset(acp5x_base);
 if (ret) {
  pr_err("ACP5x reset failed\n");
  return ret;
 }
 acp_writel(0x00, acp5x_base + ACP_CLKMUX_SEL);
 acp_writel(0x00, acp5x_base + ACP_CONTROL);
 return 0;
}

static int snd_acp5x_probe(struct pci_dev *pci,
      const struct pci_device_id *pci_id)
{
 struct acp5x_dev_data *adata;
 struct platform_device_info pdevinfo[ACP5x_DEVS];
 unsigned int irqflags, flag;
 int ret, i;
 u32 addr, val;

 /*
 * Return if ACP config flag is defined, except when board
 * supports SOF while it is not being enabled in kernel config.
 */

 flag = snd_amd_acp_find_config(pci);
 if (flag != FLAG_AMD_LEGACY &&
     (flag != FLAG_AMD_SOF || IS_ENABLED(CONFIG_SND_SOC_SOF_AMD_VANGOGH)))
  return -ENODEV;

 irqflags = IRQF_SHARED;
 if (pci->revision != 0x50)
  return -ENODEV;

 if (pci_enable_device(pci)) {
  dev_err(&pci->dev, "pci_enable_device failed\n");
  return -ENODEV;
 }

 ret = pci_request_regions(pci, "AMD ACP5x audio");
 if (ret < 0) {
  dev_err(&pci->dev, "pci_request_regions failed\n");
  goto disable_pci;
 }

 adata = devm_kzalloc(&pci->dev, sizeof(struct acp5x_dev_data),
        GFP_KERNEL);
 if (!adata) {
  ret = -ENOMEM;
  goto release_regions;
 }
 addr = pci_resource_start(pci, 0);
 adata->acp5x_base = devm_ioremap(&pci->dev, addr,
      pci_resource_len(pci, 0));
 if (!adata->acp5x_base) {
  ret = -ENOMEM;
  goto release_regions;
 }
 pci_set_master(pci);
 pci_set_drvdata(pci, adata);
 ret = acp5x_init(adata->acp5x_base);
 if (ret)
  goto release_regions;

 val = acp_readl(adata->acp5x_base + ACP_PIN_CONFIG);
 switch (val) {
 case I2S_MODE:
  adata->res = devm_kzalloc(&pci->dev,
       sizeof(struct resource) * ACP5x_RES,
       GFP_KERNEL);
  if (!adata->res) {
   ret = -ENOMEM;
   goto de_init;
  }

  adata->res[0].name = "acp5x_i2s_iomem";
  adata->res[0].flags = IORESOURCE_MEM;
  adata->res[0].start = addr;
  adata->res[0].end = addr + (ACP5x_REG_END - ACP5x_REG_START);

  adata->res[1].name = "acp5x_i2s_sp";
  adata->res[1].flags = IORESOURCE_MEM;
  adata->res[1].start = addr + ACP5x_I2STDM_REG_START;
  adata->res[1].end = addr + ACP5x_I2STDM_REG_END;

  adata->res[2].name = "acp5x_i2s_hs";
  adata->res[2].flags = IORESOURCE_MEM;
  adata->res[2].start = addr + ACP5x_HS_TDM_REG_START;
  adata->res[2].end = addr + ACP5x_HS_TDM_REG_END;

  adata->res[3].name = "acp5x_i2s_irq";
  adata->res[3].flags = IORESOURCE_IRQ;
  adata->res[3].start = pci->irq;
  adata->res[3].end = adata->res[3].start;

  adata->acp5x_audio_mode = ACP5x_I2S_MODE;

  memset(&pdevinfo, 0, sizeof(pdevinfo));
  pdevinfo[0].name = "acp5x_i2s_dma";
  pdevinfo[0].id = 0;
  pdevinfo[0].parent = &pci->dev;
  pdevinfo[0].num_res = 4;
  pdevinfo[0].res = &adata->res[0];
  pdevinfo[0].data = &irqflags;
  pdevinfo[0].size_data = sizeof(irqflags);

  pdevinfo[1].name = "acp5x_i2s_playcap";
  pdevinfo[1].id = 0;
  pdevinfo[1].parent = &pci->dev;
  pdevinfo[1].num_res = 1;
  pdevinfo[1].res = &adata->res[1];

  pdevinfo[2].name = "acp5x_i2s_playcap";
  pdevinfo[2].id = 1;
  pdevinfo[2].parent = &pci->dev;
  pdevinfo[2].num_res = 1;
  pdevinfo[2].res = &adata->res[2];

  pdevinfo[3].name = "acp5x_mach";
  pdevinfo[3].id = 0;
  pdevinfo[3].parent = &pci->dev;
  for (i = 0; i < ACP5x_DEVS; i++) {
   adata->pdev[i] =
    platform_device_register_full(&pdevinfo[i]);
   if (IS_ERR(adata->pdev[i])) {
    dev_err(&pci->dev, "cannot register %s device\n",
     pdevinfo[i].name);
    ret = PTR_ERR(adata->pdev[i]);
    goto unregister_devs;
   }
  }
  break;
 default:
  dev_info(&pci->dev, "ACP audio mode : %d\n", val);
 }
 pm_runtime_set_autosuspend_delay(&pci->dev, 2000);
 pm_runtime_use_autosuspend(&pci->dev);
 pm_runtime_put_noidle(&pci->dev);
 pm_runtime_allow(&pci->dev);
 return 0;

unregister_devs:
 for (--i; i >= 0; i--)
  platform_device_unregister(adata->pdev[i]);
de_init:
 if (acp5x_deinit(adata->acp5x_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 snd_acp5x_suspend(struct device *dev)
{
 int ret;
 struct acp5x_dev_data *adata;

 adata = dev_get_drvdata(dev);
 ret = acp5x_deinit(adata->acp5x_base);
 if (ret)
  dev_err(dev, "ACP de-init failed\n");
 else
  dev_dbg(dev, "ACP de-initialized\n");

 return ret;
}

static int snd_acp5x_resume(struct device *dev)
{
 int ret;
 struct acp5x_dev_data *adata;

 adata = dev_get_drvdata(dev);
 ret = acp5x_init(adata->acp5x_base);
 if (ret) {
  dev_err(dev, "ACP init failed\n");
  return ret;
 }
 return 0;
}

static const struct dev_pm_ops acp5x_pm = {
 RUNTIME_PM_OPS(snd_acp5x_suspend, snd_acp5x_resume, NULL)
 SYSTEM_SLEEP_PM_OPS(snd_acp5x_suspend, snd_acp5x_resume)
};

static void snd_acp5x_remove(struct pci_dev *pci)
{
 struct acp5x_dev_data *adata;
 int i, ret;

 adata = pci_get_drvdata(pci);
 if (adata->acp5x_audio_mode == ACP5x_I2S_MODE) {
  for (i = 0; i < ACP5x_DEVS; i++)
   platform_device_unregister(adata->pdev[i]);
 }
 ret = acp5x_deinit(adata->acp5x_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_acp5x_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_acp5x_ids);

static struct pci_driver acp5x_driver  = {
 .name = KBUILD_MODNAME,
 .id_table = snd_acp5x_ids,
 .probe = snd_acp5x_probe,
 .remove = snd_acp5x_remove,
 .driver = {
  .pm = pm_ptr(&acp5x_pm),
 }
};

module_pci_driver(acp5x_driver);

MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
MODULE_DESCRIPTION("AMD Vangogh ACP PCI driver");
MODULE_LICENSE("GPL v2");

Messung V0.5
C=93 H=95 G=93

¤ Dauer der Verarbeitung: 0.22 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge