18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Linux driver for M2Tech hiFace compatible devices 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright 2012-2013 (C) M2TECH S.r.l and Amarula Solutions B.V. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Authors: Michael Trimarchi <michael@amarulasolutions.com> 88c2ecf20Sopenharmony_ci * Antonio Ospite <ao2@amarulasolutions.com> 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * The driver is based on the work done in TerraTec DMX 6Fire USB 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/module.h> 148c2ecf20Sopenharmony_ci#include <linux/slab.h> 158c2ecf20Sopenharmony_ci#include <sound/initval.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include "chip.h" 188c2ecf20Sopenharmony_ci#include "pcm.h" 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ciMODULE_AUTHOR("Michael Trimarchi <michael@amarulasolutions.com>"); 218c2ecf20Sopenharmony_ciMODULE_AUTHOR("Antonio Ospite <ao2@amarulasolutions.com>"); 228c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("M2Tech hiFace USB-SPDIF audio driver"); 238c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 248c2ecf20Sopenharmony_ciMODULE_SUPPORTED_DEVICE("{{M2Tech,Young}," 258c2ecf20Sopenharmony_ci "{M2Tech,hiFace}," 268c2ecf20Sopenharmony_ci "{M2Tech,North Star}," 278c2ecf20Sopenharmony_ci "{M2Tech,W4S Young}," 288c2ecf20Sopenharmony_ci "{M2Tech,Corrson}," 298c2ecf20Sopenharmony_ci "{M2Tech,AUDIA}," 308c2ecf20Sopenharmony_ci "{M2Tech,SL Audio}," 318c2ecf20Sopenharmony_ci "{M2Tech,Empirical}," 328c2ecf20Sopenharmony_ci "{M2Tech,Rockna}," 338c2ecf20Sopenharmony_ci "{M2Tech,Pathos}," 348c2ecf20Sopenharmony_ci "{M2Tech,Metronome}," 358c2ecf20Sopenharmony_ci "{M2Tech,CAD}," 368c2ecf20Sopenharmony_ci "{M2Tech,Audio Esclusive}," 378c2ecf20Sopenharmony_ci "{M2Tech,Rotel}," 388c2ecf20Sopenharmony_ci "{M2Tech,Eeaudio}," 398c2ecf20Sopenharmony_ci "{The Chord Company,CHORD}," 408c2ecf20Sopenharmony_ci "{AVA Group A/S,Vitus}}"); 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-max */ 438c2ecf20Sopenharmony_cistatic char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* Id for card */ 448c2ecf20Sopenharmony_cistatic bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */ 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci#define DRIVER_NAME "snd-usb-hiface" 478c2ecf20Sopenharmony_ci#define CARD_NAME "hiFace" 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cimodule_param_array(index, int, NULL, 0444); 508c2ecf20Sopenharmony_ciMODULE_PARM_DESC(index, "Index value for " CARD_NAME " soundcard."); 518c2ecf20Sopenharmony_cimodule_param_array(id, charp, NULL, 0444); 528c2ecf20Sopenharmony_ciMODULE_PARM_DESC(id, "ID string for " CARD_NAME " soundcard."); 538c2ecf20Sopenharmony_cimodule_param_array(enable, bool, NULL, 0444); 548c2ecf20Sopenharmony_ciMODULE_PARM_DESC(enable, "Enable " CARD_NAME " soundcard."); 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(register_mutex); 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_cistruct hiface_vendor_quirk { 598c2ecf20Sopenharmony_ci const char *device_name; 608c2ecf20Sopenharmony_ci u8 extra_freq; 618c2ecf20Sopenharmony_ci}; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_cistatic int hiface_chip_create(struct usb_interface *intf, 648c2ecf20Sopenharmony_ci struct usb_device *device, int idx, 658c2ecf20Sopenharmony_ci const struct hiface_vendor_quirk *quirk, 668c2ecf20Sopenharmony_ci struct hiface_chip **rchip) 678c2ecf20Sopenharmony_ci{ 688c2ecf20Sopenharmony_ci struct snd_card *card = NULL; 698c2ecf20Sopenharmony_ci struct hiface_chip *chip; 708c2ecf20Sopenharmony_ci int ret; 718c2ecf20Sopenharmony_ci int len; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci *rchip = NULL; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci /* if we are here, card can be registered in alsa. */ 768c2ecf20Sopenharmony_ci ret = snd_card_new(&intf->dev, index[idx], id[idx], THIS_MODULE, 778c2ecf20Sopenharmony_ci sizeof(*chip), &card); 788c2ecf20Sopenharmony_ci if (ret < 0) { 798c2ecf20Sopenharmony_ci dev_err(&device->dev, "cannot create alsa card.\n"); 808c2ecf20Sopenharmony_ci return ret; 818c2ecf20Sopenharmony_ci } 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci strlcpy(card->driver, DRIVER_NAME, sizeof(card->driver)); 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci if (quirk && quirk->device_name) 868c2ecf20Sopenharmony_ci strlcpy(card->shortname, quirk->device_name, sizeof(card->shortname)); 878c2ecf20Sopenharmony_ci else 888c2ecf20Sopenharmony_ci strlcpy(card->shortname, "M2Tech generic audio", sizeof(card->shortname)); 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci strlcat(card->longname, card->shortname, sizeof(card->longname)); 918c2ecf20Sopenharmony_ci len = strlcat(card->longname, " at ", sizeof(card->longname)); 928c2ecf20Sopenharmony_ci if (len < sizeof(card->longname)) 938c2ecf20Sopenharmony_ci usb_make_path(device, card->longname + len, 948c2ecf20Sopenharmony_ci sizeof(card->longname) - len); 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci chip = card->private_data; 978c2ecf20Sopenharmony_ci chip->dev = device; 988c2ecf20Sopenharmony_ci chip->card = card; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci *rchip = chip; 1018c2ecf20Sopenharmony_ci return 0; 1028c2ecf20Sopenharmony_ci} 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_cistatic int hiface_chip_probe(struct usb_interface *intf, 1058c2ecf20Sopenharmony_ci const struct usb_device_id *usb_id) 1068c2ecf20Sopenharmony_ci{ 1078c2ecf20Sopenharmony_ci const struct hiface_vendor_quirk *quirk = (struct hiface_vendor_quirk *)usb_id->driver_info; 1088c2ecf20Sopenharmony_ci int ret; 1098c2ecf20Sopenharmony_ci int i; 1108c2ecf20Sopenharmony_ci struct hiface_chip *chip; 1118c2ecf20Sopenharmony_ci struct usb_device *device = interface_to_usbdev(intf); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci ret = usb_set_interface(device, 0, 0); 1148c2ecf20Sopenharmony_ci if (ret != 0) { 1158c2ecf20Sopenharmony_ci dev_err(&device->dev, "can't set first interface for " CARD_NAME " device.\n"); 1168c2ecf20Sopenharmony_ci return -EIO; 1178c2ecf20Sopenharmony_ci } 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci /* check whether the card is already registered */ 1208c2ecf20Sopenharmony_ci chip = NULL; 1218c2ecf20Sopenharmony_ci mutex_lock(®ister_mutex); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci for (i = 0; i < SNDRV_CARDS; i++) 1248c2ecf20Sopenharmony_ci if (enable[i]) 1258c2ecf20Sopenharmony_ci break; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci if (i >= SNDRV_CARDS) { 1288c2ecf20Sopenharmony_ci dev_err(&device->dev, "no available " CARD_NAME " audio device\n"); 1298c2ecf20Sopenharmony_ci ret = -ENODEV; 1308c2ecf20Sopenharmony_ci goto err; 1318c2ecf20Sopenharmony_ci } 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci ret = hiface_chip_create(intf, device, i, quirk, &chip); 1348c2ecf20Sopenharmony_ci if (ret < 0) 1358c2ecf20Sopenharmony_ci goto err; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci ret = hiface_pcm_init(chip, quirk ? quirk->extra_freq : 0); 1388c2ecf20Sopenharmony_ci if (ret < 0) 1398c2ecf20Sopenharmony_ci goto err_chip_destroy; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci ret = snd_card_register(chip->card); 1428c2ecf20Sopenharmony_ci if (ret < 0) { 1438c2ecf20Sopenharmony_ci dev_err(&device->dev, "cannot register " CARD_NAME " card\n"); 1448c2ecf20Sopenharmony_ci goto err_chip_destroy; 1458c2ecf20Sopenharmony_ci } 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci mutex_unlock(®ister_mutex); 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci usb_set_intfdata(intf, chip); 1508c2ecf20Sopenharmony_ci return 0; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_cierr_chip_destroy: 1538c2ecf20Sopenharmony_ci snd_card_free(chip->card); 1548c2ecf20Sopenharmony_cierr: 1558c2ecf20Sopenharmony_ci mutex_unlock(®ister_mutex); 1568c2ecf20Sopenharmony_ci return ret; 1578c2ecf20Sopenharmony_ci} 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_cistatic void hiface_chip_disconnect(struct usb_interface *intf) 1608c2ecf20Sopenharmony_ci{ 1618c2ecf20Sopenharmony_ci struct hiface_chip *chip; 1628c2ecf20Sopenharmony_ci struct snd_card *card; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci chip = usb_get_intfdata(intf); 1658c2ecf20Sopenharmony_ci if (!chip) 1668c2ecf20Sopenharmony_ci return; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci card = chip->card; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci /* Make sure that the userspace cannot create new request */ 1718c2ecf20Sopenharmony_ci snd_card_disconnect(card); 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci hiface_pcm_abort(chip); 1748c2ecf20Sopenharmony_ci snd_card_free_when_closed(card); 1758c2ecf20Sopenharmony_ci} 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_cistatic const struct usb_device_id device_table[] = { 1788c2ecf20Sopenharmony_ci { 1798c2ecf20Sopenharmony_ci USB_DEVICE(0x04b4, 0x0384), 1808c2ecf20Sopenharmony_ci .driver_info = (unsigned long)&(const struct hiface_vendor_quirk) { 1818c2ecf20Sopenharmony_ci .device_name = "Young", 1828c2ecf20Sopenharmony_ci .extra_freq = 1, 1838c2ecf20Sopenharmony_ci } 1848c2ecf20Sopenharmony_ci }, 1858c2ecf20Sopenharmony_ci { 1868c2ecf20Sopenharmony_ci USB_DEVICE(0x04b4, 0x930b), 1878c2ecf20Sopenharmony_ci .driver_info = (unsigned long)&(const struct hiface_vendor_quirk) { 1888c2ecf20Sopenharmony_ci .device_name = "hiFace", 1898c2ecf20Sopenharmony_ci } 1908c2ecf20Sopenharmony_ci }, 1918c2ecf20Sopenharmony_ci { 1928c2ecf20Sopenharmony_ci USB_DEVICE(0x04b4, 0x931b), 1938c2ecf20Sopenharmony_ci .driver_info = (unsigned long)&(const struct hiface_vendor_quirk) { 1948c2ecf20Sopenharmony_ci .device_name = "North Star", 1958c2ecf20Sopenharmony_ci } 1968c2ecf20Sopenharmony_ci }, 1978c2ecf20Sopenharmony_ci { 1988c2ecf20Sopenharmony_ci USB_DEVICE(0x04b4, 0x931c), 1998c2ecf20Sopenharmony_ci .driver_info = (unsigned long)&(const struct hiface_vendor_quirk) { 2008c2ecf20Sopenharmony_ci .device_name = "W4S Young", 2018c2ecf20Sopenharmony_ci } 2028c2ecf20Sopenharmony_ci }, 2038c2ecf20Sopenharmony_ci { 2048c2ecf20Sopenharmony_ci USB_DEVICE(0x04b4, 0x931d), 2058c2ecf20Sopenharmony_ci .driver_info = (unsigned long)&(const struct hiface_vendor_quirk) { 2068c2ecf20Sopenharmony_ci .device_name = "Corrson", 2078c2ecf20Sopenharmony_ci } 2088c2ecf20Sopenharmony_ci }, 2098c2ecf20Sopenharmony_ci { 2108c2ecf20Sopenharmony_ci USB_DEVICE(0x04b4, 0x931e), 2118c2ecf20Sopenharmony_ci .driver_info = (unsigned long)&(const struct hiface_vendor_quirk) { 2128c2ecf20Sopenharmony_ci .device_name = "AUDIA", 2138c2ecf20Sopenharmony_ci } 2148c2ecf20Sopenharmony_ci }, 2158c2ecf20Sopenharmony_ci { 2168c2ecf20Sopenharmony_ci USB_DEVICE(0x04b4, 0x931f), 2178c2ecf20Sopenharmony_ci .driver_info = (unsigned long)&(const struct hiface_vendor_quirk) { 2188c2ecf20Sopenharmony_ci .device_name = "SL Audio", 2198c2ecf20Sopenharmony_ci } 2208c2ecf20Sopenharmony_ci }, 2218c2ecf20Sopenharmony_ci { 2228c2ecf20Sopenharmony_ci USB_DEVICE(0x04b4, 0x9320), 2238c2ecf20Sopenharmony_ci .driver_info = (unsigned long)&(const struct hiface_vendor_quirk) { 2248c2ecf20Sopenharmony_ci .device_name = "Empirical", 2258c2ecf20Sopenharmony_ci } 2268c2ecf20Sopenharmony_ci }, 2278c2ecf20Sopenharmony_ci { 2288c2ecf20Sopenharmony_ci USB_DEVICE(0x04b4, 0x9321), 2298c2ecf20Sopenharmony_ci .driver_info = (unsigned long)&(const struct hiface_vendor_quirk) { 2308c2ecf20Sopenharmony_ci .device_name = "Rockna", 2318c2ecf20Sopenharmony_ci } 2328c2ecf20Sopenharmony_ci }, 2338c2ecf20Sopenharmony_ci { 2348c2ecf20Sopenharmony_ci USB_DEVICE(0x249c, 0x9001), 2358c2ecf20Sopenharmony_ci .driver_info = (unsigned long)&(const struct hiface_vendor_quirk) { 2368c2ecf20Sopenharmony_ci .device_name = "Pathos", 2378c2ecf20Sopenharmony_ci } 2388c2ecf20Sopenharmony_ci }, 2398c2ecf20Sopenharmony_ci { 2408c2ecf20Sopenharmony_ci USB_DEVICE(0x249c, 0x9002), 2418c2ecf20Sopenharmony_ci .driver_info = (unsigned long)&(const struct hiface_vendor_quirk) { 2428c2ecf20Sopenharmony_ci .device_name = "Metronome", 2438c2ecf20Sopenharmony_ci } 2448c2ecf20Sopenharmony_ci }, 2458c2ecf20Sopenharmony_ci { 2468c2ecf20Sopenharmony_ci USB_DEVICE(0x249c, 0x9006), 2478c2ecf20Sopenharmony_ci .driver_info = (unsigned long)&(const struct hiface_vendor_quirk) { 2488c2ecf20Sopenharmony_ci .device_name = "CAD", 2498c2ecf20Sopenharmony_ci } 2508c2ecf20Sopenharmony_ci }, 2518c2ecf20Sopenharmony_ci { 2528c2ecf20Sopenharmony_ci USB_DEVICE(0x249c, 0x9008), 2538c2ecf20Sopenharmony_ci .driver_info = (unsigned long)&(const struct hiface_vendor_quirk) { 2548c2ecf20Sopenharmony_ci .device_name = "Audio Esclusive", 2558c2ecf20Sopenharmony_ci } 2568c2ecf20Sopenharmony_ci }, 2578c2ecf20Sopenharmony_ci { 2588c2ecf20Sopenharmony_ci USB_DEVICE(0x249c, 0x931c), 2598c2ecf20Sopenharmony_ci .driver_info = (unsigned long)&(const struct hiface_vendor_quirk) { 2608c2ecf20Sopenharmony_ci .device_name = "Rotel", 2618c2ecf20Sopenharmony_ci } 2628c2ecf20Sopenharmony_ci }, 2638c2ecf20Sopenharmony_ci { 2648c2ecf20Sopenharmony_ci USB_DEVICE(0x249c, 0x932c), 2658c2ecf20Sopenharmony_ci .driver_info = (unsigned long)&(const struct hiface_vendor_quirk) { 2668c2ecf20Sopenharmony_ci .device_name = "Eeaudio", 2678c2ecf20Sopenharmony_ci } 2688c2ecf20Sopenharmony_ci }, 2698c2ecf20Sopenharmony_ci { 2708c2ecf20Sopenharmony_ci USB_DEVICE(0x245f, 0x931c), 2718c2ecf20Sopenharmony_ci .driver_info = (unsigned long)&(const struct hiface_vendor_quirk) { 2728c2ecf20Sopenharmony_ci .device_name = "CHORD", 2738c2ecf20Sopenharmony_ci } 2748c2ecf20Sopenharmony_ci }, 2758c2ecf20Sopenharmony_ci { 2768c2ecf20Sopenharmony_ci USB_DEVICE(0x25c6, 0x9002), 2778c2ecf20Sopenharmony_ci .driver_info = (unsigned long)&(const struct hiface_vendor_quirk) { 2788c2ecf20Sopenharmony_ci .device_name = "Vitus", 2798c2ecf20Sopenharmony_ci } 2808c2ecf20Sopenharmony_ci }, 2818c2ecf20Sopenharmony_ci {} 2828c2ecf20Sopenharmony_ci}; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, device_table); 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_cistatic struct usb_driver hiface_usb_driver = { 2878c2ecf20Sopenharmony_ci .name = DRIVER_NAME, 2888c2ecf20Sopenharmony_ci .probe = hiface_chip_probe, 2898c2ecf20Sopenharmony_ci .disconnect = hiface_chip_disconnect, 2908c2ecf20Sopenharmony_ci .id_table = device_table, 2918c2ecf20Sopenharmony_ci}; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_cimodule_usb_driver(hiface_usb_driver); 294