18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Driver for Digigram VX222 V2/Mic PCI soundcards 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2002 by Takashi Iwai <tiwai@suse.de> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/init.h> 98c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 108c2ecf20Sopenharmony_ci#include <linux/pci.h> 118c2ecf20Sopenharmony_ci#include <linux/slab.h> 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <sound/core.h> 148c2ecf20Sopenharmony_ci#include <sound/initval.h> 158c2ecf20Sopenharmony_ci#include <sound/tlv.h> 168c2ecf20Sopenharmony_ci#include "vx222.h" 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#define CARD_NAME "VX222" 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ciMODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>"); 218c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Digigram VX222 V2/Mic"); 228c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 238c2ecf20Sopenharmony_ciMODULE_SUPPORTED_DEVICE("{{Digigram," CARD_NAME "}}"); 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_cistatic int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ 268c2ecf20Sopenharmony_cistatic char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ 278c2ecf20Sopenharmony_cistatic bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */ 288c2ecf20Sopenharmony_cistatic bool mic[SNDRV_CARDS]; /* microphone */ 298c2ecf20Sopenharmony_cistatic int ibl[SNDRV_CARDS]; /* microphone */ 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cimodule_param_array(index, int, NULL, 0444); 328c2ecf20Sopenharmony_ciMODULE_PARM_DESC(index, "Index value for Digigram " CARD_NAME " soundcard."); 338c2ecf20Sopenharmony_cimodule_param_array(id, charp, NULL, 0444); 348c2ecf20Sopenharmony_ciMODULE_PARM_DESC(id, "ID string for Digigram " CARD_NAME " soundcard."); 358c2ecf20Sopenharmony_cimodule_param_array(enable, bool, NULL, 0444); 368c2ecf20Sopenharmony_ciMODULE_PARM_DESC(enable, "Enable Digigram " CARD_NAME " soundcard."); 378c2ecf20Sopenharmony_cimodule_param_array(mic, bool, NULL, 0444); 388c2ecf20Sopenharmony_ciMODULE_PARM_DESC(mic, "Enable Microphone."); 398c2ecf20Sopenharmony_cimodule_param_array(ibl, int, NULL, 0444); 408c2ecf20Sopenharmony_ciMODULE_PARM_DESC(ibl, "Capture IBL size."); 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci/* 438c2ecf20Sopenharmony_ci */ 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cienum { 468c2ecf20Sopenharmony_ci VX_PCI_VX222_OLD, 478c2ecf20Sopenharmony_ci VX_PCI_VX222_NEW 488c2ecf20Sopenharmony_ci}; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistatic const struct pci_device_id snd_vx222_ids[] = { 518c2ecf20Sopenharmony_ci { 0x10b5, 0x9050, 0x1369, PCI_ANY_ID, 0, 0, VX_PCI_VX222_OLD, }, /* PLX */ 528c2ecf20Sopenharmony_ci { 0x10b5, 0x9030, 0x1369, PCI_ANY_ID, 0, 0, VX_PCI_VX222_NEW, }, /* PLX */ 538c2ecf20Sopenharmony_ci { 0, } 548c2ecf20Sopenharmony_ci}; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, snd_vx222_ids); 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci/* 608c2ecf20Sopenharmony_ci */ 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(db_scale_old_vol, -11350, 50, 0); 638c2ecf20Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(db_scale_akm, -7350, 50, 0); 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic const struct snd_vx_hardware vx222_old_hw = { 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci .name = "VX222/Old", 688c2ecf20Sopenharmony_ci .type = VX_TYPE_BOARD, 698c2ecf20Sopenharmony_ci /* hw specs */ 708c2ecf20Sopenharmony_ci .num_codecs = 1, 718c2ecf20Sopenharmony_ci .num_ins = 1, 728c2ecf20Sopenharmony_ci .num_outs = 1, 738c2ecf20Sopenharmony_ci .output_level_max = VX_ANALOG_OUT_LEVEL_MAX, 748c2ecf20Sopenharmony_ci .output_level_db_scale = db_scale_old_vol, 758c2ecf20Sopenharmony_ci}; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_cistatic const struct snd_vx_hardware vx222_v2_hw = { 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci .name = "VX222/v2", 808c2ecf20Sopenharmony_ci .type = VX_TYPE_V2, 818c2ecf20Sopenharmony_ci /* hw specs */ 828c2ecf20Sopenharmony_ci .num_codecs = 1, 838c2ecf20Sopenharmony_ci .num_ins = 1, 848c2ecf20Sopenharmony_ci .num_outs = 1, 858c2ecf20Sopenharmony_ci .output_level_max = VX2_AKM_LEVEL_MAX, 868c2ecf20Sopenharmony_ci .output_level_db_scale = db_scale_akm, 878c2ecf20Sopenharmony_ci}; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_cistatic const struct snd_vx_hardware vx222_mic_hw = { 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci .name = "VX222/Mic", 928c2ecf20Sopenharmony_ci .type = VX_TYPE_MIC, 938c2ecf20Sopenharmony_ci /* hw specs */ 948c2ecf20Sopenharmony_ci .num_codecs = 1, 958c2ecf20Sopenharmony_ci .num_ins = 1, 968c2ecf20Sopenharmony_ci .num_outs = 1, 978c2ecf20Sopenharmony_ci .output_level_max = VX2_AKM_LEVEL_MAX, 988c2ecf20Sopenharmony_ci .output_level_db_scale = db_scale_akm, 998c2ecf20Sopenharmony_ci}; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci/* 1038c2ecf20Sopenharmony_ci */ 1048c2ecf20Sopenharmony_cistatic int snd_vx222_free(struct vx_core *chip) 1058c2ecf20Sopenharmony_ci{ 1068c2ecf20Sopenharmony_ci struct snd_vx222 *vx = to_vx222(chip); 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci if (chip->irq >= 0) 1098c2ecf20Sopenharmony_ci free_irq(chip->irq, (void*)chip); 1108c2ecf20Sopenharmony_ci if (vx->port[0]) 1118c2ecf20Sopenharmony_ci pci_release_regions(vx->pci); 1128c2ecf20Sopenharmony_ci pci_disable_device(vx->pci); 1138c2ecf20Sopenharmony_ci kfree(chip); 1148c2ecf20Sopenharmony_ci return 0; 1158c2ecf20Sopenharmony_ci} 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_cistatic int snd_vx222_dev_free(struct snd_device *device) 1188c2ecf20Sopenharmony_ci{ 1198c2ecf20Sopenharmony_ci struct vx_core *chip = device->device_data; 1208c2ecf20Sopenharmony_ci return snd_vx222_free(chip); 1218c2ecf20Sopenharmony_ci} 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_cistatic int snd_vx222_create(struct snd_card *card, struct pci_dev *pci, 1258c2ecf20Sopenharmony_ci const struct snd_vx_hardware *hw, 1268c2ecf20Sopenharmony_ci struct snd_vx222 **rchip) 1278c2ecf20Sopenharmony_ci{ 1288c2ecf20Sopenharmony_ci struct vx_core *chip; 1298c2ecf20Sopenharmony_ci struct snd_vx222 *vx; 1308c2ecf20Sopenharmony_ci int i, err; 1318c2ecf20Sopenharmony_ci static const struct snd_device_ops ops = { 1328c2ecf20Sopenharmony_ci .dev_free = snd_vx222_dev_free, 1338c2ecf20Sopenharmony_ci }; 1348c2ecf20Sopenharmony_ci const struct snd_vx_ops *vx_ops; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci /* enable PCI device */ 1378c2ecf20Sopenharmony_ci if ((err = pci_enable_device(pci)) < 0) 1388c2ecf20Sopenharmony_ci return err; 1398c2ecf20Sopenharmony_ci pci_set_master(pci); 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci vx_ops = hw->type == VX_TYPE_BOARD ? &vx222_old_ops : &vx222_ops; 1428c2ecf20Sopenharmony_ci chip = snd_vx_create(card, hw, vx_ops, 1438c2ecf20Sopenharmony_ci sizeof(struct snd_vx222) - sizeof(struct vx_core)); 1448c2ecf20Sopenharmony_ci if (! chip) { 1458c2ecf20Sopenharmony_ci pci_disable_device(pci); 1468c2ecf20Sopenharmony_ci return -ENOMEM; 1478c2ecf20Sopenharmony_ci } 1488c2ecf20Sopenharmony_ci vx = to_vx222(chip); 1498c2ecf20Sopenharmony_ci vx->pci = pci; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci if ((err = pci_request_regions(pci, CARD_NAME)) < 0) { 1528c2ecf20Sopenharmony_ci snd_vx222_free(chip); 1538c2ecf20Sopenharmony_ci return err; 1548c2ecf20Sopenharmony_ci } 1558c2ecf20Sopenharmony_ci for (i = 0; i < 2; i++) 1568c2ecf20Sopenharmony_ci vx->port[i] = pci_resource_start(pci, i + 1); 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci if (request_threaded_irq(pci->irq, snd_vx_irq_handler, 1598c2ecf20Sopenharmony_ci snd_vx_threaded_irq_handler, IRQF_SHARED, 1608c2ecf20Sopenharmony_ci KBUILD_MODNAME, chip)) { 1618c2ecf20Sopenharmony_ci dev_err(card->dev, "unable to grab IRQ %d\n", pci->irq); 1628c2ecf20Sopenharmony_ci snd_vx222_free(chip); 1638c2ecf20Sopenharmony_ci return -EBUSY; 1648c2ecf20Sopenharmony_ci } 1658c2ecf20Sopenharmony_ci chip->irq = pci->irq; 1668c2ecf20Sopenharmony_ci card->sync_irq = chip->irq; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { 1698c2ecf20Sopenharmony_ci snd_vx222_free(chip); 1708c2ecf20Sopenharmony_ci return err; 1718c2ecf20Sopenharmony_ci } 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci *rchip = vx; 1748c2ecf20Sopenharmony_ci return 0; 1758c2ecf20Sopenharmony_ci} 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_cistatic int snd_vx222_probe(struct pci_dev *pci, 1798c2ecf20Sopenharmony_ci const struct pci_device_id *pci_id) 1808c2ecf20Sopenharmony_ci{ 1818c2ecf20Sopenharmony_ci static int dev; 1828c2ecf20Sopenharmony_ci struct snd_card *card; 1838c2ecf20Sopenharmony_ci const struct snd_vx_hardware *hw; 1848c2ecf20Sopenharmony_ci struct snd_vx222 *vx; 1858c2ecf20Sopenharmony_ci int err; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci if (dev >= SNDRV_CARDS) 1888c2ecf20Sopenharmony_ci return -ENODEV; 1898c2ecf20Sopenharmony_ci if (!enable[dev]) { 1908c2ecf20Sopenharmony_ci dev++; 1918c2ecf20Sopenharmony_ci return -ENOENT; 1928c2ecf20Sopenharmony_ci } 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci err = snd_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE, 1958c2ecf20Sopenharmony_ci 0, &card); 1968c2ecf20Sopenharmony_ci if (err < 0) 1978c2ecf20Sopenharmony_ci return err; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci switch ((int)pci_id->driver_data) { 2008c2ecf20Sopenharmony_ci case VX_PCI_VX222_OLD: 2018c2ecf20Sopenharmony_ci hw = &vx222_old_hw; 2028c2ecf20Sopenharmony_ci break; 2038c2ecf20Sopenharmony_ci case VX_PCI_VX222_NEW: 2048c2ecf20Sopenharmony_ci default: 2058c2ecf20Sopenharmony_ci if (mic[dev]) 2068c2ecf20Sopenharmony_ci hw = &vx222_mic_hw; 2078c2ecf20Sopenharmony_ci else 2088c2ecf20Sopenharmony_ci hw = &vx222_v2_hw; 2098c2ecf20Sopenharmony_ci break; 2108c2ecf20Sopenharmony_ci } 2118c2ecf20Sopenharmony_ci if ((err = snd_vx222_create(card, pci, hw, &vx)) < 0) { 2128c2ecf20Sopenharmony_ci snd_card_free(card); 2138c2ecf20Sopenharmony_ci return err; 2148c2ecf20Sopenharmony_ci } 2158c2ecf20Sopenharmony_ci card->private_data = vx; 2168c2ecf20Sopenharmony_ci vx->core.ibl.size = ibl[dev]; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci sprintf(card->longname, "%s at 0x%lx & 0x%lx, irq %i", 2198c2ecf20Sopenharmony_ci card->shortname, vx->port[0], vx->port[1], vx->core.irq); 2208c2ecf20Sopenharmony_ci dev_dbg(card->dev, "%s at 0x%lx & 0x%lx, irq %i\n", 2218c2ecf20Sopenharmony_ci card->shortname, vx->port[0], vx->port[1], vx->core.irq); 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci#ifdef SND_VX_FW_LOADER 2248c2ecf20Sopenharmony_ci vx->core.dev = &pci->dev; 2258c2ecf20Sopenharmony_ci#endif 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci if ((err = snd_vx_setup_firmware(&vx->core)) < 0) { 2288c2ecf20Sopenharmony_ci snd_card_free(card); 2298c2ecf20Sopenharmony_ci return err; 2308c2ecf20Sopenharmony_ci } 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci if ((err = snd_card_register(card)) < 0) { 2338c2ecf20Sopenharmony_ci snd_card_free(card); 2348c2ecf20Sopenharmony_ci return err; 2358c2ecf20Sopenharmony_ci } 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci pci_set_drvdata(pci, card); 2388c2ecf20Sopenharmony_ci dev++; 2398c2ecf20Sopenharmony_ci return 0; 2408c2ecf20Sopenharmony_ci} 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_cistatic void snd_vx222_remove(struct pci_dev *pci) 2438c2ecf20Sopenharmony_ci{ 2448c2ecf20Sopenharmony_ci snd_card_free(pci_get_drvdata(pci)); 2458c2ecf20Sopenharmony_ci} 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 2488c2ecf20Sopenharmony_cistatic int snd_vx222_suspend(struct device *dev) 2498c2ecf20Sopenharmony_ci{ 2508c2ecf20Sopenharmony_ci struct snd_card *card = dev_get_drvdata(dev); 2518c2ecf20Sopenharmony_ci struct snd_vx222 *vx = card->private_data; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci return snd_vx_suspend(&vx->core); 2548c2ecf20Sopenharmony_ci} 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_cistatic int snd_vx222_resume(struct device *dev) 2578c2ecf20Sopenharmony_ci{ 2588c2ecf20Sopenharmony_ci struct snd_card *card = dev_get_drvdata(dev); 2598c2ecf20Sopenharmony_ci struct snd_vx222 *vx = card->private_data; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci return snd_vx_resume(&vx->core); 2628c2ecf20Sopenharmony_ci} 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(snd_vx222_pm, snd_vx222_suspend, snd_vx222_resume); 2658c2ecf20Sopenharmony_ci#define SND_VX222_PM_OPS &snd_vx222_pm 2668c2ecf20Sopenharmony_ci#else 2678c2ecf20Sopenharmony_ci#define SND_VX222_PM_OPS NULL 2688c2ecf20Sopenharmony_ci#endif 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_cistatic struct pci_driver vx222_driver = { 2718c2ecf20Sopenharmony_ci .name = KBUILD_MODNAME, 2728c2ecf20Sopenharmony_ci .id_table = snd_vx222_ids, 2738c2ecf20Sopenharmony_ci .probe = snd_vx222_probe, 2748c2ecf20Sopenharmony_ci .remove = snd_vx222_remove, 2758c2ecf20Sopenharmony_ci .driver = { 2768c2ecf20Sopenharmony_ci .pm = SND_VX222_PM_OPS, 2778c2ecf20Sopenharmony_ci }, 2788c2ecf20Sopenharmony_ci}; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_cimodule_pci_driver(vx222_driver); 281