mirror of
https://github.com/torvalds/linux.git
synced 2024-12-29 14:21:47 +00:00
2874c5fd28
Based on 1 normalized pattern(s): this program is free software you can redistribute it and or modify it under the terms of the gnu general public license as published by the free software foundation either version 2 of the license or at your option any later version extracted by the scancode license scanner the SPDX license identifier GPL-2.0-or-later has been chosen to replace the boilerplate/reference in 3029 file(s). Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Reviewed-by: Allison Randal <allison@lohutok.net> Cc: linux-spdx@vger.kernel.org Link: https://lkml.kernel.org/r/20190527070032.746973796@linutronix.de Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
294 lines
7.1 KiB
C
294 lines
7.1 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/*
|
|
* Linux driver for M2Tech hiFace compatible devices
|
|
*
|
|
* Copyright 2012-2013 (C) M2TECH S.r.l and Amarula Solutions B.V.
|
|
*
|
|
* Authors: Michael Trimarchi <michael@amarulasolutions.com>
|
|
* Antonio Ospite <ao2@amarulasolutions.com>
|
|
*
|
|
* The driver is based on the work done in TerraTec DMX 6Fire USB
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/slab.h>
|
|
#include <sound/initval.h>
|
|
|
|
#include "chip.h"
|
|
#include "pcm.h"
|
|
|
|
MODULE_AUTHOR("Michael Trimarchi <michael@amarulasolutions.com>");
|
|
MODULE_AUTHOR("Antonio Ospite <ao2@amarulasolutions.com>");
|
|
MODULE_DESCRIPTION("M2Tech hiFace USB-SPDIF audio driver");
|
|
MODULE_LICENSE("GPL v2");
|
|
MODULE_SUPPORTED_DEVICE("{{M2Tech,Young},"
|
|
"{M2Tech,hiFace},"
|
|
"{M2Tech,North Star},"
|
|
"{M2Tech,W4S Young},"
|
|
"{M2Tech,Corrson},"
|
|
"{M2Tech,AUDIA},"
|
|
"{M2Tech,SL Audio},"
|
|
"{M2Tech,Empirical},"
|
|
"{M2Tech,Rockna},"
|
|
"{M2Tech,Pathos},"
|
|
"{M2Tech,Metronome},"
|
|
"{M2Tech,CAD},"
|
|
"{M2Tech,Audio Esclusive},"
|
|
"{M2Tech,Rotel},"
|
|
"{M2Tech,Eeaudio},"
|
|
"{The Chord Company,CHORD},"
|
|
"{AVA Group A/S,Vitus}}");
|
|
|
|
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-max */
|
|
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* Id for card */
|
|
static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
|
|
|
|
#define DRIVER_NAME "snd-usb-hiface"
|
|
#define CARD_NAME "hiFace"
|
|
|
|
module_param_array(index, int, NULL, 0444);
|
|
MODULE_PARM_DESC(index, "Index value for " CARD_NAME " soundcard.");
|
|
module_param_array(id, charp, NULL, 0444);
|
|
MODULE_PARM_DESC(id, "ID string for " CARD_NAME " soundcard.");
|
|
module_param_array(enable, bool, NULL, 0444);
|
|
MODULE_PARM_DESC(enable, "Enable " CARD_NAME " soundcard.");
|
|
|
|
static DEFINE_MUTEX(register_mutex);
|
|
|
|
struct hiface_vendor_quirk {
|
|
const char *device_name;
|
|
u8 extra_freq;
|
|
};
|
|
|
|
static int hiface_chip_create(struct usb_interface *intf,
|
|
struct usb_device *device, int idx,
|
|
const struct hiface_vendor_quirk *quirk,
|
|
struct hiface_chip **rchip)
|
|
{
|
|
struct snd_card *card = NULL;
|
|
struct hiface_chip *chip;
|
|
int ret;
|
|
int len;
|
|
|
|
*rchip = NULL;
|
|
|
|
/* if we are here, card can be registered in alsa. */
|
|
ret = snd_card_new(&intf->dev, index[idx], id[idx], THIS_MODULE,
|
|
sizeof(*chip), &card);
|
|
if (ret < 0) {
|
|
dev_err(&device->dev, "cannot create alsa card.\n");
|
|
return ret;
|
|
}
|
|
|
|
strlcpy(card->driver, DRIVER_NAME, sizeof(card->driver));
|
|
|
|
if (quirk && quirk->device_name)
|
|
strlcpy(card->shortname, quirk->device_name, sizeof(card->shortname));
|
|
else
|
|
strlcpy(card->shortname, "M2Tech generic audio", sizeof(card->shortname));
|
|
|
|
strlcat(card->longname, card->shortname, sizeof(card->longname));
|
|
len = strlcat(card->longname, " at ", sizeof(card->longname));
|
|
if (len < sizeof(card->longname))
|
|
usb_make_path(device, card->longname + len,
|
|
sizeof(card->longname) - len);
|
|
|
|
chip = card->private_data;
|
|
chip->dev = device;
|
|
chip->card = card;
|
|
|
|
*rchip = chip;
|
|
return 0;
|
|
}
|
|
|
|
static int hiface_chip_probe(struct usb_interface *intf,
|
|
const struct usb_device_id *usb_id)
|
|
{
|
|
const struct hiface_vendor_quirk *quirk = (struct hiface_vendor_quirk *)usb_id->driver_info;
|
|
int ret;
|
|
int i;
|
|
struct hiface_chip *chip;
|
|
struct usb_device *device = interface_to_usbdev(intf);
|
|
|
|
ret = usb_set_interface(device, 0, 0);
|
|
if (ret != 0) {
|
|
dev_err(&device->dev, "can't set first interface for " CARD_NAME " device.\n");
|
|
return -EIO;
|
|
}
|
|
|
|
/* check whether the card is already registered */
|
|
chip = NULL;
|
|
mutex_lock(®ister_mutex);
|
|
|
|
for (i = 0; i < SNDRV_CARDS; i++)
|
|
if (enable[i])
|
|
break;
|
|
|
|
if (i >= SNDRV_CARDS) {
|
|
dev_err(&device->dev, "no available " CARD_NAME " audio device\n");
|
|
ret = -ENODEV;
|
|
goto err;
|
|
}
|
|
|
|
ret = hiface_chip_create(intf, device, i, quirk, &chip);
|
|
if (ret < 0)
|
|
goto err;
|
|
|
|
ret = hiface_pcm_init(chip, quirk ? quirk->extra_freq : 0);
|
|
if (ret < 0)
|
|
goto err_chip_destroy;
|
|
|
|
ret = snd_card_register(chip->card);
|
|
if (ret < 0) {
|
|
dev_err(&device->dev, "cannot register " CARD_NAME " card\n");
|
|
goto err_chip_destroy;
|
|
}
|
|
|
|
mutex_unlock(®ister_mutex);
|
|
|
|
usb_set_intfdata(intf, chip);
|
|
return 0;
|
|
|
|
err_chip_destroy:
|
|
snd_card_free(chip->card);
|
|
err:
|
|
mutex_unlock(®ister_mutex);
|
|
return ret;
|
|
}
|
|
|
|
static void hiface_chip_disconnect(struct usb_interface *intf)
|
|
{
|
|
struct hiface_chip *chip;
|
|
struct snd_card *card;
|
|
|
|
chip = usb_get_intfdata(intf);
|
|
if (!chip)
|
|
return;
|
|
|
|
card = chip->card;
|
|
|
|
/* Make sure that the userspace cannot create new request */
|
|
snd_card_disconnect(card);
|
|
|
|
hiface_pcm_abort(chip);
|
|
snd_card_free_when_closed(card);
|
|
}
|
|
|
|
static const struct usb_device_id device_table[] = {
|
|
{
|
|
USB_DEVICE(0x04b4, 0x0384),
|
|
.driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
|
|
.device_name = "Young",
|
|
.extra_freq = 1,
|
|
}
|
|
},
|
|
{
|
|
USB_DEVICE(0x04b4, 0x930b),
|
|
.driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
|
|
.device_name = "hiFace",
|
|
}
|
|
},
|
|
{
|
|
USB_DEVICE(0x04b4, 0x931b),
|
|
.driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
|
|
.device_name = "North Star",
|
|
}
|
|
},
|
|
{
|
|
USB_DEVICE(0x04b4, 0x931c),
|
|
.driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
|
|
.device_name = "W4S Young",
|
|
}
|
|
},
|
|
{
|
|
USB_DEVICE(0x04b4, 0x931d),
|
|
.driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
|
|
.device_name = "Corrson",
|
|
}
|
|
},
|
|
{
|
|
USB_DEVICE(0x04b4, 0x931e),
|
|
.driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
|
|
.device_name = "AUDIA",
|
|
}
|
|
},
|
|
{
|
|
USB_DEVICE(0x04b4, 0x931f),
|
|
.driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
|
|
.device_name = "SL Audio",
|
|
}
|
|
},
|
|
{
|
|
USB_DEVICE(0x04b4, 0x9320),
|
|
.driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
|
|
.device_name = "Empirical",
|
|
}
|
|
},
|
|
{
|
|
USB_DEVICE(0x04b4, 0x9321),
|
|
.driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
|
|
.device_name = "Rockna",
|
|
}
|
|
},
|
|
{
|
|
USB_DEVICE(0x249c, 0x9001),
|
|
.driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
|
|
.device_name = "Pathos",
|
|
}
|
|
},
|
|
{
|
|
USB_DEVICE(0x249c, 0x9002),
|
|
.driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
|
|
.device_name = "Metronome",
|
|
}
|
|
},
|
|
{
|
|
USB_DEVICE(0x249c, 0x9006),
|
|
.driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
|
|
.device_name = "CAD",
|
|
}
|
|
},
|
|
{
|
|
USB_DEVICE(0x249c, 0x9008),
|
|
.driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
|
|
.device_name = "Audio Esclusive",
|
|
}
|
|
},
|
|
{
|
|
USB_DEVICE(0x249c, 0x931c),
|
|
.driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
|
|
.device_name = "Rotel",
|
|
}
|
|
},
|
|
{
|
|
USB_DEVICE(0x249c, 0x932c),
|
|
.driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
|
|
.device_name = "Eeaudio",
|
|
}
|
|
},
|
|
{
|
|
USB_DEVICE(0x245f, 0x931c),
|
|
.driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
|
|
.device_name = "CHORD",
|
|
}
|
|
},
|
|
{
|
|
USB_DEVICE(0x25c6, 0x9002),
|
|
.driver_info = (unsigned long)&(const struct hiface_vendor_quirk) {
|
|
.device_name = "Vitus",
|
|
}
|
|
},
|
|
{}
|
|
};
|
|
|
|
MODULE_DEVICE_TABLE(usb, device_table);
|
|
|
|
static struct usb_driver hiface_usb_driver = {
|
|
.name = DRIVER_NAME,
|
|
.probe = hiface_chip_probe,
|
|
.disconnect = hiface_chip_disconnect,
|
|
.id_table = device_table,
|
|
};
|
|
|
|
module_usb_driver(hiface_usb_driver);
|