// 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 #include #if !defined (CONFIG_WITHOUT_GPIO) #define ALPX_WITH_GPIO #include #include #include "alpx_gpio.h" #define ALPX_GPIO_OPTION_STRING "" #else #warning !! GPIOs are DISABLED !! #define ALPX_GPIO_OPTION_STRING "(without GPIO)" #endif #include #include #include #include #include #include #include #include #include #include #include #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 #include #endif #ifdef WITH_REG_DEBUG #include #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<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");