summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/sound/hda_i915.h10
-rw-r--r--sound/firewire/Kconfig1
-rw-r--r--sound/firewire/amdtp-stream.c4
-rw-r--r--sound/firewire/bebob/bebob.c217
-rw-r--r--sound/firewire/bebob/bebob.h5
-rw-r--r--sound/firewire/dice/dice.c34
-rw-r--r--sound/firewire/digi00x/digi00x-transaction.c7
-rw-r--r--sound/firewire/digi00x/digi00x.c107
-rw-r--r--sound/firewire/digi00x/digi00x.h3
-rw-r--r--sound/firewire/fireworks/fireworks.c168
-rw-r--r--sound/firewire/fireworks/fireworks.h3
-rw-r--r--sound/firewire/lib.c32
-rw-r--r--sound/firewire/lib.h3
-rw-r--r--sound/firewire/oxfw/oxfw.c151
-rw-r--r--sound/firewire/oxfw/oxfw.h4
-rw-r--r--sound/hda/hdac_i915.c47
-rw-r--r--sound/pci/hda/patch_hdmi.c373
17 files changed, 775 insertions, 394 deletions
diff --git a/include/sound/hda_i915.h b/include/sound/hda_i915.h
index fa341fcb5829..eed87a7559b7 100644
--- a/include/sound/hda_i915.h
+++ b/include/sound/hda_i915.h
@@ -10,8 +10,8 @@
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_get_display_clk(struct hdac_bus *bus);
-int snd_hdac_sync_audio_rate(struct hdac_bus *bus, hda_nid_t nid, int rate);
-int snd_hdac_acomp_get_eld(struct hdac_bus *bus, hda_nid_t nid,
+int snd_hdac_sync_audio_rate(struct hdac_device *codec, hda_nid_t nid, int rate);
+int snd_hdac_acomp_get_eld(struct hdac_device *codec, hda_nid_t nid,
bool *audio_enabled, char *buffer, int max_bytes);
int snd_hdac_i915_init(struct hdac_bus *bus);
int snd_hdac_i915_exit(struct hdac_bus *bus);
@@ -29,12 +29,12 @@ static inline int snd_hdac_get_display_clk(struct hdac_bus *bus)
{
return 0;
}
-static inline int snd_hdac_sync_audio_rate(struct hdac_bus *bus, hda_nid_t nid,
- int rate)
+static inline int snd_hdac_sync_audio_rate(struct hdac_device *codec,
+ hda_nid_t nid, int rate)
{
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,
int max_bytes)
{
diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig
index 2a779c2f63ab..ab894ed1ff67 100644
--- a/sound/firewire/Kconfig
+++ b/sound/firewire/Kconfig
@@ -134,6 +134,7 @@ config SND_FIREWIRE_TASCAM
Say Y here to include support for TASCAM.
* FW-1884
* FW-1082
+ * FW-1804
To compile this driver as a module, choose M here: the module
will be called snd-firewire-tascam.
diff --git a/sound/firewire/amdtp-stream.c b/sound/firewire/amdtp-stream.c
index ed2902609a4c..4484242da0e6 100644
--- a/sound/firewire/amdtp-stream.c
+++ b/sound/firewire/amdtp-stream.c
@@ -102,6 +102,10 @@ EXPORT_SYMBOL(amdtp_stream_init);
*/
void amdtp_stream_destroy(struct amdtp_stream *s)
{
+ /* Not initialized. */
+ if (s->protocol == NULL)
+ return;
+
WARN_ON(amdtp_stream_running(s));
kfree(s->protocol);
mutex_destroy(&s->mutex);
diff --git a/sound/firewire/bebob/bebob.c b/sound/firewire/bebob/bebob.c
index 3e4e0756e3fe..f7e2cbd2a313 100644
--- a/sound/firewire/bebob/bebob.c
+++ b/sound/firewire/bebob/bebob.c
@@ -67,7 +67,7 @@ static DECLARE_BITMAP(devices_used, SNDRV_CARDS);
#define MODEL_MAUDIO_PROJECTMIX 0x00010091
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);
char vendor[24] = {0};
@@ -126,6 +126,17 @@ end:
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
* 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;
- snd_bebob_stream_destroy_duplex(bebob);
- fw_unit_put(bebob->unit);
-
- kfree(bebob->maudio_special_quirk);
-
- if (bebob->card_index >= 0) {
- mutex_lock(&devices_mutex);
- clear_bit(bebob->card_index, devices_used);
- mutex_unlock(&devices_mutex);
- }
+ mutex_lock(&devices_mutex);
+ clear_bit(bebob->card_index, devices_used);
+ mutex_unlock(&devices_mutex);
- mutex_destroy(&bebob->mutex);
+ bebob_free(card->private_data);
}
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;
}
-static int
-bebob_probe(struct fw_unit *unit,
- const struct ieee1394_device_id *entry)
+static void
+do_registration(struct work_struct *work)
{
- struct snd_card *card;
- struct snd_bebob *bebob;
- const struct snd_bebob_spec *spec;
+ struct snd_bebob *bebob =
+ container_of(work, struct snd_bebob, dwork.work);
unsigned int card_index;
int err;
+ if (bebob->registered)
+ return;
+
mutex_lock(&devices_mutex);
for (card_index = 0; card_index < SNDRV_CARDS; card_index++) {
@@ -193,64 +198,39 @@ bebob_probe(struct fw_unit *unit,
break;
}
if (card_index >= SNDRV_CARDS) {
- err = -ENOENT;
- goto end;
+ mutex_unlock(&devices_mutex);
+ return;
}
- 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))
- err = snd_bebob_maudio_load_firmware(unit);
- else
- err = -ENOSYS;
- goto end;
+ err = snd_card_new(&bebob->unit->device, index[card_index],
+ id[card_index], THIS_MODULE, 0, &bebob->card);
+ if (err < 0) {
+ mutex_unlock(&devices_mutex);
+ return;
}
- err = snd_card_new(&unit->device, index[card_index], id[card_index],
- THIS_MODULE, sizeof(struct snd_bebob), &card);
+ err = name_device(bebob);
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);
+ goto error;
- err = name_device(bebob, entry->vendor_id);
+ if (bebob->spec == &maudio_special_spec) {
+ if (bebob->entry->model_id == MODEL_MAUDIO_FW1814)
+ err = snd_bebob_maudio_special_discover(bebob, true);
+ else
+ err = snd_bebob_maudio_special_discover(bebob, false);
+ } else {
+ err = snd_bebob_stream_discover(bebob);
+ }
if (err < 0)
goto error;
- if ((entry->vendor_id == VEN_MAUDIO1) &&
- (entry->model_id == MODEL_MAUDIO_FW1814))
- err = snd_bebob_maudio_special_discover(bebob, true);
- else if ((entry->vendor_id == VEN_MAUDIO1) &&
- (entry->model_id == MODEL_MAUDIO_PROJECTMIX))
- err = snd_bebob_maudio_special_discover(bebob, false);
- else
- err = snd_bebob_stream_discover(bebob);
+ err = snd_bebob_stream_init_duplex(bebob);
if (err < 0)
goto error;
snd_bebob_proc_init(bebob);
- if ((bebob->midi_input_ports > 0) ||
- (bebob->midi_output_ports > 0)) {
+ if (bebob->midi_input_ports > 0 || bebob->midi_output_ports > 0) {
err = snd_bebob_create_midi_devices(bebob);
if (err < 0)
goto error;
@@ -264,16 +244,75 @@ bebob_probe(struct fw_unit *unit,
if (err < 0)
goto error;
- err = snd_bebob_stream_init_duplex(bebob);
+ err = snd_card_register(bebob->card);
if (err < 0)
goto error;
- if (!bebob->maudio_special_quirk) {
- err = snd_card_register(card);
- if (err < 0) {
- snd_bebob_stream_destroy_duplex(bebob);
- goto error;
- }
+ set_bit(card_index, devices_used);
+ mutex_unlock(&devices_mutex);
+
+ /*
+ * 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 {
/*
* 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
* future bus reset, registration is done in next update().
*/
- bebob->deferred_registration = true;
fw_schedule_bus_reset(fw_parent_device(bebob->unit)->card,
false, true);
}
- dev_set_drvdata(&unit->device, bebob);
-end:
- mutex_unlock(&devices_mutex);
- return err;
-error:
- mutex_unlock(&devices_mutex);
- snd_card_free(card);
- return err;
+ return 0;
}
/*
@@ -324,15 +355,11 @@ bebob_update(struct fw_unit *unit)
if (bebob == NULL)
return;
- fcp_bus_reset(bebob->unit);
-
- if (bebob->deferred_registration) {
- if (snd_card_register(bebob->card) < 0) {
- snd_bebob_stream_destroy_duplex(bebob);
- snd_card_free(bebob->card);
- }
- bebob->deferred_registration = false;
- }
+ /* Postpone a workqueue for deferred registration. */
+ if (!bebob->registered)
+ snd_fw_schedule_registration(unit, &bebob->dwork);
+ else
+ fcp_bus_reset(bebob->unit);
}
static void bebob_remove(struct fw_unit *unit)
@@ -342,8 +369,20 @@ static void bebob_remove(struct fw_unit *unit)
if (bebob == NULL)
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 = {
diff --git a/sound/firewire/bebob/bebob.h b/sound/firewire/bebob/bebob.h
index b50bb33d9d46..2a442a7a2119 100644
--- a/sound/firewire/bebob/bebob.h
+++ b/sound/firewire/bebob/bebob.h
@@ -83,6 +83,10 @@ struct snd_bebob {
struct mutex mutex;
spinlock_t lock;
+ bool registered;
+ struct delayed_work dwork;
+
+ const struct ieee1394_device_id *entry;
const struct snd_bebob_spec *spec;
unsigned int midi_input_ports;
@@ -111,7 +115,6 @@ struct snd_bebob {
/* for M-Audio special devices */
void *maudio_special_quirk;
- bool deferred_registration;
/* For BeBoB version quirk. */
unsigned int version;
diff --git a/sound/firewire/dice/dice.c b/sound/firewire/dice/dice.c
index 8b64aef31a86..96fe68f42e5d 100644
--- a/sound/firewire/dice/dice.c
+++ b/sound/firewire/dice/dice.c
@@ -20,8 +20,6 @@ MODULE_LICENSE("GPL v2");
#define WEISS_CATEGORY_ID 0x00
#define LOUD_CATEGORY_ID 0x10
-#define PROBE_DELAY_MS (2 * MSEC_PER_SEC)
-
/*
* Some models support several isochronous channels, while these streams are not
* 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);
+ err = snd_dice_stream_init_duplex(dice);
+ if (err < 0)
+ goto error;
+
snd_dice_create_proc(dice);
err = snd_dice_create_pcm(dice);
@@ -229,28 +231,14 @@ static void do_registration(struct work_struct *work)
return;
error:
+ snd_dice_stream_destroy_duplex(dice);
snd_dice_transaction_destroy(dice);
+ snd_dice_stream_destroy_duplex(dice);
snd_card_free(dice->card);
dev_info(&dice->unit->device,
"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)
{
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_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. */
INIT_DEFERRABLE_WORK(&dice->dwork, do_registration);
- schedule_registration(dice);
+ snd_fw_schedule_registration(unit, &dice->dwork);
return 0;
}
@@ -312,7 +294,7 @@ static void dice_bus_reset(struct fw_unit *unit)
/* Postpone a workqueue for deferred registration. */
if (!dice->registered)
- schedule_registration(dice);
+ snd_fw_schedule_registration(unit, &dice->dwork);
/* The handler address register becomes initialized. */
snd_dice_transaction_reinit(dice);
diff --git a/sound/firewire/digi00x/digi00x-transaction.c b/sound/firewire/digi00x/digi00x-transaction.c
index 554324d8c602..735d35640807 100644
--- a/sound/firewire/digi00x/digi00x-transaction.c
+++ b/sound/firewire/digi00x/digi00x-transaction.c
@@ -126,12 +126,17 @@ int snd_dg00x_transaction_register(struct snd_dg00x *dg00x)
return err;
error:
fw_core_remove_address_handler(&dg00x->async_handler);
- dg00x->async_handler.address_callback = NULL;
+ dg00x->async_handler.callback_data = NULL;
return err;
}
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);
fw_core_remove_address_handler(&dg00x->async_handler);
+
+ dg00x->async_handler.callback_data = NULL;
}
diff --git a/sound/firewire/digi00x/digi00x.c b/sound/firewire/digi00x/digi00x.c
index 1f33b7a1fca4..cc4776c6ded3 100644
--- a/sound/firewire/digi00x/digi00x.c
+++ b/sound/firewire/digi00x/digi00x.c
@@ -40,10 +40,8 @@ static int name_card(struct snd_dg00x *dg00x)
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_transaction_unregister(dg00x);
@@ -52,28 +50,24 @@ static void dg00x_card_free(struct snd_card *card)
mutex_destroy(&dg00x->mutex);
}
-static int snd_dg00x_probe(struct fw_unit *unit,
- const struct ieee1394_device_id *entry)
+static void dg00x_card_free(struct snd_card *card)
{
- struct snd_card *card;
- struct snd_dg00x *dg00x;
- int err;
+ dg00x_free(card->private_data);
+}
- /* create card */
- err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE,
- sizeof(struct snd_dg00x), &card);
- if (err < 0)
- return err;
- card->private_free = dg00x_card_free;
+static void do_registration(struct work_struct *work)
+{
+ struct snd_dg00x *dg00x =
+ container_of(work, struct snd_dg00x, dwork.work);
+ int err;
- /* initialize myself */
- dg00x = card->private_data;
- dg00x->card = card;
- dg00x->unit = fw_unit_get(unit);
+ if (dg00x->registered)
+ return;
- mutex_init(&dg00x->mutex);
- spin_lock_init(&dg00x->lock);
- init_waitqueue_head(&dg00x->hwdep_wait);
+ err = snd_card_new(&dg00x->unit->device, -1, NULL, THIS_MODULE, 0,
+ &dg00x->card);
+ if (err < 0)
+ return;
err = name_card(dg00x);
if (err < 0)
@@ -101,35 +95,86 @@ static int snd_dg00x_probe(struct fw_unit *unit,
if (err < 0)
goto error;
- err = snd_card_register(card);
+ err = snd_card_register(dg00x->card);
if (err < 0)
goto error;
- dev_set_drvdata(&unit->device, dg00x);
+ dg00x->card->private_free = dg00x_card_free;
+ dg00x->card->private_data = dg00x;
+ dg00x->registered = true;
- return err;
+ return;
error:
- snd_card_free(card);
- return err;
+ 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);
+
+ mutex_init(&dg00x->mutex);
+ spin_lock_init(&dg00x->lock);
+ init_waitqueue_head(&dg00x->hwdep_wait);
+
+ /* 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)
{
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);
- mutex_lock(&dg00x->mutex);
- snd_dg00x_stream_update_duplex(dg00x);
- mutex_unlock(&dg00x->mutex);
+ /*
+ * After registration, userspace can start packet streaming, then this
+ * 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)
{
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[] = {
diff --git a/sound/firewire/digi00x/digi00x.h b/sound/firewire/digi00x/digi00x.h
index 907e73993677..2cd465c0caae 100644
--- a/sound/firewire/digi00x/digi00x.h
+++ b/sound/firewire/digi00x/digi00x.h
@@ -37,6 +37,9 @@ struct snd_dg00x {
struct mutex mutex;
spinlock_t lock;
+ bool registered;
+ struct delayed_work dwork;
+
struct amdtp_stream tx_stream;
struct fw_iso_resources tx_resources;
diff --git a/sound/firewire/fireworks/fireworks.c b/sound/firewire/fireworks/fireworks.c
index 8f27b67503c8..71a0613d3da0 100644
--- a/sound/firewire/fireworks/fireworks.c
+++ b/sound/firewire/fireworks/fireworks.c
@@ -168,11 +168,34 @@ get_hardware_info(struct snd_efw *efw)
sizeof(struct snd_efw_phys_grp) * hwinfo->phys_in_grp_count);
memcpy(&efw->phys_out_grps, hwinfo->phys_out_grps,
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:
kfree(hwinfo);
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
* 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;
- 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) {
mutex_lock(&devices_mutex);
clear_bit(efw->card_index, devices_used);
mutex_unlock(&devices_mutex);
}
- mutex_destroy(&efw->mutex);
+ efw_free(card->private_data);
}
-static int
-efw_probe(struct fw_unit *unit,
- const struct ieee1394_device_id *entry)
+static void
+do_registration(struct work_struct *work)
{
- struct snd_card *card;
- struct snd_efw *efw;
- int card_index, err;
+ struct snd_efw *efw = container_of(work, struct snd_efw, dwork.work);
+ unsigned int card_index;
+ int err;
+
+ if (efw->registered)
+ return;
mutex_lock(&devices_mutex);
@@ -215,24 +234,16 @@ efw_probe(struct fw_unit *unit,
break;
}
if (card_index >= SNDRV_CARDS) {
- err = -ENOENT;
- goto end;
+ mutex_unlock(&devices_mutex);
+ return;
}
- err = snd_card_new(&unit->device, index[card_index], id[card_index],
- THIS_MODULE, sizeof(struct snd_efw), &card);
- if (err < 0)
- goto end;
- efw = card->private_data;
- 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);
+ err = snd_card_new(&efw->unit->device, index[card_index],
+ id[card_index], THIS_MODULE, 0, &efw->card);
+ if (err < 0) {
+ mutex_unlock(&devices_mutex);
+ return;
+ }
/* prepare response buffer */
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);
if (err < 0)
goto error;
- /* AudioFire8 (since 2009) and AudioFirePre8 */
- if (entry->model_id == MODEL_ECHO_AUDIOFIRE_9)
- efw->is_af9 = true;
- /* These models uses the same firmware. */
- 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;
+
+ err = snd_efw_stream_init_duplex(efw);
+ if (err < 0)
+ goto error;
snd_efw_proc_init(efw);
@@ -275,44 +280,93 @@ efw_probe(struct fw_unit *unit,
if (err < 0)
goto error;
- err = snd_efw_stream_init_duplex(efw);
+ err = snd_card_register(efw->card);
if (err < 0)
goto error;
- err = snd_card_register(card);
- if (err < 0) {
- snd_efw_stream_destroy_duplex(efw);
- goto error;
- }
-
- dev_set_drvdata(&unit->device, efw);
-end:
+ set_bit(card_index, devices_used);
mutex_unlock(&devices_mutex);
- return err;
+
+ /*
+ * After registered, efw instance can be released corresponding to
+ * releasing the sound card instance.
+ */
+ efw->card->private_free = efw_card_free;
+ efw->card->private_data = efw;
+ efw->registered = true;
+
+ return;
error:
- snd_efw_transaction_remove_instance(efw);
mutex_unlock(&devices_mutex);
- snd_card_free(card);
- return err;
+ snd_efw_transaction_remove_instance(efw);
+ 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)
{
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);
- mutex_lock(&efw->mutex);
- snd_efw_stream_update_duplex(efw);
- mutex_unlock(&efw->mutex);
+ /*
+ * After registration, userspace can start packet streaming, then this
+ * 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)
{
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[] = {
diff --git a/sound/firewire/fireworks/fireworks.h b/sound/firewire/fireworks/fireworks.h
index 96c4e0c6a9bd..471c77254dfd 100644
--- a/sound/firewire/fireworks/fireworks.h
+++ b/sound/firewire/fireworks/fireworks.h
@@ -65,6 +65,9 @@ struct snd_efw {
struct mutex mutex;
spinlock_t lock;
+ bool registered;
+ struct delayed_work dwork;
+
/* for transaction */
u32 seqnum;
bool resp_addr_changable;
diff --git a/sound/firewire/lib.c b/sound/firewire/lib.c
index f80aafa44c89..ca4dfcf43175 100644
--- a/sound/firewire/lib.c
+++ b/sound/firewire/lib.c
@@ -67,6 +67,38 @@ int snd_fw_transaction(struct fw_unit *unit, int tcode,
}
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,
void *data, size_t length,
void *callback_data)
diff --git a/sound/firewire/lib.h b/sound/firewire/lib.h
index f3f6f84c48d6..f6769312ebfc 100644
--- a/sound/firewire/lib.h
+++ b/sound/firewire/lib.h
@@ -22,6 +22,9 @@ static inline bool rcode_is_permanent_error(int rcode)
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;
typedef int (*snd_fw_async_midi_port_fill)(
struct snd_rawmidi_substream *substream,
diff --git a/sound/firewire/oxfw/oxfw.c b/sound/firewire/oxfw/oxfw.c
index abedc2207261..e629b88f7d93 100644
--- a/sound/firewire/oxfw/oxfw.c
+++ b/sound/firewire/oxfw/oxfw.c
@@ -118,15 +118,8 @@ end:
return err;
}
-/*
- * 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)
+static void oxfw_free(struct snd_oxfw *oxfw)
{
- struct snd_oxfw *oxfw = card->private_data;
unsigned int i;
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);
}
+/*
+ * 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)
{
struct fw_device *fw_dev = fw_parent_device(oxfw->unit);
@@ -205,41 +209,39 @@ static int detect_quirks(struct snd_oxfw *oxfw)
return 0;
}
-static int oxfw_probe(struct fw_unit *unit,
- const struct ieee1394_device_id *entry)
+static void do_registration(struct work_struct *work)
{
- struct snd_card *card;
- struct snd_oxfw *oxfw;
+ struct snd_oxfw *oxfw = container_of(work, struct snd_oxfw, dwork.work);
int err;
- if (entry->vendor_id == VENDOR_LOUD && !detect_loud_models(unit))
- return -ENODEV;
+ if (oxfw->registered)
+ return;
- err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE,
- sizeof(*oxfw), &card);
+ err = snd_card_new(&oxfw->unit->device, -1, NULL, THIS_MODULE, 0,
+ &oxfw->card);
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 = name_card(oxfw);
+ if (err < 0)
+ goto error;
- err = snd_oxfw_stream_discover(oxfw);
+ err = detect_quirks(oxfw);
if (err < 0)
goto error;
- err = name_card(oxfw);
+ err = snd_oxfw_stream_discover(oxfw);
if (err < 0)
goto error;
- err = detect_quirks(oxfw);
+ 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);
if (err < 0)
@@ -255,54 +257,97 @@ static int oxfw_probe(struct fw_unit *unit,
if (err < 0)
goto error;
- err = snd_oxfw_stream_init_simplex(oxfw, &oxfw->rx_stream);
+ err = snd_card_register(oxfw->card);
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_card_register(card);
- if (err < 0) {
- snd_oxfw_stream_destroy_simplex(oxfw, &oxfw->rx_stream);
- if (oxfw->has_output)
- snd_oxfw_stream_destroy_simplex(oxfw, &oxfw->tx_stream);
- goto error;
- }
+ /*
+ * After registered, oxfw instance can be released corresponding to
+ * releasing the sound card instance.
+ */
+ oxfw->card->private_free = oxfw_card_free;
+ 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);
+ 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;
-error:
- snd_card_free(card);
- return err;
}
static void oxfw_bus_reset(struct fw_unit *unit)
{
struct snd_oxfw *oxfw = dev_get_drvdata(&unit->device);
+ if (!oxfw->registered)
+ snd_fw_schedule_registration(unit, &oxfw->dwork);
+
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);
- if (oxfw->has_output)
- snd_oxfw_stream_update_simplex(oxfw, &oxfw->tx_stream);
+ snd_oxfw_stream_update_simplex(oxfw, &oxfw->rx_stream);
+ if (oxfw->has_output)
+ snd_oxfw_stream_update_simplex(oxfw, &oxfw->tx_stream);
- mutex_unlock(&oxfw->mutex);
+ mutex_unlock(&oxfw->mutex);
- if (oxfw->entry->vendor_id == OUI_STANTON)
- snd_oxfw_scs1x_update(oxfw);
+ if (oxfw->entry->vendor_id == OUI_STANTON)
+ snd_oxfw_scs1x_update(oxfw);
+ }
}
static void oxfw_remove(struct fw_unit *unit)
{
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 = {
diff --git a/sound/firewire/oxfw/oxfw.h b/sound/firewire/oxfw/oxfw.h
index 9beecc214767..2047dcb27625 100644
--- a/sound/firewire/oxfw/oxfw.h
+++ b/sound/firewire/oxfw/oxfw.h
@@ -36,10 +36,12 @@
struct snd_oxfw {
struct snd_card *card;
struct fw_unit *unit;
- const struct device_info *device_info;
struct mutex mutex;
spinlock_t lock;
+ bool registered;
+ struct delayed_work dwork;
+
bool wrong_dbs;
bool has_output;
u8 *tx_stream_formats[SND_OXFW_STREAM_FORMAT_ENTRIES];
diff --git a/sound/hda/hdac_i915.c b/sound/hda/hdac_i915.c
index 54babe1c0b16..6800e0c5a38f 100644
--- a/sound/hda/hdac_i915.c
+++ b/sound/hda/hdac_i915.c
@@ -118,22 +118,40 @@ int snd_hdac_get_display_clk(struct hdac_bus *bus)
}
EXPORT_SYMBOL_GPL(snd_hdac_get_display_clk);
-/* There is a fixed mapping between audio pin node and display port
- * on current Intel platforms:
+/* There is a fixed mapping between audio pin node and display port.
+ * on SNB, IVY, HSW, BSW, SKL, BXT, KBL:
* Pin Widget 5 - PORT B (port = 1 in i915 driver)
* Pin Widget 6 - PORT C (port = 2 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 pin_nid - 4;
+ return pin_nid - base_nid;
}
/**
* 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
* @rate: the sample rate to set
*
@@ -143,14 +161,15 @@ static int pin2port(hda_nid_t pin_nid)
* This function sets N/CTS value based on the given sample rate.
* 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;
int port;
if (!acomp || !acomp->ops || !acomp->ops->sync_audio_rate)
return -ENODEV;
- port = pin2port(nid);
+ port = pin2port(codec, nid);
if (port < 0)
return -EINVAL;
return acomp->ops->sync_audio_rate(acomp->dev, port, rate);
@@ -159,7 +178,7 @@ EXPORT_SYMBOL_GPL(snd_hdac_sync_audio_rate);
/**
* 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
* @audio_enabled: the pointer to store the current audio state
* @buffer: the buffer pointer to store ELD bytes
@@ -177,16 +196,17 @@ EXPORT_SYMBOL_GPL(snd_hdac_sync_audio_rate);
* 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.
*/
-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)
{
+ struct hdac_bus *bus = codec->bus;
struct i915_audio_component *acomp = bus->audio_component;
int port;
if (!acomp || !acomp->ops || !acomp->ops->get_eld)
return -ENODEV;
- port = pin2port(nid);
+ port = pin2port(codec, nid);
if (port < 0)
return -EINVAL;
return acomp->ops->get_eld(acomp->dev, port, audio_enabled,
@@ -298,6 +318,9 @@ int snd_hdac_i915_init(struct hdac_bus *bus)
struct i915_audio_component *acomp;
int ret;
+ if (WARN_ON(hdac_acomp))
+ return -EBUSY;
+
if (!i915_gfx_present())
return -ENODEV;
@@ -331,6 +354,7 @@ out_master_del:
out_err:
kfree(acomp);
bus->audio_component = NULL;
+ hdac_acomp = NULL;
dev_info(dev, "failed to add i915 component master (%d)\n", ret);
return ret;
@@ -364,6 +388,7 @@ int snd_hdac_i915_exit(struct hdac_bus *bus)
kfree(acomp);
bus->audio_component = NULL;
+ hdac_acomp = NULL;
return 0;
}
diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c
index 5af372d01834..4833c7bdd1e8 100644
--- a/sound/pci/hda/patch_hdmi.c
+++ b/sound/pci/hda/patch_hdmi.c
@@ -114,6 +114,9 @@ struct hdmi_ops {
int (*setup_stream)(struct hda_codec *codec, hda_nid_t cvt_nid,
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 {
@@ -684,7 +687,8 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec,
if (!channels)
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,
AC_VERB_SET_AMP_GAIN_MUTE,
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;
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));
if (err) {
@@ -884,7 +885,7 @@ static int hdmi_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid,
* of the pin.
*/
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_per_pin *per_pin;
@@ -925,8 +926,6 @@ static int hdmi_choose_cvt(struct hda_codec *codec,
if (cvt_id)
*cvt_id = cvt_idx;
- if (mux_id)
- *mux_id = mux_idx;
return 0;
}
@@ -1019,9 +1018,6 @@ static void intel_not_share_assigned_cvt_nid(struct hda_codec *codec,
int mux_idx;
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
* mux index of the pins are always the same.
* 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);
}
+/* 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
* 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)
return -EINVAL;
- err = hdmi_choose_cvt(codec, -1, &cvt_idx, NULL);
+ err = hdmi_choose_cvt(codec, -1, &cvt_idx);
if (err)
return err;
@@ -1057,7 +1064,7 @@ static int hdmi_pcm_open_no_pin(struct hda_pcm_stream *hinfo,
per_cvt->assigned = 1;
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);
/* 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 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_eld *eld;
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) {
mutex_unlock(&spec->pcm_lock);
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,
AC_VERB_SET_CONNECT_SEL,
- mux_idx);
+ per_pin->mux_idx);
/* configure unused pins to choose other converters */
- if (is_haswell_plus(codec) || is_valleyview_plus(codec))
- intel_not_share_assigned_cvt(codec, per_pin->pin_nid, mux_idx);
+ pin_cvt_fixup(codec, per_pin, 0);
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.
*/
if (eld->eld_valid && !old_eld_valid && per_pin->setup) {
- if (is_haswell_plus(codec) || is_valleyview_plus(codec)) {
- intel_verify_pin_cvt_connect(codec, per_pin);
- intel_not_share_assigned_cvt(codec, per_pin->pin_nid,
- per_pin->mux_idx);
- }
-
+ pin_cvt_fixup(codec, per_pin, 0);
hdmi_setup_audio_infoframe(codec, per_pin, per_pin->non_pcm);
}
@@ -1485,7 +1486,7 @@ static void sync_eld_via_acomp(struct hda_codec *codec,
mutex_lock(&per_pin->lock);
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_MAX_SIZE);
if (size > 0) {
@@ -1712,7 +1713,7 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
* skip pin setup and return 0 to make audio playback
* 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,
stream_tag, 0, format);
mutex_unlock(&spec->pcm_lock);
@@ -1725,23 +1726,21 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
}
per_pin = get_pin(spec, pin_idx);
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.
- * After S3, the audio driver restores pin:cvt selections
- * but this can happen before gfx is ready and such selection
- * is overlooked by HW. Thus multiple pins can share a same
- * default convertor and mute control will affect each other,
- * which can cause a resumed audio playback become silent
- * after S3.
- */
- intel_verify_pin_cvt_connect(codec, per_pin);
- intel_not_share_assigned_cvt(codec, pin_nid, per_pin->mux_idx);
- }
+
+ /* Verify pin:cvt selections to avoid silent audio after S3.
+ * After S3, the audio driver restores pin:cvt selections
+ * but this can happen before gfx is ready and such selection
+ * is overlooked by HW. Thus multiple pins can share a same
+ * default convertor and mute control will affect each other,
+ * which can cause a resumed audio playback become silent
+ * after S3.
+ */
+ pin_cvt_fixup(codec, per_pin, 0);
/* Call sync_audio_rate to set the N/CTS/M manually if necessary */
/* Todo: add DP1.2 MST audio support later */
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);
mutex_lock(&per_pin->lock);
@@ -2074,6 +2073,20 @@ static void hdmi_array_free(struct hdmi_spec *spec)
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)
{
struct hdmi_spec *spec = codec->spec;
@@ -2098,10 +2111,7 @@ static void generic_hdmi_free(struct hda_codec *codec)
spec->pcm_rec[pcm_idx].jack = NULL;
}
- if (spec->i915_bound)
- snd_hdac_i915_exit(&codec->bus->core);
- hdmi_array_free(spec);
- kfree(spec);
+ generic_spec_free(codec);
}
#ifdef CONFIG_PM
@@ -2139,6 +2149,54 @@ static const struct hdmi_ops generic_standard_hdmi_ops = {
.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;
+
+ 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,
hda_nid_t nid)
{
@@ -2216,12 +2274,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)
{
struct hda_codec *codec = audio_ptr;
- int pin_nid = port + 0x04;
+ int pin_nid;
/* we assume only from port-B to port-D */
if (port < 1 || port > 3)
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);
* the state will be updated at resume
*/
@@ -2234,95 +2303,161 @@ static void intel_pin_eld_notify(void *audio_ptr, int port)
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;
+ struct hdmi_spec *spec = codec->spec;
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
+ 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);
+}
- spec->ops = generic_standard_hdmi_ops;
- mutex_init(&spec->pcm_lock);
- snd_hdac_register_chmap_ops(&codec->core, &spec->chmap);
+/* 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);
+}
- 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;
+/* 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);
+ }
+}
- codec->spec = spec;
- hdmi_array_init(spec, 4);
+/* Intel Haswell and onwards; audio component with eld notifier */
+static int patch_i915_hsw_hdmi(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec;
+ int err;
-#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;
+ /* HSW+ requires i915 binding */
+ if (!codec->bus->core.audio_component) {
+ codec_info(codec, "No i915 binding for Intel HDMI/DP codec\n");
+ return -ENODEV;
}
-#endif
- if (is_haswell_plus(codec)) {
- intel_haswell_enable_all_pins(codec, true);
- intel_haswell_fixup_enable_dp12(codec);
- }
+ err = alloc_generic_hdmi(codec);
+ if (err < 0)
+ return err;
+ spec = codec->spec;
- /* For Valleyview/Cherryview, only the display codec is in the display
- * 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
+ 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.
- * For previous platforms, there is no such power well feature.
*/
- if (is_valleyview_plus(codec) || is_skylake(codec) ||
- is_broxton(codec))
+ if (!is_haswell(codec) && !is_broadwell(codec))
codec->core.link_power_control = 1;
- if (hdmi_parse_codec(codec) < 0) {
- if (spec->i915_bound)
- snd_hdac_i915_exit(&codec->bus->core);
- codec->spec = NULL;
- kfree(spec);
- return -EINVAL;
+ 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;
}
- 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;
+
+ 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;
}
- /* Enable runtime pm for HDMI audio codec of HSW/BDW/SKL/BYT/BSW */
- if (is_haswell_plus(codec) || is_valleyview_plus(codec))
- codec->auto_runtime_pm = 1;
+ err = alloc_generic_hdmi(codec);
+ if (err < 0)
+ return err;
+ spec = codec->spec;
- generic_hdmi_init_per_pins(codec);
+ /* For Valleyview/Cherryview, only the display codec is in the display
+ * power well and can use link_power ops to request/release the power.
+ */
+ codec->core.link_power_control = 1;
+ codec->depop_delay = 0;
+ codec->auto_runtime_pm = 1;
- 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);
+ spec->ops.pin_cvt_fixup = i915_pin_cvt_fixup;
+
+ err = hdmi_parse_codec(codec);
+ if (err < 0) {
+ generic_spec_free(codec);
+ return err;
}
- WARN_ON(spec->dyn_pcm_assign && !codec_has_acomp(codec));
+ generic_hdmi_init_per_pins(codec);
+ register_i915_notifier(codec);
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
*/
@@ -3491,21 +3626,21 @@ HDA_CODEC_ENTRY(0x11069f80, "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(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(0x80862802, "Cantiga 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(0x80862805, "CougarPoint HDMI", patch_generic_hdmi),
-HDA_CODEC_ENTRY(0x80862806, "PantherPoint HDMI", patch_generic_hdmi),
-HDA_CODEC_ENTRY(0x80862807, "Haswell HDMI", patch_generic_hdmi),
-HDA_CODEC_ENTRY(0x80862808, "Broadwell HDMI", patch_generic_hdmi),
-HDA_CODEC_ENTRY(0x80862809, "Skylake HDMI", patch_generic_hdmi),
-HDA_CODEC_ENTRY(0x8086280a, "Broxton HDMI", patch_generic_hdmi),
-HDA_CODEC_ENTRY(0x8086280b, "Kabylake HDMI", patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x80862804, "IbexPeak HDMI", patch_i915_cpt_hdmi),
+HDA_CODEC_ENTRY(0x80862805, "CougarPoint HDMI", patch_i915_cpt_hdmi),
+HDA_CODEC_ENTRY(0x80862806, "PantherPoint HDMI", patch_i915_cpt_hdmi),
+HDA_CODEC_ENTRY(0x80862807, "Haswell HDMI", patch_i915_hsw_hdmi),
+HDA_CODEC_ENTRY(0x80862808, "Broadwell HDMI", patch_i915_hsw_hdmi),
+HDA_CODEC_ENTRY(0x80862809, "Skylake HDMI", patch_i915_hsw_hdmi),
+HDA_CODEC_ENTRY(0x8086280a, "Broxton HDMI", patch_i915_hsw_hdmi),
+HDA_CODEC_ENTRY(0x8086280b, "Kabylake HDMI", patch_i915_hsw_hdmi),
HDA_CODEC_ENTRY(0x80862880, "CedarTrail HDMI", patch_generic_hdmi),
-HDA_CODEC_ENTRY(0x80862882, "Valleyview2 HDMI", patch_generic_hdmi),
-HDA_CODEC_ENTRY(0x80862883, "Braswell HDMI", patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x80862882, "Valleyview2 HDMI", patch_i915_byt_hdmi),
+HDA_CODEC_ENTRY(0x80862883, "Braswell HDMI", patch_i915_byt_hdmi),
HDA_CODEC_ENTRY(0x808629fb, "Crestline HDMI", patch_generic_hdmi),
/* special ID for generic HDMI */
HDA_CODEC_ENTRY(HDA_CODEC_ID_GENERIC_HDMI, "Generic HDMI", patch_generic_hdmi),