diff options
Diffstat (limited to 'snd-alpx/snd-alpx/alpx_core.c')
-rw-r--r-- | snd-alpx/snd-alpx/alpx_core.c | 901 |
1 files changed, 901 insertions, 0 deletions
diff --git a/snd-alpx/snd-alpx/alpx_core.c b/snd-alpx/snd-alpx/alpx_core.c new file mode 100644 index 0000000..0fb4d82 --- /dev/null +++ b/snd-alpx/snd-alpx/alpx_core.c @@ -0,0 +1,901 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* +* Support for Digigram AlpX PCI-e boards +* +* Copyright (c) 2024 Digigram Digital (info@digigram.com) +*/ + +#include "alpx.h" +#include "alpx_reg.h" +#include "alpx_mtd.h" +#include "alpx_led.h" +#include "alpx_controls.h" +#include "alpx_streams.h" +#include "alpx_version.h" +#include "alpx_variants_stereo.h" +#include "alpx_variants_stereo_apps_preFW283.h" +#include "alpx_variants_mc.h" +#include "alpx_variants_882_apps_preFW240.h" +#include "alpx_variants_dead.h" +#include "alpx_variants_madi.h" +#include "alpx_variants_dante.h" +#include "alpx_cards.h" +#include "alpx_xdma.h" +#include "snd_alpx_xdma.h" + + +#include <linux/version.h> +#include <linux/kmod.h> + +#if !defined (CONFIG_WITHOUT_GPIO) +#define ALPX_WITH_GPIO +#include <linux/gpio.h> +#include <linux/gpio/driver.h> +#include "alpx_gpio.h" +#define ALPX_GPIO_OPTION_STRING "" +#else +#warning !! GPIOs are DISABLED !! +#define ALPX_GPIO_OPTION_STRING "(without GPIO)" +#endif + + +#include <linux/mtd/partitions.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/platform_device.h> +#include <linux/delay.h> +#include <linux/version.h> +#include <sound/control.h> +#include <sound/core.h> +#include <sound/initval.h> +#include <sound/pcm.h> +#include <linux/string.h> + + +#if KERNEL_VERSION(6, 7, 0) > LINUX_VERSION_CODE +#include "core/generic/6.3/amd_xdma.h" +#include "include/6.3/amd_xdma.h" +#else +#include <linux/platform_data/amd_xdma.h> +#include <linux/dma/amd_xdma.h> +#endif + +#ifdef WITH_REG_DEBUG +#include <linux/device.h> +#endif + +/* Constants */ + +/* Structures */ + +/* Parameters */ + +#define ALPX_DEFAULT_CARD_NAME "Digigram AlpX" + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; +module_param_array(index, int, NULL, 0444); +MODULE_PARM_DESC(index, "Index value for " ALPX_DEFAULT_CARD_NAME " soundcard."); + +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; +module_param_array(id, charp, NULL, 0444); +MODULE_PARM_DESC(id, "ID string for " ALPX_DEFAULT_CARD_NAME " soundcard."); + +static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; +module_param_array(enable, bool, NULL, 0444); +MODULE_PARM_DESC(enable, "Enable " ALPX_DEFAULT_CARD_NAME " soundcard."); + +unsigned int log_transfers = 0; +module_param(log_transfers, uint, 0644); +MODULE_PARM_DESC(log_transfers, "0: no transfer logged(default), 1 : transfers logged"); + +static bool with_board_in_prod_mode = false; +module_param(with_board_in_prod_mode, bool, 0644); +MODULE_PARM_DESC(with_board_in_prod_mode, "0: board in USER mode(default) ; 1: board in PRODUCTION mode "); + +static bool with_board_in_dead_mode = false; +module_param(with_board_in_dead_mode, bool, 0644); +MODULE_PARM_DESC(with_board_in_dead_mode, "0: board in NORMAL mode(default) ; 1: board in DEAD mode "); + +static unsigned int dante_configured_fs = ALPDANTE_DEFAULT_FS; +module_param(dante_configured_fs, uint, 0644); +MODULE_PARM_DESC(dante_configured_fs, "Configured FS for Alp DANTE card (value in Hz, default: 48000)"); + +static bool dante_loopback_enabled = false; +module_param(dante_loopback_enabled, bool, 0644); +MODULE_PARM_DESC(dante_loopback_enabled, "Enable the Loopback for Alp DANTE card (default: FALSE)"); + +static bool alp222_pre_FW283_apps_support = false; +module_param(alp222_pre_FW283_apps_support, bool, 0644); +MODULE_PARM_DESC(alp222_pre_FW283_apps_support, "For ALp222e cards ONLY, Enable the compatibility mode for applications written for PRE V283 Firmwares (default: FALSE)"); + +static bool alp882_pre_FW240_apps_support = false; +module_param(alp882_pre_FW240_apps_support, bool, 0644); +MODULE_PARM_DESC(alp882_pre_FW240_apps_support, "For ALp882e cards ONLY, Enable the compatibility mode for applications written for PRE V240 Firmwares (default: FALSE)"); + + +/* Below, this is significant only if WITH_REG_DEBUG is defined */ +#ifdef WITH_REG_DEBUG +#warning BUILD with Registers Debug enabled + +static ssize_t reg_offset_show(struct device* dev, struct device_attribute* attr, char* buf ) +{ + + //HOWTO struct dev TO struct alpx_device : + struct snd_card* const card = dev_get_drvdata(dev); + struct alpx_device* const alpx_dev = card->private_data; + + dev_dbg( dev,"%s(): CALLED for card: 0x%p : REG OFFSET: 0x%08x\n", __func__, + alpx_dev, + alpx_dev->dbg_reg_offset); + + + return scnprintf(buf, PAGE_SIZE, "0x%08x\n", alpx_dev->dbg_reg_offset); +} + +static ssize_t reg_offset_store(struct device* dev, struct device_attribute* attr, const char* buf , size_t count) +{ + //HOWTO struct dev TO struct alpx_device : + struct snd_card* const card = dev_get_drvdata(dev); + struct alpx_device* const alpx_dev = card->private_data; + + unsigned int reg_offset = 0; + if (kstrtou32(buf, 0, ®_offset) < 0) + return -EINVAL; + + //Check offset range + if ((reg_offset < ALP222_MIN_REG_OFFSET) && + (reg_offset > ALP222_MAX_REG_OFFSET)) + return -EINVAL; + + alpx_dev->dbg_reg_offset = reg_offset; + dev_dbg( dev,"%s(): CALLED for alpx: %p, reg_offset: 0x%08x\n", __func__, alpx_dev, alpx_dev->dbg_reg_offset); + + return count; +} + +//static struct device_attribute dev_reg_addr = __ATTR_RW(reg_addr); +static struct device_attribute dev_reg_offset = __ATTR_RW(reg_offset); + +/******************************/ +static ssize_t reg_value_show(struct device* dev, struct device_attribute* attr, char* buf ) +{ + + //HOWTO struct dev TO struct alpx_device : + struct snd_card* const card = dev_get_drvdata(dev); + struct alpx_device* const alpx_dev = card->private_data; + /* Read the register once to cope with 'clear on read' registers */ + const uint32_t reg_value = readl(alpx_dev->base+ alpx_dev->dbg_reg_offset); + + dev_dbg( dev,"%s(): CALLED for %p, (dev:%p): [0x%08x] => 0x%08x\n", __func__, + alpx_dev, dev, + alpx_dev->dbg_reg_offset, + reg_value); + + return scnprintf(buf, PAGE_SIZE, "0x%08x\n", reg_value); +} + +static ssize_t reg_value_store(struct device* dev, struct device_attribute* attr, const char* buf , size_t count) +{ + //HOWTO struct dev TO struct alpx_device : + struct snd_card* const card = dev_get_drvdata(dev); + struct alpx_device* const alpx_dev = card->private_data; + + uint32_t reg_value = 0; + if (kstrtou32(buf, 0, ®_value) < 0) + return -EINVAL; + + + writel(reg_value, alpx_dev->base + alpx_dev->dbg_reg_offset); + + dev_dbg( dev,"%s(): CALLED for alpx: %p, [0x%08x] <= 0x%08x, Check:0x%08x\n", __func__, + alpx_dev, + alpx_dev->dbg_reg_offset, + reg_value, + readl(alpx_dev->base+ alpx_dev->dbg_reg_offset)); + + return count; +} + +static struct device_attribute dev_reg_value = __ATTR_RW(reg_value); + +#endif + +/******************************/ +static ssize_t serial_number_show(struct device* dev, struct device_attribute* attr, char* buf ) +{ + + struct snd_card* const card = dev_get_drvdata(dev); + struct alpx_device* alpx_dev = card->private_data; + + dev_dbg(alpx_dev->dev, " %s() : AFTER shift %llu / 0x%llx\n", __func__, alpx_dev->identity.serial_number, alpx_dev->identity.serial_number); + + return scnprintf(buf, PAGE_SIZE, "%llu\n", alpx_dev->identity.serial_number); +} + +static struct device_attribute dev_serial_number = __ATTR_RO(serial_number); +/***************************************/ + +static ssize_t ver_fpga_show(struct device* dev, struct device_attribute* attr, char* buf ) +{ + + struct snd_card* const card = dev_get_drvdata(dev); + struct alpx_device* alpx_dev = card->private_data; + + return scnprintf(buf, PAGE_SIZE, "%u\n", alpx_dev->identity.ver_fpga); +} + +static struct device_attribute dev_ver_fpga = __ATTR_RO(ver_fpga); + +/***************************************/ +static ssize_t ver_mcu_show(struct device* dev, struct device_attribute* attr, char* buf ) +{ + + struct snd_card* const card = dev_get_drvdata(dev); + struct alpx_device* alpx_dev = card->private_data; + + return scnprintf(buf, PAGE_SIZE, "%u.%lu\n", + ALPX_COMMON_VERSION_VERSION(alpx_dev->identity.ver_mcu), + ALPX_COMMON_VERSION_REVISION(alpx_dev->identity.ver_mcu) ); +} + +static struct device_attribute dev_ver_mcu = __ATTR_RO(ver_mcu); +/***************************************/ +static ssize_t dante_card_name_show(struct device* dev, struct device_attribute* attr, char* buf ) +{ + + struct snd_card* const card = dev_get_drvdata(dev); + struct alpx_device* alpx_dev = card->private_data; + + int result = alpdante_get_dante_name(alpx_dev, buf, ALPDANTE_NETWORK_NAME_LENGTH); + return (!result) ? strlcat(buf, "\n", PAGE_SIZE) : 0; +} + +static struct device_attribute dev_dante_card_name = __ATTR_RO(dante_card_name); +/***************************************/ + +/* PCI */ + +static void alpx_card_private_free(struct snd_card *card) +{ + struct alpx_device *alpx_dev = card->private_data; + +#if defined (ALPX_WITH_GPIO) + if (alpx_dev->variant->features & ALPX_VARIANT_FEATURE_GPIOS) + alpx_gpio_unregister(alpx_dev); +#endif + + if (alpx_dev->controls) + kfree(alpx_dev->controls); + + /* dirty crash avoid when dma fails and frees card before pipes are init. */ +} + +static inline unsigned int is_alp222_with_mic_option(struct alpx_device* alpx_dev) +{ + const u32 reg_mic = readl(alpx_dev->base + ALP222_CONTROL_BASE + ALP222_MIC_CONTROL_REG); + dev_dbg(alpx_dev->dev, "Daughter_Control[0x%08x:%x] = 0x%08x\n", + ALP222_CONTROL_BASE, + ALP222_MIC_CONTROL_REG, + reg_mic); + return reg_mic & (1<<ALP222_MIC_HERE_POS); +} + +static int alpx_core_set_pci_bus(struct alpx_device* alpx_dev) +{ + int ret = pci_enable_device(alpx_dev->pci_dev); + if (ret) { + dev_err(alpx_dev->dev, "failed to enable PCI device\n"); + return ret; + } + + /* Enable PCI relaxed ordering. */ + pcie_capability_set_word(alpx_dev->pci_dev, PCI_EXP_DEVCTL, + PCI_EXP_DEVCTL_RELAX_EN); + + /* Enable extended tagging. */ + pcie_capability_set_word(alpx_dev->pci_dev, PCI_EXP_DEVCTL, + PCI_EXP_DEVCTL_EXT_TAG); + + /* Set maximum memory read request. */ + pcie_set_readrq(alpx_dev->pci_dev, 512); + + pci_set_master(alpx_dev->pci_dev); + + return ret; +} + + +/****************************************************************/ +/* +* This function will HANDLE the XMA module dependency either from kernel or +* from the package according to the kernel's version. +* Return an error the kernel's module is in bad version (kernel <6.7) as no CYLLIC DMA +* was yet supported. +* MUST be CALLED after alpx_xdma_register() to get the xdma_dev +*/ +static int alpx_core_handle_xdma_dep (struct device *dev, struct platform_device* xdma_dev) +{ + + dev_info(dev, "Handling XDMA support for Linux : %d.\n", LINUX_VERSION_CODE); + +#if KERNEL_VERSION(6, 3, 0) > LINUX_VERSION_CODE + dev_info(dev, "OLD Kernel, USE snd-alpx-xdma.\n"); + //NO XDMA in the kernel Force the dependency to package's xdma module already done before call by + // SND_ALPX_XDMA_DEP(); + +#elif KERNEL_VERSION(6, 7, 0) <= LINUX_VERSION_CODE + #if IS_ENABLED(CONFIG_XILINX_XDMA) + dev_info(dev, "Kernel XDMA Ok, USE xdma.\n"); + //NO XDMA in the kernel Force the dependency to package's xdma module. + xdma_get_user_irq(xdma_dev, 256); //Dumb irq to for an error : no way + #else + dev_info(dev, "Kernel XDMA NOT enabled, USE snd-alpx-xdma.\n"); + //Dependency already handled by SND_ALPX_XDMA_DEP() call defined accordingly !! + #endif +#elif (KERNEL_VERSION(6, 3, 0) <= LINUX_VERSION_CODE) && \ + (KERNEL_VERSION(6, 7, 0) > LINUX_VERSION_CODE) + //FORCE snd-alpx-xdma use as kernel's one w/o cyclic support + #if IS_ENABLED(CONFIG_XILINX_XDMA) + #if IS_MODULE(CONFIG_XILINX_XDMA) + #warning "Xilinx XDMA module must be unloaded before use : NOT COMPLIANT !!" + #else + #error "Xilinx XDMA moule in Kernel is not COMPLIANT, Kernel must be Reconfigured/Rebuild without it !!!!" + #endif + #endif +#endif + return 0; +} +/****************************************************************************/ +static int alpx_probe(struct pci_dev *pci_dev, + const struct pci_device_id *pci_device_id) +{ + static int card_idx = 0; + struct device *dev = &pci_dev->dev; + struct alpx_device *alpx_dev; + struct snd_card *card; + struct snd_pcm *pcm; + int ret; + int is_update_required = 0; + + /* Module identity */ + + dev_info(dev,"version %s %s build date: %s, time: %s. %s, %s\n", + ALPX_MODULE_VERSION, + ALPX_GPIO_OPTION_STRING, + __DATE__ , + __TIME__, + SND_ALPX_XDMA_DEP(), + log_transfers ? "Transfers Logged":""); + + /* Ensure dependency on AlpX XDMA support if needed*/ + SND_ALPX_XDMA_DEP(); + + /* Card */ + + if (card_idx >= SNDRV_CARDS) + return -ENODEV; + + if (!enable[card_idx]) { + card_idx++; + return -ENOENT; + } + + dev_dbg(dev,"%s() : PCI Id : Vendor: 0x04%x, Device: 0x%04x, subvendor: 0x%04x, subdevice: 0x%04x\n", __func__, + pci_device_id->vendor, + pci_device_id->device, + pci_device_id->subvendor, + pci_device_id->subdevice); + + dev_dbg(dev,"%s() : PCI dev: Vendor: 0x04%x, Device: 0x%04x, subvendor: 0x%04x, subdevice: 0x%04x\n", __func__, + pci_dev->vendor, + pci_dev->device, + pci_dev->subsystem_vendor, + pci_dev->subsystem_device); + + + ret = snd_card_new(dev, index[card_idx], id[card_idx], THIS_MODULE, + sizeof(*alpx_dev), &card); + if (ret) { + dev_err(dev," snd_card_new() => %d\n", ret); + return ret; + } + + card->private_free = alpx_card_private_free; + + /* PCI */ + + pci_set_drvdata(pci_dev, card); + + alpx_dev = card->private_data; + alpx_dev->dev = dev; + alpx_dev->pci_dev = pci_dev; + + ret = alpx_core_set_pci_bus(alpx_dev); + if (ret){ + dev_err(dev," alpx_core_set_pci_bus(alpx_dev) => %d\n", ret); + goto error_card; + } + + //Map USER BAR now to get access to all card's resources + alpx_dev->base = pci_ioremap_bar(pci_dev, 0); + + /* PCI */ + + ret = pci_enable_device(pci_dev); + if (ret) { + dev_err(dev, "failed to enable PCI device\n"); + goto error_card; + } + + /* Enable PCI relaxed ordering. */ + pcie_capability_set_word(pci_dev, PCI_EXP_DEVCTL, + PCI_EXP_DEVCTL_RELAX_EN); + + /* Enable extended tagging. */ + pcie_capability_set_word(pci_dev, PCI_EXP_DEVCTL, + PCI_EXP_DEVCTL_EXT_TAG); + + /* Set maximum memory read request. */ + pcie_set_readrq(pci_dev, 512); + + pci_set_master(pci_dev); + + /* XDMA */ + + alpx_dev->xdma_pdev = alpx_xdma_register(pci_dev); + if (!alpx_dev->xdma_pdev) { + dev_err(dev," alpx_xdma_register(alpx_dev) => %d\n", ret); + goto disable_pci_dev; + } + + /* XDMA dependency handling*/ + ret = alpx_core_handle_xdma_dep(dev, alpx_dev->xdma_pdev); + if (ret) { + dev_err(dev, "Error %d when handling xdma dependency !\n", ret); + goto disable_pci_dev; + } + + /* Variant */ + + //Use PCI Id to select the actual variant : WARNING, MIC option is checked with user's registers !! + switch (pci_dev->subsystem_device){ + case ALPX_PCI_ID_SUBSYSTEM_ALP222: + /* HANDLE Alp222_MIC with LINE PCI Id : firmwares ante V272*/ + if (alp222_pre_FW283_apps_support) { + dev_warn(alpx_dev->dev, "!!! Alp2220 Pre FW283 Application compatibility mode ACTIVATED !!!"); + } + if (!is_alp222_with_mic_option(alpx_dev)) { + alpx_dev->variant = (!alp222_pre_FW283_apps_support) ? &alp222_variant : &alp222_app_preFW283_variant; + } + else { + alpx_dev->variant = (!alp222_pre_FW283_apps_support) ? &alp222_mic_variant : &alp222_app_preFW283_mic_variant; + alp222_mic_controls_default_config(alpx_dev); + } + break; + case ALPX_PCI_ID_SUBSYSTEM_ALP222_MIC: + if (alp222_pre_FW283_apps_support) { + dev_warn(alpx_dev->dev, "!!! Alp222 Pre FW283 Application compatibility mode ACTIVATED !!!"); + } + alpx_dev->variant = (!alp222_pre_FW283_apps_support) ? &alp222_mic_variant : &alp222_app_preFW283_mic_variant; + alp222_mic_controls_default_config(alpx_dev); + break; + case ALPX_PCI_ID_SUBSYSTEM_MADI: + alpx_dev->variant = &alpx_madi_variant; + break; + case ALPX_PCI_ID_SUBSYSTEM_ALP882: + if (alp882_pre_FW240_apps_support) + dev_warn(alpx_dev->dev, "!!! Alp882 LINE with Pre FW240 Application compatibility mode ACTIVATED !!!"); + alpx_dev->variant = (!alp882_pre_FW240_apps_support) ? &alp882_line_variant : &alp882_app_preFW240_line_variant; + break; + case ALPX_PCI_ID_SUBSYSTEM_ALP882_MIC: + if (alp882_pre_FW240_apps_support) + dev_warn(alpx_dev->dev, "!!! Alp882 MIC with Pre FW240 Application compatibility mode ACTIVATED !!!"); + alpx_dev->variant = (!alp882_pre_FW240_apps_support) ? &alp882_mic_variant : &alp882_app_preFW240_mic_variant; + break; + case ALPX_PCI_ID_SUBSYSTEM_ALP442: + alpx_dev->variant = &alp442_line_variant; + break; + case ALPX_PCI_ID_SUBSYSTEM_ALP442_MIC: + alpx_dev->variant = &alp442_mic_variant; + break; + case ALPX_PCI_ID_SUBSYSTEM_MADI_ALP_DEAD: + alpx_dev->variant = &alpx_dead_variant; + break; + case ALPX_PCI_ID_SUBSYSTEM_ALPDANTE: + alpx_dev->variant = &alpx_dante_variant; + break; + default: + dev_warn(alpx_dev->dev,"ALPX Driver: Model Id 0x%04x is not supported => DEAD by default Here !\n", + pci_dev->subsystem_device); + alpx_dev->variant = &alpx_dead_variant; + } + + /* FORCED DEAD mode, not for DANTE card */ + if (with_board_in_dead_mode) + { + /* Check DEAD mode for supported variants*/ + if (alpx_dev->variant->model != ALPX_VARIANT_MODEL_ALPDANTE) { + alpx_dev->variant = &alpx_dead_variant; + dev_warn(alpx_dev->dev, " !!! Board forced in DEAD mode !!\n"); + } + else { + dev_err(alpx_dev->dev, " NO DEAD mode supported for this board : %s\n", alpx_dev->variant->shortname); + ret = -EINVAL; + goto unregister_xdma; + } + } + + /* MTD */ + if (alpx_dev->variant->flash_partitions.qty != 0) { +// /* mtd Handle the PRODUCTION access or DEAD card, BUT not for DANTE cards*/ + with_board_in_prod_mode &= (alpx_dev->variant->model != ALPX_VARIANT_MODEL_ALPDANTE); + + if ( with_board_in_prod_mode || + (alpx_dev->variant->model == ALPX_VARIANT_DEAD)) { + dev_warn( alpx_dev->dev," Flash in PRODUCTION mode or DEAD card: full access !\n"); + ret = alpx_mtd_probe(alpx_dev, alpx_dev->variant->flash_partitions.partitions ,alpx_dev->variant->flash_partitions.qty); + } + else { + dev_dbg( alpx_dev->dev," Flash in USER mode: firmware update access only.\n"); + ret = alpx_mtd_probe(alpx_dev, + &alpx_dev->variant->flash_partitions.partitions[ALPX_FLASH_PARTITION_FW_ID] , + alpx_dev->variant->flash_partitions.qty_for_fw_update); + } + + if (ret) + goto unregister_xdma; + } + + if (alpx_dev->variant->model == ALPX_VARIANT_DEAD) { + dev_warn(alpx_dev->dev," !!!! DEAD card found : Flash in PROD mode and nothing else !!\n"); + return 0; + } + + /* finalize identity */ + switch (alpx_dev->variant->model) { + case ALPX_VARIANT_MODEL_ALP882: + case ALPX_VARIANT_MODEL_ALP882_MIC: + case ALPX_VARIANT_MODEL_ALP442: + case ALPX_VARIANT_MODEL_ALP442_MIC: + alpmultichan_finalize_identity(alpx_dev); + /* Check FW compatibility */ + if (alpx_dev->identity.ver_fpga < ALPMC_SUPPORTED_BASE_FW_VERSION) { + dev_warn(alpx_dev->dev, "UNSUPPORTED MultiChannels firmware version %d (supported started at %d), UPDATE required !!\n", + alpx_dev->identity.ver_fpga, + ALPMC_SUPPORTED_BASE_FW_VERSION); + is_update_required = 1; + } + + break; + case ALPX_VARIANT_MODEL_ALP222: + case ALPX_VARIANT_MODEL_ALP222_MIC: + alpstereo_finalize_identity(alpx_dev); + /* Check FW compatibility */ + if (alpx_dev->identity.ver_fpga < ALP222_SUPPORTED_BASE_FW_VERSION) { + dev_warn(alpx_dev->dev, "UNSUPPORTED Stereo firmware version %d (supported started at %d), UPDATE required !!\n", + alpx_dev->identity.ver_fpga, + ALP222_SUPPORTED_BASE_FW_VERSION); +// is_update_required = 1; + } + break; + case ALPX_VARIANT_MODEL_ALPDANTE: + alpdante_finalize_identity(alpx_dev); + /* Check FW compatibility */ + if (alpx_dev->identity.ver_fpga < ALPDANTE_SUPPORTED_BASE_FW_VERSION) { + dev_warn(alpx_dev->dev, "UNSUPPORTED AlpDANTE firmware version %d (supported started at %d), UPDATE required !!\n", + alpx_dev->identity.ver_fpga, + ALPDANTE_SUPPORTED_BASE_FW_VERSION); + is_update_required = 1; + } + break; + default: + dev_warn(alpx_dev->dev, " identity not finalized for this model : %d\n", alpx_dev->variant->model); + } + + + /* Remount MTD with SERIAL number added only in USER mod, to clearly identify the cards */ + if (!with_board_in_prod_mode ) { + + //Build a special partition table to add the serial number (A COPY) !! + struct mtd_partition fw_partitions_table[ALPX_FLASH_PARTITION_QTY]; + char part_names[ALPX_FLASH_PARTITION_QTY][128]; + unsigned int part_idx = 0; + + for (part_idx = 0 ; part_idx < alpx_dev->variant->flash_partitions.qty_for_fw_update ; ++part_idx) { + + fw_partitions_table[part_idx] = alpx_dev->variant->flash_partitions.partitions[ALPX_FLASH_PARTITION_FW_ID + part_idx]; + scnprintf(part_names[part_idx], PAGE_SIZE, "%s-%llu", + fw_partitions_table[part_idx].name, + alpx_dev->identity.serial_number); + + fw_partitions_table[part_idx].name = part_names[part_idx]; + } + + dev_dbg( alpx_dev->dev," Flash in USER mode: firmware partitionS with S/N appened.\n"); + /* Replace the current partitions */ + alpx_mtd_remove(alpx_dev); + ret = alpx_mtd_probe(alpx_dev, + fw_partitions_table, + alpx_dev->variant->flash_partitions.qty_for_fw_update); + } + + if (ret){ + dev_err(alpx_dev->dev," Error %d when re-creating Firmware partition\n", ret); + goto unregister_xdma; + } + + //Stop here if update required. + if (is_update_required == 1) + return 0; + + /* Information */ + + strcpy(card->driver, "Driver AlpX"); + + //Check if replaced by parameters, default : use the variant names + dev_dbg( alpx_dev->dev,"%s() : id[%d] = %s\n", __func__, + card_idx, + id[card_idx] == NULL ? "NULL" : id[card_idx]); + + if (id[card_idx]) + sprintf(card->shortname, id[card_idx]); + else + sprintf(card->shortname, alpx_dev->variant->shortname); + + strcpy(card->longname, alpx_dev->variant->longname); + strcpy(card->mixername, alpx_dev->variant->mixername); + + /* Pipes */ + + spin_lock_init(&alpx_dev->config.lock); + + alpx_pipe_init(&alpx_dev->playback, true); + alpx_pipe_init(&alpx_dev->capture, false); + + /* PCM */ + + ret = snd_pcm_new(card, card->shortname, 0, 1, 1, &pcm); + if (ret) { + dev_err(dev," snd_pcm_new(card) => %d\n", ret); + goto unregister_xdma; + } + + pcm->private_data = alpx_dev; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &alpx_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &alpx_capture_ops); + + pcm->info_flags = 0; + + strcpy(pcm->name, card->longname); + + /* Controls */ + + ret = alpx_controls_register(card); + if (ret) { + dev_err(dev," alpx_controls_register(card) => %d\n", ret); + goto unregister_xdma; + } + + /* Buffer */ + +#if KERNEL_VERSION(5, 5, 0) <= LINUX_VERSION_CODE + snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, dev, + alpx_dev->variant->playback_hw->buffer_bytes_max, + alpx_dev->variant->playback_hw->buffer_bytes_max); +#else + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, dev, + alpx_dev->variant->playback_hw->buffer_bytes_max, + alpx_dev->variant->playback_hw->buffer_bytes_max); +#endif +#if defined (ALPX_WITH_GPIO) + /* GPIO */ + + if (alpx_dev->variant->features & ALPX_VARIANT_FEATURE_GPIOS) + ret = alpx_gpio_register(alpx_dev, card->shortname); + + if (ret) { + dev_err(dev," alpx_gpio_register(alpx_dev) => %d\n", ret); + goto unregister_xdma; + } +#endif + /* Proc */ + ret = alpx_proc_probe(alpx_dev); + if (ret) { + dev_err(dev," alpx_proc_probe(alpx_dev) => %d\n", ret); + goto unregister_xdma; + } + + /* Card */ + + ret = snd_card_register(card); + if (ret){ + dev_err(dev," snd_card_register(card) => %d\n", ret); + goto error_proc; + } + + /* Sys FS files */ + /* Reg debug */ +#ifdef WITH_REG_DEBUG + dev_info(dev,"REG_DEBUG activated\n"); + alpx_dev->dbg_reg_offset = ALP222_MIN_REG_OFFSET; + + ret = device_create_file(dev, &dev_reg_offset); + if (ret) + { + dev_err(dev," device_create_file(addr) => %d\n", ret); + } + else + { + dev_err(dev," device_create_file(addr) : Ok\n"); + } + + ret = device_create_file(dev, &dev_reg_value); + if (ret) + { + dev_err(dev," device_create_file(value) => %d\n", ret); + } +#endif + + /* Serial number file */ + ret = device_create_file(dev, &dev_serial_number); + if (ret) + { + dev_err(dev," Serial Number device_create_file() => %d\n", ret); + } + + /* FPGA version file */ + ret = device_create_file(dev, &dev_ver_fpga); + if (ret) + { + dev_err(dev," FPGA Version device_create_file() => %d\n", ret); + } + + /* MCU version file */ + ret = device_create_file(dev, &dev_ver_mcu); + if (ret) + { + dev_err(dev," MCU Version device_create_file() => %d\n", ret); + } + + /* DANTE card's name */ + if (alpx_dev->variant->model == ALPX_VARIANT_MODEL_ALPDANTE) { + ret = device_create_file(dev, &dev_dante_card_name); + if (ret) + { + dev_err(dev," DANTE card's name device_create_file() => %d\n", ret); + } + } + + /* Production area replication from USER to GOLDEN if needed*/ + if (((alpx_dev->variant->model == ALPX_VARIANT_MODEL_ALP222) || + (alpx_dev->variant->model == ALPX_VARIANT_MODEL_ALP222_MIC)) && + alpx_mtd_is_available(alpx_dev) && + !alpx_mtd_is_golden_prod_area_valid(alpx_dev)) { + dev_info(alpx_dev->dev," Production area in GOLDEN must be INITIALIZED\n"); + ret = alpx_mtd_replicate_prod_area(alpx_dev); + if (!ret) { + dev_info(alpx_dev->dev," Production area in GOLDEN is INITIALIZED\n"); + } + else { + dev_warn(alpx_dev->dev," Production area in GOLDEN NOT CORRECTLY INITIALIZED !!\n"); + } + } + + switch (alpx_dev->variant->model){ + case ALPX_VARIANT_MODEL_ALP222: + case ALPX_VARIANT_MODEL_ALP222_MIC: + alpstereo_print_identity(alpx_dev, card, "Created"); + break; + case ALPX_VARIANT_MODEL_ALP882: + case ALPX_VARIANT_MODEL_ALP882_MIC: + case ALPX_VARIANT_MODEL_ALP442: + case ALPX_VARIANT_MODEL_ALP442_MIC: + alpmultichan_print_identity(alpx_dev, card, "Created"); + break; + case ALPX_VARIANT_MODEL_MADI: + case ALPX_VARIANT_MODEL_MADI_LOOPBACK: + alpmadi_print_identity(alpx_dev, card, "Created"); + break; + case ALPX_VARIANT_MODEL_ALPDANTE: + alpdante_print_identity(alpx_dev, card, "Created"); + alpdante_card_setup(alpx_dev, card, dante_configured_fs, dante_loopback_enabled); + break; + default: + dev_warn(alpx_dev->dev," !!! UNKNOW variant identity: %d !!!!\n", alpx_dev->variant->model); + }; + + card_idx++; + + return 0; + +error_proc: + dev_err(alpx_dev->dev," %s(): Error : 0x%x when creating proc entry\n", __func__, ret); + alpx_proc_remove(alpx_dev); +unregister_xdma: + alpx_xdma_unregister(pci_dev, alpx_dev->xdma_pdev); +disable_pci_dev: + pci_disable_device(pci_dev); +error_card: + dev_err(alpx_dev->dev," %s(): Error : 0x%x when creating the card\n", __func__, ret); + snd_card_free(card); + + return ret; +} + +static void alpx_remove(struct pci_dev *pci_dev) +{ + struct snd_card *card = pci_get_drvdata(pci_dev); + struct alpx_device *alpx_dev = card->private_data; + + switch (alpx_dev->variant->model){ + case ALPX_VARIANT_MODEL_ALP222: + case ALPX_VARIANT_MODEL_ALP222_MIC: + alpstereo_print_identity(alpx_dev, card, "Deleted"); + break; + case ALPX_VARIANT_MODEL_ALP882: + case ALPX_VARIANT_MODEL_ALP882_MIC: + case ALPX_VARIANT_MODEL_ALP442: + case ALPX_VARIANT_MODEL_ALP442_MIC: + if (card != NULL) { + alpmultichan_print_identity(alpx_dev, card, "Deleted"); + } + break; + case ALPX_VARIANT_MODEL_MADI: + case ALPX_VARIANT_MODEL_MADI_LOOPBACK: + alpmadi_print_identity(alpx_dev, card, "Deleted"); + break; + case ALPX_VARIANT_MODEL_ALPDANTE: + alpdante_print_identity(alpx_dev, card, "Deleted"); + break; + default: + dev_warn(alpx_dev->dev," !!! UNKNOW variant identity: %d !!!!\n", alpx_dev->variant->model); + } + + /* Reg debug */ +#ifdef WITH_REG_DEBUG + device_remove_file(alpx_dev->dev, &dev_reg_offset); + device_remove_file(alpx_dev->dev, &dev_reg_value); +#endif + device_remove_file(alpx_dev->dev, &dev_serial_number); + device_remove_file(alpx_dev->dev, &dev_ver_fpga); + device_remove_file(alpx_dev->dev, &dev_ver_mcu); + + if (alpx_dev->variant->model == ALPX_VARIANT_MODEL_ALPDANTE) { + device_remove_file(alpx_dev->dev, &dev_dante_card_name); + } + + alpx_mtd_remove(alpx_dev); + alpx_proc_remove(alpx_dev); + + if (alpx_dev->xdma_pdev != NULL) { + alpx_xdma_unregister(pci_dev, alpx_dev->xdma_pdev); + } + + if (card != NULL) { + snd_card_free(card); + } + + pci_disable_device(alpx_dev->pci_dev); +} + +static const struct pci_device_id alpx_pci_ids[] = { + { PCI_DEVICE(PCI_VENDOR_ID_DIGIGRAM, ALPX_PCI_ID_DEVICE) }, + { 0 } +}; + +MODULE_DEVICE_TABLE(pci, alpx_pci_ids); + +static struct pci_driver alpx_driver = { + .name = KBUILD_MODNAME, + .id_table = alpx_pci_ids, + .probe = alpx_probe, + .remove = alpx_remove, +}; + +module_pci_driver(alpx_driver); + +MODULE_DESCRIPTION("AlpX audio cards driver"); +MODULE_AUTHOR("Digigram Digital"); +MODULE_VERSION(ALPX_MODULE_VERSION); +MODULE_LICENSE("GPL"); |