Merge branch 'for-next' into for-linus

This commit is contained in:
Takashi Iwai 2016-05-16 09:13:08 +02:00
commit 581abbaa03
62 changed files with 1452 additions and 1050 deletions

View File

@ -655,17 +655,6 @@ development branches in general while the development for the current
and next kernels are found in for-linus and for-next branches, and next kernels are found in for-linus and for-next branches,
respectively. respectively.
If you are using the latest Linus tree, it'd be better to pull the
above GIT tree onto it. If you are using the older kernels, an easy
way to try the latest ALSA code is to build from the snapshot
tarball. There are daily tarballs and the latest snapshot tarball.
All can be built just like normal alsa-driver release packages, that
is, installed via the usual spells: configure, make and make
install(-modules). See INSTALL in the package. The snapshot tarballs
are found at:
- ftp://ftp.suse.com/pub/people/tiwai/snapshot/
Sending a Bug Report Sending a Bug Report
~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~
@ -699,7 +688,12 @@ problems.
alsa-info alsa-info
~~~~~~~~~ ~~~~~~~~~
The script `alsa-info.sh` is a very useful tool to gather the audio The script `alsa-info.sh` is a very useful tool to gather the audio
device information. You can fetch the latest version from: device information. It's included in alsa-utils package. The latest
version can be found on git repository:
- git://git.alsa-project.org/alsa-utils.git
The script can be fetched directly from the following URL, too:
- http://www.alsa-project.org/alsa-info.sh - http://www.alsa-project.org/alsa-info.sh
@ -836,15 +830,11 @@ can get a proc-file dump at the current state, get a list of control
(mixer) elements, set/get the control element value, simulate the PCM (mixer) elements, set/get the control element value, simulate the PCM
operation, the jack plugging simulation, etc. operation, the jack plugging simulation, etc.
The package is found in: The program is found in the git repository below:
- ftp://ftp.suse.com/pub/people/tiwai/misc/
A git repository is available:
- git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/hda-emu.git - git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/hda-emu.git
See README file in the tarball for more details about hda-emu See README file in the repository for more details about hda-emu
program. program.

View File

@ -149,7 +149,7 @@ Gapless Playback
================ ================
When playing thru an album, the decoders have the ability to skip the encoder When playing thru an album, the decoders have the ability to skip the encoder
delay and padding and directly move from one track content to another. The end delay and padding and directly move from one track content to another. The end
user can perceive this as gapless playback as we dont have silence while user can perceive this as gapless playback as we don't have silence while
switching from one track to another switching from one track to another
Also, there might be low-intensity noises due to encoding. Perfect gapless is Also, there might be low-intensity noises due to encoding. Perfect gapless is
@ -184,7 +184,7 @@ Sequence flow for gapless would be:
- Fill data of the first track - Fill data of the first track
- Trigger start - Trigger start
- User-space finished sending all, - User-space finished sending all,
- Indicaite next track data by sending set_next_track - Indicate next track data by sending set_next_track
- Set metadata of the next track - Set metadata of the next track
- then call partial_drain to flush most of buffer in DSP - then call partial_drain to flush most of buffer in DSP
- Fill data of the next track - Fill data of the next track

View File

@ -132,7 +132,7 @@ SOC_DAPM_SINGLE("HiFi Playback Switch", WM8731_APANA, 4, 1, 0),
SND_SOC_DAPM_MIXER("Output Mixer", WM8731_PWR, 4, 1, wm8731_output_mixer_controls, SND_SOC_DAPM_MIXER("Output Mixer", WM8731_PWR, 4, 1, wm8731_output_mixer_controls,
ARRAY_SIZE(wm8731_output_mixer_controls)), ARRAY_SIZE(wm8731_output_mixer_controls)),
If you dont want the mixer elements prefixed with the name of the mixer widget, If you don't want the mixer elements prefixed with the name of the mixer widget,
you can use SND_SOC_DAPM_MIXER_NAMED_CTL instead. the parameters are the same you can use SND_SOC_DAPM_MIXER_NAMED_CTL instead. the parameters are the same
as for SND_SOC_DAPM_MIXER. as for SND_SOC_DAPM_MIXER.

View File

@ -63,7 +63,7 @@ multiple re-usable component drivers :-
and any audio DSP drivers for that platform. and any audio DSP drivers for that platform.
* Machine class driver: The machine driver class acts as the glue that * Machine class driver: The machine driver class acts as the glue that
decribes and binds the other component drivers together to form an ALSA describes and binds the other component drivers together to form an ALSA
"sound card device". It handles any machine specific controls and "sound card device". It handles any machine specific controls and
machine level audio events (e.g. turning on an amp at start of playback). machine level audio events (e.g. turning on an amp at start of playback).

View File

@ -129,7 +129,7 @@ will be required to issue multiple queries and perform an
interpolation of the results interpolation of the results
In some hardware-specific configuration, the system timestamp is In some hardware-specific configuration, the system timestamp is
latched by a low-level audio subsytem, and the information provided latched by a low-level audio subsystem, and the information provided
back to the driver. Due to potential delays in the communication with back to the driver. Due to potential delays in the communication with
the hardware, there is a risk of misalignment with the avail and delay the hardware, there is a risk of misalignment with the avail and delay
information. To make sure applications are not confused, a information. To make sure applications are not confused, a

View File

@ -36,6 +36,8 @@ struct hdac_chmap_ops {
int (*chmap_validate)(struct hdac_chmap *hchmap, int ca, int (*chmap_validate)(struct hdac_chmap *hchmap, int ca,
int channels, unsigned char *chmap); int channels, unsigned char *chmap);
int (*get_spk_alloc)(struct hdac_device *hdac, int pcm_idx);
void (*get_chmap)(struct hdac_device *hdac, int pcm_idx, void (*get_chmap)(struct hdac_device *hdac, int pcm_idx,
unsigned char *chmap); unsigned char *chmap);
void (*set_chmap)(struct hdac_device *hdac, int pcm_idx, void (*set_chmap)(struct hdac_device *hdac, int pcm_idx,

View File

@ -10,8 +10,8 @@
int snd_hdac_set_codec_wakeup(struct hdac_bus *bus, bool enable); int snd_hdac_set_codec_wakeup(struct hdac_bus *bus, bool enable);
int snd_hdac_display_power(struct hdac_bus *bus, bool enable); int snd_hdac_display_power(struct hdac_bus *bus, bool enable);
void snd_hdac_i915_set_bclk(struct hdac_bus *bus); void snd_hdac_i915_set_bclk(struct hdac_bus *bus);
int snd_hdac_sync_audio_rate(struct hdac_bus *bus, hda_nid_t nid, int rate); int snd_hdac_sync_audio_rate(struct hdac_device *codec, hda_nid_t nid, int rate);
int snd_hdac_acomp_get_eld(struct hdac_bus *bus, hda_nid_t nid, int snd_hdac_acomp_get_eld(struct hdac_device *codec, hda_nid_t nid,
bool *audio_enabled, char *buffer, int max_bytes); bool *audio_enabled, char *buffer, int max_bytes);
int snd_hdac_i915_init(struct hdac_bus *bus); int snd_hdac_i915_init(struct hdac_bus *bus);
int snd_hdac_i915_exit(struct hdac_bus *bus); int snd_hdac_i915_exit(struct hdac_bus *bus);
@ -28,12 +28,12 @@ static inline int snd_hdac_display_power(struct hdac_bus *bus, bool enable)
static inline void snd_hdac_i915_set_bclk(struct hdac_bus *bus) static inline void snd_hdac_i915_set_bclk(struct hdac_bus *bus)
{ {
} }
static inline int snd_hdac_sync_audio_rate(struct hdac_bus *bus, hda_nid_t nid, static inline int snd_hdac_sync_audio_rate(struct hdac_device *codec,
int rate) hda_nid_t nid, int rate)
{ {
return 0; return 0;
} }
static inline int snd_hdac_acomp_get_eld(struct hdac_bus *bus, hda_nid_t nid, static inline int snd_hdac_acomp_get_eld(struct hdac_device *codec, hda_nid_t nid,
bool *audio_enabled, char *buffer, bool *audio_enabled, char *buffer,
int max_bytes) int max_bytes)
{ {

View File

@ -672,7 +672,7 @@ enum {
/* global timers (device member) */ /* global timers (device member) */
#define SNDRV_TIMER_GLOBAL_SYSTEM 0 #define SNDRV_TIMER_GLOBAL_SYSTEM 0
#define SNDRV_TIMER_GLOBAL_RTC 1 #define SNDRV_TIMER_GLOBAL_RTC 1 /* unused */
#define SNDRV_TIMER_GLOBAL_HPET 2 #define SNDRV_TIMER_GLOBAL_HPET 2
#define SNDRV_TIMER_GLOBAL_HRTIMER 3 #define SNDRV_TIMER_GLOBAL_HRTIMER 3

View File

@ -141,35 +141,6 @@ config SND_SEQ_HRTIMER_DEFAULT
Say Y here to use the HR-timer backend as the default sequencer Say Y here to use the HR-timer backend as the default sequencer
timer. timer.
config SND_RTCTIMER
tristate "RTC Timer support"
depends on RTC
select SND_TIMER
help
Say Y here to enable RTC timer support for ALSA. ALSA uses
the RTC timer as a precise timing source and maps the RTC
timer to ALSA's timer interface. The ALSA sequencer code also
can use this timing source.
To compile this driver as a module, choose M here: the module
will be called snd-rtctimer.
Note that this option is exclusive with the new RTC drivers
(CONFIG_RTC_CLASS) since this requires the old API.
config SND_SEQ_RTCTIMER_DEFAULT
bool "Use RTC as default sequencer timer"
depends on SND_RTCTIMER && SND_SEQUENCER
depends on !SND_SEQ_HRTIMER_DEFAULT
default y
help
Say Y here to use the RTC timer as the default sequencer
timer. This is strongly recommended because it ensures
precise MIDI timing even when the system timer runs at less
than 1000 Hz.
If in doubt, say Y.
config SND_DYNAMIC_MINORS config SND_DYNAMIC_MINORS
bool "Dynamic device file minor numbers" bool "Dynamic device file minor numbers"
help help

View File

@ -37,7 +37,6 @@ obj-$(CONFIG_SND) += snd.o
obj-$(CONFIG_SND_HWDEP) += snd-hwdep.o obj-$(CONFIG_SND_HWDEP) += snd-hwdep.o
obj-$(CONFIG_SND_TIMER) += snd-timer.o obj-$(CONFIG_SND_TIMER) += snd-timer.o
obj-$(CONFIG_SND_HRTIMER) += snd-hrtimer.o obj-$(CONFIG_SND_HRTIMER) += snd-hrtimer.o
obj-$(CONFIG_SND_RTCTIMER) += snd-rtctimer.o
obj-$(CONFIG_SND_PCM) += snd-pcm.o obj-$(CONFIG_SND_PCM) += snd-pcm.o
obj-$(CONFIG_SND_DMAENGINE_PCM) += snd-pcm-dmaengine.o obj-$(CONFIG_SND_DMAENGINE_PCM) += snd-pcm-dmaengine.o
obj-$(CONFIG_SND_RAWMIDI) += snd-rawmidi.o obj-$(CONFIG_SND_RAWMIDI) += snd-rawmidi.o

View File

@ -288,9 +288,12 @@ static ssize_t snd_compr_write(struct file *f, const char __user *buf,
stream = &data->stream; stream = &data->stream;
mutex_lock(&stream->device->lock); mutex_lock(&stream->device->lock);
/* write is allowed when stream is running or has been steup */ /* write is allowed when stream is running or has been steup */
if (stream->runtime->state != SNDRV_PCM_STATE_SETUP && switch (stream->runtime->state) {
stream->runtime->state != SNDRV_PCM_STATE_PREPARED && case SNDRV_PCM_STATE_SETUP:
stream->runtime->state != SNDRV_PCM_STATE_RUNNING) { case SNDRV_PCM_STATE_PREPARED:
case SNDRV_PCM_STATE_RUNNING:
break;
default:
mutex_unlock(&stream->device->lock); mutex_unlock(&stream->device->lock);
return -EBADFD; return -EBADFD;
} }
@ -391,14 +394,13 @@ static unsigned int snd_compr_poll(struct file *f, poll_table *wait)
int retval = 0; int retval = 0;
if (snd_BUG_ON(!data)) if (snd_BUG_ON(!data))
return -EFAULT; return POLLERR;
stream = &data->stream; stream = &data->stream;
if (snd_BUG_ON(!stream))
return -EFAULT;
mutex_lock(&stream->device->lock); mutex_lock(&stream->device->lock);
if (stream->runtime->state == SNDRV_PCM_STATE_OPEN) { if (stream->runtime->state == SNDRV_PCM_STATE_OPEN) {
retval = -EBADFD; retval = snd_compr_get_poll(stream) | POLLERR;
goto out; goto out;
} }
poll_wait(f, &stream->runtime->sleep, wait); poll_wait(f, &stream->runtime->sleep, wait);
@ -421,10 +423,7 @@ static unsigned int snd_compr_poll(struct file *f, poll_table *wait)
retval = snd_compr_get_poll(stream); retval = snd_compr_get_poll(stream);
break; break;
default: default:
if (stream->direction == SND_COMPRESS_PLAYBACK) retval = snd_compr_get_poll(stream) | POLLERR;
retval = POLLOUT | POLLWRNORM | POLLERR;
else
retval = POLLIN | POLLRDNORM | POLLERR;
break; break;
} }
out: out:
@ -802,9 +801,9 @@ static long snd_compr_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
if (snd_BUG_ON(!data)) if (snd_BUG_ON(!data))
return -EFAULT; return -EFAULT;
stream = &data->stream; stream = &data->stream;
if (snd_BUG_ON(!stream))
return -EFAULT;
mutex_lock(&stream->device->lock); mutex_lock(&stream->device->lock);
switch (_IOC_NR(cmd)) { switch (_IOC_NR(cmd)) {
case _IOC_NR(SNDRV_COMPRESS_IOCTL_VERSION): case _IOC_NR(SNDRV_COMPRESS_IOCTL_VERSION):

View File

@ -38,37 +38,53 @@ static unsigned int resolution;
struct snd_hrtimer { struct snd_hrtimer {
struct snd_timer *timer; struct snd_timer *timer;
struct hrtimer hrt; struct hrtimer hrt;
atomic_t running; bool in_callback;
}; };
static enum hrtimer_restart snd_hrtimer_callback(struct hrtimer *hrt) static enum hrtimer_restart snd_hrtimer_callback(struct hrtimer *hrt)
{ {
struct snd_hrtimer *stime = container_of(hrt, struct snd_hrtimer, hrt); struct snd_hrtimer *stime = container_of(hrt, struct snd_hrtimer, hrt);
struct snd_timer *t = stime->timer; struct snd_timer *t = stime->timer;
unsigned long oruns; ktime_t delta;
unsigned long ticks;
enum hrtimer_restart ret = HRTIMER_NORESTART;
if (!atomic_read(&stime->running)) spin_lock(&t->lock);
return HRTIMER_NORESTART; if (!t->running)
goto out; /* fast path */
stime->in_callback = true;
ticks = t->sticks;
spin_unlock(&t->lock);
oruns = hrtimer_forward_now(hrt, ns_to_ktime(t->sticks * resolution)); /* calculate the drift */
snd_timer_interrupt(stime->timer, t->sticks * oruns); delta = ktime_sub(hrt->base->get_time(), hrtimer_get_expires(hrt));
if (delta.tv64 > 0)
ticks += ktime_divns(delta, ticks * resolution);
if (!atomic_read(&stime->running)) snd_timer_interrupt(stime->timer, ticks);
return HRTIMER_NORESTART;
return HRTIMER_RESTART; spin_lock(&t->lock);
if (t->running) {
hrtimer_add_expires_ns(hrt, t->sticks * resolution);
ret = HRTIMER_RESTART;
}
stime->in_callback = false;
out:
spin_unlock(&t->lock);
return ret;
} }
static int snd_hrtimer_open(struct snd_timer *t) static int snd_hrtimer_open(struct snd_timer *t)
{ {
struct snd_hrtimer *stime; struct snd_hrtimer *stime;
stime = kmalloc(sizeof(*stime), GFP_KERNEL); stime = kzalloc(sizeof(*stime), GFP_KERNEL);
if (!stime) if (!stime)
return -ENOMEM; return -ENOMEM;
hrtimer_init(&stime->hrt, CLOCK_MONOTONIC, HRTIMER_MODE_REL); hrtimer_init(&stime->hrt, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
stime->timer = t; stime->timer = t;
stime->hrt.function = snd_hrtimer_callback; stime->hrt.function = snd_hrtimer_callback;
atomic_set(&stime->running, 0);
t->private_data = stime; t->private_data = stime;
return 0; return 0;
} }
@ -78,6 +94,11 @@ static int snd_hrtimer_close(struct snd_timer *t)
struct snd_hrtimer *stime = t->private_data; struct snd_hrtimer *stime = t->private_data;
if (stime) { if (stime) {
spin_lock_irq(&t->lock);
t->running = 0; /* just to be sure */
stime->in_callback = 1; /* skip start/stop */
spin_unlock_irq(&t->lock);
hrtimer_cancel(&stime->hrt); hrtimer_cancel(&stime->hrt);
kfree(stime); kfree(stime);
t->private_data = NULL; t->private_data = NULL;
@ -89,18 +110,19 @@ static int snd_hrtimer_start(struct snd_timer *t)
{ {
struct snd_hrtimer *stime = t->private_data; struct snd_hrtimer *stime = t->private_data;
atomic_set(&stime->running, 0); if (stime->in_callback)
hrtimer_try_to_cancel(&stime->hrt); return 0;
hrtimer_start(&stime->hrt, ns_to_ktime(t->sticks * resolution), hrtimer_start(&stime->hrt, ns_to_ktime(t->sticks * resolution),
HRTIMER_MODE_REL); HRTIMER_MODE_REL);
atomic_set(&stime->running, 1);
return 0; return 0;
} }
static int snd_hrtimer_stop(struct snd_timer *t) static int snd_hrtimer_stop(struct snd_timer *t)
{ {
struct snd_hrtimer *stime = t->private_data; struct snd_hrtimer *stime = t->private_data;
atomic_set(&stime->running, 0);
if (stime->in_callback)
return 0;
hrtimer_try_to_cancel(&stime->hrt); hrtimer_try_to_cancel(&stime->hrt);
return 0; return 0;
} }

View File

@ -1886,8 +1886,8 @@ void snd_pcm_period_elapsed(struct snd_pcm_substream *substream)
snd_timer_interrupt(substream->timer, 1); snd_timer_interrupt(substream->timer, 1);
#endif #endif
_end: _end:
snd_pcm_stream_unlock_irqrestore(substream, flags);
kill_fasync(&runtime->fasync, SIGIO, POLL_IN); kill_fasync(&runtime->fasync, SIGIO, POLL_IN);
snd_pcm_stream_unlock_irqrestore(substream, flags);
} }
EXPORT_SYMBOL(snd_pcm_period_elapsed); EXPORT_SYMBOL(snd_pcm_period_elapsed);
@ -2595,6 +2595,8 @@ int snd_pcm_add_chmap_ctls(struct snd_pcm *pcm, int stream,
}; };
int err; int err;
if (WARN_ON(pcm->streams[stream].chmap_kctl))
return -EBUSY;
info = kzalloc(sizeof(*info), GFP_KERNEL); info = kzalloc(sizeof(*info), GFP_KERNEL);
if (!info) if (!info)
return -ENOMEM; return -ENOMEM;

View File

@ -3161,7 +3161,7 @@ static unsigned int snd_pcm_playback_poll(struct file *file, poll_table * wait)
substream = pcm_file->substream; substream = pcm_file->substream;
if (PCM_RUNTIME_CHECK(substream)) if (PCM_RUNTIME_CHECK(substream))
return -ENXIO; return POLLOUT | POLLWRNORM | POLLERR;
runtime = substream->runtime; runtime = substream->runtime;
poll_wait(file, &runtime->sleep, wait); poll_wait(file, &runtime->sleep, wait);
@ -3200,7 +3200,7 @@ static unsigned int snd_pcm_capture_poll(struct file *file, poll_table * wait)
substream = pcm_file->substream; substream = pcm_file->substream;
if (PCM_RUNTIME_CHECK(substream)) if (PCM_RUNTIME_CHECK(substream))
return -ENXIO; return POLLIN | POLLRDNORM | POLLERR;
runtime = substream->runtime; runtime = substream->runtime;
poll_wait(file, &runtime->sleep, wait); poll_wait(file, &runtime->sleep, wait);

View File

@ -1,187 +0,0 @@
/*
* RTC based high-frequency timer
*
* Copyright (C) 2000 Takashi Iwai
* based on rtctimer.c by Steve Ratcliffe
*
* 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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/log2.h>
#include <sound/core.h>
#include <sound/timer.h>
#if IS_ENABLED(CONFIG_RTC)
#include <linux/mc146818rtc.h>
#define RTC_FREQ 1024 /* default frequency */
#define NANO_SEC 1000000000L /* 10^9 in sec */
/*
* prototypes
*/
static int rtctimer_open(struct snd_timer *t);
static int rtctimer_close(struct snd_timer *t);
static int rtctimer_start(struct snd_timer *t);
static int rtctimer_stop(struct snd_timer *t);
/*
* The hardware dependent description for this timer.
*/
static struct snd_timer_hardware rtc_hw = {
.flags = SNDRV_TIMER_HW_AUTO |
SNDRV_TIMER_HW_FIRST |
SNDRV_TIMER_HW_TASKLET,
.ticks = 100000000L, /* FIXME: XXX */
.open = rtctimer_open,
.close = rtctimer_close,
.start = rtctimer_start,
.stop = rtctimer_stop,
};
static int rtctimer_freq = RTC_FREQ; /* frequency */
static struct snd_timer *rtctimer;
static struct tasklet_struct rtc_tasklet;
static rtc_task_t rtc_task;
static int
rtctimer_open(struct snd_timer *t)
{
int err;
err = rtc_register(&rtc_task);
if (err < 0)
return err;
t->private_data = &rtc_task;
return 0;
}
static int
rtctimer_close(struct snd_timer *t)
{
rtc_task_t *rtc = t->private_data;
if (rtc) {
rtc_unregister(rtc);
tasklet_kill(&rtc_tasklet);
t->private_data = NULL;
}
return 0;
}
static int
rtctimer_start(struct snd_timer *timer)
{
rtc_task_t *rtc = timer->private_data;
if (snd_BUG_ON(!rtc))
return -EINVAL;
rtc_control(rtc, RTC_IRQP_SET, rtctimer_freq);
rtc_control(rtc, RTC_PIE_ON, 0);
return 0;
}
static int
rtctimer_stop(struct snd_timer *timer)
{
rtc_task_t *rtc = timer->private_data;
if (snd_BUG_ON(!rtc))
return -EINVAL;
rtc_control(rtc, RTC_PIE_OFF, 0);
return 0;
}
static void rtctimer_tasklet(unsigned long data)
{
snd_timer_interrupt((struct snd_timer *)data, 1);
}
/*
* interrupt
*/
static void rtctimer_interrupt(void *private_data)
{
tasklet_schedule(private_data);
}
/*
* ENTRY functions
*/
static int __init rtctimer_init(void)
{
int err;
struct snd_timer *timer;
if (rtctimer_freq < 2 || rtctimer_freq > 8192 ||
!is_power_of_2(rtctimer_freq)) {
pr_err("ALSA: rtctimer: invalid frequency %d\n", rtctimer_freq);
return -EINVAL;
}
/* Create a new timer and set up the fields */
err = snd_timer_global_new("rtc", SNDRV_TIMER_GLOBAL_RTC, &timer);
if (err < 0)
return err;
timer->module = THIS_MODULE;
strcpy(timer->name, "RTC timer");
timer->hw = rtc_hw;
timer->hw.resolution = NANO_SEC / rtctimer_freq;
tasklet_init(&rtc_tasklet, rtctimer_tasklet, (unsigned long)timer);
/* set up RTC callback */
rtc_task.func = rtctimer_interrupt;
rtc_task.private_data = &rtc_tasklet;
err = snd_timer_global_register(timer);
if (err < 0) {
snd_timer_global_free(timer);
return err;
}
rtctimer = timer; /* remember this */
return 0;
}
static void __exit rtctimer_exit(void)
{
if (rtctimer) {
snd_timer_global_free(rtctimer);
rtctimer = NULL;
}
}
/*
* exported stuff
*/
module_init(rtctimer_init)
module_exit(rtctimer_exit)
module_param(rtctimer_freq, int, 0444);
MODULE_PARM_DESC(rtctimer_freq, "timer frequency in Hz");
MODULE_LICENSE("GPL");
MODULE_ALIAS("snd-timer-" __stringify(SNDRV_TIMER_GLOBAL_RTC));
#endif /* IS_ENABLED(CONFIG_RTC) */

View File

@ -47,8 +47,6 @@ int seq_default_timer_card = -1;
int seq_default_timer_device = int seq_default_timer_device =
#ifdef CONFIG_SND_SEQ_HRTIMER_DEFAULT #ifdef CONFIG_SND_SEQ_HRTIMER_DEFAULT
SNDRV_TIMER_GLOBAL_HRTIMER SNDRV_TIMER_GLOBAL_HRTIMER
#elif defined(CONFIG_SND_SEQ_RTCTIMER_DEFAULT)
SNDRV_TIMER_GLOBAL_RTC
#else #else
SNDRV_TIMER_GLOBAL_SYSTEM SNDRV_TIMER_GLOBAL_SYSTEM
#endif #endif

View File

@ -37,8 +37,6 @@
#if IS_ENABLED(CONFIG_SND_HRTIMER) #if IS_ENABLED(CONFIG_SND_HRTIMER)
#define DEFAULT_TIMER_LIMIT 4 #define DEFAULT_TIMER_LIMIT 4
#elif IS_ENABLED(CONFIG_SND_RTCTIMER)
#define DEFAULT_TIMER_LIMIT 2
#else #else
#define DEFAULT_TIMER_LIMIT 1 #define DEFAULT_TIMER_LIMIT 1
#endif #endif
@ -1225,6 +1223,7 @@ static void snd_timer_user_ccallback(struct snd_timer_instance *timeri,
tu->tstamp = *tstamp; tu->tstamp = *tstamp;
if ((tu->filter & (1 << event)) == 0 || !tu->tread) if ((tu->filter & (1 << event)) == 0 || !tu->tread)
return; return;
memset(&r1, 0, sizeof(r1));
r1.event = event; r1.event = event;
r1.tstamp = *tstamp; r1.tstamp = *tstamp;
r1.val = resolution; r1.val = resolution;
@ -1267,6 +1266,7 @@ static void snd_timer_user_tinterrupt(struct snd_timer_instance *timeri,
} }
if ((tu->filter & (1 << SNDRV_TIMER_EVENT_RESOLUTION)) && if ((tu->filter & (1 << SNDRV_TIMER_EVENT_RESOLUTION)) &&
tu->last_resolution != resolution) { tu->last_resolution != resolution) {
memset(&r1, 0, sizeof(r1));
r1.event = SNDRV_TIMER_EVENT_RESOLUTION; r1.event = SNDRV_TIMER_EVENT_RESOLUTION;
r1.tstamp = tstamp; r1.tstamp = tstamp;
r1.val = resolution; r1.val = resolution;
@ -1739,6 +1739,7 @@ static int snd_timer_user_params(struct file *file,
if (tu->timeri->flags & SNDRV_TIMER_IFLG_EARLY_EVENT) { if (tu->timeri->flags & SNDRV_TIMER_IFLG_EARLY_EVENT) {
if (tu->tread) { if (tu->tread) {
struct snd_timer_tread tread; struct snd_timer_tread tread;
memset(&tread, 0, sizeof(tread));
tread.event = SNDRV_TIMER_EVENT_EARLY; tread.event = SNDRV_TIMER_EVENT_EARLY;
tread.tstamp.tv_sec = 0; tread.tstamp.tv_sec = 0;
tread.tstamp.tv_nsec = 0; tread.tstamp.tv_nsec = 0;

View File

@ -134,6 +134,7 @@ config SND_FIREWIRE_TASCAM
Say Y here to include support for TASCAM. Say Y here to include support for TASCAM.
* FW-1884 * FW-1884
* FW-1082 * FW-1082
* FW-1804
To compile this driver as a module, choose M here: the module To compile this driver as a module, choose M here: the module
will be called snd-firewire-tascam. will be called snd-firewire-tascam.

View File

@ -1,3 +1,6 @@
# To find a header included by define_trace.h.
CFLAGS_amdtp-stream.o := -I$(src)
snd-firewire-lib-objs := lib.o iso-resources.o packets-buffer.o \ snd-firewire-lib-objs := lib.o iso-resources.o packets-buffer.o \
fcp.o cmp.o amdtp-stream.o amdtp-am824.o fcp.o cmp.o amdtp-stream.o amdtp-am824.o
snd-isight-objs := isight.o snd-isight-objs := isight.o

View File

@ -0,0 +1,110 @@
/*
* amdtp-stream-trace.h - tracepoint definitions to dump a part of packet data
*
* Copyright (c) 2016 Takashi Sakamoto
* Licensed under the terms of the GNU General Public License, version 2.
*/
#undef TRACE_SYSTEM
#define TRACE_SYSTEM snd_firewire_lib
#if !defined(_AMDTP_STREAM_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
#define _AMDTP_STREAM_TRACE_H
#include <linux/tracepoint.h>
TRACE_EVENT(in_packet,
TP_PROTO(const struct amdtp_stream *s, u32 cycles, u32 cip_header[2], unsigned int payload_quadlets, unsigned int index),
TP_ARGS(s, cycles, cip_header, payload_quadlets, index),
TP_STRUCT__entry(
__field(unsigned int, second)
__field(unsigned int, cycle)
__field(int, channel)
__field(int, src)
__field(int, dest)
__field(u32, cip_header0)
__field(u32, cip_header1)
__field(unsigned int, payload_quadlets)
__field(unsigned int, packet_index)
__field(bool, irq)
__field(unsigned int, index)
),
TP_fast_assign(
__entry->second = cycles / CYCLES_PER_SECOND;
__entry->cycle = cycles % CYCLES_PER_SECOND;
__entry->channel = s->context->channel;
__entry->src = fw_parent_device(s->unit)->node_id;
__entry->dest = fw_parent_device(s->unit)->card->node_id;
__entry->cip_header0 = cip_header[0];
__entry->cip_header1 = cip_header[1];
__entry->payload_quadlets = payload_quadlets;
__entry->packet_index = s->packet_index;
__entry->irq = in_interrupt();
__entry->index = index;
),
TP_printk(
"%02u %04u %04x %04x %02d %08x %08x %03u %02u %01u %02u",
__entry->second,
__entry->cycle,
__entry->src,
__entry->dest,
__entry->channel,
__entry->cip_header0,
__entry->cip_header1,
__entry->payload_quadlets,
__entry->packet_index,
__entry->irq,
__entry->index)
);
TRACE_EVENT(out_packet,
TP_PROTO(const struct amdtp_stream *s, u32 cycles, __be32 *cip_header, unsigned int payload_length, unsigned int index),
TP_ARGS(s, cycles, cip_header, payload_length, index),
TP_STRUCT__entry(
__field(unsigned int, second)
__field(unsigned int, cycle)
__field(int, channel)
__field(int, src)
__field(int, dest)
__field(u32, cip_header0)
__field(u32, cip_header1)
__field(unsigned int, payload_quadlets)
__field(unsigned int, packet_index)
__field(bool, irq)
__field(unsigned int, index)
),
TP_fast_assign(
__entry->second = cycles / CYCLES_PER_SECOND;
__entry->cycle = cycles % CYCLES_PER_SECOND;
__entry->channel = s->context->channel;
__entry->src = fw_parent_device(s->unit)->card->node_id;
__entry->dest = fw_parent_device(s->unit)->node_id;
__entry->cip_header0 = be32_to_cpu(cip_header[0]);
__entry->cip_header1 = be32_to_cpu(cip_header[1]);
__entry->payload_quadlets = payload_length / 4;
__entry->packet_index = s->packet_index;
__entry->irq = in_interrupt();
__entry->index = index;
),
TP_printk(
"%02u %04u %04x %04x %02d %08x %08x %03u %02u %01u %02u",
__entry->second,
__entry->cycle,
__entry->src,
__entry->dest,
__entry->channel,
__entry->cip_header0,
__entry->cip_header1,
__entry->payload_quadlets,
__entry->packet_index,
__entry->irq,
__entry->index)
);
#endif
#undef TRACE_INCLUDE_PATH
#define TRACE_INCLUDE_PATH .
#undef TRACE_INCLUDE_FILE
#define TRACE_INCLUDE_FILE amdtp-stream-trace
#include <trace/define_trace.h>

View File

@ -19,6 +19,10 @@
#define CYCLES_PER_SECOND 8000 #define CYCLES_PER_SECOND 8000
#define TICKS_PER_SECOND (TICKS_PER_CYCLE * CYCLES_PER_SECOND) #define TICKS_PER_SECOND (TICKS_PER_CYCLE * CYCLES_PER_SECOND)
/* Always support Linux tracing subsystem. */
#define CREATE_TRACE_POINTS
#include "amdtp-stream-trace.h"
#define TRANSFER_DELAY_TICKS 0x2e00 /* 479.17 microseconds */ #define TRANSFER_DELAY_TICKS 0x2e00 /* 479.17 microseconds */
/* isochronous header parameters */ /* isochronous header parameters */
@ -87,7 +91,6 @@ int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit,
init_waitqueue_head(&s->callback_wait); init_waitqueue_head(&s->callback_wait);
s->callbacked = false; s->callbacked = false;
s->sync_slave = NULL;
s->fmt = fmt; s->fmt = fmt;
s->process_data_blocks = process_data_blocks; s->process_data_blocks = process_data_blocks;
@ -102,6 +105,10 @@ EXPORT_SYMBOL(amdtp_stream_init);
*/ */
void amdtp_stream_destroy(struct amdtp_stream *s) void amdtp_stream_destroy(struct amdtp_stream *s)
{ {
/* Not initialized. */
if (s->protocol == NULL)
return;
WARN_ON(amdtp_stream_running(s)); WARN_ON(amdtp_stream_running(s));
kfree(s->protocol); kfree(s->protocol);
mutex_destroy(&s->mutex); mutex_destroy(&s->mutex);
@ -244,7 +251,6 @@ void amdtp_stream_pcm_prepare(struct amdtp_stream *s)
tasklet_kill(&s->period_tasklet); tasklet_kill(&s->period_tasklet);
s->pcm_buffer_pointer = 0; s->pcm_buffer_pointer = 0;
s->pcm_period_pointer = 0; s->pcm_period_pointer = 0;
s->pointer_flush = true;
} }
EXPORT_SYMBOL(amdtp_stream_pcm_prepare); EXPORT_SYMBOL(amdtp_stream_pcm_prepare);
@ -349,7 +355,6 @@ static void update_pcm_pointers(struct amdtp_stream *s,
s->pcm_period_pointer += frames; s->pcm_period_pointer += frames;
if (s->pcm_period_pointer >= pcm->runtime->period_size) { if (s->pcm_period_pointer >= pcm->runtime->period_size) {
s->pcm_period_pointer -= pcm->runtime->period_size; s->pcm_period_pointer -= pcm->runtime->period_size;
s->pointer_flush = false;
tasklet_hi_schedule(&s->period_tasklet); tasklet_hi_schedule(&s->period_tasklet);
} }
} }
@ -363,9 +368,8 @@ static void pcm_period_tasklet(unsigned long data)
snd_pcm_period_elapsed(pcm); snd_pcm_period_elapsed(pcm);
} }
static int queue_packet(struct amdtp_stream *s, static int queue_packet(struct amdtp_stream *s, unsigned int header_length,
unsigned int header_length, unsigned int payload_length)
unsigned int payload_length, bool skip)
{ {
struct fw_iso_packet p = {0}; struct fw_iso_packet p = {0};
int err = 0; int err = 0;
@ -376,8 +380,10 @@ static int queue_packet(struct amdtp_stream *s,
p.interrupt = IS_ALIGNED(s->packet_index + 1, INTERRUPT_INTERVAL); p.interrupt = IS_ALIGNED(s->packet_index + 1, INTERRUPT_INTERVAL);
p.tag = TAG_CIP; p.tag = TAG_CIP;
p.header_length = header_length; p.header_length = header_length;
p.payload_length = (!skip) ? payload_length : 0; if (payload_length > 0)
p.skip = skip; p.payload_length = payload_length;
else
p.skip = true;
err = fw_iso_context_queue(s->context, &p, &s->buffer.iso_buffer, err = fw_iso_context_queue(s->context, &p, &s->buffer.iso_buffer,
s->buffer.packets[s->packet_index].offset); s->buffer.packets[s->packet_index].offset);
if (err < 0) { if (err < 0) {
@ -392,27 +398,30 @@ end:
} }
static inline int queue_out_packet(struct amdtp_stream *s, static inline int queue_out_packet(struct amdtp_stream *s,
unsigned int payload_length, bool skip) unsigned int payload_length)
{ {
return queue_packet(s, OUT_PACKET_HEADER_SIZE, return queue_packet(s, OUT_PACKET_HEADER_SIZE, payload_length);
payload_length, skip);
} }
static inline int queue_in_packet(struct amdtp_stream *s) static inline int queue_in_packet(struct amdtp_stream *s)
{ {
return queue_packet(s, IN_PACKET_HEADER_SIZE, return queue_packet(s, IN_PACKET_HEADER_SIZE,
amdtp_stream_get_max_payload(s), false); amdtp_stream_get_max_payload(s));
} }
static int handle_out_packet(struct amdtp_stream *s, unsigned int data_blocks, static int handle_out_packet(struct amdtp_stream *s, unsigned int cycle,
unsigned int syt) unsigned int index)
{ {
__be32 *buffer; __be32 *buffer;
unsigned int syt;
unsigned int data_blocks;
unsigned int payload_length; unsigned int payload_length;
unsigned int pcm_frames; unsigned int pcm_frames;
struct snd_pcm_substream *pcm; struct snd_pcm_substream *pcm;
buffer = s->buffer.packets[s->packet_index].buffer; buffer = s->buffer.packets[s->packet_index].buffer;
syt = calculate_syt(s, cycle);
data_blocks = calculate_data_blocks(s, syt);
pcm_frames = s->process_data_blocks(s, buffer + 2, data_blocks, &syt); pcm_frames = s->process_data_blocks(s, buffer + 2, data_blocks, &syt);
buffer[0] = cpu_to_be32(ACCESS_ONCE(s->source_node_id_field) | buffer[0] = cpu_to_be32(ACCESS_ONCE(s->source_node_id_field) |
@ -424,9 +433,11 @@ static int handle_out_packet(struct amdtp_stream *s, unsigned int data_blocks,
(syt & CIP_SYT_MASK)); (syt & CIP_SYT_MASK));
s->data_block_counter = (s->data_block_counter + data_blocks) & 0xff; s->data_block_counter = (s->data_block_counter + data_blocks) & 0xff;
payload_length = 8 + data_blocks * 4 * s->data_block_quadlets; payload_length = 8 + data_blocks * 4 * s->data_block_quadlets;
if (queue_out_packet(s, payload_length, false) < 0)
trace_out_packet(s, cycle, buffer, payload_length, index);
if (queue_out_packet(s, payload_length) < 0)
return -EIO; return -EIO;
pcm = ACCESS_ONCE(s->pcm); pcm = ACCESS_ONCE(s->pcm);
@ -438,19 +449,24 @@ static int handle_out_packet(struct amdtp_stream *s, unsigned int data_blocks,
} }
static int handle_in_packet(struct amdtp_stream *s, static int handle_in_packet(struct amdtp_stream *s,
unsigned int payload_quadlets, __be32 *buffer, unsigned int payload_quadlets, unsigned int cycle,
unsigned int *data_blocks, unsigned int syt) unsigned int index)
{ {
__be32 *buffer;
u32 cip_header[2]; u32 cip_header[2];
unsigned int fmt, fdf; unsigned int fmt, fdf, syt;
unsigned int data_block_quadlets, data_block_counter, dbc_interval; unsigned int data_block_quadlets, data_block_counter, dbc_interval;
unsigned int data_blocks;
struct snd_pcm_substream *pcm; struct snd_pcm_substream *pcm;
unsigned int pcm_frames; unsigned int pcm_frames;
bool lost; bool lost;
buffer = s->buffer.packets[s->packet_index].buffer;
cip_header[0] = be32_to_cpu(buffer[0]); cip_header[0] = be32_to_cpu(buffer[0]);
cip_header[1] = be32_to_cpu(buffer[1]); cip_header[1] = be32_to_cpu(buffer[1]);
trace_in_packet(s, cycle, cip_header, payload_quadlets, index);
/* /*
* This module supports 'Two-quadlet CIP header with SYT field'. * This module supports 'Two-quadlet CIP header with SYT field'.
* For convenience, also check FMT field is AM824 or not. * For convenience, also check FMT field is AM824 or not.
@ -460,7 +476,7 @@ static int handle_in_packet(struct amdtp_stream *s,
dev_info_ratelimited(&s->unit->device, dev_info_ratelimited(&s->unit->device,
"Invalid CIP header for AMDTP: %08X:%08X\n", "Invalid CIP header for AMDTP: %08X:%08X\n",
cip_header[0], cip_header[1]); cip_header[0], cip_header[1]);
*data_blocks = 0; data_blocks = 0;
pcm_frames = 0; pcm_frames = 0;
goto end; goto end;
} }
@ -471,7 +487,7 @@ static int handle_in_packet(struct amdtp_stream *s,
dev_info_ratelimited(&s->unit->device, dev_info_ratelimited(&s->unit->device,
"Detect unexpected protocol: %08x %08x\n", "Detect unexpected protocol: %08x %08x\n",
cip_header[0], cip_header[1]); cip_header[0], cip_header[1]);
*data_blocks = 0; data_blocks = 0;
pcm_frames = 0; pcm_frames = 0;
goto end; goto end;
} }
@ -480,7 +496,7 @@ static int handle_in_packet(struct amdtp_stream *s,
fdf = (cip_header[1] & CIP_FDF_MASK) >> CIP_FDF_SHIFT; fdf = (cip_header[1] & CIP_FDF_MASK) >> CIP_FDF_SHIFT;
if (payload_quadlets < 3 || if (payload_quadlets < 3 ||
(fmt == CIP_FMT_AM && fdf == AMDTP_FDF_NO_DATA)) { (fmt == CIP_FMT_AM && fdf == AMDTP_FDF_NO_DATA)) {
*data_blocks = 0; data_blocks = 0;
} else { } else {
data_block_quadlets = data_block_quadlets =
(cip_header[0] & CIP_DBS_MASK) >> CIP_DBS_SHIFT; (cip_header[0] & CIP_DBS_MASK) >> CIP_DBS_SHIFT;
@ -494,12 +510,12 @@ static int handle_in_packet(struct amdtp_stream *s,
if (s->flags & CIP_WRONG_DBS) if (s->flags & CIP_WRONG_DBS)
data_block_quadlets = s->data_block_quadlets; data_block_quadlets = s->data_block_quadlets;
*data_blocks = (payload_quadlets - 2) / data_block_quadlets; data_blocks = (payload_quadlets - 2) / data_block_quadlets;
} }
/* Check data block counter continuity */ /* Check data block counter continuity */
data_block_counter = cip_header[0] & CIP_DBC_MASK; data_block_counter = cip_header[0] & CIP_DBC_MASK;
if (*data_blocks == 0 && (s->flags & CIP_EMPTY_HAS_WRONG_DBC) && if (data_blocks == 0 && (s->flags & CIP_EMPTY_HAS_WRONG_DBC) &&
s->data_block_counter != UINT_MAX) s->data_block_counter != UINT_MAX)
data_block_counter = s->data_block_counter; data_block_counter = s->data_block_counter;
@ -510,10 +526,10 @@ static int handle_in_packet(struct amdtp_stream *s,
} else if (!(s->flags & CIP_DBC_IS_END_EVENT)) { } else if (!(s->flags & CIP_DBC_IS_END_EVENT)) {
lost = data_block_counter != s->data_block_counter; lost = data_block_counter != s->data_block_counter;
} else { } else {
if ((*data_blocks > 0) && (s->tx_dbc_interval > 0)) if (data_blocks > 0 && s->tx_dbc_interval > 0)
dbc_interval = s->tx_dbc_interval; dbc_interval = s->tx_dbc_interval;
else else
dbc_interval = *data_blocks; dbc_interval = data_blocks;
lost = data_block_counter != lost = data_block_counter !=
((s->data_block_counter + dbc_interval) & 0xff); ((s->data_block_counter + dbc_interval) & 0xff);
@ -526,13 +542,14 @@ static int handle_in_packet(struct amdtp_stream *s,
return -EIO; return -EIO;
} }
pcm_frames = s->process_data_blocks(s, buffer + 2, *data_blocks, &syt); syt = be32_to_cpu(buffer[1]) & CIP_SYT_MASK;
pcm_frames = s->process_data_blocks(s, buffer + 2, data_blocks, &syt);
if (s->flags & CIP_DBC_IS_END_EVENT) if (s->flags & CIP_DBC_IS_END_EVENT)
s->data_block_counter = data_block_counter; s->data_block_counter = data_block_counter;
else else
s->data_block_counter = s->data_block_counter =
(data_block_counter + *data_blocks) & 0xff; (data_block_counter + data_blocks) & 0xff;
end: end:
if (queue_in_packet(s) < 0) if (queue_in_packet(s) < 0)
return -EIO; return -EIO;
@ -544,29 +561,50 @@ end:
return 0; return 0;
} }
static void out_stream_callback(struct fw_iso_context *context, u32 cycle, /*
* In CYCLE_TIMER register of IEEE 1394, 7 bits are used to represent second. On
* the other hand, in DMA descriptors of 1394 OHCI, 3 bits are used to represent
* it. Thus, via Linux firewire subsystem, we can get the 3 bits for second.
*/
static inline u32 compute_cycle_count(u32 tstamp)
{
return (((tstamp >> 13) & 0x07) * 8000) + (tstamp & 0x1fff);
}
static inline u32 increment_cycle_count(u32 cycle, unsigned int addend)
{
cycle += addend;
if (cycle >= 8 * CYCLES_PER_SECOND)
cycle -= 8 * CYCLES_PER_SECOND;
return cycle;
}
static inline u32 decrement_cycle_count(u32 cycle, unsigned int subtrahend)
{
if (cycle < subtrahend)
cycle += 8 * CYCLES_PER_SECOND;
return cycle - subtrahend;
}
static void out_stream_callback(struct fw_iso_context *context, u32 tstamp,
size_t header_length, void *header, size_t header_length, void *header,
void *private_data) void *private_data)
{ {
struct amdtp_stream *s = private_data; struct amdtp_stream *s = private_data;
unsigned int i, syt, packets = header_length / 4; unsigned int i, packets = header_length / 4;
unsigned int data_blocks; u32 cycle;
if (s->packet_index < 0) if (s->packet_index < 0)
return; return;
/* cycle = compute_cycle_count(tstamp);
* Compute the cycle of the last queued packet.
* (We need only the four lowest bits for the SYT, so we can ignore /* Align to actual cycle count for the last packet. */
* that bits 0-11 must wrap around at 3072.) cycle = increment_cycle_count(cycle, QUEUE_LENGTH - packets);
*/
cycle += QUEUE_LENGTH - packets;
for (i = 0; i < packets; ++i) { for (i = 0; i < packets; ++i) {
syt = calculate_syt(s, ++cycle); cycle = increment_cycle_count(cycle, 1);
data_blocks = calculate_data_blocks(s, syt); if (handle_out_packet(s, cycle, i) < 0) {
if (handle_out_packet(s, data_blocks, syt) < 0) {
s->packet_index = -1; s->packet_index = -1;
amdtp_stream_pcm_abort(s); amdtp_stream_pcm_abort(s);
return; return;
@ -576,15 +614,15 @@ static void out_stream_callback(struct fw_iso_context *context, u32 cycle,
fw_iso_context_queue_flush(s->context); fw_iso_context_queue_flush(s->context);
} }
static void in_stream_callback(struct fw_iso_context *context, u32 cycle, static void in_stream_callback(struct fw_iso_context *context, u32 tstamp,
size_t header_length, void *header, size_t header_length, void *header,
void *private_data) void *private_data)
{ {
struct amdtp_stream *s = private_data; struct amdtp_stream *s = private_data;
unsigned int p, syt, packets; unsigned int i, packets;
unsigned int payload_quadlets, max_payload_quadlets; unsigned int payload_quadlets, max_payload_quadlets;
unsigned int data_blocks; __be32 *headers = header;
__be32 *buffer, *headers = header; u32 cycle;
if (s->packet_index < 0) if (s->packet_index < 0)
return; return;
@ -592,70 +630,44 @@ static void in_stream_callback(struct fw_iso_context *context, u32 cycle,
/* The number of packets in buffer */ /* The number of packets in buffer */
packets = header_length / IN_PACKET_HEADER_SIZE; packets = header_length / IN_PACKET_HEADER_SIZE;
cycle = compute_cycle_count(tstamp);
/* Align to actual cycle count for the last packet. */
cycle = decrement_cycle_count(cycle, packets);
/* For buffer-over-run prevention. */ /* For buffer-over-run prevention. */
max_payload_quadlets = amdtp_stream_get_max_payload(s) / 4; max_payload_quadlets = amdtp_stream_get_max_payload(s) / 4;
for (p = 0; p < packets; p++) { for (i = 0; i < packets; i++) {
buffer = s->buffer.packets[s->packet_index].buffer; cycle = increment_cycle_count(cycle, 1);
/* The number of quadlets in this packet */ /* The number of quadlets in this packet */
payload_quadlets = payload_quadlets =
(be32_to_cpu(headers[p]) >> ISO_DATA_LENGTH_SHIFT) / 4; (be32_to_cpu(headers[i]) >> ISO_DATA_LENGTH_SHIFT) / 4;
if (payload_quadlets > max_payload_quadlets) { if (payload_quadlets > max_payload_quadlets) {
dev_err(&s->unit->device, dev_err(&s->unit->device,
"Detect jumbo payload: %02x %02x\n", "Detect jumbo payload: %02x %02x\n",
payload_quadlets, max_payload_quadlets); payload_quadlets, max_payload_quadlets);
s->packet_index = -1;
break; break;
} }
syt = be32_to_cpu(buffer[1]) & CIP_SYT_MASK; if (handle_in_packet(s, payload_quadlets, cycle, i) < 0)
if (handle_in_packet(s, payload_quadlets, buffer,
&data_blocks, syt) < 0) {
s->packet_index = -1;
break; break;
}
/* Process sync slave stream */
if (s->sync_slave && s->sync_slave->callbacked) {
if (handle_out_packet(s->sync_slave,
data_blocks, syt) < 0) {
s->packet_index = -1;
break;
}
}
} }
/* Queueing error or detecting discontinuity */ /* Queueing error or detecting invalid payload. */
if (s->packet_index < 0) { if (i < packets) {
s->packet_index = -1;
amdtp_stream_pcm_abort(s); amdtp_stream_pcm_abort(s);
/* Abort sync slave. */
if (s->sync_slave) {
s->sync_slave->packet_index = -1;
amdtp_stream_pcm_abort(s->sync_slave);
}
return; return;
} }
/* when sync to device, flush the packets for slave stream */
if (s->sync_slave && s->sync_slave->callbacked)
fw_iso_context_queue_flush(s->sync_slave->context);
fw_iso_context_queue_flush(s->context); fw_iso_context_queue_flush(s->context);
} }
/* processing is done by master callback */
static void slave_stream_callback(struct fw_iso_context *context, u32 cycle,
size_t header_length, void *header,
void *private_data)
{
return;
}
/* this is executed one time */ /* this is executed one time */
static void amdtp_stream_first_callback(struct fw_iso_context *context, static void amdtp_stream_first_callback(struct fw_iso_context *context,
u32 cycle, size_t header_length, u32 tstamp, size_t header_length,
void *header, void *private_data) void *header, void *private_data)
{ {
struct amdtp_stream *s = private_data; struct amdtp_stream *s = private_data;
@ -669,12 +681,10 @@ static void amdtp_stream_first_callback(struct fw_iso_context *context,
if (s->direction == AMDTP_IN_STREAM) if (s->direction == AMDTP_IN_STREAM)
context->callback.sc = in_stream_callback; context->callback.sc = in_stream_callback;
else if (s->flags & CIP_SYNC_TO_DEVICE)
context->callback.sc = slave_stream_callback;
else else
context->callback.sc = out_stream_callback; context->callback.sc = out_stream_callback;
context->callback.sc(context, cycle, header_length, header, s); context->callback.sc(context, tstamp, header_length, header, s);
} }
/** /**
@ -713,8 +723,7 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
goto err_unlock; goto err_unlock;
} }
if (s->direction == AMDTP_IN_STREAM && if (s->direction == AMDTP_IN_STREAM)
s->flags & CIP_SKIP_INIT_DBC_CHECK)
s->data_block_counter = UINT_MAX; s->data_block_counter = UINT_MAX;
else else
s->data_block_counter = 0; s->data_block_counter = 0;
@ -755,7 +764,7 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
if (s->direction == AMDTP_IN_STREAM) if (s->direction == AMDTP_IN_STREAM)
err = queue_in_packet(s); err = queue_in_packet(s);
else else
err = queue_out_packet(s, 0, true); err = queue_out_packet(s, 0);
if (err < 0) if (err < 0)
goto err_context; goto err_context;
} while (s->packet_index > 0); } while (s->packet_index > 0);
@ -794,11 +803,24 @@ EXPORT_SYMBOL(amdtp_stream_start);
*/ */
unsigned long amdtp_stream_pcm_pointer(struct amdtp_stream *s) unsigned long amdtp_stream_pcm_pointer(struct amdtp_stream *s)
{ {
/* this optimization is allowed to be racy */ /*
if (s->pointer_flush && amdtp_stream_running(s)) * This function is called in software IRQ context of period_tasklet or
* process context.
*
* When the software IRQ context was scheduled by software IRQ context
* of IR/IT contexts, queued packets were already handled. Therefore,
* no need to flush the queue in buffer anymore.
*
* When the process context reach here, some packets will be already
* queued in the buffer. These packets should be handled immediately
* to keep better granularity of PCM pointer.
*
* Later, the process context will sometimes schedules software IRQ
* context of the period_tasklet. Then, no need to flush the queue by
* the same reason as described for IR/IT contexts.
*/
if (!in_interrupt() && amdtp_stream_running(s))
fw_iso_context_flush_completions(s->context); fw_iso_context_flush_completions(s->context);
else
s->pointer_flush = true;
return ACCESS_ONCE(s->pcm_buffer_pointer); return ACCESS_ONCE(s->pcm_buffer_pointer);
} }

View File

@ -17,8 +17,6 @@
* @CIP_BLOCKING: In blocking mode, each packet contains either zero or * @CIP_BLOCKING: In blocking mode, each packet contains either zero or
* SYT_INTERVAL samples, with these two types alternating so that * SYT_INTERVAL samples, with these two types alternating so that
* the overall sample rate comes out right. * the overall sample rate comes out right.
* @CIP_SYNC_TO_DEVICE: In sync to device mode, time stamp in out packets is
* generated by in packets. Defaultly this driver generates timestamp.
* @CIP_EMPTY_WITH_TAG0: Only for in-stream. Empty in-packets have TAG0. * @CIP_EMPTY_WITH_TAG0: Only for in-stream. Empty in-packets have TAG0.
* @CIP_DBC_IS_END_EVENT: Only for in-stream. The value of dbc in an in-packet * @CIP_DBC_IS_END_EVENT: Only for in-stream. The value of dbc in an in-packet
* corresponds to the end of event in the packet. Out of IEC 61883. * corresponds to the end of event in the packet. Out of IEC 61883.
@ -26,8 +24,6 @@
* The value of data_block_quadlets is used instead of reported value. * The value of data_block_quadlets is used instead of reported value.
* @CIP_SKIP_DBC_ZERO_CHECK: Only for in-stream. Packets with zero in dbc is * @CIP_SKIP_DBC_ZERO_CHECK: Only for in-stream. Packets with zero in dbc is
* skipped for detecting discontinuity. * skipped for detecting discontinuity.
* @CIP_SKIP_INIT_DBC_CHECK: Only for in-stream. The value of dbc in first
* packet is not continuous from an initial value.
* @CIP_EMPTY_HAS_WRONG_DBC: Only for in-stream. The value of dbc in empty * @CIP_EMPTY_HAS_WRONG_DBC: Only for in-stream. The value of dbc in empty
* packet is wrong but the others are correct. * packet is wrong but the others are correct.
* @CIP_JUMBO_PAYLOAD: Only for in-stream. The number of data blocks in an * @CIP_JUMBO_PAYLOAD: Only for in-stream. The number of data blocks in an
@ -37,14 +33,12 @@
enum cip_flags { enum cip_flags {
CIP_NONBLOCKING = 0x00, CIP_NONBLOCKING = 0x00,
CIP_BLOCKING = 0x01, CIP_BLOCKING = 0x01,
CIP_SYNC_TO_DEVICE = 0x02, CIP_EMPTY_WITH_TAG0 = 0x02,
CIP_EMPTY_WITH_TAG0 = 0x04, CIP_DBC_IS_END_EVENT = 0x04,
CIP_DBC_IS_END_EVENT = 0x08, CIP_WRONG_DBS = 0x08,
CIP_WRONG_DBS = 0x10, CIP_SKIP_DBC_ZERO_CHECK = 0x10,
CIP_SKIP_DBC_ZERO_CHECK = 0x20, CIP_EMPTY_HAS_WRONG_DBC = 0x20,
CIP_SKIP_INIT_DBC_CHECK = 0x40, CIP_JUMBO_PAYLOAD = 0x40,
CIP_EMPTY_HAS_WRONG_DBC = 0x80,
CIP_JUMBO_PAYLOAD = 0x100,
}; };
/** /**
@ -132,12 +126,10 @@ struct amdtp_stream {
struct tasklet_struct period_tasklet; struct tasklet_struct period_tasklet;
unsigned int pcm_buffer_pointer; unsigned int pcm_buffer_pointer;
unsigned int pcm_period_pointer; unsigned int pcm_period_pointer;
bool pointer_flush;
/* To wait for first packet. */ /* To wait for first packet. */
bool callbacked; bool callbacked;
wait_queue_head_t callback_wait; wait_queue_head_t callback_wait;
struct amdtp_stream *sync_slave;
/* For backends to process data blocks. */ /* For backends to process data blocks. */
void *protocol; void *protocol;
@ -223,23 +215,6 @@ static inline bool cip_sfc_is_base_44100(enum cip_sfc sfc)
return sfc & 1; return sfc & 1;
} }
static inline void amdtp_stream_set_sync(enum cip_flags sync_mode,
struct amdtp_stream *master,
struct amdtp_stream *slave)
{
if (sync_mode == CIP_SYNC_TO_DEVICE) {
master->flags |= CIP_SYNC_TO_DEVICE;
slave->flags |= CIP_SYNC_TO_DEVICE;
master->sync_slave = slave;
} else {
master->flags &= ~CIP_SYNC_TO_DEVICE;
slave->flags &= ~CIP_SYNC_TO_DEVICE;
master->sync_slave = NULL;
}
slave->sync_slave = NULL;
}
/** /**
* amdtp_stream_wait_callback - sleep till callbacked or timeout * amdtp_stream_wait_callback - sleep till callbacked or timeout
* @s: the AMDTP stream * @s: the AMDTP stream

View File

@ -67,7 +67,7 @@ static DECLARE_BITMAP(devices_used, SNDRV_CARDS);
#define MODEL_MAUDIO_PROJECTMIX 0x00010091 #define MODEL_MAUDIO_PROJECTMIX 0x00010091
static int static int
name_device(struct snd_bebob *bebob, unsigned int vendor_id) name_device(struct snd_bebob *bebob)
{ {
struct fw_device *fw_dev = fw_parent_device(bebob->unit); struct fw_device *fw_dev = fw_parent_device(bebob->unit);
char vendor[24] = {0}; char vendor[24] = {0};
@ -126,6 +126,17 @@ end:
return err; return err;
} }
static void bebob_free(struct snd_bebob *bebob)
{
snd_bebob_stream_destroy_duplex(bebob);
fw_unit_put(bebob->unit);
kfree(bebob->maudio_special_quirk);
mutex_destroy(&bebob->mutex);
kfree(bebob);
}
/* /*
* This module releases the FireWire unit data after all ALSA character devices * This module releases the FireWire unit data after all ALSA character devices
* are released by applications. This is for releasing stream data or finishing * are released by applications. This is for releasing stream data or finishing
@ -137,18 +148,11 @@ bebob_card_free(struct snd_card *card)
{ {
struct snd_bebob *bebob = card->private_data; struct snd_bebob *bebob = card->private_data;
snd_bebob_stream_destroy_duplex(bebob); mutex_lock(&devices_mutex);
fw_unit_put(bebob->unit); clear_bit(bebob->card_index, devices_used);
mutex_unlock(&devices_mutex);
kfree(bebob->maudio_special_quirk); bebob_free(card->private_data);
if (bebob->card_index >= 0) {
mutex_lock(&devices_mutex);
clear_bit(bebob->card_index, devices_used);
mutex_unlock(&devices_mutex);
}
mutex_destroy(&bebob->mutex);
} }
static const struct snd_bebob_spec * static const struct snd_bebob_spec *
@ -176,16 +180,17 @@ check_audiophile_booted(struct fw_unit *unit)
return strncmp(name, "FW Audiophile Bootloader", 15) != 0; return strncmp(name, "FW Audiophile Bootloader", 15) != 0;
} }
static int static void
bebob_probe(struct fw_unit *unit, do_registration(struct work_struct *work)
const struct ieee1394_device_id *entry)
{ {
struct snd_card *card; struct snd_bebob *bebob =
struct snd_bebob *bebob; container_of(work, struct snd_bebob, dwork.work);
const struct snd_bebob_spec *spec;
unsigned int card_index; unsigned int card_index;
int err; int err;
if (bebob->registered)
return;
mutex_lock(&devices_mutex); mutex_lock(&devices_mutex);
for (card_index = 0; card_index < SNDRV_CARDS; card_index++) { for (card_index = 0; card_index < SNDRV_CARDS; card_index++) {
@ -193,64 +198,39 @@ bebob_probe(struct fw_unit *unit,
break; break;
} }
if (card_index >= SNDRV_CARDS) { if (card_index >= SNDRV_CARDS) {
err = -ENOENT; mutex_unlock(&devices_mutex);
goto end; return;
} }
if ((entry->vendor_id == VEN_FOCUSRITE) && err = snd_card_new(&bebob->unit->device, index[card_index],
(entry->model_id == MODEL_FOCUSRITE_SAFFIRE_BOTH)) id[card_index], THIS_MODULE, 0, &bebob->card);
spec = get_saffire_spec(unit); if (err < 0) {
else if ((entry->vendor_id == VEN_MAUDIO1) && mutex_unlock(&devices_mutex);
(entry->model_id == MODEL_MAUDIO_AUDIOPHILE_BOTH) && return;
!check_audiophile_booted(unit))
spec = NULL;
else
spec = (const struct snd_bebob_spec *)entry->driver_data;
if (spec == NULL) {
if ((entry->vendor_id == VEN_MAUDIO1) ||
(entry->vendor_id == VEN_MAUDIO2))
err = snd_bebob_maudio_load_firmware(unit);
else
err = -ENOSYS;
goto end;
} }
err = snd_card_new(&unit->device, index[card_index], id[card_index], err = name_device(bebob);
THIS_MODULE, sizeof(struct snd_bebob), &card);
if (err < 0)
goto end;
bebob = card->private_data;
bebob->card_index = card_index;
set_bit(card_index, devices_used);
card->private_free = bebob_card_free;
bebob->card = card;
bebob->unit = fw_unit_get(unit);
bebob->spec = spec;
mutex_init(&bebob->mutex);
spin_lock_init(&bebob->lock);
init_waitqueue_head(&bebob->hwdep_wait);
err = name_device(bebob, entry->vendor_id);
if (err < 0) if (err < 0)
goto error; goto error;
if ((entry->vendor_id == VEN_MAUDIO1) && if (bebob->spec == &maudio_special_spec) {
(entry->model_id == MODEL_MAUDIO_FW1814)) if (bebob->entry->model_id == MODEL_MAUDIO_FW1814)
err = snd_bebob_maudio_special_discover(bebob, true); err = snd_bebob_maudio_special_discover(bebob, true);
else if ((entry->vendor_id == VEN_MAUDIO1) && else
(entry->model_id == MODEL_MAUDIO_PROJECTMIX)) err = snd_bebob_maudio_special_discover(bebob, false);
err = snd_bebob_maudio_special_discover(bebob, false); } else {
else
err = snd_bebob_stream_discover(bebob); err = snd_bebob_stream_discover(bebob);
}
if (err < 0)
goto error;
err = snd_bebob_stream_init_duplex(bebob);
if (err < 0) if (err < 0)
goto error; goto error;
snd_bebob_proc_init(bebob); snd_bebob_proc_init(bebob);
if ((bebob->midi_input_ports > 0) || if (bebob->midi_input_ports > 0 || bebob->midi_output_ports > 0) {
(bebob->midi_output_ports > 0)) {
err = snd_bebob_create_midi_devices(bebob); err = snd_bebob_create_midi_devices(bebob);
if (err < 0) if (err < 0)
goto error; goto error;
@ -264,16 +244,75 @@ bebob_probe(struct fw_unit *unit,
if (err < 0) if (err < 0)
goto error; goto error;
err = snd_bebob_stream_init_duplex(bebob); err = snd_card_register(bebob->card);
if (err < 0) if (err < 0)
goto error; goto error;
if (!bebob->maudio_special_quirk) { set_bit(card_index, devices_used);
err = snd_card_register(card); mutex_unlock(&devices_mutex);
if (err < 0) {
snd_bebob_stream_destroy_duplex(bebob); /*
goto error; * After registered, bebob instance can be released corresponding to
} * releasing the sound card instance.
*/
bebob->card->private_free = bebob_card_free;
bebob->card->private_data = bebob;
bebob->registered = true;
return;
error:
mutex_unlock(&devices_mutex);
snd_bebob_stream_destroy_duplex(bebob);
snd_card_free(bebob->card);
dev_info(&bebob->unit->device,
"Sound card registration failed: %d\n", err);
}
static int
bebob_probe(struct fw_unit *unit, const struct ieee1394_device_id *entry)
{
struct snd_bebob *bebob;
const struct snd_bebob_spec *spec;
if (entry->vendor_id == VEN_FOCUSRITE &&
entry->model_id == MODEL_FOCUSRITE_SAFFIRE_BOTH)
spec = get_saffire_spec(unit);
else if (entry->vendor_id == VEN_MAUDIO1 &&
entry->model_id == MODEL_MAUDIO_AUDIOPHILE_BOTH &&
!check_audiophile_booted(unit))
spec = NULL;
else
spec = (const struct snd_bebob_spec *)entry->driver_data;
if (spec == NULL) {
if (entry->vendor_id == VEN_MAUDIO1 ||
entry->vendor_id == VEN_MAUDIO2)
return snd_bebob_maudio_load_firmware(unit);
else
return -ENODEV;
}
/* Allocate this independent of sound card instance. */
bebob = kzalloc(sizeof(struct snd_bebob), GFP_KERNEL);
if (bebob == NULL)
return -ENOMEM;
bebob->unit = fw_unit_get(unit);
bebob->entry = entry;
bebob->spec = spec;
dev_set_drvdata(&unit->device, bebob);
mutex_init(&bebob->mutex);
spin_lock_init(&bebob->lock);
init_waitqueue_head(&bebob->hwdep_wait);
/* Allocate and register this sound card later. */
INIT_DEFERRABLE_WORK(&bebob->dwork, do_registration);
if (entry->vendor_id != VEN_MAUDIO1 ||
(entry->model_id != MODEL_MAUDIO_FW1814 &&
entry->model_id != MODEL_MAUDIO_PROJECTMIX)) {
snd_fw_schedule_registration(unit, &bebob->dwork);
} else { } else {
/* /*
* This is a workaround. This bus reset seems to have an effect * This is a workaround. This bus reset seems to have an effect
@ -285,19 +324,11 @@ bebob_probe(struct fw_unit *unit,
* signals from dbus and starts I/Os. To avoid I/Os till the * signals from dbus and starts I/Os. To avoid I/Os till the
* future bus reset, registration is done in next update(). * future bus reset, registration is done in next update().
*/ */
bebob->deferred_registration = true;
fw_schedule_bus_reset(fw_parent_device(bebob->unit)->card, fw_schedule_bus_reset(fw_parent_device(bebob->unit)->card,
false, true); false, true);
} }
dev_set_drvdata(&unit->device, bebob); return 0;
end:
mutex_unlock(&devices_mutex);
return err;
error:
mutex_unlock(&devices_mutex);
snd_card_free(card);
return err;
} }
/* /*
@ -324,15 +355,11 @@ bebob_update(struct fw_unit *unit)
if (bebob == NULL) if (bebob == NULL)
return; return;
fcp_bus_reset(bebob->unit); /* Postpone a workqueue for deferred registration. */
if (!bebob->registered)
if (bebob->deferred_registration) { snd_fw_schedule_registration(unit, &bebob->dwork);
if (snd_card_register(bebob->card) < 0) { else
snd_bebob_stream_destroy_duplex(bebob); fcp_bus_reset(bebob->unit);
snd_card_free(bebob->card);
}
bebob->deferred_registration = false;
}
} }
static void bebob_remove(struct fw_unit *unit) static void bebob_remove(struct fw_unit *unit)
@ -342,8 +369,20 @@ static void bebob_remove(struct fw_unit *unit)
if (bebob == NULL) if (bebob == NULL)
return; return;
/* No need to wait for releasing card object in this context. */ /*
snd_card_free_when_closed(bebob->card); * Confirm to stop the work for registration before the sound card is
* going to be released. The work is not scheduled again because bus
* reset handler is not called anymore.
*/
cancel_delayed_work_sync(&bebob->dwork);
if (bebob->registered) {
/* No need to wait for releasing card object in this context. */
snd_card_free_when_closed(bebob->card);
} else {
/* Don't forget this case. */
bebob_free(bebob);
}
} }
static const struct snd_bebob_rate_spec normal_rate_spec = { static const struct snd_bebob_rate_spec normal_rate_spec = {

View File

@ -83,6 +83,10 @@ struct snd_bebob {
struct mutex mutex; struct mutex mutex;
spinlock_t lock; spinlock_t lock;
bool registered;
struct delayed_work dwork;
const struct ieee1394_device_id *entry;
const struct snd_bebob_spec *spec; const struct snd_bebob_spec *spec;
unsigned int midi_input_ports; unsigned int midi_input_ports;
@ -90,7 +94,6 @@ struct snd_bebob {
bool connected; bool connected;
struct amdtp_stream *master;
struct amdtp_stream tx_stream; struct amdtp_stream tx_stream;
struct amdtp_stream rx_stream; struct amdtp_stream rx_stream;
struct cmp_connection out_conn; struct cmp_connection out_conn;
@ -111,7 +114,6 @@ struct snd_bebob {
/* for M-Audio special devices */ /* for M-Audio special devices */
void *maudio_special_quirk; void *maudio_special_quirk;
bool deferred_registration;
/* For BeBoB version quirk. */ /* For BeBoB version quirk. */
unsigned int version; unsigned int version;

View File

@ -483,30 +483,6 @@ destroy_both_connections(struct snd_bebob *bebob)
cmp_connection_destroy(&bebob->out_conn); cmp_connection_destroy(&bebob->out_conn);
} }
static int
get_sync_mode(struct snd_bebob *bebob, enum cip_flags *sync_mode)
{
enum snd_bebob_clock_type src;
int err;
err = snd_bebob_stream_get_clock_src(bebob, &src);
if (err < 0)
return err;
switch (src) {
case SND_BEBOB_CLOCK_TYPE_INTERNAL:
case SND_BEBOB_CLOCK_TYPE_EXTERNAL:
*sync_mode = CIP_SYNC_TO_DEVICE;
break;
default:
case SND_BEBOB_CLOCK_TYPE_SYT:
*sync_mode = 0;
break;
}
return 0;
}
static int static int
start_stream(struct snd_bebob *bebob, struct amdtp_stream *stream, start_stream(struct snd_bebob *bebob, struct amdtp_stream *stream,
unsigned int rate) unsigned int rate)
@ -550,8 +526,6 @@ int snd_bebob_stream_init_duplex(struct snd_bebob *bebob)
goto end; goto end;
} }
bebob->tx_stream.flags |= CIP_SKIP_INIT_DBC_CHECK;
/* /*
* BeBoB v3 transfers packets with these qurks: * BeBoB v3 transfers packets with these qurks:
* - In the beginning of streaming, the value of dbc is incremented * - In the beginning of streaming, the value of dbc is incremented
@ -584,8 +558,6 @@ end:
int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate) int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate)
{ {
const struct snd_bebob_rate_spec *rate_spec = bebob->spec->rate; const struct snd_bebob_rate_spec *rate_spec = bebob->spec->rate;
struct amdtp_stream *master, *slave;
enum cip_flags sync_mode;
unsigned int curr_rate; unsigned int curr_rate;
int err = 0; int err = 0;
@ -593,22 +565,11 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate)
if (bebob->substreams_counter == 0) if (bebob->substreams_counter == 0)
goto end; goto end;
err = get_sync_mode(bebob, &sync_mode);
if (err < 0)
goto end;
if (sync_mode == CIP_SYNC_TO_DEVICE) {
master = &bebob->tx_stream;
slave = &bebob->rx_stream;
} else {
master = &bebob->rx_stream;
slave = &bebob->tx_stream;
}
/* /*
* Considering JACK/FFADO streaming: * Considering JACK/FFADO streaming:
* TODO: This can be removed hwdep functionality becomes popular. * TODO: This can be removed hwdep functionality becomes popular.
*/ */
err = check_connection_used_by_others(bebob, master); err = check_connection_used_by_others(bebob, &bebob->rx_stream);
if (err < 0) if (err < 0)
goto end; goto end;
@ -618,11 +579,12 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate)
* At bus reset, connections should not be broken here. So streams need * At bus reset, connections should not be broken here. So streams need
* to be re-started. This is a reason to use SKIP_INIT_DBC_CHECK flag. * to be re-started. This is a reason to use SKIP_INIT_DBC_CHECK flag.
*/ */
if (amdtp_streaming_error(master)) if (amdtp_streaming_error(&bebob->rx_stream))
amdtp_stream_stop(master); amdtp_stream_stop(&bebob->rx_stream);
if (amdtp_streaming_error(slave)) if (amdtp_streaming_error(&bebob->tx_stream))
amdtp_stream_stop(slave); amdtp_stream_stop(&bebob->tx_stream);
if (!amdtp_stream_running(master) && !amdtp_stream_running(slave)) if (!amdtp_stream_running(&bebob->rx_stream) &&
!amdtp_stream_running(&bebob->tx_stream))
break_both_connections(bebob); break_both_connections(bebob);
/* stop streams if rate is different */ /* stop streams if rate is different */
@ -635,16 +597,13 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate)
if (rate == 0) if (rate == 0)
rate = curr_rate; rate = curr_rate;
if (rate != curr_rate) { if (rate != curr_rate) {
amdtp_stream_stop(master); amdtp_stream_stop(&bebob->rx_stream);
amdtp_stream_stop(slave); amdtp_stream_stop(&bebob->tx_stream);
break_both_connections(bebob); break_both_connections(bebob);
} }
/* master should be always running */ /* master should be always running */
if (!amdtp_stream_running(master)) { if (!amdtp_stream_running(&bebob->rx_stream)) {
amdtp_stream_set_sync(sync_mode, master, slave);
bebob->master = master;
/* /*
* NOTE: * NOTE:
* If establishing connections at first, Yamaha GO46 * If establishing connections at first, Yamaha GO46
@ -666,7 +625,7 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate)
if (err < 0) if (err < 0)
goto end; goto end;
err = start_stream(bebob, master, rate); err = start_stream(bebob, &bebob->rx_stream, rate);
if (err < 0) { if (err < 0) {
dev_err(&bebob->unit->device, dev_err(&bebob->unit->device,
"fail to run AMDTP master stream:%d\n", err); "fail to run AMDTP master stream:%d\n", err);
@ -685,15 +644,16 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate)
dev_err(&bebob->unit->device, dev_err(&bebob->unit->device,
"fail to ensure sampling rate: %d\n", "fail to ensure sampling rate: %d\n",
err); err);
amdtp_stream_stop(master); amdtp_stream_stop(&bebob->rx_stream);
break_both_connections(bebob); break_both_connections(bebob);
goto end; goto end;
} }
} }
/* wait first callback */ /* wait first callback */
if (!amdtp_stream_wait_callback(master, CALLBACK_TIMEOUT)) { if (!amdtp_stream_wait_callback(&bebob->rx_stream,
amdtp_stream_stop(master); CALLBACK_TIMEOUT)) {
amdtp_stream_stop(&bebob->rx_stream);
break_both_connections(bebob); break_both_connections(bebob);
err = -ETIMEDOUT; err = -ETIMEDOUT;
goto end; goto end;
@ -701,20 +661,21 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate)
} }
/* start slave if needed */ /* start slave if needed */
if (!amdtp_stream_running(slave)) { if (!amdtp_stream_running(&bebob->tx_stream)) {
err = start_stream(bebob, slave, rate); err = start_stream(bebob, &bebob->tx_stream, rate);
if (err < 0) { if (err < 0) {
dev_err(&bebob->unit->device, dev_err(&bebob->unit->device,
"fail to run AMDTP slave stream:%d\n", err); "fail to run AMDTP slave stream:%d\n", err);
amdtp_stream_stop(master); amdtp_stream_stop(&bebob->rx_stream);
break_both_connections(bebob); break_both_connections(bebob);
goto end; goto end;
} }
/* wait first callback */ /* wait first callback */
if (!amdtp_stream_wait_callback(slave, CALLBACK_TIMEOUT)) { if (!amdtp_stream_wait_callback(&bebob->tx_stream,
amdtp_stream_stop(slave); CALLBACK_TIMEOUT)) {
amdtp_stream_stop(master); amdtp_stream_stop(&bebob->tx_stream);
amdtp_stream_stop(&bebob->rx_stream);
break_both_connections(bebob); break_both_connections(bebob);
err = -ETIMEDOUT; err = -ETIMEDOUT;
} }
@ -725,22 +686,12 @@ end:
void snd_bebob_stream_stop_duplex(struct snd_bebob *bebob) void snd_bebob_stream_stop_duplex(struct snd_bebob *bebob)
{ {
struct amdtp_stream *master, *slave;
if (bebob->master == &bebob->rx_stream) {
slave = &bebob->tx_stream;
master = &bebob->rx_stream;
} else {
slave = &bebob->rx_stream;
master = &bebob->tx_stream;
}
if (bebob->substreams_counter == 0) { if (bebob->substreams_counter == 0) {
amdtp_stream_pcm_abort(master); amdtp_stream_pcm_abort(&bebob->rx_stream);
amdtp_stream_stop(master); amdtp_stream_stop(&bebob->rx_stream);
amdtp_stream_pcm_abort(slave); amdtp_stream_pcm_abort(&bebob->tx_stream);
amdtp_stream_stop(slave); amdtp_stream_stop(&bebob->tx_stream);
break_both_connections(bebob); break_both_connections(bebob);
} }

View File

@ -20,8 +20,6 @@ MODULE_LICENSE("GPL v2");
#define WEISS_CATEGORY_ID 0x00 #define WEISS_CATEGORY_ID 0x00
#define LOUD_CATEGORY_ID 0x10 #define LOUD_CATEGORY_ID 0x10
#define PROBE_DELAY_MS (2 * MSEC_PER_SEC)
/* /*
* Some models support several isochronous channels, while these streams are not * Some models support several isochronous channels, while these streams are not
* always available. In this case, add the model name to this list. * always available. In this case, add the model name to this list.
@ -201,6 +199,10 @@ static void do_registration(struct work_struct *work)
dice_card_strings(dice); dice_card_strings(dice);
err = snd_dice_stream_init_duplex(dice);
if (err < 0)
goto error;
snd_dice_create_proc(dice); snd_dice_create_proc(dice);
err = snd_dice_create_pcm(dice); err = snd_dice_create_pcm(dice);
@ -229,28 +231,14 @@ static void do_registration(struct work_struct *work)
return; return;
error: error:
snd_dice_stream_destroy_duplex(dice);
snd_dice_transaction_destroy(dice); snd_dice_transaction_destroy(dice);
snd_dice_stream_destroy_duplex(dice);
snd_card_free(dice->card); snd_card_free(dice->card);
dev_info(&dice->unit->device, dev_info(&dice->unit->device,
"Sound card registration failed: %d\n", err); "Sound card registration failed: %d\n", err);
} }
static void schedule_registration(struct snd_dice *dice)
{
struct fw_card *fw_card = fw_parent_device(dice->unit)->card;
u64 now, delay;
now = get_jiffies_64();
delay = fw_card->reset_jiffies + msecs_to_jiffies(PROBE_DELAY_MS);
if (time_after64(delay, now))
delay -= now;
else
delay = 0;
mod_delayed_work(system_wq, &dice->dwork, delay);
}
static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id) static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id)
{ {
struct snd_dice *dice; struct snd_dice *dice;
@ -273,15 +261,9 @@ static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id)
init_completion(&dice->clock_accepted); init_completion(&dice->clock_accepted);
init_waitqueue_head(&dice->hwdep_wait); init_waitqueue_head(&dice->hwdep_wait);
err = snd_dice_stream_init_duplex(dice);
if (err < 0) {
dice_free(dice);
return err;
}
/* Allocate and register this sound card later. */ /* Allocate and register this sound card later. */
INIT_DEFERRABLE_WORK(&dice->dwork, do_registration); INIT_DEFERRABLE_WORK(&dice->dwork, do_registration);
schedule_registration(dice); snd_fw_schedule_registration(unit, &dice->dwork);
return 0; return 0;
} }
@ -312,7 +294,7 @@ static void dice_bus_reset(struct fw_unit *unit)
/* Postpone a workqueue for deferred registration. */ /* Postpone a workqueue for deferred registration. */
if (!dice->registered) if (!dice->registered)
schedule_registration(dice); snd_fw_schedule_registration(unit, &dice->dwork);
/* The handler address register becomes initialized. */ /* The handler address register becomes initialized. */
snd_dice_transaction_reinit(dice); snd_dice_transaction_reinit(dice);
@ -335,6 +317,13 @@ static const struct ieee1394_device_id dice_id_table[] = {
.match_flags = IEEE1394_MATCH_VERSION, .match_flags = IEEE1394_MATCH_VERSION,
.version = DICE_INTERFACE, .version = DICE_INTERFACE,
}, },
/* M-Audio Profire 610/2626 has a different value in version field. */
{
.match_flags = IEEE1394_MATCH_VENDOR_ID |
IEEE1394_MATCH_SPECIFIER_ID,
.vendor_id = 0x000d6c,
.specifier_id = 0x000d6c,
},
{ } { }
}; };
MODULE_DEVICE_TABLE(ieee1394, dice_id_table); MODULE_DEVICE_TABLE(ieee1394, dice_id_table);

View File

@ -421,7 +421,7 @@ int amdtp_dot_init(struct amdtp_stream *s, struct fw_unit *unit,
/* Use different mode between incoming/outgoing. */ /* Use different mode between incoming/outgoing. */
if (dir == AMDTP_IN_STREAM) { if (dir == AMDTP_IN_STREAM) {
flags = CIP_NONBLOCKING | CIP_SKIP_INIT_DBC_CHECK; flags = CIP_NONBLOCKING;
process_data_blocks = process_tx_data_blocks; process_data_blocks = process_tx_data_blocks;
} else { } else {
flags = CIP_BLOCKING; flags = CIP_BLOCKING;

View File

@ -126,12 +126,17 @@ int snd_dg00x_transaction_register(struct snd_dg00x *dg00x)
return err; return err;
error: error:
fw_core_remove_address_handler(&dg00x->async_handler); fw_core_remove_address_handler(&dg00x->async_handler);
dg00x->async_handler.address_callback = NULL; dg00x->async_handler.callback_data = NULL;
return err; return err;
} }
void snd_dg00x_transaction_unregister(struct snd_dg00x *dg00x) void snd_dg00x_transaction_unregister(struct snd_dg00x *dg00x)
{ {
if (dg00x->async_handler.callback_data == NULL)
return;
snd_fw_async_midi_port_destroy(&dg00x->out_control); snd_fw_async_midi_port_destroy(&dg00x->out_control);
fw_core_remove_address_handler(&dg00x->async_handler); fw_core_remove_address_handler(&dg00x->async_handler);
dg00x->async_handler.callback_data = NULL;
} }

View File

@ -40,10 +40,8 @@ static int name_card(struct snd_dg00x *dg00x)
return 0; return 0;
} }
static void dg00x_card_free(struct snd_card *card) static void dg00x_free(struct snd_dg00x *dg00x)
{ {
struct snd_dg00x *dg00x = card->private_data;
snd_dg00x_stream_destroy_duplex(dg00x); snd_dg00x_stream_destroy_duplex(dg00x);
snd_dg00x_transaction_unregister(dg00x); snd_dg00x_transaction_unregister(dg00x);
@ -52,28 +50,24 @@ static void dg00x_card_free(struct snd_card *card)
mutex_destroy(&dg00x->mutex); mutex_destroy(&dg00x->mutex);
} }
static int snd_dg00x_probe(struct fw_unit *unit, static void dg00x_card_free(struct snd_card *card)
const struct ieee1394_device_id *entry)
{ {
struct snd_card *card; dg00x_free(card->private_data);
struct snd_dg00x *dg00x; }
static void do_registration(struct work_struct *work)
{
struct snd_dg00x *dg00x =
container_of(work, struct snd_dg00x, dwork.work);
int err; int err;
/* create card */ if (dg00x->registered)
err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE, return;
sizeof(struct snd_dg00x), &card);
err = snd_card_new(&dg00x->unit->device, -1, NULL, THIS_MODULE, 0,
&dg00x->card);
if (err < 0) if (err < 0)
return err; return;
card->private_free = dg00x_card_free;
/* initialize myself */
dg00x = card->private_data;
dg00x->card = card;
dg00x->unit = fw_unit_get(unit);
mutex_init(&dg00x->mutex);
spin_lock_init(&dg00x->lock);
init_waitqueue_head(&dg00x->hwdep_wait);
err = name_card(dg00x); err = name_card(dg00x);
if (err < 0) if (err < 0)
@ -101,35 +95,86 @@ static int snd_dg00x_probe(struct fw_unit *unit,
if (err < 0) if (err < 0)
goto error; goto error;
err = snd_card_register(card); err = snd_card_register(dg00x->card);
if (err < 0) if (err < 0)
goto error; goto error;
dg00x->card->private_free = dg00x_card_free;
dg00x->card->private_data = dg00x;
dg00x->registered = true;
return;
error:
snd_dg00x_transaction_unregister(dg00x);
snd_dg00x_stream_destroy_duplex(dg00x);
snd_card_free(dg00x->card);
dev_info(&dg00x->unit->device,
"Sound card registration failed: %d\n", err);
}
static int snd_dg00x_probe(struct fw_unit *unit,
const struct ieee1394_device_id *entry)
{
struct snd_dg00x *dg00x;
/* Allocate this independent of sound card instance. */
dg00x = kzalloc(sizeof(struct snd_dg00x), GFP_KERNEL);
if (dg00x == NULL)
return -ENOMEM;
dg00x->unit = fw_unit_get(unit);
dev_set_drvdata(&unit->device, dg00x); dev_set_drvdata(&unit->device, dg00x);
return err; mutex_init(&dg00x->mutex);
error: spin_lock_init(&dg00x->lock);
snd_card_free(card); init_waitqueue_head(&dg00x->hwdep_wait);
return err;
/* Allocate and register this sound card later. */
INIT_DEFERRABLE_WORK(&dg00x->dwork, do_registration);
snd_fw_schedule_registration(unit, &dg00x->dwork);
return 0;
} }
static void snd_dg00x_update(struct fw_unit *unit) static void snd_dg00x_update(struct fw_unit *unit)
{ {
struct snd_dg00x *dg00x = dev_get_drvdata(&unit->device); struct snd_dg00x *dg00x = dev_get_drvdata(&unit->device);
/* Postpone a workqueue for deferred registration. */
if (!dg00x->registered)
snd_fw_schedule_registration(unit, &dg00x->dwork);
snd_dg00x_transaction_reregister(dg00x); snd_dg00x_transaction_reregister(dg00x);
mutex_lock(&dg00x->mutex); /*
snd_dg00x_stream_update_duplex(dg00x); * After registration, userspace can start packet streaming, then this
mutex_unlock(&dg00x->mutex); * code block works fine.
*/
if (dg00x->registered) {
mutex_lock(&dg00x->mutex);
snd_dg00x_stream_update_duplex(dg00x);
mutex_unlock(&dg00x->mutex);
}
} }
static void snd_dg00x_remove(struct fw_unit *unit) static void snd_dg00x_remove(struct fw_unit *unit)
{ {
struct snd_dg00x *dg00x = dev_get_drvdata(&unit->device); struct snd_dg00x *dg00x = dev_get_drvdata(&unit->device);
/* No need to wait for releasing card object in this context. */ /*
snd_card_free_when_closed(dg00x->card); * Confirm to stop the work for registration before the sound card is
* going to be released. The work is not scheduled again because bus
* reset handler is not called anymore.
*/
cancel_delayed_work_sync(&dg00x->dwork);
if (dg00x->registered) {
/* No need to wait for releasing card object in this context. */
snd_card_free_when_closed(dg00x->card);
} else {
/* Don't forget this case. */
dg00x_free(dg00x);
}
} }
static const struct ieee1394_device_id snd_dg00x_id_table[] = { static const struct ieee1394_device_id snd_dg00x_id_table[] = {

View File

@ -37,6 +37,9 @@ struct snd_dg00x {
struct mutex mutex; struct mutex mutex;
spinlock_t lock; spinlock_t lock;
bool registered;
struct delayed_work dwork;
struct amdtp_stream tx_stream; struct amdtp_stream tx_stream;
struct fw_iso_resources tx_resources; struct fw_iso_resources tx_resources;

View File

@ -168,11 +168,34 @@ get_hardware_info(struct snd_efw *efw)
sizeof(struct snd_efw_phys_grp) * hwinfo->phys_in_grp_count); sizeof(struct snd_efw_phys_grp) * hwinfo->phys_in_grp_count);
memcpy(&efw->phys_out_grps, hwinfo->phys_out_grps, memcpy(&efw->phys_out_grps, hwinfo->phys_out_grps,
sizeof(struct snd_efw_phys_grp) * hwinfo->phys_out_grp_count); sizeof(struct snd_efw_phys_grp) * hwinfo->phys_out_grp_count);
/* AudioFire8 (since 2009) and AudioFirePre8 */
if (hwinfo->type == MODEL_ECHO_AUDIOFIRE_9)
efw->is_af9 = true;
/* These models uses the same firmware. */
if (hwinfo->type == MODEL_ECHO_AUDIOFIRE_2 ||
hwinfo->type == MODEL_ECHO_AUDIOFIRE_4 ||
hwinfo->type == MODEL_ECHO_AUDIOFIRE_9 ||
hwinfo->type == MODEL_GIBSON_RIP ||
hwinfo->type == MODEL_GIBSON_GOLDTOP)
efw->is_fireworks3 = true;
end: end:
kfree(hwinfo); kfree(hwinfo);
return err; return err;
} }
static void efw_free(struct snd_efw *efw)
{
snd_efw_stream_destroy_duplex(efw);
snd_efw_transaction_remove_instance(efw);
fw_unit_put(efw->unit);
kfree(efw->resp_buf);
mutex_destroy(&efw->mutex);
kfree(efw);
}
/* /*
* This module releases the FireWire unit data after all ALSA character devices * This module releases the FireWire unit data after all ALSA character devices
* are released by applications. This is for releasing stream data or finishing * are released by applications. This is for releasing stream data or finishing
@ -184,28 +207,24 @@ efw_card_free(struct snd_card *card)
{ {
struct snd_efw *efw = card->private_data; struct snd_efw *efw = card->private_data;
snd_efw_stream_destroy_duplex(efw);
snd_efw_transaction_remove_instance(efw);
fw_unit_put(efw->unit);
kfree(efw->resp_buf);
if (efw->card_index >= 0) { if (efw->card_index >= 0) {
mutex_lock(&devices_mutex); mutex_lock(&devices_mutex);
clear_bit(efw->card_index, devices_used); clear_bit(efw->card_index, devices_used);
mutex_unlock(&devices_mutex); mutex_unlock(&devices_mutex);
} }
mutex_destroy(&efw->mutex); efw_free(card->private_data);
} }
static int static void
efw_probe(struct fw_unit *unit, do_registration(struct work_struct *work)
const struct ieee1394_device_id *entry)
{ {
struct snd_card *card; struct snd_efw *efw = container_of(work, struct snd_efw, dwork.work);
struct snd_efw *efw; unsigned int card_index;
int card_index, err; int err;
if (efw->registered)
return;
mutex_lock(&devices_mutex); mutex_lock(&devices_mutex);
@ -215,24 +234,16 @@ efw_probe(struct fw_unit *unit,
break; break;
} }
if (card_index >= SNDRV_CARDS) { if (card_index >= SNDRV_CARDS) {
err = -ENOENT; mutex_unlock(&devices_mutex);
goto end; return;
} }
err = snd_card_new(&unit->device, index[card_index], id[card_index], err = snd_card_new(&efw->unit->device, index[card_index],
THIS_MODULE, sizeof(struct snd_efw), &card); id[card_index], THIS_MODULE, 0, &efw->card);
if (err < 0) if (err < 0) {
goto end; mutex_unlock(&devices_mutex);
efw = card->private_data; return;
efw->card_index = card_index; }
set_bit(card_index, devices_used);
card->private_free = efw_card_free;
efw->card = card;
efw->unit = fw_unit_get(unit);
mutex_init(&efw->mutex);
spin_lock_init(&efw->lock);
init_waitqueue_head(&efw->hwdep_wait);
/* prepare response buffer */ /* prepare response buffer */
snd_efw_resp_buf_size = clamp(snd_efw_resp_buf_size, snd_efw_resp_buf_size = clamp(snd_efw_resp_buf_size,
@ -248,16 +259,10 @@ efw_probe(struct fw_unit *unit,
err = get_hardware_info(efw); err = get_hardware_info(efw);
if (err < 0) if (err < 0)
goto error; goto error;
/* AudioFire8 (since 2009) and AudioFirePre8 */
if (entry->model_id == MODEL_ECHO_AUDIOFIRE_9) err = snd_efw_stream_init_duplex(efw);
efw->is_af9 = true; if (err < 0)
/* These models uses the same firmware. */ goto error;
if (entry->model_id == MODEL_ECHO_AUDIOFIRE_2 ||
entry->model_id == MODEL_ECHO_AUDIOFIRE_4 ||
entry->model_id == MODEL_ECHO_AUDIOFIRE_9 ||
entry->model_id == MODEL_GIBSON_RIP ||
entry->model_id == MODEL_GIBSON_GOLDTOP)
efw->is_fireworks3 = true;
snd_efw_proc_init(efw); snd_efw_proc_init(efw);
@ -275,44 +280,93 @@ efw_probe(struct fw_unit *unit,
if (err < 0) if (err < 0)
goto error; goto error;
err = snd_efw_stream_init_duplex(efw); err = snd_card_register(efw->card);
if (err < 0) if (err < 0)
goto error; goto error;
err = snd_card_register(card); set_bit(card_index, devices_used);
if (err < 0) { mutex_unlock(&devices_mutex);
snd_efw_stream_destroy_duplex(efw);
goto error;
}
dev_set_drvdata(&unit->device, efw); /*
end: * After registered, efw instance can be released corresponding to
mutex_unlock(&devices_mutex); * releasing the sound card instance.
return err; */
efw->card->private_free = efw_card_free;
efw->card->private_data = efw;
efw->registered = true;
return;
error: error:
snd_efw_transaction_remove_instance(efw);
mutex_unlock(&devices_mutex); mutex_unlock(&devices_mutex);
snd_card_free(card); snd_efw_transaction_remove_instance(efw);
return err; snd_efw_stream_destroy_duplex(efw);
snd_card_free(efw->card);
dev_info(&efw->unit->device,
"Sound card registration failed: %d\n", err);
}
static int
efw_probe(struct fw_unit *unit, const struct ieee1394_device_id *entry)
{
struct snd_efw *efw;
efw = kzalloc(sizeof(struct snd_efw), GFP_KERNEL);
if (efw == NULL)
return -ENOMEM;
efw->unit = fw_unit_get(unit);
dev_set_drvdata(&unit->device, efw);
mutex_init(&efw->mutex);
spin_lock_init(&efw->lock);
init_waitqueue_head(&efw->hwdep_wait);
/* Allocate and register this sound card later. */
INIT_DEFERRABLE_WORK(&efw->dwork, do_registration);
snd_fw_schedule_registration(unit, &efw->dwork);
return 0;
} }
static void efw_update(struct fw_unit *unit) static void efw_update(struct fw_unit *unit)
{ {
struct snd_efw *efw = dev_get_drvdata(&unit->device); struct snd_efw *efw = dev_get_drvdata(&unit->device);
/* Postpone a workqueue for deferred registration. */
if (!efw->registered)
snd_fw_schedule_registration(unit, &efw->dwork);
snd_efw_transaction_bus_reset(efw->unit); snd_efw_transaction_bus_reset(efw->unit);
mutex_lock(&efw->mutex); /*
snd_efw_stream_update_duplex(efw); * After registration, userspace can start packet streaming, then this
mutex_unlock(&efw->mutex); * code block works fine.
*/
if (efw->registered) {
mutex_lock(&efw->mutex);
snd_efw_stream_update_duplex(efw);
mutex_unlock(&efw->mutex);
}
} }
static void efw_remove(struct fw_unit *unit) static void efw_remove(struct fw_unit *unit)
{ {
struct snd_efw *efw = dev_get_drvdata(&unit->device); struct snd_efw *efw = dev_get_drvdata(&unit->device);
/* No need to wait for releasing card object in this context. */ /*
snd_card_free_when_closed(efw->card); * Confirm to stop the work for registration before the sound card is
* going to be released. The work is not scheduled again because bus
* reset handler is not called anymore.
*/
cancel_delayed_work_sync(&efw->dwork);
if (efw->registered) {
/* No need to wait for releasing card object in this context. */
snd_card_free_when_closed(efw->card);
} else {
/* Don't forget this case. */
efw_free(efw);
}
} }
static const struct ieee1394_device_id efw_id_table[] = { static const struct ieee1394_device_id efw_id_table[] = {

View File

@ -65,6 +65,9 @@ struct snd_efw {
struct mutex mutex; struct mutex mutex;
spinlock_t lock; spinlock_t lock;
bool registered;
struct delayed_work dwork;
/* for transaction */ /* for transaction */
u32 seqnum; u32 seqnum;
bool resp_addr_changable; bool resp_addr_changable;
@ -81,7 +84,6 @@ struct snd_efw {
unsigned int pcm_capture_channels[SND_EFW_MULTIPLIER_MODES]; unsigned int pcm_capture_channels[SND_EFW_MULTIPLIER_MODES];
unsigned int pcm_playback_channels[SND_EFW_MULTIPLIER_MODES]; unsigned int pcm_playback_channels[SND_EFW_MULTIPLIER_MODES];
struct amdtp_stream *master;
struct amdtp_stream tx_stream; struct amdtp_stream tx_stream;
struct amdtp_stream rx_stream; struct amdtp_stream rx_stream;
struct cmp_connection out_conn; struct cmp_connection out_conn;

View File

@ -120,23 +120,6 @@ destroy_stream(struct snd_efw *efw, struct amdtp_stream *stream)
cmp_connection_destroy(&efw->out_conn); cmp_connection_destroy(&efw->out_conn);
} }
static int
get_sync_mode(struct snd_efw *efw, enum cip_flags *sync_mode)
{
enum snd_efw_clock_source clock_source;
int err;
err = snd_efw_command_get_clock_source(efw, &clock_source);
if (err < 0)
return err;
if (clock_source == SND_EFW_CLOCK_SOURCE_SYTMATCH)
return -ENOSYS;
*sync_mode = CIP_SYNC_TO_DEVICE;
return 0;
}
static int static int
check_connection_used_by_others(struct snd_efw *efw, struct amdtp_stream *s) check_connection_used_by_others(struct snd_efw *efw, struct amdtp_stream *s)
{ {
@ -208,9 +191,6 @@ end:
int snd_efw_stream_start_duplex(struct snd_efw *efw, unsigned int rate) int snd_efw_stream_start_duplex(struct snd_efw *efw, unsigned int rate)
{ {
struct amdtp_stream *master, *slave;
unsigned int slave_substreams;
enum cip_flags sync_mode;
unsigned int curr_rate; unsigned int curr_rate;
int err = 0; int err = 0;
@ -218,32 +198,19 @@ int snd_efw_stream_start_duplex(struct snd_efw *efw, unsigned int rate)
if (efw->playback_substreams == 0 && efw->capture_substreams == 0) if (efw->playback_substreams == 0 && efw->capture_substreams == 0)
goto end; goto end;
err = get_sync_mode(efw, &sync_mode);
if (err < 0)
goto end;
if (sync_mode == CIP_SYNC_TO_DEVICE) {
master = &efw->tx_stream;
slave = &efw->rx_stream;
slave_substreams = efw->playback_substreams;
} else {
master = &efw->rx_stream;
slave = &efw->tx_stream;
slave_substreams = efw->capture_substreams;
}
/* /*
* Considering JACK/FFADO streaming: * Considering JACK/FFADO streaming:
* TODO: This can be removed hwdep functionality becomes popular. * TODO: This can be removed hwdep functionality becomes popular.
*/ */
err = check_connection_used_by_others(efw, master); err = check_connection_used_by_others(efw, &efw->rx_stream);
if (err < 0) if (err < 0)
goto end; goto end;
/* packet queueing error */ /* packet queueing error */
if (amdtp_streaming_error(slave)) if (amdtp_streaming_error(&efw->tx_stream))
stop_stream(efw, slave); stop_stream(efw, &efw->tx_stream);
if (amdtp_streaming_error(master)) if (amdtp_streaming_error(&efw->rx_stream))
stop_stream(efw, master); stop_stream(efw, &efw->rx_stream);
/* stop streams if rate is different */ /* stop streams if rate is different */
err = snd_efw_command_get_sampling_rate(efw, &curr_rate); err = snd_efw_command_get_sampling_rate(efw, &curr_rate);
@ -252,20 +219,17 @@ int snd_efw_stream_start_duplex(struct snd_efw *efw, unsigned int rate)
if (rate == 0) if (rate == 0)
rate = curr_rate; rate = curr_rate;
if (rate != curr_rate) { if (rate != curr_rate) {
stop_stream(efw, slave); stop_stream(efw, &efw->tx_stream);
stop_stream(efw, master); stop_stream(efw, &efw->rx_stream);
} }
/* master should be always running */ /* master should be always running */
if (!amdtp_stream_running(master)) { if (!amdtp_stream_running(&efw->rx_stream)) {
amdtp_stream_set_sync(sync_mode, master, slave);
efw->master = master;
err = snd_efw_command_set_sampling_rate(efw, rate); err = snd_efw_command_set_sampling_rate(efw, rate);
if (err < 0) if (err < 0)
goto end; goto end;
err = start_stream(efw, master, rate); err = start_stream(efw, &efw->rx_stream, rate);
if (err < 0) { if (err < 0) {
dev_err(&efw->unit->device, dev_err(&efw->unit->device,
"fail to start AMDTP master stream:%d\n", err); "fail to start AMDTP master stream:%d\n", err);
@ -274,12 +238,13 @@ int snd_efw_stream_start_duplex(struct snd_efw *efw, unsigned int rate)
} }
/* start slave if needed */ /* start slave if needed */
if (slave_substreams > 0 && !amdtp_stream_running(slave)) { if (efw->capture_substreams > 0 &&
err = start_stream(efw, slave, rate); !amdtp_stream_running(&efw->tx_stream)) {
err = start_stream(efw, &efw->tx_stream, rate);
if (err < 0) { if (err < 0) {
dev_err(&efw->unit->device, dev_err(&efw->unit->device,
"fail to start AMDTP slave stream:%d\n", err); "fail to start AMDTP slave stream:%d\n", err);
stop_stream(efw, master); stop_stream(efw, &efw->rx_stream);
} }
} }
end: end:
@ -288,26 +253,11 @@ end:
void snd_efw_stream_stop_duplex(struct snd_efw *efw) void snd_efw_stream_stop_duplex(struct snd_efw *efw)
{ {
struct amdtp_stream *master, *slave; if (efw->capture_substreams == 0) {
unsigned int master_substreams, slave_substreams; stop_stream(efw, &efw->tx_stream);
if (efw->master == &efw->rx_stream) { if (efw->playback_substreams == 0)
slave = &efw->tx_stream; stop_stream(efw, &efw->rx_stream);
master = &efw->rx_stream;
slave_substreams = efw->capture_substreams;
master_substreams = efw->playback_substreams;
} else {
slave = &efw->rx_stream;
master = &efw->tx_stream;
slave_substreams = efw->playback_substreams;
master_substreams = efw->capture_substreams;
}
if (slave_substreams == 0) {
stop_stream(efw, slave);
if (master_substreams == 0)
stop_stream(efw, master);
} }
} }

View File

@ -67,6 +67,38 @@ int snd_fw_transaction(struct fw_unit *unit, int tcode,
} }
EXPORT_SYMBOL(snd_fw_transaction); EXPORT_SYMBOL(snd_fw_transaction);
#define PROBE_DELAY_MS (2 * MSEC_PER_SEC)
/**
* snd_fw_schedule_registration - schedule work for sound card registration
* @unit: an instance for unit on IEEE 1394 bus
* @dwork: delayed work with callback function
*
* This function is not designed for general purposes. When new unit is
* connected to IEEE 1394 bus, the bus is under bus-reset state because of
* topological change. In this state, units tend to fail both of asynchronous
* and isochronous communication. To avoid this problem, this function is used
* to postpone sound card registration after the state. The callers must
* set up instance of delayed work in advance.
*/
void snd_fw_schedule_registration(struct fw_unit *unit,
struct delayed_work *dwork)
{
u64 now, delay;
now = get_jiffies_64();
delay = fw_parent_device(unit)->card->reset_jiffies
+ msecs_to_jiffies(PROBE_DELAY_MS);
if (time_after64(delay, now))
delay -= now;
else
delay = 0;
mod_delayed_work(system_wq, dwork, delay);
}
EXPORT_SYMBOL(snd_fw_schedule_registration);
static void async_midi_port_callback(struct fw_card *card, int rcode, static void async_midi_port_callback(struct fw_card *card, int rcode,
void *data, size_t length, void *data, size_t length,
void *callback_data) void *callback_data)

View File

@ -22,6 +22,9 @@ static inline bool rcode_is_permanent_error(int rcode)
return rcode == RCODE_TYPE_ERROR || rcode == RCODE_ADDRESS_ERROR; return rcode == RCODE_TYPE_ERROR || rcode == RCODE_ADDRESS_ERROR;
} }
void snd_fw_schedule_registration(struct fw_unit *unit,
struct delayed_work *dwork);
struct snd_fw_async_midi_port; struct snd_fw_async_midi_port;
typedef int (*snd_fw_async_midi_port_fill)( typedef int (*snd_fw_async_midi_port_fill)(
struct snd_rawmidi_substream *substream, struct snd_rawmidi_substream *substream,

View File

@ -242,8 +242,7 @@ int snd_oxfw_stream_init_simplex(struct snd_oxfw *oxfw,
* blocks than IEC 61883-6 defines. * blocks than IEC 61883-6 defines.
*/ */
if (stream == &oxfw->tx_stream) { if (stream == &oxfw->tx_stream) {
oxfw->tx_stream.flags |= CIP_SKIP_INIT_DBC_CHECK | oxfw->tx_stream.flags |= CIP_JUMBO_PAYLOAD;
CIP_JUMBO_PAYLOAD;
if (oxfw->wrong_dbs) if (oxfw->wrong_dbs)
oxfw->tx_stream.flags |= CIP_WRONG_DBS; oxfw->tx_stream.flags |= CIP_WRONG_DBS;
} }

View File

@ -118,15 +118,8 @@ end:
return err; return err;
} }
/* static void oxfw_free(struct snd_oxfw *oxfw)
* This module releases the FireWire unit data after all ALSA character devices
* are released by applications. This is for releasing stream data or finishing
* transactions safely. Thus at returning from .remove(), this module still keep
* references for the unit.
*/
static void oxfw_card_free(struct snd_card *card)
{ {
struct snd_oxfw *oxfw = card->private_data;
unsigned int i; unsigned int i;
snd_oxfw_stream_destroy_simplex(oxfw, &oxfw->rx_stream); snd_oxfw_stream_destroy_simplex(oxfw, &oxfw->rx_stream);
@ -144,6 +137,17 @@ static void oxfw_card_free(struct snd_card *card)
mutex_destroy(&oxfw->mutex); mutex_destroy(&oxfw->mutex);
} }
/*
* This module releases the FireWire unit data after all ALSA character devices
* are released by applications. This is for releasing stream data or finishing
* transactions safely. Thus at returning from .remove(), this module still keep
* references for the unit.
*/
static void oxfw_card_free(struct snd_card *card)
{
oxfw_free(card->private_data);
}
static int detect_quirks(struct snd_oxfw *oxfw) static int detect_quirks(struct snd_oxfw *oxfw)
{ {
struct fw_device *fw_dev = fw_parent_device(oxfw->unit); struct fw_device *fw_dev = fw_parent_device(oxfw->unit);
@ -205,33 +209,18 @@ static int detect_quirks(struct snd_oxfw *oxfw)
return 0; return 0;
} }
static int oxfw_probe(struct fw_unit *unit, static void do_registration(struct work_struct *work)
const struct ieee1394_device_id *entry)
{ {
struct snd_card *card; struct snd_oxfw *oxfw = container_of(work, struct snd_oxfw, dwork.work);
struct snd_oxfw *oxfw;
int err; int err;
if (entry->vendor_id == VENDOR_LOUD && !detect_loud_models(unit)) if (oxfw->registered)
return -ENODEV; return;
err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE, err = snd_card_new(&oxfw->unit->device, -1, NULL, THIS_MODULE, 0,
sizeof(*oxfw), &card); &oxfw->card);
if (err < 0) if (err < 0)
return err; return;
card->private_free = oxfw_card_free;
oxfw = card->private_data;
oxfw->card = card;
mutex_init(&oxfw->mutex);
oxfw->unit = fw_unit_get(unit);
oxfw->entry = entry;
spin_lock_init(&oxfw->lock);
init_waitqueue_head(&oxfw->hwdep_wait);
err = snd_oxfw_stream_discover(oxfw);
if (err < 0)
goto error;
err = name_card(oxfw); err = name_card(oxfw);
if (err < 0) if (err < 0)
@ -241,6 +230,19 @@ static int oxfw_probe(struct fw_unit *unit,
if (err < 0) if (err < 0)
goto error; goto error;
err = snd_oxfw_stream_discover(oxfw);
if (err < 0)
goto error;
err = snd_oxfw_stream_init_simplex(oxfw, &oxfw->rx_stream);
if (err < 0)
goto error;
if (oxfw->has_output) {
err = snd_oxfw_stream_init_simplex(oxfw, &oxfw->tx_stream);
if (err < 0)
goto error;
}
err = snd_oxfw_create_pcm(oxfw); err = snd_oxfw_create_pcm(oxfw);
if (err < 0) if (err < 0)
goto error; goto error;
@ -255,54 +257,97 @@ static int oxfw_probe(struct fw_unit *unit,
if (err < 0) if (err < 0)
goto error; goto error;
err = snd_oxfw_stream_init_simplex(oxfw, &oxfw->rx_stream); err = snd_card_register(oxfw->card);
if (err < 0) if (err < 0)
goto error; goto error;
if (oxfw->has_output) {
err = snd_oxfw_stream_init_simplex(oxfw, &oxfw->tx_stream);
if (err < 0)
goto error;
}
err = snd_card_register(card); /*
if (err < 0) { * After registered, oxfw instance can be released corresponding to
snd_oxfw_stream_destroy_simplex(oxfw, &oxfw->rx_stream); * releasing the sound card instance.
if (oxfw->has_output) */
snd_oxfw_stream_destroy_simplex(oxfw, &oxfw->tx_stream); oxfw->card->private_free = oxfw_card_free;
goto error; oxfw->card->private_data = oxfw;
} oxfw->registered = true;
return;
error:
snd_oxfw_stream_destroy_simplex(oxfw, &oxfw->rx_stream);
if (oxfw->has_output)
snd_oxfw_stream_destroy_simplex(oxfw, &oxfw->tx_stream);
snd_card_free(oxfw->card);
dev_info(&oxfw->unit->device,
"Sound card registration failed: %d\n", err);
}
static int oxfw_probe(struct fw_unit *unit,
const struct ieee1394_device_id *entry)
{
struct snd_oxfw *oxfw;
if (entry->vendor_id == VENDOR_LOUD && !detect_loud_models(unit))
return -ENODEV;
/* Allocate this independent of sound card instance. */
oxfw = kzalloc(sizeof(struct snd_oxfw), GFP_KERNEL);
if (oxfw == NULL)
return -ENOMEM;
oxfw->entry = entry;
oxfw->unit = fw_unit_get(unit);
dev_set_drvdata(&unit->device, oxfw); dev_set_drvdata(&unit->device, oxfw);
mutex_init(&oxfw->mutex);
spin_lock_init(&oxfw->lock);
init_waitqueue_head(&oxfw->hwdep_wait);
/* Allocate and register this sound card later. */
INIT_DEFERRABLE_WORK(&oxfw->dwork, do_registration);
snd_fw_schedule_registration(unit, &oxfw->dwork);
return 0; return 0;
error:
snd_card_free(card);
return err;
} }
static void oxfw_bus_reset(struct fw_unit *unit) static void oxfw_bus_reset(struct fw_unit *unit)
{ {
struct snd_oxfw *oxfw = dev_get_drvdata(&unit->device); struct snd_oxfw *oxfw = dev_get_drvdata(&unit->device);
if (!oxfw->registered)
snd_fw_schedule_registration(unit, &oxfw->dwork);
fcp_bus_reset(oxfw->unit); fcp_bus_reset(oxfw->unit);
mutex_lock(&oxfw->mutex); if (oxfw->registered) {
mutex_lock(&oxfw->mutex);
snd_oxfw_stream_update_simplex(oxfw, &oxfw->rx_stream); snd_oxfw_stream_update_simplex(oxfw, &oxfw->rx_stream);
if (oxfw->has_output) if (oxfw->has_output)
snd_oxfw_stream_update_simplex(oxfw, &oxfw->tx_stream); snd_oxfw_stream_update_simplex(oxfw, &oxfw->tx_stream);
mutex_unlock(&oxfw->mutex); mutex_unlock(&oxfw->mutex);
if (oxfw->entry->vendor_id == OUI_STANTON) if (oxfw->entry->vendor_id == OUI_STANTON)
snd_oxfw_scs1x_update(oxfw); snd_oxfw_scs1x_update(oxfw);
}
} }
static void oxfw_remove(struct fw_unit *unit) static void oxfw_remove(struct fw_unit *unit)
{ {
struct snd_oxfw *oxfw = dev_get_drvdata(&unit->device); struct snd_oxfw *oxfw = dev_get_drvdata(&unit->device);
/* No need to wait for releasing card object in this context. */ /*
snd_card_free_when_closed(oxfw->card); * Confirm to stop the work for registration before the sound card is
* going to be released. The work is not scheduled again because bus
* reset handler is not called anymore.
*/
cancel_delayed_work_sync(&oxfw->dwork);
if (oxfw->registered) {
/* No need to wait for releasing card object in this context. */
snd_card_free_when_closed(oxfw->card);
} else {
/* Don't forget this case. */
oxfw_free(oxfw);
}
} }
static const struct compat_info griffin_firewave = { static const struct compat_info griffin_firewave = {

View File

@ -36,10 +36,12 @@
struct snd_oxfw { struct snd_oxfw {
struct snd_card *card; struct snd_card *card;
struct fw_unit *unit; struct fw_unit *unit;
const struct device_info *device_info;
struct mutex mutex; struct mutex mutex;
spinlock_t lock; spinlock_t lock;
bool registered;
struct delayed_work dwork;
bool wrong_dbs; bool wrong_dbs;
bool has_output; bool has_output;
u8 *tx_stream_formats[SND_OXFW_STREAM_FORMAT_ENTRIES]; u8 *tx_stream_formats[SND_OXFW_STREAM_FORMAT_ENTRIES];

View File

@ -381,19 +381,17 @@ int snd_tscm_stream_start_duplex(struct snd_tscm *tscm, unsigned int rate)
if (err < 0) if (err < 0)
return err; return err;
if (curr_rate != rate || if (curr_rate != rate ||
amdtp_streaming_error(&tscm->tx_stream) || amdtp_streaming_error(&tscm->rx_stream) ||
amdtp_streaming_error(&tscm->rx_stream)) { amdtp_streaming_error(&tscm->tx_stream)) {
finish_session(tscm); finish_session(tscm);
amdtp_stream_stop(&tscm->tx_stream);
amdtp_stream_stop(&tscm->rx_stream); amdtp_stream_stop(&tscm->rx_stream);
amdtp_stream_stop(&tscm->tx_stream);
release_resources(tscm); release_resources(tscm);
} }
if (!amdtp_stream_running(&tscm->tx_stream)) { if (!amdtp_stream_running(&tscm->rx_stream)) {
amdtp_stream_set_sync(CIP_SYNC_TO_DEVICE,
&tscm->tx_stream, &tscm->rx_stream);
err = keep_resources(tscm, rate); err = keep_resources(tscm, rate);
if (err < 0) if (err < 0)
goto error; goto error;
@ -406,20 +404,6 @@ int snd_tscm_stream_start_duplex(struct snd_tscm *tscm, unsigned int rate)
if (err < 0) if (err < 0)
goto error; goto error;
err = amdtp_stream_start(&tscm->tx_stream,
tscm->tx_resources.channel,
fw_parent_device(tscm->unit)->max_speed);
if (err < 0)
goto error;
if (!amdtp_stream_wait_callback(&tscm->tx_stream,
CALLBACK_TIMEOUT)) {
err = -ETIMEDOUT;
goto error;
}
}
if (!amdtp_stream_running(&tscm->rx_stream)) {
err = amdtp_stream_start(&tscm->rx_stream, err = amdtp_stream_start(&tscm->rx_stream,
tscm->rx_resources.channel, tscm->rx_resources.channel,
fw_parent_device(tscm->unit)->max_speed); fw_parent_device(tscm->unit)->max_speed);
@ -433,10 +417,24 @@ int snd_tscm_stream_start_duplex(struct snd_tscm *tscm, unsigned int rate)
} }
} }
if (!amdtp_stream_running(&tscm->tx_stream)) {
err = amdtp_stream_start(&tscm->tx_stream,
tscm->tx_resources.channel,
fw_parent_device(tscm->unit)->max_speed);
if (err < 0)
goto error;
if (!amdtp_stream_wait_callback(&tscm->tx_stream,
CALLBACK_TIMEOUT)) {
err = -ETIMEDOUT;
goto error;
}
}
return 0; return 0;
error: error:
amdtp_stream_stop(&tscm->tx_stream);
amdtp_stream_stop(&tscm->rx_stream); amdtp_stream_stop(&tscm->rx_stream);
amdtp_stream_stop(&tscm->tx_stream);
finish_session(tscm); finish_session(tscm);
release_resources(tscm); release_resources(tscm);

View File

@ -85,10 +85,8 @@ static int identify_model(struct snd_tscm *tscm)
return 0; return 0;
} }
static void tscm_card_free(struct snd_card *card) static void tscm_free(struct snd_tscm *tscm)
{ {
struct snd_tscm *tscm = card->private_data;
snd_tscm_transaction_unregister(tscm); snd_tscm_transaction_unregister(tscm);
snd_tscm_stream_destroy_duplex(tscm); snd_tscm_stream_destroy_duplex(tscm);
@ -97,44 +95,36 @@ static void tscm_card_free(struct snd_card *card)
mutex_destroy(&tscm->mutex); mutex_destroy(&tscm->mutex);
} }
static int snd_tscm_probe(struct fw_unit *unit, static void tscm_card_free(struct snd_card *card)
const struct ieee1394_device_id *entry)
{ {
struct snd_card *card; tscm_free(card->private_data);
struct snd_tscm *tscm; }
static void do_registration(struct work_struct *work)
{
struct snd_tscm *tscm = container_of(work, struct snd_tscm, dwork.work);
int err; int err;
/* create card */ err = snd_card_new(&tscm->unit->device, -1, NULL, THIS_MODULE, 0,
err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE, &tscm->card);
sizeof(struct snd_tscm), &card);
if (err < 0) if (err < 0)
return err; return;
card->private_free = tscm_card_free;
/* initialize myself */
tscm = card->private_data;
tscm->card = card;
tscm->unit = fw_unit_get(unit);
mutex_init(&tscm->mutex);
spin_lock_init(&tscm->lock);
init_waitqueue_head(&tscm->hwdep_wait);
err = identify_model(tscm); err = identify_model(tscm);
if (err < 0) if (err < 0)
goto error; goto error;
snd_tscm_proc_init(tscm); err = snd_tscm_transaction_register(tscm);
if (err < 0)
goto error;
err = snd_tscm_stream_init_duplex(tscm); err = snd_tscm_stream_init_duplex(tscm);
if (err < 0) if (err < 0)
goto error; goto error;
err = snd_tscm_create_pcm_devices(tscm); snd_tscm_proc_init(tscm);
if (err < 0)
goto error;
err = snd_tscm_transaction_register(tscm); err = snd_tscm_create_pcm_devices(tscm);
if (err < 0) if (err < 0)
goto error; goto error;
@ -146,35 +136,91 @@ static int snd_tscm_probe(struct fw_unit *unit,
if (err < 0) if (err < 0)
goto error; goto error;
err = snd_card_register(card); err = snd_card_register(tscm->card);
if (err < 0) if (err < 0)
goto error; goto error;
/*
* After registered, tscm instance can be released corresponding to
* releasing the sound card instance.
*/
tscm->card->private_free = tscm_card_free;
tscm->card->private_data = tscm;
tscm->registered = true;
return;
error:
snd_tscm_transaction_unregister(tscm);
snd_tscm_stream_destroy_duplex(tscm);
snd_card_free(tscm->card);
dev_info(&tscm->unit->device,
"Sound card registration failed: %d\n", err);
}
static int snd_tscm_probe(struct fw_unit *unit,
const struct ieee1394_device_id *entry)
{
struct snd_tscm *tscm;
/* Allocate this independent of sound card instance. */
tscm = kzalloc(sizeof(struct snd_tscm), GFP_KERNEL);
if (tscm == NULL)
return -ENOMEM;
/* initialize myself */
tscm->unit = fw_unit_get(unit);
dev_set_drvdata(&unit->device, tscm); dev_set_drvdata(&unit->device, tscm);
return err; mutex_init(&tscm->mutex);
error: spin_lock_init(&tscm->lock);
snd_card_free(card); init_waitqueue_head(&tscm->hwdep_wait);
return err;
/* Allocate and register this sound card later. */
INIT_DEFERRABLE_WORK(&tscm->dwork, do_registration);
snd_fw_schedule_registration(unit, &tscm->dwork);
return 0;
} }
static void snd_tscm_update(struct fw_unit *unit) static void snd_tscm_update(struct fw_unit *unit)
{ {
struct snd_tscm *tscm = dev_get_drvdata(&unit->device); struct snd_tscm *tscm = dev_get_drvdata(&unit->device);
/* Postpone a workqueue for deferred registration. */
if (!tscm->registered)
snd_fw_schedule_registration(unit, &tscm->dwork);
snd_tscm_transaction_reregister(tscm); snd_tscm_transaction_reregister(tscm);
mutex_lock(&tscm->mutex); /*
snd_tscm_stream_update_duplex(tscm); * After registration, userspace can start packet streaming, then this
mutex_unlock(&tscm->mutex); * code block works fine.
*/
if (tscm->registered) {
mutex_lock(&tscm->mutex);
snd_tscm_stream_update_duplex(tscm);
mutex_unlock(&tscm->mutex);
}
} }
static void snd_tscm_remove(struct fw_unit *unit) static void snd_tscm_remove(struct fw_unit *unit)
{ {
struct snd_tscm *tscm = dev_get_drvdata(&unit->device); struct snd_tscm *tscm = dev_get_drvdata(&unit->device);
/* No need to wait for releasing card object in this context. */ /*
snd_card_free_when_closed(tscm->card); * Confirm to stop the work for registration before the sound card is
* going to be released. The work is not scheduled again because bus
* reset handler is not called anymore.
*/
cancel_delayed_work_sync(&tscm->dwork);
if (tscm->registered) {
/* No need to wait for releasing card object in this context. */
snd_card_free_when_closed(tscm->card);
} else {
/* Don't forget this case. */
tscm_free(tscm);
}
} }
static const struct ieee1394_device_id snd_tscm_id_table[] = { static const struct ieee1394_device_id snd_tscm_id_table[] = {

View File

@ -51,6 +51,8 @@ struct snd_tscm {
struct mutex mutex; struct mutex mutex;
spinlock_t lock; spinlock_t lock;
bool registered;
struct delayed_work dwork;
const struct snd_tscm_spec *spec; const struct snd_tscm_spec *spec;
struct fw_iso_resources tx_resources; struct fw_iso_resources tx_resources;

View File

@ -144,6 +144,7 @@ int snd_hdac_ext_bus_device_init(struct hdac_ext_bus *ebus, int addr)
if (!edev) if (!edev)
return -ENOMEM; return -ENOMEM;
hdev = &edev->hdac; hdev = &edev->hdac;
edev->ebus = ebus;
snprintf(name, sizeof(name), "ehdaudio%dD%d", ebus->idx, addr); snprintf(name, sizeof(name), "ehdaudio%dD%d", ebus->idx, addr);

View File

@ -80,6 +80,22 @@ void snd_hdac_bus_init_cmd_io(struct hdac_bus *bus)
} }
EXPORT_SYMBOL_GPL(snd_hdac_bus_init_cmd_io); EXPORT_SYMBOL_GPL(snd_hdac_bus_init_cmd_io);
/* wait for cmd dmas till they are stopped */
static void hdac_wait_for_cmd_dmas(struct hdac_bus *bus)
{
unsigned long timeout;
timeout = jiffies + msecs_to_jiffies(100);
while ((snd_hdac_chip_readb(bus, RIRBCTL) & AZX_RBCTL_DMA_EN)
&& time_before(jiffies, timeout))
udelay(10);
timeout = jiffies + msecs_to_jiffies(100);
while ((snd_hdac_chip_readb(bus, CORBCTL) & AZX_CORBCTL_RUN)
&& time_before(jiffies, timeout))
udelay(10);
}
/** /**
* snd_hdac_bus_stop_cmd_io - clean up CORB/RIRB buffers * snd_hdac_bus_stop_cmd_io - clean up CORB/RIRB buffers
* @bus: HD-audio core bus * @bus: HD-audio core bus
@ -90,6 +106,7 @@ void snd_hdac_bus_stop_cmd_io(struct hdac_bus *bus)
/* disable ringbuffer DMAs */ /* disable ringbuffer DMAs */
snd_hdac_chip_writeb(bus, RIRBCTL, 0); snd_hdac_chip_writeb(bus, RIRBCTL, 0);
snd_hdac_chip_writeb(bus, CORBCTL, 0); snd_hdac_chip_writeb(bus, CORBCTL, 0);
hdac_wait_for_cmd_dmas(bus);
/* disable unsolicited responses */ /* disable unsolicited responses */
snd_hdac_chip_updatel(bus, GCTL, AZX_GCTL_UNSOL, 0); snd_hdac_chip_updatel(bus, GCTL, AZX_GCTL_UNSOL, 0);
spin_unlock_irq(&bus->reg_lock); spin_unlock_irq(&bus->reg_lock);

View File

@ -158,22 +158,40 @@ void snd_hdac_i915_set_bclk(struct hdac_bus *bus)
} }
EXPORT_SYMBOL_GPL(snd_hdac_i915_set_bclk); EXPORT_SYMBOL_GPL(snd_hdac_i915_set_bclk);
/* There is a fixed mapping between audio pin node and display port /* There is a fixed mapping between audio pin node and display port.
* on current Intel platforms: * on SNB, IVY, HSW, BSW, SKL, BXT, KBL:
* Pin Widget 5 - PORT B (port = 1 in i915 driver) * Pin Widget 5 - PORT B (port = 1 in i915 driver)
* Pin Widget 6 - PORT C (port = 2 in i915 driver) * Pin Widget 6 - PORT C (port = 2 in i915 driver)
* Pin Widget 7 - PORT D (port = 3 in i915 driver) * Pin Widget 7 - PORT D (port = 3 in i915 driver)
*
* on VLV, ILK:
* Pin Widget 4 - PORT B (port = 1 in i915 driver)
* Pin Widget 5 - PORT C (port = 2 in i915 driver)
* Pin Widget 6 - PORT D (port = 3 in i915 driver)
*/ */
static int pin2port(hda_nid_t pin_nid) static int pin2port(struct hdac_device *codec, hda_nid_t pin_nid)
{ {
if (WARN_ON(pin_nid < 5 || pin_nid > 7)) int base_nid;
switch (codec->vendor_id) {
case 0x80860054: /* ILK */
case 0x80862804: /* ILK */
case 0x80862882: /* VLV */
base_nid = 3;
break;
default:
base_nid = 4;
break;
}
if (WARN_ON(pin_nid <= base_nid || pin_nid > base_nid + 3))
return -1; return -1;
return pin_nid - 4; return pin_nid - base_nid;
} }
/** /**
* snd_hdac_sync_audio_rate - Set N/CTS based on the sample rate * snd_hdac_sync_audio_rate - Set N/CTS based on the sample rate
* @bus: HDA core bus * @codec: HDA codec
* @nid: the pin widget NID * @nid: the pin widget NID
* @rate: the sample rate to set * @rate: the sample rate to set
* *
@ -183,14 +201,15 @@ static int pin2port(hda_nid_t pin_nid)
* This function sets N/CTS value based on the given sample rate. * This function sets N/CTS value based on the given sample rate.
* Returns zero for success, or a negative error code. * Returns zero for success, or a negative error code.
*/ */
int snd_hdac_sync_audio_rate(struct hdac_bus *bus, hda_nid_t nid, int rate) int snd_hdac_sync_audio_rate(struct hdac_device *codec, hda_nid_t nid, int rate)
{ {
struct hdac_bus *bus = codec->bus;
struct i915_audio_component *acomp = bus->audio_component; struct i915_audio_component *acomp = bus->audio_component;
int port; int port;
if (!acomp || !acomp->ops || !acomp->ops->sync_audio_rate) if (!acomp || !acomp->ops || !acomp->ops->sync_audio_rate)
return -ENODEV; return -ENODEV;
port = pin2port(nid); port = pin2port(codec, nid);
if (port < 0) if (port < 0)
return -EINVAL; return -EINVAL;
return acomp->ops->sync_audio_rate(acomp->dev, port, rate); return acomp->ops->sync_audio_rate(acomp->dev, port, rate);
@ -199,7 +218,7 @@ EXPORT_SYMBOL_GPL(snd_hdac_sync_audio_rate);
/** /**
* snd_hdac_acomp_get_eld - Get the audio state and ELD via component * snd_hdac_acomp_get_eld - Get the audio state and ELD via component
* @bus: HDA core bus * @codec: HDA codec
* @nid: the pin widget NID * @nid: the pin widget NID
* @audio_enabled: the pointer to store the current audio state * @audio_enabled: the pointer to store the current audio state
* @buffer: the buffer pointer to store ELD bytes * @buffer: the buffer pointer to store ELD bytes
@ -217,16 +236,17 @@ EXPORT_SYMBOL_GPL(snd_hdac_sync_audio_rate);
* thus it may be over @max_bytes. If it's over @max_bytes, it implies * thus it may be over @max_bytes. If it's over @max_bytes, it implies
* that only a part of ELD bytes have been fetched. * that only a part of ELD bytes have been fetched.
*/ */
int snd_hdac_acomp_get_eld(struct hdac_bus *bus, hda_nid_t nid, int snd_hdac_acomp_get_eld(struct hdac_device *codec, hda_nid_t nid,
bool *audio_enabled, char *buffer, int max_bytes) bool *audio_enabled, char *buffer, int max_bytes)
{ {
struct hdac_bus *bus = codec->bus;
struct i915_audio_component *acomp = bus->audio_component; struct i915_audio_component *acomp = bus->audio_component;
int port; int port;
if (!acomp || !acomp->ops || !acomp->ops->get_eld) if (!acomp || !acomp->ops || !acomp->ops->get_eld)
return -ENODEV; return -ENODEV;
port = pin2port(nid); port = pin2port(codec, nid);
if (port < 0) if (port < 0)
return -EINVAL; return -EINVAL;
return acomp->ops->get_eld(acomp->dev, port, audio_enabled, return acomp->ops->get_eld(acomp->dev, port, audio_enabled,
@ -338,6 +358,9 @@ int snd_hdac_i915_init(struct hdac_bus *bus)
struct i915_audio_component *acomp; struct i915_audio_component *acomp;
int ret; int ret;
if (WARN_ON(hdac_acomp))
return -EBUSY;
if (!i915_gfx_present()) if (!i915_gfx_present())
return -ENODEV; return -ENODEV;
@ -371,6 +394,7 @@ out_master_del:
out_err: out_err:
kfree(acomp); kfree(acomp);
bus->audio_component = NULL; bus->audio_component = NULL;
hdac_acomp = NULL;
dev_info(dev, "failed to add i915 component master (%d)\n", ret); dev_info(dev, "failed to add i915 component master (%d)\n", ret);
return ret; return ret;
@ -404,6 +428,7 @@ int snd_hdac_i915_exit(struct hdac_bus *bus)
kfree(acomp); kfree(acomp);
bus->audio_component = NULL; bus->audio_component = NULL;
hdac_acomp = NULL;
return 0; return 0;
} }

View File

@ -625,13 +625,30 @@ static void hdmi_cea_alloc_to_tlv_chmap(struct hdac_chmap *hchmap,
WARN_ON(count != channels); WARN_ON(count != channels);
} }
static int spk_mask_from_spk_alloc(int spk_alloc)
{
int i;
int spk_mask = eld_speaker_allocation_bits[0];
for (i = 0; i < ARRAY_SIZE(eld_speaker_allocation_bits); i++) {
if (spk_alloc & (1 << i))
spk_mask |= eld_speaker_allocation_bits[i];
}
return spk_mask;
}
static int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag, static int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag,
unsigned int size, unsigned int __user *tlv) unsigned int size, unsigned int __user *tlv)
{ {
struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
struct hdac_chmap *chmap = info->private_data; struct hdac_chmap *chmap = info->private_data;
int pcm_idx = kcontrol->private_value;
unsigned int __user *dst; unsigned int __user *dst;
int chs, count = 0; int chs, count = 0;
unsigned long max_chs;
int type;
int spk_alloc, spk_mask;
if (size < 8) if (size < 8)
return -ENOMEM; return -ENOMEM;
@ -639,40 +656,59 @@ static int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag,
return -EFAULT; return -EFAULT;
size -= 8; size -= 8;
dst = tlv + 2; dst = tlv + 2;
for (chs = 2; chs <= chmap->channels_max; chs++) {
spk_alloc = chmap->ops.get_spk_alloc(chmap->hdac, pcm_idx);
spk_mask = spk_mask_from_spk_alloc(spk_alloc);
max_chs = hweight_long(spk_mask);
for (chs = 2; chs <= max_chs; chs++) {
int i; int i;
struct hdac_cea_channel_speaker_allocation *cap; struct hdac_cea_channel_speaker_allocation *cap;
cap = channel_allocations; cap = channel_allocations;
for (i = 0; i < ARRAY_SIZE(channel_allocations); i++, cap++) { for (i = 0; i < ARRAY_SIZE(channel_allocations); i++, cap++) {
int chs_bytes = chs * 4; int chs_bytes = chs * 4;
int type = chmap->ops.chmap_cea_alloc_validate_get_type(
chmap, cap, chs);
unsigned int tlv_chmap[8]; unsigned int tlv_chmap[8];
if (type < 0) if (cap->channels != chs)
continue; continue;
if (!(cap->spk_mask == (spk_mask & cap->spk_mask)))
continue;
type = chmap->ops.chmap_cea_alloc_validate_get_type(
chmap, cap, chs);
if (type < 0)
return -ENODEV;
if (size < 8) if (size < 8)
return -ENOMEM; return -ENOMEM;
if (put_user(type, dst) || if (put_user(type, dst) ||
put_user(chs_bytes, dst + 1)) put_user(chs_bytes, dst + 1))
return -EFAULT; return -EFAULT;
dst += 2; dst += 2;
size -= 8; size -= 8;
count += 8; count += 8;
if (size < chs_bytes) if (size < chs_bytes)
return -ENOMEM; return -ENOMEM;
size -= chs_bytes; size -= chs_bytes;
count += chs_bytes; count += chs_bytes;
chmap->ops.cea_alloc_to_tlv_chmap(chmap, cap, chmap->ops.cea_alloc_to_tlv_chmap(chmap, cap,
tlv_chmap, chs); tlv_chmap, chs);
if (copy_to_user(dst, tlv_chmap, chs_bytes)) if (copy_to_user(dst, tlv_chmap, chs_bytes))
return -EFAULT; return -EFAULT;
dst += chs; dst += chs;
} }
} }
if (put_user(count, tlv + 1)) if (put_user(count, tlv + 1))
return -EFAULT; return -EFAULT;
return 0; return 0;
} }

View File

@ -785,6 +785,9 @@ wavefront_send_patch (snd_wavefront_t *dev, wavefront_patch_info *header)
DPRINT (WF_DEBUG_LOAD_PATCH, "downloading patch %d\n", DPRINT (WF_DEBUG_LOAD_PATCH, "downloading patch %d\n",
header->number); header->number);
if (header->number >= ARRAY_SIZE(dev->patch_status))
return -EINVAL;
dev->patch_status[header->number] |= WF_SLOT_FILLED; dev->patch_status[header->number] |= WF_SLOT_FILLED;
bptr = buf; bptr = buf;
@ -809,6 +812,9 @@ wavefront_send_program (snd_wavefront_t *dev, wavefront_patch_info *header)
DPRINT (WF_DEBUG_LOAD_PATCH, "downloading program %d\n", DPRINT (WF_DEBUG_LOAD_PATCH, "downloading program %d\n",
header->number); header->number);
if (header->number >= ARRAY_SIZE(dev->prog_status))
return -EINVAL;
dev->prog_status[header->number] = WF_SLOT_USED; dev->prog_status[header->number] = WF_SLOT_USED;
/* XXX need to zero existing SLOT_USED bit for program_status[i] /* XXX need to zero existing SLOT_USED bit for program_status[i]
@ -898,6 +904,9 @@ wavefront_send_sample (snd_wavefront_t *dev,
header->number = x; header->number = x;
} }
if (header->number >= WF_MAX_SAMPLE)
return -EINVAL;
if (header->size) { if (header->size) {
/* XXX it's a debatable point whether or not RDONLY semantics /* XXX it's a debatable point whether or not RDONLY semantics

View File

@ -2151,8 +2151,7 @@ vortex_adb_allocroute(vortex_t *vortex, int dma, int nr_ch, int dir,
stream->resources, en, stream->resources, en,
VORTEX_RESOURCE_SRC)) < 0) { VORTEX_RESOURCE_SRC)) < 0) {
memset(stream->resources, 0, memset(stream->resources, 0,
sizeof(unsigned char) * sizeof(stream->resources));
VORTEX_RESOURCE_LAST);
return -EBUSY; return -EBUSY;
} }
if (stream->type != VORTEX_PCM_A3D) { if (stream->type != VORTEX_PCM_A3D) {
@ -2162,7 +2161,7 @@ vortex_adb_allocroute(vortex_t *vortex, int dma, int nr_ch, int dir,
VORTEX_RESOURCE_MIXIN)) < 0) { VORTEX_RESOURCE_MIXIN)) < 0) {
memset(stream->resources, memset(stream->resources,
0, 0,
sizeof(unsigned char) * VORTEX_RESOURCE_LAST); sizeof(stream->resources));
return -EBUSY; return -EBUSY;
} }
} }
@ -2175,8 +2174,7 @@ vortex_adb_allocroute(vortex_t *vortex, int dma, int nr_ch, int dir,
stream->resources, en, stream->resources, en,
VORTEX_RESOURCE_A3D)) < 0) { VORTEX_RESOURCE_A3D)) < 0) {
memset(stream->resources, 0, memset(stream->resources, 0,
sizeof(unsigned char) * sizeof(stream->resources));
VORTEX_RESOURCE_LAST);
dev_err(vortex->card->dev, dev_err(vortex->card->dev,
"out of A3D sources. Sorry\n"); "out of A3D sources. Sorry\n");
return -EBUSY; return -EBUSY;
@ -2290,8 +2288,7 @@ vortex_adb_allocroute(vortex_t *vortex, int dma, int nr_ch, int dir,
VORTEX_RESOURCE_MIXOUT)) VORTEX_RESOURCE_MIXOUT))
< 0) { < 0) {
memset(stream->resources, 0, memset(stream->resources, 0,
sizeof(unsigned char) * sizeof(stream->resources));
VORTEX_RESOURCE_LAST);
return -EBUSY; return -EBUSY;
} }
if ((src[i] = if ((src[i] =
@ -2299,8 +2296,7 @@ vortex_adb_allocroute(vortex_t *vortex, int dma, int nr_ch, int dir,
stream->resources, en, stream->resources, en,
VORTEX_RESOURCE_SRC)) < 0) { VORTEX_RESOURCE_SRC)) < 0) {
memset(stream->resources, 0, memset(stream->resources, 0,
sizeof(unsigned char) * sizeof(stream->resources));
VORTEX_RESOURCE_LAST);
return -EBUSY; return -EBUSY;
} }
} }

View File

@ -432,7 +432,10 @@ static snd_pcm_uframes_t snd_vortex_pcm_pointer(struct snd_pcm_substream *substr
#endif #endif
//printk(KERN_INFO "vortex: pointer = 0x%x\n", current_ptr); //printk(KERN_INFO "vortex: pointer = 0x%x\n", current_ptr);
spin_unlock(&chip->lock); spin_unlock(&chip->lock);
return (bytes_to_frames(substream->runtime, current_ptr)); current_ptr = bytes_to_frames(substream->runtime, current_ptr);
if (current_ptr >= substream->runtime->buffer_size)
current_ptr = 0;
return current_ptr;
} }
/* operators */ /* operators */

View File

@ -49,7 +49,7 @@ struct ct_timer {
spinlock_t lock; /* global timer lock (for xfitimer) */ spinlock_t lock; /* global timer lock (for xfitimer) */
spinlock_t list_lock; /* lock for instance list */ spinlock_t list_lock; /* lock for instance list */
struct ct_atc *atc; struct ct_atc *atc;
struct ct_timer_ops *ops; const struct ct_timer_ops *ops;
struct list_head instance_head; struct list_head instance_head;
struct list_head running_head; struct list_head running_head;
unsigned int wc; /* current wallclock */ unsigned int wc; /* current wallclock */
@ -128,7 +128,7 @@ static void ct_systimer_prepare(struct ct_timer_instance *ti)
#define ct_systimer_free ct_systimer_prepare #define ct_systimer_free ct_systimer_prepare
static struct ct_timer_ops ct_systimer_ops = { static const struct ct_timer_ops ct_systimer_ops = {
.init = ct_systimer_init, .init = ct_systimer_init,
.free_instance = ct_systimer_free, .free_instance = ct_systimer_free,
.prepare = ct_systimer_prepare, .prepare = ct_systimer_prepare,
@ -322,7 +322,7 @@ static void ct_xfitimer_free_global(struct ct_timer *atimer)
ct_xfitimer_irq_stop(atimer); ct_xfitimer_irq_stop(atimer);
} }
static struct ct_timer_ops ct_xfitimer_ops = { static const struct ct_timer_ops ct_xfitimer_ops = {
.prepare = ct_xfitimer_prepare, .prepare = ct_xfitimer_prepare,
.start = ct_xfitimer_start, .start = ct_xfitimer_start,
.stop = ct_xfitimer_stop, .stop = ct_xfitimer_stop,

View File

@ -1548,7 +1548,7 @@ static int snd_es1373_line_get(struct snd_kcontrol *kcontrol,
int val = 0; int val = 0;
spin_lock_irq(&ensoniq->reg_lock); spin_lock_irq(&ensoniq->reg_lock);
if ((ensoniq->ctrl & ES_1371_GPIO_OUTM) >= 4) if (ensoniq->ctrl & ES_1371_GPIO_OUT(4))
val = 1; val = 1;
ucontrol->value.integer.value[0] = val; ucontrol->value.integer.value[0] = val;
spin_unlock_irq(&ensoniq->reg_lock); spin_unlock_irq(&ensoniq->reg_lock);

View File

@ -50,9 +50,13 @@ config SND_HDA_RECONFIG
bool "Allow dynamic codec reconfiguration" bool "Allow dynamic codec reconfiguration"
help help
Say Y here to enable the HD-audio codec re-configuration feature. Say Y here to enable the HD-audio codec re-configuration feature.
This adds the sysfs interfaces to allow user to clear the whole It allows user to clear the whole codec configuration, change the
codec configuration, change the codec setup, add extra verbs, codec setup, add extra verbs, and re-configure the codec dynamically.
and re-configure the codec dynamically.
Note that this item alone doesn't provide the sysfs interface, but
enables the feature just for the patch loader below.
If you need the traditional sysfs entries for the manual interaction,
turn on CONFIG_SND_HDA_HWDEP as well.
config SND_HDA_INPUT_BEEP config SND_HDA_INPUT_BEEP
bool "Support digital beep via input layer" bool "Support digital beep via input layer"

View File

@ -5434,6 +5434,7 @@ static int dyn_adc_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
spec->cur_adc_stream_tag = stream_tag; spec->cur_adc_stream_tag = stream_tag;
spec->cur_adc_format = format; spec->cur_adc_format = format;
snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format); snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format);
call_pcm_capture_hook(hinfo, codec, substream, HDA_GEN_PCM_ACT_PREPARE);
return 0; return 0;
} }
@ -5444,6 +5445,7 @@ static int dyn_adc_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
struct hda_gen_spec *spec = codec->spec; struct hda_gen_spec *spec = codec->spec;
snd_hda_codec_cleanup_stream(codec, spec->cur_adc); snd_hda_codec_cleanup_stream(codec, spec->cur_adc);
spec->cur_adc = 0; spec->cur_adc = 0;
call_pcm_capture_hook(hinfo, codec, substream, HDA_GEN_PCM_ACT_CLEANUP);
return 0; return 0;
} }

View File

@ -114,6 +114,9 @@ struct hdmi_ops {
int (*setup_stream)(struct hda_codec *codec, hda_nid_t cvt_nid, int (*setup_stream)(struct hda_codec *codec, hda_nid_t cvt_nid,
hda_nid_t pin_nid, u32 stream_tag, int format); hda_nid_t pin_nid, u32 stream_tag, int format);
void (*pin_cvt_fixup)(struct hda_codec *codec,
struct hdmi_spec_per_pin *per_pin,
hda_nid_t cvt_nid);
}; };
struct hdmi_pcm { struct hdmi_pcm {
@ -684,7 +687,8 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec,
if (!channels) if (!channels)
return; return;
if (is_haswell_plus(codec)) /* some HW (e.g. HSW+) needs reprogramming the amp at each time */
if (get_wcaps(codec, pin_nid) & AC_WCAP_OUT_AMP)
snd_hda_codec_write(codec, pin_nid, 0, snd_hda_codec_write(codec, pin_nid, 0,
AC_VERB_SET_AMP_GAIN_MUTE, AC_VERB_SET_AMP_GAIN_MUTE,
AMP_OUT_UNMUTE); AMP_OUT_UNMUTE);
@ -864,9 +868,6 @@ static int hdmi_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid,
struct hdmi_spec *spec = codec->spec; struct hdmi_spec *spec = codec->spec;
int err; int err;
if (is_haswell_plus(codec))
haswell_verify_D0(codec, cvt_nid, pin_nid);
err = spec->ops.pin_hbr_setup(codec, pin_nid, is_hbr_format(format)); err = spec->ops.pin_hbr_setup(codec, pin_nid, is_hbr_format(format));
if (err) { if (err) {
@ -884,7 +885,7 @@ static int hdmi_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid,
* of the pin. * of the pin.
*/ */
static int hdmi_choose_cvt(struct hda_codec *codec, static int hdmi_choose_cvt(struct hda_codec *codec,
int pin_idx, int *cvt_id, int *mux_id) int pin_idx, int *cvt_id)
{ {
struct hdmi_spec *spec = codec->spec; struct hdmi_spec *spec = codec->spec;
struct hdmi_spec_per_pin *per_pin; struct hdmi_spec_per_pin *per_pin;
@ -925,8 +926,6 @@ static int hdmi_choose_cvt(struct hda_codec *codec,
if (cvt_id) if (cvt_id)
*cvt_id = cvt_idx; *cvt_id = cvt_idx;
if (mux_id)
*mux_id = mux_idx;
return 0; return 0;
} }
@ -1019,9 +1018,6 @@ static void intel_not_share_assigned_cvt_nid(struct hda_codec *codec,
int mux_idx; int mux_idx;
struct hdmi_spec *spec = codec->spec; struct hdmi_spec *spec = codec->spec;
if (!is_haswell_plus(codec) && !is_valleyview_plus(codec))
return;
/* On Intel platform, the mapping of converter nid to /* On Intel platform, the mapping of converter nid to
* mux index of the pins are always the same. * mux index of the pins are always the same.
* The pin nid may be 0, this means all pins will not * The pin nid may be 0, this means all pins will not
@ -1032,6 +1028,17 @@ static void intel_not_share_assigned_cvt_nid(struct hda_codec *codec,
intel_not_share_assigned_cvt(codec, pin_nid, mux_idx); intel_not_share_assigned_cvt(codec, pin_nid, mux_idx);
} }
/* skeleton caller of pin_cvt_fixup ops */
static void pin_cvt_fixup(struct hda_codec *codec,
struct hdmi_spec_per_pin *per_pin,
hda_nid_t cvt_nid)
{
struct hdmi_spec *spec = codec->spec;
if (spec->ops.pin_cvt_fixup)
spec->ops.pin_cvt_fixup(codec, per_pin, cvt_nid);
}
/* called in hdmi_pcm_open when no pin is assigned to the PCM /* called in hdmi_pcm_open when no pin is assigned to the PCM
* in dyn_pcm_assign mode. * in dyn_pcm_assign mode.
*/ */
@ -1049,7 +1056,7 @@ static int hdmi_pcm_open_no_pin(struct hda_pcm_stream *hinfo,
if (pcm_idx < 0) if (pcm_idx < 0)
return -EINVAL; return -EINVAL;
err = hdmi_choose_cvt(codec, -1, &cvt_idx, NULL); err = hdmi_choose_cvt(codec, -1, &cvt_idx);
if (err) if (err)
return err; return err;
@ -1057,7 +1064,7 @@ static int hdmi_pcm_open_no_pin(struct hda_pcm_stream *hinfo,
per_cvt->assigned = 1; per_cvt->assigned = 1;
hinfo->nid = per_cvt->cvt_nid; hinfo->nid = per_cvt->cvt_nid;
intel_not_share_assigned_cvt_nid(codec, 0, per_cvt->cvt_nid); pin_cvt_fixup(codec, NULL, per_cvt->cvt_nid);
set_bit(pcm_idx, &spec->pcm_in_use); set_bit(pcm_idx, &spec->pcm_in_use);
/* todo: setup spdif ctls assign */ /* todo: setup spdif ctls assign */
@ -1089,7 +1096,7 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo,
{ {
struct hdmi_spec *spec = codec->spec; struct hdmi_spec *spec = codec->spec;
struct snd_pcm_runtime *runtime = substream->runtime; struct snd_pcm_runtime *runtime = substream->runtime;
int pin_idx, cvt_idx, pcm_idx, mux_idx = 0; int pin_idx, cvt_idx, pcm_idx;
struct hdmi_spec_per_pin *per_pin; struct hdmi_spec_per_pin *per_pin;
struct hdmi_eld *eld; struct hdmi_eld *eld;
struct hdmi_spec_per_cvt *per_cvt = NULL; struct hdmi_spec_per_cvt *per_cvt = NULL;
@ -1118,7 +1125,7 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo,
} }
} }
err = hdmi_choose_cvt(codec, pin_idx, &cvt_idx, &mux_idx); err = hdmi_choose_cvt(codec, pin_idx, &cvt_idx);
if (err < 0) { if (err < 0) {
mutex_unlock(&spec->pcm_lock); mutex_unlock(&spec->pcm_lock);
return err; return err;
@ -1135,11 +1142,10 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo,
snd_hda_codec_write_cache(codec, per_pin->pin_nid, 0, snd_hda_codec_write_cache(codec, per_pin->pin_nid, 0,
AC_VERB_SET_CONNECT_SEL, AC_VERB_SET_CONNECT_SEL,
mux_idx); per_pin->mux_idx);
/* configure unused pins to choose other converters */ /* configure unused pins to choose other converters */
if (is_haswell_plus(codec) || is_valleyview_plus(codec)) pin_cvt_fixup(codec, per_pin, 0);
intel_not_share_assigned_cvt(codec, per_pin->pin_nid, mux_idx);
snd_hda_spdif_ctls_assign(codec, pcm_idx, per_cvt->cvt_nid); snd_hda_spdif_ctls_assign(codec, pcm_idx, per_cvt->cvt_nid);
@ -1372,12 +1378,7 @@ static void update_eld(struct hda_codec *codec,
* and this can make HW reset converter selection on a pin. * and this can make HW reset converter selection on a pin.
*/ */
if (eld->eld_valid && !old_eld_valid && per_pin->setup) { if (eld->eld_valid && !old_eld_valid && per_pin->setup) {
if (is_haswell_plus(codec) || is_valleyview_plus(codec)) { pin_cvt_fixup(codec, per_pin, 0);
intel_verify_pin_cvt_connect(codec, per_pin);
intel_not_share_assigned_cvt(codec, per_pin->pin_nid,
per_pin->mux_idx);
}
hdmi_setup_audio_infoframe(codec, per_pin, per_pin->non_pcm); hdmi_setup_audio_infoframe(codec, per_pin, per_pin->non_pcm);
} }
@ -1484,7 +1485,7 @@ static void sync_eld_via_acomp(struct hda_codec *codec,
mutex_lock(&per_pin->lock); mutex_lock(&per_pin->lock);
eld->monitor_present = false; eld->monitor_present = false;
size = snd_hdac_acomp_get_eld(&codec->bus->core, per_pin->pin_nid, size = snd_hdac_acomp_get_eld(&codec->core, per_pin->pin_nid,
&eld->monitor_present, eld->eld_buffer, &eld->monitor_present, eld->eld_buffer,
ELD_MAX_SIZE); ELD_MAX_SIZE);
if (size > 0) { if (size > 0) {
@ -1711,7 +1712,7 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
* skip pin setup and return 0 to make audio playback * skip pin setup and return 0 to make audio playback
* be ongoing * be ongoing
*/ */
intel_not_share_assigned_cvt_nid(codec, 0, cvt_nid); pin_cvt_fixup(codec, NULL, cvt_nid);
snd_hda_codec_setup_stream(codec, cvt_nid, snd_hda_codec_setup_stream(codec, cvt_nid,
stream_tag, 0, format); stream_tag, 0, format);
mutex_unlock(&spec->pcm_lock); mutex_unlock(&spec->pcm_lock);
@ -1724,23 +1725,21 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
} }
per_pin = get_pin(spec, pin_idx); per_pin = get_pin(spec, pin_idx);
pin_nid = per_pin->pin_nid; pin_nid = per_pin->pin_nid;
if (is_haswell_plus(codec) || is_valleyview_plus(codec)) {
/* Verify pin:cvt selections to avoid silent audio after S3. /* Verify pin:cvt selections to avoid silent audio after S3.
* After S3, the audio driver restores pin:cvt selections * After S3, the audio driver restores pin:cvt selections
* but this can happen before gfx is ready and such selection * but this can happen before gfx is ready and such selection
* is overlooked by HW. Thus multiple pins can share a same * is overlooked by HW. Thus multiple pins can share a same
* default convertor and mute control will affect each other, * default convertor and mute control will affect each other,
* which can cause a resumed audio playback become silent * which can cause a resumed audio playback become silent
* after S3. * after S3.
*/ */
intel_verify_pin_cvt_connect(codec, per_pin); pin_cvt_fixup(codec, per_pin, 0);
intel_not_share_assigned_cvt(codec, pin_nid, per_pin->mux_idx);
}
/* Call sync_audio_rate to set the N/CTS/M manually if necessary */ /* Call sync_audio_rate to set the N/CTS/M manually if necessary */
/* Todo: add DP1.2 MST audio support later */ /* Todo: add DP1.2 MST audio support later */
if (codec_has_acomp(codec)) if (codec_has_acomp(codec))
snd_hdac_sync_audio_rate(&codec->bus->core, pin_nid, runtime->rate); snd_hdac_sync_audio_rate(&codec->core, pin_nid, runtime->rate);
non_pcm = check_non_pcm_per_cvt(codec, cvt_nid); non_pcm = check_non_pcm_per_cvt(codec, cvt_nid);
mutex_lock(&per_pin->lock); mutex_lock(&per_pin->lock);
@ -1837,6 +1836,18 @@ static const struct hda_pcm_ops generic_ops = {
.cleanup = generic_hdmi_playback_pcm_cleanup, .cleanup = generic_hdmi_playback_pcm_cleanup,
}; };
static int hdmi_get_spk_alloc(struct hdac_device *hdac, int pcm_idx)
{
struct hda_codec *codec = container_of(hdac, struct hda_codec, core);
struct hdmi_spec *spec = codec->spec;
struct hdmi_spec_per_pin *per_pin = pcm_idx_to_pin(spec, pcm_idx);
if (!per_pin)
return 0;
return per_pin->sink_eld.info.spk_alloc;
}
static void hdmi_get_chmap(struct hdac_device *hdac, int pcm_idx, static void hdmi_get_chmap(struct hdac_device *hdac, int pcm_idx,
unsigned char *chmap) unsigned char *chmap)
{ {
@ -2075,6 +2086,20 @@ static void hdmi_array_free(struct hdmi_spec *spec)
snd_array_free(&spec->cvts); snd_array_free(&spec->cvts);
} }
static void generic_spec_free(struct hda_codec *codec)
{
struct hdmi_spec *spec = codec->spec;
if (spec) {
if (spec->i915_bound)
snd_hdac_i915_exit(&codec->bus->core);
hdmi_array_free(spec);
kfree(spec);
codec->spec = NULL;
}
codec->dp_mst = false;
}
static void generic_hdmi_free(struct hda_codec *codec) static void generic_hdmi_free(struct hda_codec *codec)
{ {
struct hdmi_spec *spec = codec->spec; struct hdmi_spec *spec = codec->spec;
@ -2099,10 +2124,7 @@ static void generic_hdmi_free(struct hda_codec *codec)
spec->pcm_rec[pcm_idx].jack = NULL; spec->pcm_rec[pcm_idx].jack = NULL;
} }
if (spec->i915_bound) generic_spec_free(codec);
snd_hdac_i915_exit(&codec->bus->core);
hdmi_array_free(spec);
kfree(spec);
} }
#ifdef CONFIG_PM #ifdef CONFIG_PM
@ -2140,6 +2162,55 @@ static const struct hdmi_ops generic_standard_hdmi_ops = {
.setup_stream = hdmi_setup_stream, .setup_stream = hdmi_setup_stream,
}; };
/* allocate codec->spec and assign/initialize generic parser ops */
static int alloc_generic_hdmi(struct hda_codec *codec)
{
struct hdmi_spec *spec;
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
if (!spec)
return -ENOMEM;
spec->ops = generic_standard_hdmi_ops;
mutex_init(&spec->pcm_lock);
snd_hdac_register_chmap_ops(&codec->core, &spec->chmap);
spec->chmap.ops.get_chmap = hdmi_get_chmap;
spec->chmap.ops.set_chmap = hdmi_set_chmap;
spec->chmap.ops.is_pcm_attached = is_hdmi_pcm_attached;
spec->chmap.ops.get_spk_alloc = hdmi_get_spk_alloc,
codec->spec = spec;
hdmi_array_init(spec, 4);
codec->patch_ops = generic_hdmi_patch_ops;
return 0;
}
/* generic HDMI parser */
static int patch_generic_hdmi(struct hda_codec *codec)
{
int err;
err = alloc_generic_hdmi(codec);
if (err < 0)
return err;
err = hdmi_parse_codec(codec);
if (err < 0) {
generic_spec_free(codec);
return err;
}
generic_hdmi_init_per_pins(codec);
return 0;
}
/*
* Intel codec parsers and helpers
*/
static void intel_haswell_fixup_connect_list(struct hda_codec *codec, static void intel_haswell_fixup_connect_list(struct hda_codec *codec,
hda_nid_t nid) hda_nid_t nid)
{ {
@ -2217,12 +2288,23 @@ static void haswell_set_power_state(struct hda_codec *codec, hda_nid_t fg,
static void intel_pin_eld_notify(void *audio_ptr, int port) static void intel_pin_eld_notify(void *audio_ptr, int port)
{ {
struct hda_codec *codec = audio_ptr; struct hda_codec *codec = audio_ptr;
int pin_nid = port + 0x04; int pin_nid;
/* we assume only from port-B to port-D */ /* we assume only from port-B to port-D */
if (port < 1 || port > 3) if (port < 1 || port > 3)
return; return;
switch (codec->core.vendor_id) {
case 0x80860054: /* ILK */
case 0x80862804: /* ILK */
case 0x80862882: /* VLV */
pin_nid = port + 0x03;
break;
default:
pin_nid = port + 0x04;
break;
}
/* skip notification during system suspend (but not in runtime PM); /* skip notification during system suspend (but not in runtime PM);
* the state will be updated at resume * the state will be updated at resume
*/ */
@ -2236,95 +2318,161 @@ static void intel_pin_eld_notify(void *audio_ptr, int port)
check_presence_and_report(codec, pin_nid); check_presence_and_report(codec, pin_nid);
} }
static int patch_generic_hdmi(struct hda_codec *codec) /* register i915 component pin_eld_notify callback */
static void register_i915_notifier(struct hda_codec *codec)
{
struct hdmi_spec *spec = codec->spec;
spec->use_acomp_notifier = true;
spec->i915_audio_ops.audio_ptr = codec;
/* intel_audio_codec_enable() or intel_audio_codec_disable()
* will call pin_eld_notify with using audio_ptr pointer
* We need make sure audio_ptr is really setup
*/
wmb();
spec->i915_audio_ops.pin_eld_notify = intel_pin_eld_notify;
snd_hdac_i915_register_notifier(&spec->i915_audio_ops);
}
/* setup_stream ops override for HSW+ */
static int i915_hsw_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid,
hda_nid_t pin_nid, u32 stream_tag, int format)
{
haswell_verify_D0(codec, cvt_nid, pin_nid);
return hdmi_setup_stream(codec, cvt_nid, pin_nid, stream_tag, format);
}
/* pin_cvt_fixup ops override for HSW+ and VLV+ */
static void i915_pin_cvt_fixup(struct hda_codec *codec,
struct hdmi_spec_per_pin *per_pin,
hda_nid_t cvt_nid)
{
if (per_pin) {
intel_verify_pin_cvt_connect(codec, per_pin);
intel_not_share_assigned_cvt(codec, per_pin->pin_nid,
per_pin->mux_idx);
} else {
intel_not_share_assigned_cvt_nid(codec, 0, cvt_nid);
}
}
/* Intel Haswell and onwards; audio component with eld notifier */
static int patch_i915_hsw_hdmi(struct hda_codec *codec)
{ {
struct hdmi_spec *spec; struct hdmi_spec *spec;
int err;
spec = kzalloc(sizeof(*spec), GFP_KERNEL); /* HSW+ requires i915 binding */
if (spec == NULL) if (!codec->bus->core.audio_component) {
return -ENOMEM; codec_info(codec, "No i915 binding for Intel HDMI/DP codec\n");
return -ENODEV;
spec->ops = generic_standard_hdmi_ops;
mutex_init(&spec->pcm_lock);
snd_hdac_register_chmap_ops(&codec->core, &spec->chmap);
spec->chmap.ops.get_chmap = hdmi_get_chmap;
spec->chmap.ops.set_chmap = hdmi_set_chmap;
spec->chmap.ops.is_pcm_attached = is_hdmi_pcm_attached;
codec->spec = spec;
hdmi_array_init(spec, 4);
#ifdef CONFIG_SND_HDA_I915
/* Try to bind with i915 for Intel HSW+ codecs (if not done yet) */
if ((codec->core.vendor_id >> 16) == 0x8086 &&
is_haswell_plus(codec)) {
#if 0
/* on-demand binding leads to an unbalanced refcount when
* both i915 and hda drivers are probed concurrently;
* disabled temporarily for now
*/
if (!codec->bus->core.audio_component)
if (!snd_hdac_i915_init(&codec->bus->core))
spec->i915_bound = true;
#endif
/* use i915 audio component notifier for hotplug */
if (codec->bus->core.audio_component)
spec->use_acomp_notifier = true;
} }
#endif
if (is_haswell_plus(codec)) { err = alloc_generic_hdmi(codec);
intel_haswell_enable_all_pins(codec, true); if (err < 0)
intel_haswell_fixup_enable_dp12(codec); return err;
spec = codec->spec;
intel_haswell_enable_all_pins(codec, true);
intel_haswell_fixup_enable_dp12(codec);
/* For Haswell/Broadwell, the controller is also in the power well and
* can cover the codec power request, and so need not set this flag.
*/
if (!is_haswell(codec) && !is_broadwell(codec))
codec->core.link_power_control = 1;
codec->patch_ops.set_power_state = haswell_set_power_state;
codec->dp_mst = true;
codec->depop_delay = 0;
codec->auto_runtime_pm = 1;
spec->ops.setup_stream = i915_hsw_setup_stream;
spec->ops.pin_cvt_fixup = i915_pin_cvt_fixup;
err = hdmi_parse_codec(codec);
if (err < 0) {
generic_spec_free(codec);
return err;
} }
generic_hdmi_init_per_pins(codec);
register_i915_notifier(codec);
return 0;
}
/* Intel Baytrail and Braswell; with eld notifier */
static int patch_i915_byt_hdmi(struct hda_codec *codec)
{
struct hdmi_spec *spec;
int err;
/* requires i915 binding */
if (!codec->bus->core.audio_component) {
codec_info(codec, "No i915 binding for Intel HDMI/DP codec\n");
return -ENODEV;
}
err = alloc_generic_hdmi(codec);
if (err < 0)
return err;
spec = codec->spec;
/* For Valleyview/Cherryview, only the display codec is in the display /* For Valleyview/Cherryview, only the display codec is in the display
* power well and can use link_power ops to request/release the power. * power well and can use link_power ops to request/release the power.
* For Haswell/Broadwell, the controller is also in the power well and
* can cover the codec power request, and so need not set this flag.
* For previous platforms, there is no such power well feature.
*/ */
if (is_valleyview_plus(codec) || is_skylake(codec) || codec->core.link_power_control = 1;
is_broxton(codec))
codec->core.link_power_control = 1;
if (hdmi_parse_codec(codec) < 0) { codec->depop_delay = 0;
if (spec->i915_bound) codec->auto_runtime_pm = 1;
snd_hdac_i915_exit(&codec->bus->core);
codec->spec = NULL;
kfree(spec);
return -EINVAL;
}
codec->patch_ops = generic_hdmi_patch_ops;
if (is_haswell_plus(codec)) {
codec->patch_ops.set_power_state = haswell_set_power_state;
codec->dp_mst = true;
}
/* Enable runtime pm for HDMI audio codec of HSW/BDW/SKL/BYT/BSW */ spec->ops.pin_cvt_fixup = i915_pin_cvt_fixup;
if (is_haswell_plus(codec) || is_valleyview_plus(codec))
codec->auto_runtime_pm = 1; err = hdmi_parse_codec(codec);
if (err < 0) {
generic_spec_free(codec);
return err;
}
generic_hdmi_init_per_pins(codec); generic_hdmi_init_per_pins(codec);
register_i915_notifier(codec);
if (codec_has_acomp(codec)) {
codec->depop_delay = 0;
spec->i915_audio_ops.audio_ptr = codec;
/* intel_audio_codec_enable() or intel_audio_codec_disable()
* will call pin_eld_notify with using audio_ptr pointer
* We need make sure audio_ptr is really setup
*/
wmb();
spec->i915_audio_ops.pin_eld_notify = intel_pin_eld_notify;
snd_hdac_i915_register_notifier(&spec->i915_audio_ops);
}
WARN_ON(spec->dyn_pcm_assign && !codec_has_acomp(codec));
return 0; return 0;
} }
/* Intel IronLake, SandyBridge and IvyBridge; with eld notifier */
static int patch_i915_cpt_hdmi(struct hda_codec *codec)
{
struct hdmi_spec *spec;
int err;
/* no i915 component should have been bound before this */
if (WARN_ON(codec->bus->core.audio_component))
return -EBUSY;
err = alloc_generic_hdmi(codec);
if (err < 0)
return err;
spec = codec->spec;
/* Try to bind with i915 now */
err = snd_hdac_i915_init(&codec->bus->core);
if (err < 0)
goto error;
spec->i915_bound = true;
err = hdmi_parse_codec(codec);
if (err < 0)
goto error;
generic_hdmi_init_per_pins(codec);
register_i915_notifier(codec);
return 0;
error:
generic_spec_free(codec);
return err;
}
/* /*
* Shared non-generic implementations * Shared non-generic implementations
*/ */
@ -3492,21 +3640,21 @@ HDA_CODEC_ENTRY(0x11069f80, "VX900 HDMI/DP", patch_via_hdmi),
HDA_CODEC_ENTRY(0x11069f81, "VX900 HDMI/DP", patch_via_hdmi), HDA_CODEC_ENTRY(0x11069f81, "VX900 HDMI/DP", patch_via_hdmi),
HDA_CODEC_ENTRY(0x11069f84, "VX11 HDMI/DP", patch_generic_hdmi), HDA_CODEC_ENTRY(0x11069f84, "VX11 HDMI/DP", patch_generic_hdmi),
HDA_CODEC_ENTRY(0x11069f85, "VX11 HDMI/DP", patch_generic_hdmi), HDA_CODEC_ENTRY(0x11069f85, "VX11 HDMI/DP", patch_generic_hdmi),
HDA_CODEC_ENTRY(0x80860054, "IbexPeak HDMI", patch_generic_hdmi), HDA_CODEC_ENTRY(0x80860054, "IbexPeak HDMI", patch_i915_cpt_hdmi),
HDA_CODEC_ENTRY(0x80862801, "Bearlake HDMI", patch_generic_hdmi), HDA_CODEC_ENTRY(0x80862801, "Bearlake HDMI", patch_generic_hdmi),
HDA_CODEC_ENTRY(0x80862802, "Cantiga HDMI", patch_generic_hdmi), HDA_CODEC_ENTRY(0x80862802, "Cantiga HDMI", patch_generic_hdmi),
HDA_CODEC_ENTRY(0x80862803, "Eaglelake HDMI", patch_generic_hdmi), HDA_CODEC_ENTRY(0x80862803, "Eaglelake HDMI", patch_generic_hdmi),
HDA_CODEC_ENTRY(0x80862804, "IbexPeak HDMI", patch_generic_hdmi), HDA_CODEC_ENTRY(0x80862804, "IbexPeak HDMI", patch_i915_cpt_hdmi),
HDA_CODEC_ENTRY(0x80862805, "CougarPoint HDMI", patch_generic_hdmi), HDA_CODEC_ENTRY(0x80862805, "CougarPoint HDMI", patch_i915_cpt_hdmi),
HDA_CODEC_ENTRY(0x80862806, "PantherPoint HDMI", patch_generic_hdmi), HDA_CODEC_ENTRY(0x80862806, "PantherPoint HDMI", patch_i915_cpt_hdmi),
HDA_CODEC_ENTRY(0x80862807, "Haswell HDMI", patch_generic_hdmi), HDA_CODEC_ENTRY(0x80862807, "Haswell HDMI", patch_i915_hsw_hdmi),
HDA_CODEC_ENTRY(0x80862808, "Broadwell HDMI", patch_generic_hdmi), HDA_CODEC_ENTRY(0x80862808, "Broadwell HDMI", patch_i915_hsw_hdmi),
HDA_CODEC_ENTRY(0x80862809, "Skylake HDMI", patch_generic_hdmi), HDA_CODEC_ENTRY(0x80862809, "Skylake HDMI", patch_i915_hsw_hdmi),
HDA_CODEC_ENTRY(0x8086280a, "Broxton HDMI", patch_generic_hdmi), HDA_CODEC_ENTRY(0x8086280a, "Broxton HDMI", patch_i915_hsw_hdmi),
HDA_CODEC_ENTRY(0x8086280b, "Kabylake HDMI", patch_generic_hdmi), HDA_CODEC_ENTRY(0x8086280b, "Kabylake HDMI", patch_i915_hsw_hdmi),
HDA_CODEC_ENTRY(0x80862880, "CedarTrail HDMI", patch_generic_hdmi), HDA_CODEC_ENTRY(0x80862880, "CedarTrail HDMI", patch_generic_hdmi),
HDA_CODEC_ENTRY(0x80862882, "Valleyview2 HDMI", patch_generic_hdmi), HDA_CODEC_ENTRY(0x80862882, "Valleyview2 HDMI", patch_i915_byt_hdmi),
HDA_CODEC_ENTRY(0x80862883, "Braswell HDMI", patch_generic_hdmi), HDA_CODEC_ENTRY(0x80862883, "Braswell HDMI", patch_i915_byt_hdmi),
HDA_CODEC_ENTRY(0x808629fb, "Crestline HDMI", patch_generic_hdmi), HDA_CODEC_ENTRY(0x808629fb, "Crestline HDMI", patch_generic_hdmi),
/* special ID for generic HDMI */ /* special ID for generic HDMI */
HDA_CODEC_ENTRY(HDA_CODEC_ID_GENERIC_HDMI, "Generic HDMI", patch_generic_hdmi), HDA_CODEC_ENTRY(HDA_CODEC_ID_GENERIC_HDMI, "Generic HDMI", patch_generic_hdmi),

View File

@ -342,6 +342,11 @@ static void alc_fill_eapd_coef(struct hda_codec *codec)
case 0x10ec0293: case 0x10ec0293:
alc_update_coef_idx(codec, 0xa, 1<<13, 0); alc_update_coef_idx(codec, 0xa, 1<<13, 0);
break; break;
case 0x10ec0234:
case 0x10ec0274:
case 0x10ec0294:
alc_update_coef_idx(codec, 0x10, 1<<15, 0);
break;
case 0x10ec0662: case 0x10ec0662:
if ((coef & 0x00f0) == 0x0030) if ((coef & 0x00f0) == 0x0030)
alc_update_coef_idx(codec, 0x4, 1<<10, 0); /* EAPD Ctrl */ alc_update_coef_idx(codec, 0x4, 1<<10, 0); /* EAPD Ctrl */
@ -2647,6 +2652,7 @@ enum {
ALC269_TYPE_ALC255, ALC269_TYPE_ALC255,
ALC269_TYPE_ALC256, ALC269_TYPE_ALC256,
ALC269_TYPE_ALC225, ALC269_TYPE_ALC225,
ALC269_TYPE_ALC294,
}; };
/* /*
@ -2677,6 +2683,7 @@ static int alc269_parse_auto_config(struct hda_codec *codec)
case ALC269_TYPE_ALC255: case ALC269_TYPE_ALC255:
case ALC269_TYPE_ALC256: case ALC269_TYPE_ALC256:
case ALC269_TYPE_ALC225: case ALC269_TYPE_ALC225:
case ALC269_TYPE_ALC294:
ssids = alc269_ssids; ssids = alc269_ssids;
break; break;
default: default:
@ -6028,6 +6035,11 @@ static int patch_alc269(struct hda_codec *codec)
case 0x10ec0225: case 0x10ec0225:
spec->codec_variant = ALC269_TYPE_ALC225; spec->codec_variant = ALC269_TYPE_ALC225;
break; break;
case 0x10ec0234:
case 0x10ec0274:
case 0x10ec0294:
spec->codec_variant = ALC269_TYPE_ALC294;
break;
} }
if (snd_hda_codec_read(codec, 0x51, 0, AC_VERB_PARAMETERS, 0) == 0x10ec5505) { if (snd_hda_codec_read(codec, 0x51, 0, AC_VERB_PARAMETERS, 0) == 0x10ec5505) {
@ -6942,6 +6954,7 @@ static const struct hda_device_id snd_hda_id_realtek[] = {
HDA_CODEC_ENTRY(0x10ec0225, "ALC225", patch_alc269), HDA_CODEC_ENTRY(0x10ec0225, "ALC225", patch_alc269),
HDA_CODEC_ENTRY(0x10ec0231, "ALC231", patch_alc269), HDA_CODEC_ENTRY(0x10ec0231, "ALC231", patch_alc269),
HDA_CODEC_ENTRY(0x10ec0233, "ALC233", patch_alc269), HDA_CODEC_ENTRY(0x10ec0233, "ALC233", patch_alc269),
HDA_CODEC_ENTRY(0x10ec0234, "ALC234", patch_alc269),
HDA_CODEC_ENTRY(0x10ec0235, "ALC233", patch_alc269), HDA_CODEC_ENTRY(0x10ec0235, "ALC233", patch_alc269),
HDA_CODEC_ENTRY(0x10ec0255, "ALC255", patch_alc269), HDA_CODEC_ENTRY(0x10ec0255, "ALC255", patch_alc269),
HDA_CODEC_ENTRY(0x10ec0256, "ALC256", patch_alc269), HDA_CODEC_ENTRY(0x10ec0256, "ALC256", patch_alc269),
@ -6952,6 +6965,7 @@ static const struct hda_device_id snd_hda_id_realtek[] = {
HDA_CODEC_ENTRY(0x10ec0269, "ALC269", patch_alc269), HDA_CODEC_ENTRY(0x10ec0269, "ALC269", patch_alc269),
HDA_CODEC_ENTRY(0x10ec0270, "ALC270", patch_alc269), HDA_CODEC_ENTRY(0x10ec0270, "ALC270", patch_alc269),
HDA_CODEC_ENTRY(0x10ec0272, "ALC272", patch_alc662), HDA_CODEC_ENTRY(0x10ec0272, "ALC272", patch_alc662),
HDA_CODEC_ENTRY(0x10ec0274, "ALC274", patch_alc269),
HDA_CODEC_ENTRY(0x10ec0275, "ALC275", patch_alc269), HDA_CODEC_ENTRY(0x10ec0275, "ALC275", patch_alc269),
HDA_CODEC_ENTRY(0x10ec0276, "ALC276", patch_alc269), HDA_CODEC_ENTRY(0x10ec0276, "ALC276", patch_alc269),
HDA_CODEC_ENTRY(0x10ec0280, "ALC280", patch_alc269), HDA_CODEC_ENTRY(0x10ec0280, "ALC280", patch_alc269),
@ -6964,6 +6978,7 @@ static const struct hda_device_id snd_hda_id_realtek[] = {
HDA_CODEC_ENTRY(0x10ec0290, "ALC290", patch_alc269), HDA_CODEC_ENTRY(0x10ec0290, "ALC290", patch_alc269),
HDA_CODEC_ENTRY(0x10ec0292, "ALC292", patch_alc269), HDA_CODEC_ENTRY(0x10ec0292, "ALC292", patch_alc269),
HDA_CODEC_ENTRY(0x10ec0293, "ALC293", patch_alc269), HDA_CODEC_ENTRY(0x10ec0293, "ALC293", patch_alc269),
HDA_CODEC_ENTRY(0x10ec0294, "ALC294", patch_alc269),
HDA_CODEC_ENTRY(0x10ec0298, "ALC298", patch_alc269), HDA_CODEC_ENTRY(0x10ec0298, "ALC298", patch_alc269),
HDA_CODEC_REV_ENTRY(0x10ec0861, 0x100340, "ALC660", patch_alc861), HDA_CODEC_REV_ENTRY(0x10ec0861, 0x100340, "ALC660", patch_alc861),
HDA_CODEC_ENTRY(0x10ec0660, "ALC660-VD", patch_alc861vd), HDA_CODEC_ENTRY(0x10ec0660, "ALC660-VD", patch_alc861vd),

View File

@ -42,12 +42,6 @@
#include <asm/pgtable.h> #include <asm/pgtable.h>
#include <asm/cacheflush.h> #include <asm/cacheflush.h>
#ifdef CONFIG_KVM_GUEST
#include <linux/kvm_para.h>
#else
#define kvm_para_available() (0)
#endif
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>"); MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
MODULE_DESCRIPTION("Intel 82801AA,82901AB,i810,i820,i830,i840,i845,MX440; SiS 7012; Ali 5455"); MODULE_DESCRIPTION("Intel 82801AA,82901AB,i810,i820,i830,i840,i845,MX440; SiS 7012; Ali 5455");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
@ -2972,25 +2966,17 @@ static int snd_intel8x0_inside_vm(struct pci_dev *pci)
goto fini; goto fini;
} }
/* detect KVM and Parallels virtual environments */
result = kvm_para_available();
#ifdef X86_FEATURE_HYPERVISOR
result = result || boot_cpu_has(X86_FEATURE_HYPERVISOR);
#endif
if (!result)
goto fini;
/* check for known (emulated) devices */ /* check for known (emulated) devices */
result = 0;
if (pci->subsystem_vendor == PCI_SUBVENDOR_ID_REDHAT_QUMRANET && if (pci->subsystem_vendor == PCI_SUBVENDOR_ID_REDHAT_QUMRANET &&
pci->subsystem_device == PCI_SUBDEVICE_ID_QEMU) { pci->subsystem_device == PCI_SUBDEVICE_ID_QEMU) {
/* KVM emulated sound, PCI SSID: 1af4:1100 */ /* KVM emulated sound, PCI SSID: 1af4:1100 */
msg = "enable KVM"; msg = "enable KVM";
result = 1;
} else if (pci->subsystem_vendor == 0x1ab8) { } else if (pci->subsystem_vendor == 0x1ab8) {
/* Parallels VM emulated sound, PCI SSID: 1ab8:xxxx */ /* Parallels VM emulated sound, PCI SSID: 1ab8:xxxx */
msg = "enable Parallels VM"; msg = "enable Parallels VM";
} else { result = 1;
msg = "disable (unknown or VT-d) VM";
result = 0;
} }
fini: fini:

View File

@ -644,7 +644,7 @@ static int lx_pipe_wait_for_state(struct lx6464es *chip, u32 pipe,
if (err < 0) if (err < 0)
return err; return err;
if (current_state == state) if (!err && current_state == state)
return 0; return 0;
mdelay(1); mdelay(1);

View File

@ -350,6 +350,7 @@ static int snd_usb_audio_create(struct usb_interface *intf,
case USB_SPEED_HIGH: case USB_SPEED_HIGH:
case USB_SPEED_WIRELESS: case USB_SPEED_WIRELESS:
case USB_SPEED_SUPER: case USB_SPEED_SUPER:
case USB_SPEED_SUPER_PLUS:
break; break;
default: default:
dev_err(&dev->dev, "unknown device speed %d\n", snd_usb_get_speed(dev)); dev_err(&dev->dev, "unknown device speed %d\n", snd_usb_get_speed(dev));
@ -450,6 +451,9 @@ static int snd_usb_audio_create(struct usb_interface *intf,
case USB_SPEED_SUPER: case USB_SPEED_SUPER:
strlcat(card->longname, ", super speed", sizeof(card->longname)); strlcat(card->longname, ", super speed", sizeof(card->longname));
break; break;
case USB_SPEED_SUPER_PLUS:
strlcat(card->longname, ", super speed plus", sizeof(card->longname));
break;
default: default:
break; break;
} }

View File

@ -309,6 +309,9 @@ static int set_sample_rate_v1(struct snd_usb_audio *chip, int iface,
* support reading */ * support reading */
if (snd_usb_get_sample_rate_quirk(chip)) if (snd_usb_get_sample_rate_quirk(chip))
return 0; return 0;
/* the firmware is likely buggy, don't repeat to fail too many times */
if (chip->sample_rate_read_error > 2)
return 0;
if ((err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC_GET_CUR, if ((err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC_GET_CUR,
USB_TYPE_CLASS | USB_RECIP_ENDPOINT | USB_DIR_IN, USB_TYPE_CLASS | USB_RECIP_ENDPOINT | USB_DIR_IN,
@ -316,6 +319,7 @@ static int set_sample_rate_v1(struct snd_usb_audio *chip, int iface,
data, sizeof(data))) < 0) { data, sizeof(data))) < 0) {
dev_err(&dev->dev, "%d:%d: cannot get freq at ep %#x\n", dev_err(&dev->dev, "%d:%d: cannot get freq at ep %#x\n",
iface, fmt->altsetting, ep); iface, fmt->altsetting, ep);
chip->sample_rate_read_error++;
return 0; /* some devices don't support reading */ return 0; /* some devices don't support reading */
} }

View File

@ -120,6 +120,7 @@ unsigned char snd_usb_parse_datainterval(struct snd_usb_audio *chip,
case USB_SPEED_HIGH: case USB_SPEED_HIGH:
case USB_SPEED_WIRELESS: case USB_SPEED_WIRELESS:
case USB_SPEED_SUPER: case USB_SPEED_SUPER:
case USB_SPEED_SUPER_PLUS:
if (get_endpoint(alts, 0)->bInterval >= 1 && if (get_endpoint(alts, 0)->bInterval >= 1 &&
get_endpoint(alts, 0)->bInterval <= 4) get_endpoint(alts, 0)->bInterval <= 4)
return get_endpoint(alts, 0)->bInterval - 1; return get_endpoint(alts, 0)->bInterval - 1;

View File

@ -911,6 +911,7 @@ static void snd_usbmidi_us122l_output(struct snd_usb_midi_out_endpoint *ep,
switch (snd_usb_get_speed(ep->umidi->dev)) { switch (snd_usb_get_speed(ep->umidi->dev)) {
case USB_SPEED_HIGH: case USB_SPEED_HIGH:
case USB_SPEED_SUPER: case USB_SPEED_SUPER:
case USB_SPEED_SUPER_PLUS:
count = 1; count = 1;
break; break;
default: default:

View File

@ -45,6 +45,7 @@
#include <linux/bitops.h> #include <linux/bitops.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/list.h> #include <linux/list.h>
#include <linux/log2.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/string.h> #include <linux/string.h>
#include <linux/usb.h> #include <linux/usb.h>
@ -1378,6 +1379,71 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
snd_usb_mixer_add_control(&cval->head, kctl); snd_usb_mixer_add_control(&cval->head, kctl);
} }
static int parse_clock_source_unit(struct mixer_build *state, int unitid,
void *_ftr)
{
struct uac_clock_source_descriptor *hdr = _ftr;
struct usb_mixer_elem_info *cval;
struct snd_kcontrol *kctl;
char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
int ret;
if (state->mixer->protocol != UAC_VERSION_2)
return -EINVAL;
if (hdr->bLength != sizeof(*hdr)) {
usb_audio_dbg(state->chip,
"Bogus clock source descriptor length of %d, ignoring.\n",
hdr->bLength);
return 0;
}
/*
* The only property of this unit we are interested in is the
* clock source validity. If that isn't readable, just bail out.
*/
if (!uac2_control_is_readable(hdr->bmControls,
ilog2(UAC2_CS_CONTROL_CLOCK_VALID)))
return 0;
cval = kzalloc(sizeof(*cval), GFP_KERNEL);
if (!cval)
return -ENOMEM;
snd_usb_mixer_elem_init_std(&cval->head, state->mixer, hdr->bClockID);
cval->min = 0;
cval->max = 1;
cval->channels = 1;
cval->val_type = USB_MIXER_BOOLEAN;
cval->control = UAC2_CS_CONTROL_CLOCK_VALID;
if (uac2_control_is_writeable(hdr->bmControls,
ilog2(UAC2_CS_CONTROL_CLOCK_VALID)))
kctl = snd_ctl_new1(&usb_feature_unit_ctl, cval);
else {
cval->master_readonly = 1;
kctl = snd_ctl_new1(&usb_feature_unit_ctl_ro, cval);
}
if (!kctl) {
kfree(cval);
return -ENOMEM;
}
kctl->private_free = snd_usb_mixer_elem_free;
ret = snd_usb_copy_string_desc(state, hdr->iClockSource,
name, sizeof(name));
if (ret > 0)
snprintf(kctl->id.name, sizeof(kctl->id.name),
"%s Validity", name);
else
snprintf(kctl->id.name, sizeof(kctl->id.name),
"Clock Source %d Validity", hdr->bClockID);
return snd_usb_mixer_add_control(&cval->head, kctl);
}
/* /*
* parse a feature unit * parse a feature unit
* *
@ -2126,10 +2192,11 @@ static int parse_audio_unit(struct mixer_build *state, int unitid)
switch (p1[2]) { switch (p1[2]) {
case UAC_INPUT_TERMINAL: case UAC_INPUT_TERMINAL:
case UAC2_CLOCK_SOURCE:
return 0; /* NOP */ return 0; /* NOP */
case UAC_MIXER_UNIT: case UAC_MIXER_UNIT:
return parse_audio_mixer_unit(state, unitid, p1); return parse_audio_mixer_unit(state, unitid, p1);
case UAC2_CLOCK_SOURCE:
return parse_clock_source_unit(state, unitid, p1);
case UAC_SELECTOR_UNIT: case UAC_SELECTOR_UNIT:
case UAC2_CLOCK_SELECTOR: case UAC2_CLOCK_SELECTOR:
return parse_audio_selector_unit(state, unitid, p1); return parse_audio_selector_unit(state, unitid, p1);
@ -2307,6 +2374,7 @@ static void snd_usb_mixer_interrupt_v2(struct usb_mixer_interface *mixer,
__u8 unitid = (index >> 8) & 0xff; __u8 unitid = (index >> 8) & 0xff;
__u8 control = (value >> 8) & 0xff; __u8 control = (value >> 8) & 0xff;
__u8 channel = value & 0xff; __u8 channel = value & 0xff;
unsigned int count = 0;
if (channel >= MAX_CHANNELS) { if (channel >= MAX_CHANNELS) {
usb_audio_dbg(mixer->chip, usb_audio_dbg(mixer->chip,
@ -2315,6 +2383,12 @@ static void snd_usb_mixer_interrupt_v2(struct usb_mixer_interface *mixer,
return; return;
} }
for (list = mixer->id_elems[unitid]; list; list = list->next_id_elem)
count++;
if (count == 0)
return;
for (list = mixer->id_elems[unitid]; list; list = list->next_id_elem) { for (list = mixer->id_elems[unitid]; list; list = list->next_id_elem) {
struct usb_mixer_elem_info *info; struct usb_mixer_elem_info *info;
@ -2322,7 +2396,7 @@ static void snd_usb_mixer_interrupt_v2(struct usb_mixer_interface *mixer,
continue; continue;
info = (struct usb_mixer_elem_info *)list; info = (struct usb_mixer_elem_info *)list;
if (info->control != control) if (count > 1 && info->control != control)
continue; continue;
switch (attribute) { switch (attribute) {

View File

@ -47,6 +47,7 @@ struct snd_usb_audio {
int num_interfaces; int num_interfaces;
int num_suspended_intf; int num_suspended_intf;
int sample_rate_read_error;
struct list_head pcm_list; /* list of pcm streams */ struct list_head pcm_list; /* list of pcm streams */
struct list_head ep_list; /* list of audio-related endpoints */ struct list_head ep_list; /* list of audio-related endpoints */