diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2016-10-05 12:13:14 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2016-10-05 12:13:14 -0700 |
commit | 5691f0e9a3e7855832d5fd094801bf600347c2d0 (patch) | |
tree | 4cd4538de2bde556be944e984a3882a64b25dfcb /sound/usb/line6/podhd.c | |
parent | 878fb5dc96b9dfae1de45be1b85aba40aca3356e (diff) | |
parent | eeea8b40cd2866ca24f25e5ef09225edb076ae45 (diff) |
Merge tag 'sound-4.9-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound
Pull sound updates from Takashi Iwai:
"Again the diffstat shows a widely distributed pattern at this cycle,
as there've been many code cleanups and refactoring allover the
places. Other than that, the development was relatively calm, and no
big surprise shouldn't be expected. Here are some highlights:
Core:
- Sequencer code refactoring / documentation updates
- TLV code moved to uapi, following some relevant cleanups
USB-Audio:
- Lots of LINE6 driver fixes / updates
- DragonFly and TEAC device quirk updates
HD-audio:
- Usual fixupes for Dell, Lenovo and HP machines
- Link-audio time reporting capability
ASoC:
- Large refactoring of simple-card code to be shared with rcar driver
- Removal of some duplicated ops over lots of CODEC drivers
- Again quite a few Intel SKL driver updates
- New drivers for Nuvoton NAU88C10, Realtek RT5660 and RT5663"
* tag 'sound-4.9-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound: (465 commits)
ASoC: fsl: Fix lockups with recent cache changes
ASoC: Intel: Skylake: fix memory leak of module on error exit path
ASoC: rsnd: add SNDRV_PCM_TRIGGER_SUSPEND/RESUME
ASoC: wm8960: remove usage of obsoleted TLV-related macro
ASoC: rt5616: remove usage of obsoleted TLV-related macro
ASoC: max9867: remove usage of obsoleted TLV-related macro
ASoC: trivial: system spelling fix
ASoC: da7219: fix inappropriate condition statement
ASoC: tlv320aic31xx: do not declare support for mono DAI
ASoC: stac9766: fix wrong usage of DECLARE_TLV_DB_LINEAR()
ASoC: wm8991: remove unused variable
ASoC: wm8991: fix wrong usage of DECLARE_TLV_DB_LINEAR()
ASOC: tpa6130a2: add static qualifier for file local symbols
ASoC: sst-bxt-rt298: fix obsoleted initializers for array
ASoC: sst-bxt-da7219_max98357a: fix obsoleted initializers for array
ASoC: rt5616: add static qualifier for file local symbols
ASoC: arizona: Add output power up/down delays for speaker path
ASoC: arizona: Add debug prints for output power up/down times
ALSA: hda - Add the top speaker pin config for HP Spectre x360
ASoC: Intel: Add DMIC channel constraint for bxt machine
...
Diffstat (limited to 'sound/usb/line6/podhd.c')
-rw-r--r-- | sound/usb/line6/podhd.c | 300 |
1 files changed, 279 insertions, 21 deletions
diff --git a/sound/usb/line6/podhd.c b/sound/usb/line6/podhd.c index 63dcaef41ac3..9352a44ae6e4 100644 --- a/sound/usb/line6/podhd.c +++ b/sound/usb/line6/podhd.c @@ -2,6 +2,7 @@ * Line 6 Pod HD * * Copyright (C) 2011 Stefan Hajnoczi <stefanha@gmail.com> + * Copyright (C) 2015 Andrej Krutak <dev@andree.sk> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -18,14 +19,46 @@ #include "driver.h" #include "pcm.h" +#define PODHD_STARTUP_DELAY 500 + +/* + * Stages of POD startup procedure + */ +enum { + PODHD_STARTUP_INIT = 1, + PODHD_STARTUP_SCHEDULE_WORKQUEUE, + PODHD_STARTUP_SETUP, + PODHD_STARTUP_LAST = PODHD_STARTUP_SETUP - 1 +}; + enum { LINE6_PODHD300, LINE6_PODHD400, LINE6_PODHD500_0, LINE6_PODHD500_1, + LINE6_PODX3, + LINE6_PODX3LIVE }; -#define PODHD_BYTES_PER_FRAME 6 /* 24bit audio (stereo) */ +struct usb_line6_podhd { + /* Generic Line 6 USB data */ + struct usb_line6 line6; + + /* Timer for device initialization */ + struct timer_list startup_timer; + + /* Work handler for device initialization */ + struct work_struct startup_work; + + /* Current progress in startup procedure */ + int startup_progress; + + /* Serial number of device */ + u32 serial_number; + + /* Firmware version */ + int firmware_version; +}; static struct snd_ratden podhd_ratden = { .num_min = 48000, @@ -73,29 +106,233 @@ static struct line6_pcm_properties podhd_pcm_properties = { .rates = { .nrats = 1, .rats = &podhd_ratden}, - .bytes_per_frame = PODHD_BYTES_PER_FRAME + .bytes_per_channel = 3 /* SNDRV_PCM_FMTBIT_S24_3LE */ +}; + +static struct line6_pcm_properties podx3_pcm_properties = { + .playback_hw = { + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_SYNC_START), + .formats = SNDRV_PCM_FMTBIT_S24_3LE, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = 60000, + .period_bytes_min = 64, + .period_bytes_max = 8192, + .periods_min = 1, + .periods_max = 1024}, + .capture_hw = { + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_SYNC_START), + .formats = SNDRV_PCM_FMTBIT_S24_3LE, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, + /* 1+2: Main signal (out), 3+4: Tone 1, + * 5+6: Tone 2, 7+8: raw + */ + .channels_min = 8, + .channels_max = 8, + .buffer_bytes_max = 60000, + .period_bytes_min = 64, + .period_bytes_max = 8192, + .periods_min = 1, + .periods_max = 1024}, + .rates = { + .nrats = 1, + .rats = &podhd_ratden}, + .bytes_per_channel = 3 /* SNDRV_PCM_FMTBIT_S24_3LE */ +}; + +static void podhd_startup_start_workqueue(unsigned long data); +static void podhd_startup_workqueue(struct work_struct *work); +static int podhd_startup_finalize(struct usb_line6_podhd *pod); + +static ssize_t serial_number_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct snd_card *card = dev_to_snd_card(dev); + struct usb_line6_podhd *pod = card->private_data; + + return sprintf(buf, "%u\n", pod->serial_number); +} + +static ssize_t firmware_version_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct snd_card *card = dev_to_snd_card(dev); + struct usb_line6_podhd *pod = card->private_data; + + return sprintf(buf, "%06x\n", pod->firmware_version); +} + +static DEVICE_ATTR_RO(firmware_version); +static DEVICE_ATTR_RO(serial_number); + +static struct attribute *podhd_dev_attrs[] = { + &dev_attr_firmware_version.attr, + &dev_attr_serial_number.attr, + NULL +}; + +static const struct attribute_group podhd_dev_attr_group = { + .name = "podhd", + .attrs = podhd_dev_attrs, }; /* + * POD X3 startup procedure. + * + * May be compatible with other POD HD's, since it's also similar to the + * previous POD setup. In any case, it doesn't seem to be required for the + * audio nor bulk interfaces to work. + */ + +static void podhd_startup(struct usb_line6_podhd *pod) +{ + CHECK_STARTUP_PROGRESS(pod->startup_progress, PODHD_STARTUP_INIT); + + /* delay startup procedure: */ + line6_start_timer(&pod->startup_timer, PODHD_STARTUP_DELAY, + podhd_startup_start_workqueue, (unsigned long)pod); +} + +static void podhd_startup_start_workqueue(unsigned long data) +{ + struct usb_line6_podhd *pod = (struct usb_line6_podhd *)data; + + CHECK_STARTUP_PROGRESS(pod->startup_progress, + PODHD_STARTUP_SCHEDULE_WORKQUEUE); + + /* schedule work for global work queue: */ + schedule_work(&pod->startup_work); +} + +static int podhd_dev_start(struct usb_line6_podhd *pod) +{ + int ret; + u8 init_bytes[8]; + int i; + struct usb_device *usbdev = pod->line6.usbdev; + + ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0), + 0x67, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT, + 0x11, 0, + NULL, 0, LINE6_TIMEOUT * HZ); + if (ret < 0) { + dev_err(pod->line6.ifcdev, "read request failed (error %d)\n", ret); + return ret; + } + + /* NOTE: looks like some kind of ping message */ + ret = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0), 0x67, + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, + 0x11, 0x0, + &init_bytes, 3, LINE6_TIMEOUT * HZ); + if (ret < 0) { + dev_err(pod->line6.ifcdev, + "receive length failed (error %d)\n", ret); + return ret; + } + + pod->firmware_version = + (init_bytes[0] << 16) | (init_bytes[1] << 8) | (init_bytes[2] << 0); + + for (i = 0; i <= 16; i++) { + ret = line6_read_data(&pod->line6, 0xf000 + 0x08 * i, init_bytes, 8); + if (ret < 0) + return ret; + } + + ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0), + USB_REQ_SET_FEATURE, + USB_TYPE_STANDARD | USB_RECIP_DEVICE | USB_DIR_OUT, + 1, 0, + NULL, 0, LINE6_TIMEOUT * HZ); + if (ret < 0) + return ret; + + return 0; +} + +static void podhd_startup_workqueue(struct work_struct *work) +{ + struct usb_line6_podhd *pod = + container_of(work, struct usb_line6_podhd, startup_work); + + CHECK_STARTUP_PROGRESS(pod->startup_progress, PODHD_STARTUP_SETUP); + + podhd_dev_start(pod); + line6_read_serial_number(&pod->line6, &pod->serial_number); + + podhd_startup_finalize(pod); +} + +static int podhd_startup_finalize(struct usb_line6_podhd *pod) +{ + struct usb_line6 *line6 = &pod->line6; + + /* ALSA audio interface: */ + return snd_card_register(line6->card); +} + +static void podhd_disconnect(struct usb_line6 *line6) +{ + struct usb_line6_podhd *pod = (struct usb_line6_podhd *)line6; + + if (pod->line6.properties->capabilities & LINE6_CAP_CONTROL) { + del_timer_sync(&pod->startup_timer); + cancel_work_sync(&pod->startup_work); + } +} + +/* Try to init POD HD device. */ static int podhd_init(struct usb_line6 *line6, const struct usb_device_id *id) { int err; + struct usb_line6_podhd *pod = (struct usb_line6_podhd *) line6; - /* initialize MIDI subsystem: */ - err = line6_init_midi(line6); - if (err < 0) - return err; + line6->disconnect = podhd_disconnect; - /* initialize PCM subsystem: */ - err = line6_init_pcm(line6, &podhd_pcm_properties); - if (err < 0) - return err; + if (pod->line6.properties->capabilities & LINE6_CAP_CONTROL) { + /* create sysfs entries: */ + err = snd_card_add_dev_attr(line6->card, &podhd_dev_attr_group); + if (err < 0) + return err; + } - /* register USB audio system: */ - return snd_card_register(line6->card); + if (pod->line6.properties->capabilities & LINE6_CAP_PCM) { + /* initialize PCM subsystem: */ + err = line6_init_pcm(line6, + (id->driver_info == LINE6_PODX3) ? &podx3_pcm_properties : + &podhd_pcm_properties); + if (err < 0) + return err; + } + + if (!(pod->line6.properties->capabilities & LINE6_CAP_CONTROL)) { + /* register USB audio system directly */ + return podhd_startup_finalize(pod); + } + + /* init device and delay registering */ + init_timer(&pod->startup_timer); + INIT_WORK(&pod->startup_work, podhd_startup_workqueue); + podhd_startup(pod); + return 0; } #define LINE6_DEVICE(prod) USB_DEVICE(0x0e41, prod) @@ -103,10 +340,13 @@ static int podhd_init(struct usb_line6 *line6, /* table of devices that work with this driver */ static const struct usb_device_id podhd_id_table[] = { + /* TODO: no need to alloc data interfaces when only audio is used */ { LINE6_DEVICE(0x5057), .driver_info = LINE6_PODHD300 }, { LINE6_DEVICE(0x5058), .driver_info = LINE6_PODHD400 }, { LINE6_IF_NUM(0x414D, 0), .driver_info = LINE6_PODHD500_0 }, { LINE6_IF_NUM(0x414D, 1), .driver_info = LINE6_PODHD500_1 }, + { LINE6_IF_NUM(0x414A, 0), .driver_info = LINE6_PODX3 }, + { LINE6_IF_NUM(0x414B, 0), .driver_info = LINE6_PODX3LIVE }, {} }; @@ -116,8 +356,7 @@ static const struct line6_properties podhd_properties_table[] = { [LINE6_PODHD300] = { .id = "PODHD300", .name = "POD HD300", - .capabilities = LINE6_CAP_CONTROL - | LINE6_CAP_PCM + .capabilities = LINE6_CAP_PCM | LINE6_CAP_HWMON, .altsetting = 5, .ep_ctrl_r = 0x84, @@ -128,8 +367,7 @@ static const struct line6_properties podhd_properties_table[] = { [LINE6_PODHD400] = { .id = "PODHD400", .name = "POD HD400", - .capabilities = LINE6_CAP_CONTROL - | LINE6_CAP_PCM + .capabilities = LINE6_CAP_PCM | LINE6_CAP_HWMON, .altsetting = 5, .ep_ctrl_r = 0x84, @@ -140,8 +378,7 @@ static const struct line6_properties podhd_properties_table[] = { [LINE6_PODHD500_0] = { .id = "PODHD500", .name = "POD HD500", - .capabilities = LINE6_CAP_CONTROL - | LINE6_CAP_PCM + .capabilities = LINE6_CAP_PCM | LINE6_CAP_HWMON, .altsetting = 1, .ep_ctrl_r = 0x81, @@ -152,8 +389,7 @@ static const struct line6_properties podhd_properties_table[] = { [LINE6_PODHD500_1] = { .id = "PODHD500", .name = "POD HD500", - .capabilities = LINE6_CAP_CONTROL - | LINE6_CAP_PCM + .capabilities = LINE6_CAP_PCM | LINE6_CAP_HWMON, .altsetting = 1, .ep_ctrl_r = 0x81, @@ -161,6 +397,28 @@ static const struct line6_properties podhd_properties_table[] = { .ep_audio_r = 0x86, .ep_audio_w = 0x02, }, + [LINE6_PODX3] = { + .id = "PODX3", + .name = "POD X3", + .capabilities = LINE6_CAP_CONTROL + | LINE6_CAP_PCM | LINE6_CAP_HWMON | LINE6_CAP_IN_NEEDS_OUT, + .altsetting = 1, + .ep_ctrl_r = 0x81, + .ep_ctrl_w = 0x01, + .ep_audio_r = 0x86, + .ep_audio_w = 0x02, + }, + [LINE6_PODX3LIVE] = { + .id = "PODX3LIVE", + .name = "POD X3 LIVE", + .capabilities = LINE6_CAP_CONTROL + | LINE6_CAP_PCM | LINE6_CAP_HWMON | LINE6_CAP_IN_NEEDS_OUT, + .altsetting = 1, + .ep_ctrl_r = 0x81, + .ep_ctrl_w = 0x01, + .ep_audio_r = 0x86, + .ep_audio_w = 0x02, + }, }; /* @@ -171,7 +429,7 @@ static int podhd_probe(struct usb_interface *interface, { return line6_probe(interface, id, "Line6-PODHD", &podhd_properties_table[id->driver_info], - podhd_init, sizeof(struct usb_line6)); + podhd_init, sizeof(struct usb_line6_podhd)); } static struct usb_driver podhd_driver = { |