diff options
Diffstat (limited to 'sound')
33 files changed, 1099 insertions, 1569 deletions
diff --git a/sound/core/Kconfig b/sound/core/Kconfig index a2a1e24becc6..6d12ca9bcb80 100644 --- a/sound/core/Kconfig +++ b/sound/core/Kconfig @@ -24,12 +24,15 @@ config SND_RAWMIDI config SND_COMPRESS_OFFLOAD tristate -# To be effective this also requires INPUT - users should say: -# select SND_JACK if INPUT=y || INPUT=SND -# to avoid having to force INPUT on. config SND_JACK bool +# enable input device support in jack layer +config SND_JACK_INPUT_DEV + bool + depends on SND_JACK + default y if INPUT=y || INPUT=SND + config SND_SEQUENCER tristate "Sequencer support" select SND_TIMER diff --git a/sound/core/jack.c b/sound/core/jack.c index 7237acbdcbbc..f652e90efd7e 100644 --- a/sound/core/jack.c +++ b/sound/core/jack.c @@ -32,6 +32,7 @@ struct snd_jack_kctl { unsigned int mask_bits; /* only masked status bits are reported via kctl */ }; +#ifdef CONFIG_SND_JACK_INPUT_DEV static int jack_switch_types[SND_JACK_SWITCH_TYPES] = { SW_HEADPHONE_INSERT, SW_MICROPHONE_INSERT, @@ -40,9 +41,11 @@ static int jack_switch_types[SND_JACK_SWITCH_TYPES] = { SW_VIDEOOUT_INSERT, SW_LINEIN_INSERT, }; +#endif /* CONFIG_SND_JACK_INPUT_DEV */ static int snd_jack_dev_disconnect(struct snd_device *device) { +#ifdef CONFIG_SND_JACK_INPUT_DEV struct snd_jack *jack = device->device_data; if (!jack->input_dev) @@ -55,6 +58,7 @@ static int snd_jack_dev_disconnect(struct snd_device *device) else input_free_device(jack->input_dev); jack->input_dev = NULL; +#endif /* CONFIG_SND_JACK_INPUT_DEV */ return 0; } @@ -79,6 +83,7 @@ static int snd_jack_dev_free(struct snd_device *device) return 0; } +#ifdef CONFIG_SND_JACK_INPUT_DEV static int snd_jack_dev_register(struct snd_device *device) { struct snd_jack *jack = device->device_data; @@ -116,6 +121,7 @@ static int snd_jack_dev_register(struct snd_device *device) return err; } +#endif /* CONFIG_SND_JACK_INPUT_DEV */ static void snd_jack_kctl_private_free(struct snd_kcontrol *kctl) { @@ -209,11 +215,12 @@ int snd_jack_new(struct snd_card *card, const char *id, int type, struct snd_jack *jack; struct snd_jack_kctl *jack_kctl = NULL; int err; - int i; static struct snd_device_ops ops = { .dev_free = snd_jack_dev_free, +#ifdef CONFIG_SND_JACK_INPUT_DEV .dev_register = snd_jack_dev_register, .dev_disconnect = snd_jack_dev_disconnect, +#endif /* CONFIG_SND_JACK_INPUT_DEV */ }; if (initial_kctl) { @@ -230,6 +237,9 @@ int snd_jack_new(struct snd_card *card, const char *id, int type, /* don't creat input device for phantom jack */ if (!phantom_jack) { +#ifdef CONFIG_SND_JACK_INPUT_DEV + int i; + jack->input_dev = input_allocate_device(); if (jack->input_dev == NULL) { err = -ENOMEM; @@ -245,6 +255,7 @@ int snd_jack_new(struct snd_card *card, const char *id, int type, input_set_capability(jack->input_dev, EV_SW, jack_switch_types[i]); +#endif /* CONFIG_SND_JACK_INPUT_DEV */ } err = snd_device_new(card, SNDRV_DEV_JACK, jack, &ops); @@ -262,13 +273,16 @@ int snd_jack_new(struct snd_card *card, const char *id, int type, return 0; fail_input: +#ifdef CONFIG_SND_JACK_INPUT_DEV input_free_device(jack->input_dev); +#endif kfree(jack->id); kfree(jack); return err; } EXPORT_SYMBOL(snd_jack_new); +#ifdef CONFIG_SND_JACK_INPUT_DEV /** * snd_jack_set_parent - Set the parent device for a jack * @@ -326,10 +340,10 @@ int snd_jack_set_key(struct snd_jack *jack, enum snd_jack_types type, jack->type |= type; jack->key[key] = keytype; - return 0; } EXPORT_SYMBOL(snd_jack_set_key); +#endif /* CONFIG_SND_JACK_INPUT_DEV */ /** * snd_jack_report - Report the current status of a jack @@ -340,7 +354,9 @@ EXPORT_SYMBOL(snd_jack_set_key); void snd_jack_report(struct snd_jack *jack, int status) { struct snd_jack_kctl *jack_kctl; +#ifdef CONFIG_SND_JACK_INPUT_DEV int i; +#endif if (!jack) return; @@ -349,6 +365,7 @@ void snd_jack_report(struct snd_jack *jack, int status) snd_kctl_jack_report(jack->card, jack_kctl->kctl, status & jack_kctl->mask_bits); +#ifdef CONFIG_SND_JACK_INPUT_DEV if (!jack->input_dev) return; @@ -369,6 +386,6 @@ void snd_jack_report(struct snd_jack *jack, int status) } input_sync(jack->input_dev); - +#endif /* CONFIG_SND_JACK_INPUT_DEV */ } EXPORT_SYMBOL(snd_jack_report); diff --git a/sound/core/timer.c b/sound/core/timer.c index dca817fc7894..aa1b15c155d1 100644 --- a/sound/core/timer.c +++ b/sound/core/timer.c @@ -305,8 +305,6 @@ int snd_timer_open(struct snd_timer_instance **ti, return 0; } -static int _snd_timer_stop(struct snd_timer_instance *timeri, int event); - /* * close a timer instance */ @@ -318,25 +316,14 @@ int snd_timer_close(struct snd_timer_instance *timeri) if (snd_BUG_ON(!timeri)) return -ENXIO; + mutex_lock(®ister_mutex); + list_del(&timeri->open_list); + /* force to stop the timer */ snd_timer_stop(timeri); - if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) { - /* wait, until the active callback is finished */ - spin_lock_irq(&slave_active_lock); - while (timeri->flags & SNDRV_TIMER_IFLG_CALLBACK) { - spin_unlock_irq(&slave_active_lock); - udelay(10); - spin_lock_irq(&slave_active_lock); - } - spin_unlock_irq(&slave_active_lock); - mutex_lock(®ister_mutex); - list_del(&timeri->open_list); - mutex_unlock(®ister_mutex); - } else { - timer = timeri->timer; - if (snd_BUG_ON(!timer)) - goto out; + timer = timeri->timer; + if (timer) { /* wait, until the active callback is finished */ spin_lock_irq(&timer->lock); while (timeri->flags & SNDRV_TIMER_IFLG_CALLBACK) { @@ -345,11 +332,7 @@ int snd_timer_close(struct snd_timer_instance *timeri) spin_lock_irq(&timer->lock); } spin_unlock_irq(&timer->lock); - mutex_lock(®ister_mutex); - list_del(&timeri->open_list); - if (list_empty(&timer->open_list_head) && - timer->hw.close) - timer->hw.close(timer); + /* remove slave links */ spin_lock_irq(&slave_active_lock); spin_lock(&timer->lock); @@ -363,18 +346,27 @@ int snd_timer_close(struct snd_timer_instance *timeri) } spin_unlock(&timer->lock); spin_unlock_irq(&slave_active_lock); - /* release a card refcount for safe disconnection */ - if (timer->card) - put_device(&timer->card->card_dev); - mutex_unlock(®ister_mutex); + + /* slave doesn't need to release timer resources below */ + if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) + timer = NULL; } - out: + if (timeri->private_free) timeri->private_free(timeri); kfree(timeri->owner); kfree(timeri); - if (timer) + + if (timer) { + if (list_empty(&timer->open_list_head) && timer->hw.close) + timer->hw.close(timer); + /* release a card refcount for safe disconnection */ + if (timer->card) + put_device(&timer->card->card_dev); module_put(timer->module); + } + + mutex_unlock(®ister_mutex); return 0; } @@ -395,7 +387,6 @@ unsigned long snd_timer_resolution(struct snd_timer_instance *timeri) static void snd_timer_notify1(struct snd_timer_instance *ti, int event) { struct snd_timer *timer; - unsigned long flags; unsigned long resolution = 0; struct snd_timer_instance *ts; struct timespec tstamp; @@ -419,34 +410,66 @@ static void snd_timer_notify1(struct snd_timer_instance *ti, int event) return; if (timer->hw.flags & SNDRV_TIMER_HW_SLAVE) return; - spin_lock_irqsave(&timer->lock, flags); list_for_each_entry(ts, &ti->slave_active_head, active_list) if (ts->ccallback) ts->ccallback(ts, event + 100, &tstamp, resolution); - spin_unlock_irqrestore(&timer->lock, flags); } -static int snd_timer_start1(struct snd_timer *timer, struct snd_timer_instance *timeri, - unsigned long sticks) +/* start/continue a master timer */ +static int snd_timer_start1(struct snd_timer_instance *timeri, + bool start, unsigned long ticks) { + struct snd_timer *timer; + int result; + unsigned long flags; + + timer = timeri->timer; + if (!timer) + return -EINVAL; + + spin_lock_irqsave(&timer->lock, flags); + if (timer->card && timer->card->shutdown) { + result = -ENODEV; + goto unlock; + } + if (timeri->flags & (SNDRV_TIMER_IFLG_RUNNING | + SNDRV_TIMER_IFLG_START)) { + result = -EBUSY; + goto unlock; + } + + if (start) + timeri->ticks = timeri->cticks = ticks; + else if (!timeri->cticks) + timeri->cticks = 1; + timeri->pticks = 0; + list_move_tail(&timeri->active_list, &timer->active_list_head); if (timer->running) { if (timer->hw.flags & SNDRV_TIMER_HW_SLAVE) goto __start_now; timer->flags |= SNDRV_TIMER_FLG_RESCHED; timeri->flags |= SNDRV_TIMER_IFLG_START; - return 1; /* delayed start */ + result = 1; /* delayed start */ } else { - timer->sticks = sticks; + if (start) + timer->sticks = ticks; timer->hw.start(timer); __start_now: timer->running++; timeri->flags |= SNDRV_TIMER_IFLG_RUNNING; - return 0; + result = 0; } + snd_timer_notify1(timeri, start ? SNDRV_TIMER_EVENT_START : + SNDRV_TIMER_EVENT_CONTINUE); + unlock: + spin_unlock_irqrestore(&timer->lock, flags); + return result; } -static int snd_timer_start_slave(struct snd_timer_instance *timeri) +/* start/continue a slave timer */ +static int snd_timer_start_slave(struct snd_timer_instance *timeri, + bool start) { unsigned long flags; @@ -460,88 +483,37 @@ static int snd_timer_start_slave(struct snd_timer_instance *timeri) spin_lock(&timeri->timer->lock); list_add_tail(&timeri->active_list, &timeri->master->slave_active_head); + snd_timer_notify1(timeri, start ? SNDRV_TIMER_EVENT_START : + SNDRV_TIMER_EVENT_CONTINUE); spin_unlock(&timeri->timer->lock); } spin_unlock_irqrestore(&slave_active_lock, flags); return 1; /* delayed start */ } -/* - * start the timer instance - */ -int snd_timer_start(struct snd_timer_instance *timeri, unsigned int ticks) +/* stop/pause a master timer */ +static int snd_timer_stop1(struct snd_timer_instance *timeri, bool stop) { struct snd_timer *timer; - int result = -EINVAL; + int result = 0; unsigned long flags; - if (timeri == NULL || ticks < 1) - return -EINVAL; - if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) { - result = snd_timer_start_slave(timeri); - if (result >= 0) - snd_timer_notify1(timeri, SNDRV_TIMER_EVENT_START); - return result; - } - timer = timeri->timer; - if (timer == NULL) - return -EINVAL; - if (timer->card && timer->card->shutdown) - return -ENODEV; - spin_lock_irqsave(&timer->lock, flags); - if (timeri->flags & (SNDRV_TIMER_IFLG_RUNNING | - SNDRV_TIMER_IFLG_START)) { - result = -EBUSY; - goto unlock; - } - timeri->ticks = timeri->cticks = ticks; - timeri->pticks = 0; - result = snd_timer_start1(timer, timeri, ticks); - unlock: - spin_unlock_irqrestore(&timer->lock, flags); - if (result >= 0) - snd_timer_notify1(timeri, SNDRV_TIMER_EVENT_START); - return result; -} - -static int _snd_timer_stop(struct snd_timer_instance *timeri, int event) -{ - struct snd_timer *timer; - unsigned long flags; - - if (snd_BUG_ON(!timeri)) - return -ENXIO; - - if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) { - spin_lock_irqsave(&slave_active_lock, flags); - if (!(timeri->flags & SNDRV_TIMER_IFLG_RUNNING)) { - spin_unlock_irqrestore(&slave_active_lock, flags); - return -EBUSY; - } - if (timeri->timer) - spin_lock(&timeri->timer->lock); - timeri->flags &= ~SNDRV_TIMER_IFLG_RUNNING; - list_del_init(&timeri->ack_list); - list_del_init(&timeri->active_list); - if (timeri->timer) - spin_unlock(&timeri->timer->lock); - spin_unlock_irqrestore(&slave_active_lock, flags); - goto __end; - } timer = timeri->timer; if (!timer) return -EINVAL; spin_lock_irqsave(&timer->lock, flags); if (!(timeri->flags & (SNDRV_TIMER_IFLG_RUNNING | SNDRV_TIMER_IFLG_START))) { - spin_unlock_irqrestore(&timer->lock, flags); - return -EBUSY; + result = -EBUSY; + goto unlock; } list_del_init(&timeri->ack_list); list_del_init(&timeri->active_list); - if (timer->card && timer->card->shutdown) { - spin_unlock_irqrestore(&timer->lock, flags); - return 0; + if (timer->card && timer->card->shutdown) + goto unlock; + if (stop) { + timeri->cticks = timeri->ticks; + timeri->pticks = 0; } if ((timeri->flags & SNDRV_TIMER_IFLG_RUNNING) && !(--timer->running)) { @@ -556,35 +528,60 @@ static int _snd_timer_stop(struct snd_timer_instance *timeri, int event) } } timeri->flags &= ~(SNDRV_TIMER_IFLG_RUNNING | SNDRV_TIMER_IFLG_START); + snd_timer_notify1(timeri, stop ? SNDRV_TIMER_EVENT_STOP : + SNDRV_TIMER_EVENT_CONTINUE); + unlock: spin_unlock_irqrestore(&timer->lock, flags); - __end: - if (event != SNDRV_TIMER_EVENT_RESOLUTION) - snd_timer_notify1(timeri, event); + return result; +} + +/* stop/pause a slave timer */ +static int snd_timer_stop_slave(struct snd_timer_instance *timeri, bool stop) +{ + unsigned long flags; + + spin_lock_irqsave(&slave_active_lock, flags); + if (!(timeri->flags & SNDRV_TIMER_IFLG_RUNNING)) { + spin_unlock_irqrestore(&slave_active_lock, flags); + return -EBUSY; + } + timeri->flags &= ~SNDRV_TIMER_IFLG_RUNNING; + if (timeri->timer) { + spin_lock(&timeri->timer->lock); + list_del_init(&timeri->ack_list); + list_del_init(&timeri->active_list); + snd_timer_notify1(timeri, stop ? SNDRV_TIMER_EVENT_STOP : + SNDRV_TIMER_EVENT_CONTINUE); + spin_unlock(&timeri->timer->lock); + } + spin_unlock_irqrestore(&slave_active_lock, flags); return 0; } /* + * start the timer instance + */ +int snd_timer_start(struct snd_timer_instance *timeri, unsigned int ticks) +{ + if (timeri == NULL || ticks < 1) + return -EINVAL; + if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) + return snd_timer_start_slave(timeri, true); + else + return snd_timer_start1(timeri, true, ticks); +} + +/* * stop the timer instance. * * do not call this from the timer callback! */ int snd_timer_stop(struct snd_timer_instance *timeri) { - struct snd_timer *timer; - unsigned long flags; - int err; - - err = _snd_timer_stop(timeri, SNDRV_TIMER_EVENT_STOP); - if (err < 0) - return err; - timer = timeri->timer; - if (!timer) - return -EINVAL; - spin_lock_irqsave(&timer->lock, flags); - timeri->cticks = timeri->ticks; - timeri->pticks = 0; - spin_unlock_irqrestore(&timer->lock, flags); - return 0; + if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) + return snd_timer_stop_slave(timeri, true); + else + return snd_timer_stop1(timeri, true); } /* @@ -592,32 +589,10 @@ int snd_timer_stop(struct snd_timer_instance *timeri) */ int snd_timer_continue(struct snd_timer_instance *timeri) { - struct snd_timer *timer; - int result = -EINVAL; - unsigned long flags; - - if (timeri == NULL) - return result; if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) - return snd_timer_start_slave(timeri); - timer = timeri->timer; - if (! timer) - return -EINVAL; - if (timer->card && timer->card->shutdown) - return -ENODEV; - spin_lock_irqsave(&timer->lock, flags); - if (timeri->flags & SNDRV_TIMER_IFLG_RUNNING) { - result = -EBUSY; - goto unlock; - } - if (!timeri->cticks) - timeri->cticks = 1; - timeri->pticks = 0; - result = snd_timer_start1(timer, timeri, timer->sticks); - unlock: - spin_unlock_irqrestore(&timer->lock, flags); - snd_timer_notify1(timeri, SNDRV_TIMER_EVENT_CONTINUE); - return result; + return snd_timer_start_slave(timeri, false); + else + return snd_timer_start1(timeri, false, 0); } /* @@ -625,7 +600,10 @@ int snd_timer_continue(struct snd_timer_instance *timeri) */ int snd_timer_pause(struct snd_timer_instance * timeri) { - return _snd_timer_stop(timeri, SNDRV_TIMER_EVENT_PAUSE); + if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) + return snd_timer_stop_slave(timeri, false); + else + return snd_timer_stop1(timeri, false); } /* diff --git a/sound/drivers/mts64.c b/sound/drivers/mts64.c index 2a008a9ccf85..fd4d18df84d3 100644 --- a/sound/drivers/mts64.c +++ b/sound/drivers/mts64.c @@ -65,8 +65,6 @@ struct mts64 { struct snd_card *card; struct snd_rawmidi *rmidi; struct pardevice *pardev; - int pardev_claimed; - int open_count; int current_midi_output_port; int current_midi_input_port; @@ -850,30 +848,6 @@ __out: spin_unlock(&mts->lock); } -static int snd_mts64_probe_port(struct parport *p) -{ - struct pardevice *pardev; - int res; - - pardev = parport_register_device(p, DRIVER_NAME, - NULL, NULL, NULL, - 0, NULL); - if (!pardev) - return -EIO; - - if (parport_claim(pardev)) { - parport_unregister_device(pardev); - return -EIO; - } - - res = mts64_probe(p); - - parport_release(pardev); - parport_unregister_device(pardev); - - return res; -} - static void snd_mts64_attach(struct parport *p) { struct platform_device *device; @@ -907,10 +881,20 @@ static void snd_mts64_detach(struct parport *p) /* nothing to do here */ } +static int snd_mts64_dev_probe(struct pardevice *pardev) +{ + if (strcmp(pardev->name, DRIVER_NAME)) + return -ENODEV; + + return 0; +} + static struct parport_driver mts64_parport_driver = { - .name = "mts64", - .attach = snd_mts64_attach, - .detach = snd_mts64_detach + .name = "mts64", + .probe = snd_mts64_dev_probe, + .match_port = snd_mts64_attach, + .detach = snd_mts64_detach, + .devmodel = true, }; /********************************************************************* @@ -922,8 +906,7 @@ static void snd_mts64_card_private_free(struct snd_card *card) struct pardevice *pardev = mts->pardev; if (pardev) { - if (mts->pardev_claimed) - parport_release(pardev); + parport_release(pardev); parport_unregister_device(pardev); } @@ -938,6 +921,12 @@ static int snd_mts64_probe(struct platform_device *pdev) struct snd_card *card = NULL; struct mts64 *mts = NULL; int err; + struct pardev_cb mts64_cb = { + .preempt = NULL, + .wakeup = NULL, + .irq_func = snd_mts64_interrupt, /* ISR */ + .flags = PARPORT_DEV_EXCL, /* flags */ + }; p = platform_get_drvdata(pdev); platform_set_drvdata(pdev, NULL); @@ -946,8 +935,6 @@ static int snd_mts64_probe(struct platform_device *pdev) return -ENODEV; if (!enable[dev]) return -ENOENT; - if ((err = snd_mts64_probe_port(p)) < 0) - return err; err = snd_card_new(&pdev->dev, index[dev], id[dev], THIS_MODULE, 0, &card); @@ -960,40 +947,42 @@ static int snd_mts64_probe(struct platform_device *pdev) sprintf(card->longname, "%s at 0x%lx, irq %i", card->shortname, p->base, p->irq); - pardev = parport_register_device(p, /* port */ - DRIVER_NAME, /* name */ - NULL, /* preempt */ - NULL, /* wakeup */ - snd_mts64_interrupt, /* ISR */ - PARPORT_DEV_EXCL, /* flags */ - (void *)card); /* private */ - if (pardev == NULL) { + mts64_cb.private = card; /* private */ + pardev = parport_register_dev_model(p, /* port */ + DRIVER_NAME, /* name */ + &mts64_cb, /* callbacks */ + pdev->id); /* device number */ + if (!pardev) { snd_printd("Cannot register pardevice\n"); err = -EIO; goto __err; } + /* claim parport */ + if (parport_claim(pardev)) { + snd_printd("Cannot claim parport 0x%lx\n", pardev->port->base); + err = -EIO; + goto free_pardev; + } + if ((err = snd_mts64_create(card, pardev, &mts)) < 0) { snd_printd("Cannot create main component\n"); - parport_unregister_device(pardev); - goto __err; + goto release_pardev; } card->private_data = mts; card->private_free = snd_mts64_card_private_free; + + err = mts64_probe(p); + if (err) { + err = -EIO; + goto __err; + } if ((err = snd_mts64_rawmidi_create(card)) < 0) { snd_printd("Creating Rawmidi component failed\n"); goto __err; } - /* claim parport */ - if (parport_claim(pardev)) { - snd_printd("Cannot claim parport 0x%lx\n", pardev->port->base); - err = -EIO; - goto __err; - } - mts->pardev_claimed = 1; - /* init device */ if ((err = mts64_device_init(p)) < 0) goto __err; @@ -1009,6 +998,10 @@ static int snd_mts64_probe(struct platform_device *pdev) snd_printk(KERN_INFO "ESI Miditerminal 4140 on 0x%lx\n", p->base); return 0; +release_pardev: + parport_release(pardev); +free_pardev: + parport_unregister_device(pardev); __err: snd_card_free(card); return err; @@ -1024,7 +1017,6 @@ static int snd_mts64_remove(struct platform_device *pdev) return 0; } - static struct platform_driver snd_mts64_driver = { .probe = snd_mts64_probe, .remove = snd_mts64_remove, diff --git a/sound/drivers/portman2x4.c b/sound/drivers/portman2x4.c index 464385a480e4..189e3e7028af 100644 --- a/sound/drivers/portman2x4.c +++ b/sound/drivers/portman2x4.c @@ -83,8 +83,6 @@ struct portman { struct snd_card *card; struct snd_rawmidi *rmidi; struct pardevice *pardev; - int pardev_claimed; - int open_count; int mode[PORTMAN_NUM_INPUT_PORTS]; struct snd_rawmidi_substream *midi_input[PORTMAN_NUM_INPUT_PORTS]; @@ -648,30 +646,6 @@ static void snd_portman_interrupt(void *userdata) spin_unlock(&pm->reg_lock); } -static int snd_portman_probe_port(struct parport *p) -{ - struct pardevice *pardev; - int res; - - pardev = parport_register_device(p, DRIVER_NAME, - NULL, NULL, NULL, - 0, NULL); - if (!pardev) - return -EIO; - - if (parport_claim(pardev)) { - parport_unregister_device(pardev); - return -EIO; - } - - res = portman_probe(p); - - parport_release(pardev); - parport_unregister_device(pardev); - - return res ? -EIO : 0; -} - static void snd_portman_attach(struct parport *p) { struct platform_device *device; @@ -705,10 +679,20 @@ static void snd_portman_detach(struct parport *p) /* nothing to do here */ } +static int snd_portman_dev_probe(struct pardevice *pardev) +{ + if (strcmp(pardev->name, DRIVER_NAME)) + return -ENODEV; + + return 0; +} + static struct parport_driver portman_parport_driver = { - .name = "portman2x4", - .attach = snd_portman_attach, - .detach = snd_portman_detach + .name = "portman2x4", + .probe = snd_portman_dev_probe, + .match_port = snd_portman_attach, + .detach = snd_portman_detach, + .devmodel = true, }; /********************************************************************* @@ -720,8 +704,7 @@ static void snd_portman_card_private_free(struct snd_card *card) struct pardevice *pardev = pm->pardev; if (pardev) { - if (pm->pardev_claimed) - parport_release(pardev); + parport_release(pardev); parport_unregister_device(pardev); } @@ -736,6 +719,12 @@ static int snd_portman_probe(struct platform_device *pdev) struct snd_card *card = NULL; struct portman *pm = NULL; int err; + struct pardev_cb portman_cb = { + .preempt = NULL, + .wakeup = NULL, + .irq_func = snd_portman_interrupt, /* ISR */ + .flags = PARPORT_DEV_EXCL, /* flags */ + }; p = platform_get_drvdata(pdev); platform_set_drvdata(pdev, NULL); @@ -745,9 +734,6 @@ static int snd_portman_probe(struct platform_device *pdev) if (!enable[dev]) return -ENOENT; - if ((err = snd_portman_probe_port(p)) < 0) - return err; - err = snd_card_new(&pdev->dev, index[dev], id[dev], THIS_MODULE, 0, &card); if (err < 0) { @@ -759,40 +745,42 @@ static int snd_portman_probe(struct platform_device *pdev) sprintf(card->longname, "%s at 0x%lx, irq %i", card->shortname, p->base, p->irq); - pardev = parport_register_device(p, /* port */ - DRIVER_NAME, /* name */ - NULL, /* preempt */ - NULL, /* wakeup */ - snd_portman_interrupt, /* ISR */ - PARPORT_DEV_EXCL, /* flags */ - (void *)card); /* private */ + portman_cb.private = card; /* private */ + pardev = parport_register_dev_model(p, /* port */ + DRIVER_NAME, /* name */ + &portman_cb, /* callbacks */ + pdev->id); /* device number */ if (pardev == NULL) { snd_printd("Cannot register pardevice\n"); err = -EIO; goto __err; } + /* claim parport */ + if (parport_claim(pardev)) { + snd_printd("Cannot claim parport 0x%lx\n", pardev->port->base); + err = -EIO; + goto free_pardev; + } + if ((err = portman_create(card, pardev, &pm)) < 0) { snd_printd("Cannot create main component\n"); - parport_unregister_device(pardev); - goto __err; + goto release_pardev; } card->private_data = pm; card->private_free = snd_portman_card_private_free; + + err = portman_probe(p); + if (err) { + err = -EIO; + goto __err; + } if ((err = snd_portman_rawmidi_create(card)) < 0) { snd_printd("Creating Rawmidi component failed\n"); goto __err; } - /* claim parport */ - if (parport_claim(pardev)) { - snd_printd("Cannot claim parport 0x%lx\n", pardev->port->base); - err = -EIO; - goto __err; - } - pm->pardev_claimed = 1; - /* init device */ if ((err = portman_device_init(pm)) < 0) goto __err; @@ -808,6 +796,10 @@ static int snd_portman_probe(struct platform_device *pdev) snd_printk(KERN_INFO "Portman 2x4 on 0x%lx\n", p->base); return 0; +release_pardev: + parport_release(pardev); +free_pardev: + parport_unregister_device(pardev); __err: snd_card_free(card); return err; diff --git a/sound/firewire/bebob/bebob.c b/sound/firewire/bebob/bebob.c index 091290d1f3ea..3e4e0756e3fe 100644 --- a/sound/firewire/bebob/bebob.c +++ b/sound/firewire/bebob/bebob.c @@ -300,6 +300,22 @@ error: return err; } +/* + * This driver doesn't update streams in bus reset handler. + * + * DM1000/ DM1100/DM1500 chipsets with BeBoB firmware transfer packets with + * discontinued counter at bus reset. This discontinuity is immediately + * detected in packet streaming layer, then it sets XRUN to PCM substream. + * + * ALSA PCM applications can know the XRUN by getting -EPIPE from PCM operation. + * Then, they can recover the PCM substream by executing ioctl(2) with + * SNDRV_PCM_IOCTL_PREPARE. 'struct snd_pcm_ops.prepare' is called and drivers + * restart packet streaming. + * + * The above processing may be executed before this bus-reset handler is + * executed. When this handler updates streams with current isochronous + * channels, the streams already have the current ones. + */ static void bebob_update(struct fw_unit *unit) { @@ -309,7 +325,6 @@ bebob_update(struct fw_unit *unit) return; fcp_bus_reset(bebob->unit); - snd_bebob_stream_update_duplex(bebob); if (bebob->deferred_registration) { if (snd_card_register(bebob->card) < 0) { @@ -327,10 +342,6 @@ static void bebob_remove(struct fw_unit *unit) if (bebob == NULL) return; - /* Awake bus-reset waiters. */ - if (!completion_done(&bebob->bus_reset)) - complete_all(&bebob->bus_reset); - /* No need to wait for releasing card object in this context. */ snd_card_free_when_closed(bebob->card); } diff --git a/sound/firewire/bebob/bebob.h b/sound/firewire/bebob/bebob.h index 4d8fcc78e747..b50bb33d9d46 100644 --- a/sound/firewire/bebob/bebob.h +++ b/sound/firewire/bebob/bebob.h @@ -88,8 +88,6 @@ struct snd_bebob { unsigned int midi_input_ports; unsigned int midi_output_ports; - /* for bus reset quirk */ - struct completion bus_reset; bool connected; struct amdtp_stream *master; @@ -97,7 +95,7 @@ struct snd_bebob { struct amdtp_stream rx_stream; struct cmp_connection out_conn; struct cmp_connection in_conn; - atomic_t substreams_counter; + unsigned int substreams_counter; struct snd_bebob_stream_formation tx_stream_formations[SND_BEBOB_STRM_FMT_ENTRIES]; @@ -219,7 +217,6 @@ int snd_bebob_stream_discover(struct snd_bebob *bebob); int snd_bebob_stream_init_duplex(struct snd_bebob *bebob); int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate); void snd_bebob_stream_stop_duplex(struct snd_bebob *bebob); -void snd_bebob_stream_update_duplex(struct snd_bebob *bebob); void snd_bebob_stream_destroy_duplex(struct snd_bebob *bebob); void snd_bebob_stream_lock_changed(struct snd_bebob *bebob); diff --git a/sound/firewire/bebob/bebob_midi.c b/sound/firewire/bebob/bebob_midi.c index 90d95be499b0..868eb0decbec 100644 --- a/sound/firewire/bebob/bebob_midi.c +++ b/sound/firewire/bebob/bebob_midi.c @@ -17,8 +17,10 @@ static int midi_capture_open(struct snd_rawmidi_substream *substream) if (err < 0) goto end; - atomic_inc(&bebob->substreams_counter); + mutex_lock(&bebob->mutex); + bebob->substreams_counter++; err = snd_bebob_stream_start_duplex(bebob, 0); + mutex_unlock(&bebob->mutex); if (err < 0) snd_bebob_stream_lock_release(bebob); end: @@ -34,8 +36,10 @@ static int midi_playback_open(struct snd_rawmidi_substream *substream) if (err < 0) goto end; - atomic_inc(&bebob->substreams_counter); + mutex_lock(&bebob->mutex); + bebob->substreams_counter++; err = snd_bebob_stream_start_duplex(bebob, 0); + mutex_unlock(&bebob->mutex); if (err < 0) snd_bebob_stream_lock_release(bebob); end: @@ -46,8 +50,10 @@ static int midi_capture_close(struct snd_rawmidi_substream *substream) { struct snd_bebob *bebob = substream->rmidi->private_data; - atomic_dec(&bebob->substreams_counter); + mutex_lock(&bebob->mutex); + bebob->substreams_counter--; snd_bebob_stream_stop_duplex(bebob); + mutex_unlock(&bebob->mutex); snd_bebob_stream_lock_release(bebob); return 0; @@ -57,8 +63,10 @@ static int midi_playback_close(struct snd_rawmidi_substream *substream) { struct snd_bebob *bebob = substream->rmidi->private_data; - atomic_dec(&bebob->substreams_counter); + mutex_lock(&bebob->mutex); + bebob->substreams_counter--; snd_bebob_stream_stop_duplex(bebob); + mutex_unlock(&bebob->mutex); snd_bebob_stream_lock_release(bebob); return 0; diff --git a/sound/firewire/bebob/bebob_pcm.c b/sound/firewire/bebob/bebob_pcm.c index ef224d6f5c24..5d7b9343fa85 100644 --- a/sound/firewire/bebob/bebob_pcm.c +++ b/sound/firewire/bebob/bebob_pcm.c @@ -218,8 +218,11 @@ pcm_capture_hw_params(struct snd_pcm_substream *substream, if (err < 0) return err; - if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) - atomic_inc(&bebob->substreams_counter); + if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { + mutex_lock(&bebob->mutex); + bebob->substreams_counter++; + mutex_unlock(&bebob->mutex); + } amdtp_am824_set_pcm_format(&bebob->tx_stream, params_format(hw_params)); @@ -237,8 +240,11 @@ pcm_playback_hw_params(struct snd_pcm_substream *substream, if (err < 0) return err; - if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) - atomic_inc(&bebob->substreams_counter); + if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { + mutex_lock(&bebob->mutex); + bebob->substreams_counter++; + mutex_unlock(&bebob->mutex); + } amdtp_am824_set_pcm_format(&bebob->rx_stream, params_format(hw_params)); @@ -250,8 +256,11 @@ pcm_capture_hw_free(struct snd_pcm_substream *substream) { struct snd_bebob *bebob = substream->private_data; - if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) - atomic_dec(&bebob->substreams_counter); + if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) { + mutex_lock(&bebob->mutex); + bebob->substreams_counter--; + mutex_unlock(&bebob->mutex); + } snd_bebob_stream_stop_duplex(bebob); @@ -262,8 +271,11 @@ pcm_playback_hw_free(struct snd_pcm_substream *substream) { struct snd_bebob *bebob = substream->private_data; - if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) - atomic_dec(&bebob->substreams_counter); + if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) { + mutex_lock(&bebob->mutex); + bebob->substreams_counter--; + mutex_unlock(&bebob->mutex); + } snd_bebob_stream_stop_duplex(bebob); diff --git a/sound/firewire/bebob/bebob_stream.c b/sound/firewire/bebob/bebob_stream.c index 5022c9b97ddf..77cbb02bff34 100644 --- a/sound/firewire/bebob/bebob_stream.c +++ b/sound/firewire/bebob/bebob_stream.c @@ -549,8 +549,7 @@ int snd_bebob_stream_init_duplex(struct snd_bebob *bebob) destroy_both_connections(bebob); goto end; } - /* See comments in next function */ - init_completion(&bebob->bus_reset); + bebob->tx_stream.flags |= CIP_SKIP_INIT_DBC_CHECK; /* @@ -588,29 +587,10 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate) struct amdtp_stream *master, *slave; enum cip_flags sync_mode; unsigned int curr_rate; - bool updated = false; int err = 0; - /* - * Normal BeBoB firmware has a quirk at bus reset to transmits packets - * with discontinuous value in dbc field. - * - * This 'struct completion' is used to call .update() at first to update - * connections/streams. Next following codes handle streaming error. - */ - if (amdtp_streaming_error(&bebob->tx_stream)) { - if (completion_done(&bebob->bus_reset)) - reinit_completion(&bebob->bus_reset); - - updated = (wait_for_completion_interruptible_timeout( - &bebob->bus_reset, - msecs_to_jiffies(FW_ISO_RESOURCE_DELAY)) > 0); - } - - mutex_lock(&bebob->mutex); - /* Need no substreams */ - if (atomic_read(&bebob->substreams_counter) == 0) + if (bebob->substreams_counter == 0) goto end; err = get_sync_mode(bebob, &sync_mode); @@ -642,8 +622,7 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate) amdtp_stream_stop(master); if (amdtp_streaming_error(slave)) amdtp_stream_stop(slave); - if (!updated && - !amdtp_stream_running(master) && !amdtp_stream_running(slave)) + if (!amdtp_stream_running(master) && !amdtp_stream_running(slave)) break_both_connections(bebob); /* stop streams if rate is different */ @@ -741,7 +720,6 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate) } } end: - mutex_unlock(&bebob->mutex); return err; } @@ -757,9 +735,7 @@ void snd_bebob_stream_stop_duplex(struct snd_bebob *bebob) master = &bebob->tx_stream; } - mutex_lock(&bebob->mutex); - - if (atomic_read(&bebob->substreams_counter) == 0) { + if (bebob->substreams_counter == 0) { amdtp_stream_pcm_abort(master); amdtp_stream_stop(master); @@ -768,32 +744,6 @@ void snd_bebob_stream_stop_duplex(struct snd_bebob *bebob) break_both_connections(bebob); } - - mutex_unlock(&bebob->mutex); -} - -void snd_bebob_stream_update_duplex(struct snd_bebob *bebob) -{ - /* vs. XRUN recovery due to discontinuity at bus reset */ - mutex_lock(&bebob->mutex); - - if ((cmp_connection_update(&bebob->in_conn) < 0) || - (cmp_connection_update(&bebob->out_conn) < 0)) { - amdtp_stream_pcm_abort(&bebob->rx_stream); - amdtp_stream_pcm_abort(&bebob->tx_stream); - amdtp_stream_stop(&bebob->rx_stream); - amdtp_stream_stop(&bebob->tx_stream); - break_both_connections(bebob); - } else { - amdtp_stream_update(&bebob->rx_stream); - amdtp_stream_update(&bebob->tx_stream); - } - - /* wake up stream_start_duplex() */ - if (!completion_done(&bebob->bus_reset)) - complete_all(&bebob->bus_reset); - - mutex_unlock(&bebob->mutex); } /* diff --git a/sound/firewire/dice/dice-midi.c b/sound/firewire/dice/dice-midi.c index 151b09f240f2..2461311e695a 100644 --- a/sound/firewire/dice/dice-midi.c +++ b/sound/firewire/dice/dice-midi.c @@ -103,16 +103,27 @@ static void set_midi_substream_names(struct snd_dice *dice, int snd_dice_create_midi(struct snd_dice *dice) { + __be32 reg; struct snd_rawmidi *rmidi; struct snd_rawmidi_str *str; - unsigned int i, midi_in_ports, midi_out_ports; + unsigned int midi_in_ports, midi_out_ports; int err; - midi_in_ports = midi_out_ports = 0; - for (i = 0; i < 3; i++) { - midi_in_ports = max(dice->tx_midi_ports[i], midi_in_ports); - midi_out_ports = max(dice->rx_midi_ports[i], midi_out_ports); - } + /* + * Use the number of MIDI conformant data channel at current sampling + * transfer frequency. + */ + err = snd_dice_transaction_read_tx(dice, TX_NUMBER_MIDI, + ®, sizeof(reg)); + if (err < 0) + return err; + midi_in_ports = be32_to_cpu(reg); + + err = snd_dice_transaction_read_rx(dice, RX_NUMBER_MIDI, + ®, sizeof(reg)); + if (err < 0) + return err; + midi_out_ports = be32_to_cpu(reg); if (midi_in_ports + midi_out_ports == 0) return 0; diff --git a/sound/firewire/dice/dice-pcm.c b/sound/firewire/dice/dice-pcm.c index 9b3431999fc8..a5c9b58655ef 100644 --- a/sound/firewire/dice/dice-pcm.c +++ b/sound/firewire/dice/dice-pcm.c @@ -9,99 +9,40 @@ #include "dice.h" -static int dice_rate_constraint(struct snd_pcm_hw_params *params, - struct snd_pcm_hw_rule *rule) -{ - struct snd_pcm_substream *substream = rule->private; - struct snd_dice *dice = substream->private_data; - - const struct snd_interval *c = - hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS); - struct snd_interval *r = - hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); - struct snd_interval rates = { - .min = UINT_MAX, .max = 0, .integer = 1 - }; - unsigned int i, rate, mode, *pcm_channels; - - if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) - pcm_channels = dice->tx_channels; - else - pcm_channels = dice->rx_channels; - - for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) { - rate = snd_dice_rates[i]; - if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0) - continue; - - if (!snd_interval_test(c, pcm_channels[mode])) - continue; - - rates.min = min(rates.min, rate); - rates.max = max(rates.max, rate); - } - - return snd_interval_refine(r, &rates); -} - -static int dice_channels_constraint(struct snd_pcm_hw_params *params, - struct snd_pcm_hw_rule *rule) -{ - struct snd_pcm_substream *substream = rule->private; - struct snd_dice *dice = substream->private_data; - - const struct snd_interval *r = - hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE); - struct snd_interval *c = - hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); - struct snd_interval channels = { - .min = UINT_MAX, .max = 0, .integer = 1 - }; - unsigned int i, rate, mode, *pcm_channels; - - if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) - pcm_channels = dice->tx_channels; - else - pcm_channels = dice->rx_channels; - - for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) { - rate = snd_dice_rates[i]; - if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0) - continue; - - if (!snd_interval_test(r, rate)) - continue; - - channels.min = min(channels.min, pcm_channels[mode]); - channels.max = max(channels.max, pcm_channels[mode]); - } - - return snd_interval_refine(c, &channels); -} - -static void limit_channels_and_rates(struct snd_dice *dice, - struct snd_pcm_runtime *runtime, - unsigned int *pcm_channels) +static int limit_channels_and_rates(struct snd_dice *dice, + struct snd_pcm_runtime *runtime, + struct amdtp_stream *stream) { struct snd_pcm_hardware *hw = &runtime->hw; - unsigned int i, rate, mode; + unsigned int rate; + __be32 reg[2]; + int err; - hw->channels_min = UINT_MAX; - hw->channels_max = 0; + /* + * Retrieve current Multi Bit Linear Audio data channel and limit to + * it. + */ + if (stream == &dice->tx_stream) { + err = snd_dice_transaction_read_tx(dice, TX_NUMBER_AUDIO, + reg, sizeof(reg)); + } else { + err = snd_dice_transaction_read_rx(dice, RX_NUMBER_AUDIO, + reg, sizeof(reg)); + } + if (err < 0) + return err; - for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) { - rate = snd_dice_rates[i]; - if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0) - continue; - hw->rates |= snd_pcm_rate_to_rate_bit(rate); + hw->channels_min = hw->channels_max = be32_to_cpu(reg[0]); - if (pcm_channels[mode] == 0) - continue; - hw->channels_min = min(hw->channels_min, pcm_channels[mode]); - hw->channels_max = max(hw->channels_max, pcm_channels[mode]); - } + /* Retrieve current sampling transfer frequency and limit to it. */ + err = snd_dice_transaction_get_rate(dice, &rate); + if (err < 0) + return err; + hw->rates = snd_pcm_rate_to_rate_bit(rate); snd_pcm_limit_hw_rates(runtime); + + return 0; } static void limit_period_and_buffer(struct snd_pcm_hardware *hw) @@ -122,7 +63,6 @@ static int init_hw_info(struct snd_dice *dice, struct snd_pcm_runtime *runtime = substream->runtime; struct snd_pcm_hardware *hw = &runtime->hw; struct amdtp_stream *stream; - unsigned int *pcm_channels; int err; hw->info = SNDRV_PCM_INFO_MMAP | @@ -135,37 +75,22 @@ static int init_hw_info(struct snd_dice *dice, if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { hw->formats = AM824_IN_PCM_FORMAT_BITS; stream = &dice->tx_stream; - pcm_channels = dice->tx_channels; } else { hw->formats = AM824_OUT_PCM_FORMAT_BITS; stream = &dice->rx_stream; - pcm_channels = dice->rx_channels; } - limit_channels_and_rates(dice, runtime, pcm_channels); - limit_period_and_buffer(hw); - - err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, - dice_rate_constraint, substream, - SNDRV_PCM_HW_PARAM_CHANNELS, -1); - if (err < 0) - goto end; - err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, - dice_channels_constraint, substream, - SNDRV_PCM_HW_PARAM_RATE, -1); + err = limit_channels_and_rates(dice, runtime, stream); if (err < 0) - goto end; + return err; + limit_period_and_buffer(hw); - err = amdtp_am824_add_pcm_hw_constraints(stream, runtime); -end: - return err; + return amdtp_am824_add_pcm_hw_constraints(stream, runtime); } static int pcm_open(struct snd_pcm_substream *substream) { struct snd_dice *dice = substream->private_data; - unsigned int source, rate; - bool internal; int err; err = snd_dice_stream_lock_try(dice); @@ -176,39 +101,6 @@ static int pcm_open(struct snd_pcm_substream *substream) if (err < 0) goto err_locked; - err = snd_dice_transaction_get_clock_source(dice, &source); - if (err < 0) - goto err_locked; - switch (source) { - case CLOCK_SOURCE_AES1: - case CLOCK_SOURCE_AES2: - case CLOCK_SOURCE_AES3: - case CLOCK_SOURCE_AES4: - case CLOCK_SOURCE_AES_ANY: - case CLOCK_SOURCE_ADAT: - case CLOCK_SOURCE_TDIF: - case CLOCK_SOURCE_WC: - internal = false; - break; - default: - internal = true; - break; - } - - /* - * When source of clock is not internal or any PCM streams are running, - * available sampling rate is limited at current sampling rate. - */ - if (!internal || - amdtp_stream_pcm_running(&dice->tx_stream) || - amdtp_stream_pcm_running(&dice->rx_stream)) { - err = snd_dice_transaction_get_rate(dice, &rate); - if (err < 0) - goto err_locked; - substream->runtime->hw.rate_min = rate; - substream->runtime->hw.rate_max = rate; - } - snd_pcm_set_sync(substream); end: return err; @@ -402,17 +294,30 @@ int snd_dice_create_pcm(struct snd_dice *dice) .page = snd_pcm_lib_get_vmalloc_page, .mmap = snd_pcm_lib_mmap_vmalloc, }; + __be32 reg; struct snd_pcm *pcm; - unsigned int i, capture, playback; + unsigned int capture, playback; int err; - capture = playback = 0; - for (i = 0; i < 3; i++) { - if (dice->tx_channels[i] > 0) - capture = 1; - if (dice->rx_channels[i] > 0) - playback = 1; - } + /* + * Check whether PCM substreams are required. + * + * TODO: in the case that any PCM substreams are not avail at a certain + * sampling transfer frequency? + */ + err = snd_dice_transaction_read_tx(dice, TX_NUMBER_AUDIO, + ®, sizeof(reg)); + if (err < 0) + return err; + if (be32_to_cpu(reg) > 0) + capture = 1; + + err = snd_dice_transaction_read_rx(dice, RX_NUMBER_AUDIO, + ®, sizeof(reg)); + if (err < 0) + return err; + if (be32_to_cpu(reg) > 0) + playback = 1; err = snd_pcm_new(dice->card, "DICE", 0, playback, capture, &pcm); if (err < 0) diff --git a/sound/firewire/dice/dice-stream.c b/sound/firewire/dice/dice-stream.c index a6a39f7ef58d..df035b1dd44a 100644 --- a/sound/firewire/dice/dice-stream.c +++ b/sound/firewire/dice/dice-stream.c @@ -10,6 +10,7 @@ #include "dice.h" #define CALLBACK_TIMEOUT 200 +#define NOTIFICATION_TIMEOUT_MS (2 * MSEC_PER_SEC) const unsigned int snd_dice_rates[SND_DICE_RATES_COUNT] = { /* mode 0 */ @@ -24,21 +25,44 @@ const unsigned int snd_dice_rates[SND_DICE_RATES_COUNT] = { [6] = 192000, }; -int snd_dice_stream_get_rate_mode(struct snd_dice *dice, unsigned int rate, - unsigned int *mode) +/* + * This operation has an effect to synchronize GLOBAL_STATUS/GLOBAL_SAMPLE_RATE + * to GLOBAL_STATUS. Especially, just after powering on, these are different. + */ +static int ensure_phase_lock(struct snd_dice *dice) { - int i; + __be32 reg, nominal; + int err; + + err = snd_dice_transaction_read_global(dice, GLOBAL_CLOCK_SELECT, + ®, sizeof(reg)); + if (err < 0) + return err; - for (i = 0; i < ARRAY_SIZE(snd_dice_rates); i++) { - if (!(dice->clock_caps & BIT(i))) - continue; - if (snd_dice_rates[i] != rate) - continue; + if (completion_done(&dice->clock_accepted)) + reinit_completion(&dice->clock_accepted); - *mode = (i - 1) / 2; - return 0; + err = snd_dice_transaction_write_global(dice, GLOBAL_CLOCK_SELECT, + ®, sizeof(reg)); + if (err < 0) + return err; + + if (wait_for_completion_timeout(&dice->clock_accepted, + msecs_to_jiffies(NOTIFICATION_TIMEOUT_MS)) == 0) { + /* + * Old versions of Dice firmware transfer no notification when + * the same clock status as current one is set. In this case, + * just check current clock status. + */ + err = snd_dice_transaction_read_global(dice, GLOBAL_STATUS, + &nominal, sizeof(nominal)); + if (err < 0) + return err; + if (!(be32_to_cpu(nominal) & STATUS_SOURCE_LOCKED)) + return -ETIMEDOUT; } - return -EINVAL; + + return 0; } static void release_resources(struct snd_dice *dice, @@ -99,23 +123,27 @@ static int start_stream(struct snd_dice *dice, struct amdtp_stream *stream, unsigned int rate) { struct fw_iso_resources *resources; - unsigned int i, mode, pcm_chs, midi_ports; + __be32 reg[2]; + unsigned int i, pcm_chs, midi_ports; bool double_pcm_frames; int err; - err = snd_dice_stream_get_rate_mode(dice, rate, &mode); - if (err < 0) - goto end; if (stream == &dice->tx_stream) { resources = &dice->tx_resources; - pcm_chs = dice->tx_channels[mode]; - midi_ports = dice->tx_midi_ports[mode]; + err = snd_dice_transaction_read_tx(dice, TX_NUMBER_AUDIO, + reg, sizeof(reg)); } else { resources = &dice->rx_resources; - pcm_chs = dice->rx_channels[mode]; - midi_ports = dice->rx_midi_ports[mode]; + err = snd_dice_transaction_read_rx(dice, RX_NUMBER_AUDIO, + reg, sizeof(reg)); } + if (err < 0) + goto end; + + pcm_chs = be32_to_cpu(reg[0]); + midi_ports = be32_to_cpu(reg[1]); + /* * At 176.4/192.0 kHz, Dice has a quirk to transfer two PCM frames in * one data block of AMDTP packet. Thus sampling transfer frequency is @@ -126,7 +154,7 @@ static int start_stream(struct snd_dice *dice, struct amdtp_stream *stream, * For this quirk, blocking mode is required and PCM buffer size should * be aligned to SYT_INTERVAL. */ - double_pcm_frames = mode > 1; + double_pcm_frames = rate > 96000; if (double_pcm_frames) { rate /= 2; pcm_chs *= 2; @@ -163,53 +191,17 @@ end: return err; } -static int get_sync_mode(struct snd_dice *dice, enum cip_flags *sync_mode) -{ - u32 source; - int err; - - err = snd_dice_transaction_get_clock_source(dice, &source); - if (err < 0) - goto end; - - switch (source) { - /* So-called 'SYT Match' modes, sync_to_syt value of packets received */ - case CLOCK_SOURCE_ARX4: /* in 4th stream */ - case CLOCK_SOURCE_ARX3: /* in 3rd stream */ - case CLOCK_SOURCE_ARX2: /* in 2nd stream */ - err = -ENOSYS; - break; - case CLOCK_SOURCE_ARX1: /* in 1st stream, which this driver uses */ - *sync_mode = 0; - break; - default: - *sync_mode = CIP_SYNC_TO_DEVICE; - break; - } -end: - return err; -} - int snd_dice_stream_start_duplex(struct snd_dice *dice, unsigned int rate) { struct amdtp_stream *master, *slave; unsigned int curr_rate; - enum cip_flags sync_mode; int err = 0; if (dice->substreams_counter == 0) goto end; - err = get_sync_mode(dice, &sync_mode); - if (err < 0) - goto end; - if (sync_mode == CIP_SYNC_TO_DEVICE) { - master = &dice->tx_stream; - slave = &dice->rx_stream; - } else { - master = &dice->rx_stream; - slave = &dice->tx_stream; - } + master = &dice->rx_stream; + slave = &dice->tx_stream; /* Some packet queueing errors. */ if (amdtp_streaming_error(master) || amdtp_streaming_error(slave)) @@ -224,19 +216,19 @@ int snd_dice_stream_start_duplex(struct snd_dice *dice, unsigned int rate) } if (rate == 0) rate = curr_rate; - if (rate != curr_rate) - stop_stream(dice, master); + if (rate != curr_rate) { + err = -EINVAL; + goto end; + } if (!amdtp_stream_running(master)) { stop_stream(dice, slave); snd_dice_transaction_clear_enable(dice); - amdtp_stream_set_sync(sync_mode, master, slave); - - err = snd_dice_transaction_set_rate(dice, rate); + err = ensure_phase_lock(dice); if (err < 0) { dev_err(&dice->unit->device, - "fail to set sampling rate\n"); + "fail to ensure phase lock\n"); goto end; } diff --git a/sound/firewire/dice/dice-transaction.c b/sound/firewire/dice/dice-transaction.c index a4ff4e0bc0af..0f0350320ae8 100644 --- a/sound/firewire/dice/dice-transaction.c +++ b/sound/firewire/dice/dice-transaction.c @@ -9,8 +9,6 @@ #include "dice.h" -#define NOTIFICATION_TIMEOUT_MS (2 * MSEC_PER_SEC) - static u64 get_subaddr(struct snd_dice *dice, enum snd_dice_addr_type type, u64 offset) { @@ -62,54 +60,6 @@ static unsigned int get_clock_info(struct snd_dice *dice, __be32 *info) info, 4); } -static int set_clock_info(struct snd_dice *dice, - unsigned int rate, unsigned int source) -{ - unsigned int i; - __be32 info; - u32 mask; - u32 clock; - int err; - - err = get_clock_info(dice, &info); - if (err < 0) - return err; - - clock = be32_to_cpu(info); - if (source != UINT_MAX) { - mask = CLOCK_SOURCE_MASK; - clock &= ~mask; - clock |= source; - } - if (rate != UINT_MAX) { - for (i = 0; i < ARRAY_SIZE(snd_dice_rates); i++) { - if (snd_dice_rates[i] == rate) - break; - } - if (i == ARRAY_SIZE(snd_dice_rates)) - return -EINVAL; - - mask = CLOCK_RATE_MASK; - clock &= ~mask; - clock |= i << CLOCK_RATE_SHIFT; - } - info = cpu_to_be32(clock); - - if (completion_done(&dice->clock_accepted)) - reinit_completion(&dice->clock_accepted); - - err = snd_dice_transaction_write_global(dice, GLOBAL_CLOCK_SELECT, - &info, 4); - if (err < 0) - return err; - - if (wait_for_completion_timeout(&dice->clock_accepted, - msecs_to_jiffies(NOTIFICATION_TIMEOUT_MS)) == 0) - return -ETIMEDOUT; - - return 0; -} - int snd_dice_transaction_get_clock_source(struct snd_dice *dice, unsigned int *source) { @@ -143,10 +93,6 @@ int snd_dice_transaction_get_rate(struct snd_dice *dice, unsigned int *rate) end: return err; } -int snd_dice_transaction_set_rate(struct snd_dice *dice, unsigned int rate) -{ - return set_clock_info(dice, rate, UINT_MAX); -} int snd_dice_transaction_set_enable(struct snd_dice *dice) { @@ -210,7 +156,7 @@ static void dice_notification(struct fw_card *card, struct fw_request *request, fw_send_response(card, request, RCODE_COMPLETE); - if (bits & NOTIFY_CLOCK_ACCEPTED) + if (bits & NOTIFY_LOCK_CHG) complete(&dice->clock_accepted); wake_up(&dice->hwdep_wait); } diff --git a/sound/firewire/dice/dice.c b/sound/firewire/dice/dice.c index b91b3739c810..f7303a650ac2 100644 --- a/sound/firewire/dice/dice.c +++ b/sound/firewire/dice/dice.c @@ -57,65 +57,10 @@ static int check_dice_category(struct fw_unit *unit) return 0; } -static int highest_supported_mode_rate(struct snd_dice *dice, - unsigned int mode, unsigned int *rate) -{ - unsigned int i, m; - - for (i = ARRAY_SIZE(snd_dice_rates); i > 0; i--) { - *rate = snd_dice_rates[i - 1]; - if (snd_dice_stream_get_rate_mode(dice, *rate, &m) < 0) - continue; - if (mode == m) - break; - } - if (i == 0) - return -EINVAL; - - return 0; -} - -static int dice_read_mode_params(struct snd_dice *dice, unsigned int mode) -{ - __be32 values[2]; - unsigned int rate; - int err; - - if (highest_supported_mode_rate(dice, mode, &rate) < 0) { - dice->tx_channels[mode] = 0; - dice->tx_midi_ports[mode] = 0; - dice->rx_channels[mode] = 0; - dice->rx_midi_ports[mode] = 0; - return 0; - } - - err = snd_dice_transaction_set_rate(dice, rate); - if (err < 0) - return err; - - err = snd_dice_transaction_read_tx(dice, TX_NUMBER_AUDIO, - values, sizeof(values)); - if (err < 0) - return err; - - dice->tx_channels[mode] = be32_to_cpu(values[0]); - dice->tx_midi_ports[mode] = be32_to_cpu(values[1]); - - err = snd_dice_transaction_read_rx(dice, RX_NUMBER_AUDIO, - values, sizeof(values)); - if (err < 0) - return err; - - dice->rx_channels[mode] = be32_to_cpu(values[0]); - dice->rx_midi_ports[mode] = be32_to_cpu(values[1]); - - return 0; -} - -static int dice_read_params(struct snd_dice *dice) +static int check_clock_caps(struct snd_dice *dice) { __be32 value; - int mode, err; + int err; /* some very old firmwares don't tell about their clock support */ if (dice->clock_caps > 0) { @@ -133,12 +78,6 @@ static int dice_read_params(struct snd_dice *dice) CLOCK_CAP_SOURCE_INTERNAL; } - for (mode = 2; mode >= 0; --mode) { - err = dice_read_mode_params(dice, mode); - if (err < 0) - return err; - } - return 0; } @@ -215,7 +154,7 @@ static void do_registration(struct work_struct *work) if (err < 0) goto error; - err = dice_read_params(dice); + err = check_clock_caps(dice); if (err < 0) goto error; diff --git a/sound/firewire/dice/dice.h b/sound/firewire/dice/dice.h index 3d5ebebe61ea..423cdba99726 100644 --- a/sound/firewire/dice/dice.h +++ b/sound/firewire/dice/dice.h @@ -56,10 +56,6 @@ struct snd_dice { unsigned int rsrv_offset; unsigned int clock_caps; - unsigned int tx_channels[3]; - unsigned int rx_channels[3]; - unsigned int tx_midi_ports[3]; - unsigned int rx_midi_ports[3]; struct fw_address_handler notification_handler; int owner_generation; @@ -158,7 +154,6 @@ static inline int snd_dice_transaction_read_sync(struct snd_dice *dice, int snd_dice_transaction_get_clock_source(struct snd_dice *dice, unsigned int *source); -int snd_dice_transaction_set_rate(struct snd_dice *dice, unsigned int rate); int snd_dice_transaction_get_rate(struct snd_dice *dice, unsigned int *rate); int snd_dice_transaction_set_enable(struct snd_dice *dice); void snd_dice_transaction_clear_enable(struct snd_dice *dice); @@ -169,9 +164,6 @@ void snd_dice_transaction_destroy(struct snd_dice *dice); #define SND_DICE_RATES_COUNT 7 extern const unsigned int snd_dice_rates[SND_DICE_RATES_COUNT]; -int snd_dice_stream_get_rate_mode(struct snd_dice *dice, - unsigned int rate, unsigned int *mode); - int snd_dice_stream_start_duplex(struct snd_dice *dice, unsigned int rate); void snd_dice_stream_stop_duplex(struct snd_dice *dice); int snd_dice_stream_init_duplex(struct snd_dice *dice); diff --git a/sound/firewire/fireworks/fireworks.c b/sound/firewire/fireworks/fireworks.c index d5b19bc11e59..8f27b67503c8 100644 --- a/sound/firewire/fireworks/fireworks.c +++ b/sound/firewire/fireworks/fireworks.c @@ -301,7 +301,10 @@ static void efw_update(struct fw_unit *unit) struct snd_efw *efw = dev_get_drvdata(&unit->device); snd_efw_transaction_bus_reset(efw->unit); + + mutex_lock(&efw->mutex); snd_efw_stream_update_duplex(efw); + mutex_unlock(&efw->mutex); } static void efw_remove(struct fw_unit *unit) diff --git a/sound/firewire/fireworks/fireworks_stream.c b/sound/firewire/fireworks/fireworks_stream.c index 968a40a1beb2..425db8d88235 100644 --- a/sound/firewire/fireworks/fireworks_stream.c +++ b/sound/firewire/fireworks/fireworks_stream.c @@ -313,12 +313,10 @@ void snd_efw_stream_stop_duplex(struct snd_efw *efw) void snd_efw_stream_update_duplex(struct snd_efw *efw) { - if ((cmp_connection_update(&efw->out_conn) < 0) || - (cmp_connection_update(&efw->in_conn) < 0)) { - mutex_lock(&efw->mutex); + if (cmp_connection_update(&efw->out_conn) < 0 || + cmp_connection_update(&efw->in_conn) < 0) { stop_stream(efw, &efw->rx_stream); stop_stream(efw, &efw->tx_stream); - mutex_unlock(&efw->mutex); } else { amdtp_stream_update(&efw->rx_stream); amdtp_stream_update(&efw->tx_stream); diff --git a/sound/firewire/oxfw/oxfw-scs1x.c b/sound/firewire/oxfw/oxfw-scs1x.c index bb53eb35721b..f897c9831077 100644 --- a/sound/firewire/oxfw/oxfw-scs1x.c +++ b/sound/firewire/oxfw/oxfw-scs1x.c @@ -26,11 +26,13 @@ struct fw_scs1x { u8 output_bytes; bool output_escaped; bool output_escape_high_nibble; - struct tasklet_struct tasklet; + struct work_struct work; wait_queue_head_t idle_wait; u8 buffer[HSS1394_MAX_PACKET_SIZE]; bool transaction_running; struct fw_transaction transaction; + unsigned int transaction_bytes; + bool error; struct fw_device *fw_dev; }; @@ -125,11 +127,16 @@ static void scs_write_callback(struct fw_card *card, int rcode, { struct fw_scs1x *scs = callback_data; - if (rcode == RCODE_GENERATION) - ; /* TODO: retry this packet */ + if (!rcode_is_permanent_error(rcode)) { + /* Don't retry for this data. */ + if (rcode == RCODE_COMPLETE) + scs->transaction_bytes = 0; + } else { + scs->error = true; + } scs->transaction_running = false; - tasklet_schedule(&scs->tasklet); + schedule_work(&scs->work); } static bool is_valid_running_status(u8 status) @@ -165,9 +172,9 @@ static bool is_invalid_cmd(u8 status) status == 0xfd; } -static void scs_output_tasklet(unsigned long data) +static void scs_output_work(struct work_struct *work) { - struct fw_scs1x *scs = (struct fw_scs1x *)data; + struct fw_scs1x *scs = container_of(work, struct fw_scs1x, work); struct snd_rawmidi_substream *stream; unsigned int i; u8 byte; @@ -177,12 +184,15 @@ static void scs_output_tasklet(unsigned long data) return; stream = ACCESS_ONCE(scs->output); - if (!stream) { + if (!stream || scs->error) { scs->output_idle = true; wake_up(&scs->idle_wait); return; } + if (scs->transaction_bytes > 0) + goto retry; + i = scs->output_bytes; for (;;) { if (snd_rawmidi_transmit(stream, &byte, 1) != 1) { @@ -253,13 +263,16 @@ static void scs_output_tasklet(unsigned long data) scs->output_bytes = 1; scs->output_escaped = false; + scs->transaction_bytes = i; +retry: scs->transaction_running = true; generation = scs->fw_dev->generation; smp_rmb(); /* node_id vs. generation */ fw_send_request(scs->fw_dev->card, &scs->transaction, TCODE_WRITE_BLOCK_REQUEST, scs->fw_dev->node_id, generation, scs->fw_dev->max_speed, HSS1394_ADDRESS, - scs->buffer, i, scs_write_callback, scs); + scs->buffer, scs->transaction_bytes, + scs_write_callback, scs); } static int midi_capture_open(struct snd_rawmidi_substream *stream) @@ -309,9 +322,11 @@ static void midi_playback_trigger(struct snd_rawmidi_substream *stream, int up) scs->output_bytes = 1; scs->output_escaped = false; scs->output_idle = false; + scs->transaction_bytes = 0; + scs->error = false; ACCESS_ONCE(scs->output) = stream; - tasklet_schedule(&scs->tasklet); + schedule_work(&scs->work); } else { ACCESS_ONCE(scs->output) = NULL; } @@ -395,7 +410,7 @@ int snd_oxfw_scs1x_add(struct snd_oxfw *oxfw) snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &midi_playback_ops); - tasklet_init(&scs->tasklet, scs_output_tasklet, (unsigned long)scs); + INIT_WORK(&scs->work, scs_output_work); init_waitqueue_head(&scs->idle_wait); scs->output_idle = true; diff --git a/sound/mips/Kconfig b/sound/mips/Kconfig index 2153d31fb663..4a4705031cb9 100644 --- a/sound/mips/Kconfig +++ b/sound/mips/Kconfig @@ -23,17 +23,5 @@ config SND_SGI_HAL2 help Sound support for the SGI Indy and Indigo2 Workstation. - -config SND_AU1X00 - tristate "Au1x00 AC97 Port Driver (DEPRECATED)" - depends on MIPS_ALCHEMY - select SND_PCM - select SND_AC97_CODEC - help - ALSA Sound driver for the Au1x00's AC97 port. - - Newer drivers for ASoC are available, please do not use - this driver as it will be removed in the future. - endif # SND_MIPS diff --git a/sound/mips/Makefile b/sound/mips/Makefile index 861ec0a574b4..b977c44330d6 100644 --- a/sound/mips/Makefile +++ b/sound/mips/Makefile @@ -2,11 +2,9 @@ # Makefile for ALSA # -snd-au1x00-objs := au1x00.o snd-sgi-o2-objs := sgio2audio.o ad1843.o snd-sgi-hal2-objs := hal2.o # Toplevel Module Dependency -obj-$(CONFIG_SND_AU1X00) += snd-au1x00.o obj-$(CONFIG_SND_SGI_O2) += snd-sgi-o2.o obj-$(CONFIG_SND_SGI_HAL2) += snd-sgi-hal2.o diff --git a/sound/mips/au1x00.c b/sound/mips/au1x00.c deleted file mode 100644 index 1e30e8475431..000000000000 --- a/sound/mips/au1x00.c +++ /dev/null @@ -1,734 +0,0 @@ -/* - * BRIEF MODULE DESCRIPTION - * Driver for AMD Au1000 MIPS Processor, AC'97 Sound Port - * - * Copyright 2004 Cooper Street Innovations Inc. - * Author: Charles Eidsness <charles@cooper-street.com> - * - * 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN - * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * 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., - * 675 Mass Ave, Cambridge, MA 02139, USA. - * - * History: - * - * 2004-09-09 Charles Eidsness -- Original verion -- based on - * sa11xx-uda1341.c ALSA driver and the - * au1000.c OSS driver. - * 2004-09-09 Matt Porter -- Added support for ALSA 1.0.6 - * - */ - -#include <linux/ioport.h> -#include <linux/interrupt.h> -#include <linux/init.h> -#include <linux/platform_device.h> -#include <linux/slab.h> -#include <linux/module.h> -#include <sound/core.h> -#include <sound/initval.h> -#include <sound/pcm.h> -#include <sound/pcm_params.h> -#include <sound/ac97_codec.h> -#include <asm/mach-au1x00/au1000.h> -#include <asm/mach-au1x00/au1000_dma.h> - -MODULE_AUTHOR("Charles Eidsness <charles@cooper-street.com>"); -MODULE_DESCRIPTION("Au1000 AC'97 ALSA Driver"); -MODULE_LICENSE("GPL"); -MODULE_SUPPORTED_DEVICE("{{AMD,Au1000 AC'97}}"); - -#define PLAYBACK 0 -#define CAPTURE 1 -#define AC97_SLOT_3 0x01 -#define AC97_SLOT_4 0x02 -#define AC97_SLOT_6 0x08 -#define AC97_CMD_IRQ 31 -#define READ 0 -#define WRITE 1 -#define READ_WAIT 2 -#define RW_DONE 3 - -struct au1000_period -{ - u32 start; - u32 relative_end; /*realtive to start of buffer*/ - struct au1000_period * next; -}; - -/*Au1000 AC97 Port Control Reisters*/ -struct au1000_ac97_reg { - u32 volatile config; - u32 volatile status; - u32 volatile data; - u32 volatile cmd; - u32 volatile cntrl; -}; - -struct audio_stream { - struct snd_pcm_substream *substream; - int dma; - spinlock_t dma_lock; - struct au1000_period * buffer; - unsigned int period_size; - unsigned int periods; -}; - -struct snd_au1000 { - struct snd_card *card; - struct au1000_ac97_reg volatile *ac97_ioport; - - struct resource *ac97_res_port; - spinlock_t ac97_lock; - struct snd_ac97 *ac97; - - struct snd_pcm *pcm; - struct audio_stream *stream[2]; /* playback & capture */ - int dmaid[2]; /* tx(0)/rx(1) DMA ids */ -}; - -/*--------------------------- Local Functions --------------------------------*/ -static void -au1000_set_ac97_xmit_slots(struct snd_au1000 *au1000, long xmit_slots) -{ - u32 volatile ac97_config; - - spin_lock(&au1000->ac97_lock); - ac97_config = au1000->ac97_ioport->config; - ac97_config = ac97_config & ~AC97C_XMIT_SLOTS_MASK; - ac97_config |= (xmit_slots << AC97C_XMIT_SLOTS_BIT); - au1000->ac97_ioport->config = ac97_config; - spin_unlock(&au1000->ac97_lock); -} - -static void -au1000_set_ac97_recv_slots(struct snd_au1000 *au1000, long recv_slots) -{ - u32 volatile ac97_config; - - spin_lock(&au1000->ac97_lock); - ac97_config = au1000->ac97_ioport->config; - ac97_config = ac97_config & ~AC97C_RECV_SLOTS_MASK; - ac97_config |= (recv_slots << AC97C_RECV_SLOTS_BIT); - au1000->ac97_ioport->config = ac97_config; - spin_unlock(&au1000->ac97_lock); -} - - -static void -au1000_release_dma_link(struct audio_stream *stream) -{ - struct au1000_period * pointer; - struct au1000_period * pointer_next; - - stream->period_size = 0; - stream->periods = 0; - pointer = stream->buffer; - if (! pointer) - return; - do { - pointer_next = pointer->next; - kfree(pointer); - pointer = pointer_next; - } while (pointer != stream->buffer); - stream->buffer = NULL; -} - -static int -au1000_setup_dma_link(struct audio_stream *stream, unsigned int period_bytes, - unsigned int periods) -{ - struct snd_pcm_substream *substream = stream->substream; - struct snd_pcm_runtime *runtime = substream->runtime; - struct au1000_period *pointer; - unsigned long dma_start; - int i; - - dma_start = virt_to_phys(runtime->dma_area); - - if (stream->period_size == period_bytes && - stream->periods == periods) - return 0; /* not changed */ - - au1000_release_dma_link(stream); - - stream->period_size = period_bytes; - stream->periods = periods; - - stream->buffer = kmalloc(sizeof(struct au1000_period), GFP_KERNEL); - if (! stream->buffer) - return -ENOMEM; - pointer = stream->buffer; - for (i = 0; i < periods; i++) { - pointer->start = (u32)(dma_start + (i * period_bytes)); - pointer->relative_end = (u32) (((i+1) * period_bytes) - 0x1); - if (i < periods - 1) { - pointer->next = kmalloc(sizeof(struct au1000_period), GFP_KERNEL); - if (! pointer->next) { - au1000_release_dma_link(stream); - return -ENOMEM; - } - pointer = pointer->next; - } - } - pointer->next = stream->buffer; - return 0; -} - -static void -au1000_dma_stop(struct audio_stream *stream) -{ - if (snd_BUG_ON(!stream->buffer)) - return; - disable_dma(stream->dma); -} - -static void -au1000_dma_start(struct audio_stream *stream) -{ - if (snd_BUG_ON(!stream->buffer)) - return; - - init_dma(stream->dma); - if (get_dma_active_buffer(stream->dma) == 0) { - clear_dma_done0(stream->dma); - set_dma_addr0(stream->dma, stream->buffer->start); - set_dma_count0(stream->dma, stream->period_size >> 1); - set_dma_addr1(stream->dma, stream->buffer->next->start); - set_dma_count1(stream->dma, stream->period_size >> 1); - } else { - clear_dma_done1(stream->dma); - set_dma_addr1(stream->dma, stream->buffer->start); - set_dma_count1(stream->dma, stream->period_size >> 1); - set_dma_addr0(stream->dma, stream->buffer->next->start); - set_dma_count0(stream->dma, stream->period_size >> 1); - } - enable_dma_buffers(stream->dma); - start_dma(stream->dma); -} - -static irqreturn_t -au1000_dma_interrupt(int irq, void *dev_id) -{ - struct audio_stream *stream = (struct audio_stream *) dev_id; - struct snd_pcm_substream *substream = stream->substream; - - spin_lock(&stream->dma_lock); - switch (get_dma_buffer_done(stream->dma)) { - case DMA_D0: - stream->buffer = stream->buffer->next; - clear_dma_done0(stream->dma); - set_dma_addr0(stream->dma, stream->buffer->next->start); - set_dma_count0(stream->dma, stream->period_size >> 1); - enable_dma_buffer0(stream->dma); - break; - case DMA_D1: - stream->buffer = stream->buffer->next; - clear_dma_done1(stream->dma); - set_dma_addr1(stream->dma, stream->buffer->next->start); - set_dma_count1(stream->dma, stream->period_size >> 1); - enable_dma_buffer1(stream->dma); - break; - case (DMA_D0 | DMA_D1): - printk(KERN_ERR "DMA %d missed interrupt.\n",stream->dma); - au1000_dma_stop(stream); - au1000_dma_start(stream); - break; - case (~DMA_D0 & ~DMA_D1): - printk(KERN_ERR "DMA %d empty irq.\n",stream->dma); - } - spin_unlock(&stream->dma_lock); - snd_pcm_period_elapsed(substream); - return IRQ_HANDLED; -} - -/*-------------------------- PCM Audio Streams -------------------------------*/ - -static unsigned int rates[] = {8000, 11025, 16000, 22050}; -static struct snd_pcm_hw_constraint_list hw_constraints_rates = { - .count = ARRAY_SIZE(rates), - .list = rates, - .mask = 0, -}; - -static struct snd_pcm_hardware snd_au1000_hw = -{ - .info = (SNDRV_PCM_INFO_INTERLEAVED | \ - SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID), - .formats = SNDRV_PCM_FMTBIT_S16_LE, - .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | - SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050), - .rate_min = 8000, - .rate_max = 22050, - .channels_min = 1, - .channels_max = 2, - .buffer_bytes_max = 128*1024, - .period_bytes_min = 32, - .period_bytes_max = 16*1024, - .periods_min = 8, - .periods_max = 255, - .fifo_size = 16, -}; - -static int -snd_au1000_playback_open(struct snd_pcm_substream *substream) -{ - struct snd_au1000 *au1000 = substream->pcm->private_data; - - au1000->stream[PLAYBACK]->substream = substream; - au1000->stream[PLAYBACK]->buffer = NULL; - substream->private_data = au1000->stream[PLAYBACK]; - substream->runtime->hw = snd_au1000_hw; - return (snd_pcm_hw_constraint_list(substream->runtime, 0, - SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates) < 0); -} - -static int -snd_au1000_capture_open(struct snd_pcm_substream *substream) -{ - struct snd_au1000 *au1000 = substream->pcm->private_data; - - au1000->stream[CAPTURE]->substream = substream; - au1000->stream[CAPTURE]->buffer = NULL; - substream->private_data = au1000->stream[CAPTURE]; - substream->runtime->hw = snd_au1000_hw; - return (snd_pcm_hw_constraint_list(substream->runtime, 0, - SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates) < 0); -} - -static int -snd_au1000_playback_close(struct snd_pcm_substream *substream) -{ - struct snd_au1000 *au1000 = substream->pcm->private_data; - - au1000->stream[PLAYBACK]->substream = NULL; - return 0; -} - -static int -snd_au1000_capture_close(struct snd_pcm_substream *substream) -{ - struct snd_au1000 *au1000 = substream->pcm->private_data; - - au1000->stream[CAPTURE]->substream = NULL; - return 0; -} - -static int -snd_au1000_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *hw_params) -{ - struct audio_stream *stream = substream->private_data; - int err; - - err = snd_pcm_lib_malloc_pages(substream, - params_buffer_bytes(hw_params)); - if (err < 0) - return err; - return au1000_setup_dma_link(stream, - params_period_bytes(hw_params), - params_periods(hw_params)); -} - -static int -snd_au1000_hw_free(struct snd_pcm_substream *substream) -{ - struct audio_stream *stream = substream->private_data; - au1000_release_dma_link(stream); - return snd_pcm_lib_free_pages(substream); -} - -static int -snd_au1000_playback_prepare(struct snd_pcm_substream *substream) -{ - struct snd_au1000 *au1000 = substream->pcm->private_data; - struct snd_pcm_runtime *runtime = substream->runtime; - - if (runtime->channels == 1) - au1000_set_ac97_xmit_slots(au1000, AC97_SLOT_4); - else - au1000_set_ac97_xmit_slots(au1000, AC97_SLOT_3 | AC97_SLOT_4); - snd_ac97_set_rate(au1000->ac97, AC97_PCM_FRONT_DAC_RATE, runtime->rate); - return 0; -} - -static int -snd_au1000_capture_prepare(struct snd_pcm_substream *substream) -{ - struct snd_au1000 *au1000 = substream->pcm->private_data; - struct snd_pcm_runtime *runtime = substream->runtime; - - if (runtime->channels == 1) - au1000_set_ac97_recv_slots(au1000, AC97_SLOT_4); - else - au1000_set_ac97_recv_slots(au1000, AC97_SLOT_3 | AC97_SLOT_4); - snd_ac97_set_rate(au1000->ac97, AC97_PCM_LR_ADC_RATE, runtime->rate); - return 0; -} - -static int -snd_au1000_trigger(struct snd_pcm_substream *substream, int cmd) -{ - struct audio_stream *stream = substream->private_data; - int err = 0; - - spin_lock(&stream->dma_lock); - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - au1000_dma_start(stream); - break; - case SNDRV_PCM_TRIGGER_STOP: - au1000_dma_stop(stream); - break; - default: - err = -EINVAL; - break; - } - spin_unlock(&stream->dma_lock); - return err; -} - -static snd_pcm_uframes_t -snd_au1000_pointer(struct snd_pcm_substream *substream) -{ - struct audio_stream *stream = substream->private_data; - struct snd_pcm_runtime *runtime = substream->runtime; - long location; - - spin_lock(&stream->dma_lock); - location = get_dma_residue(stream->dma); - spin_unlock(&stream->dma_lock); - location = stream->buffer->relative_end - location; - if (location == -1) - location = 0; - return bytes_to_frames(runtime,location); -} - -static struct snd_pcm_ops snd_card_au1000_playback_ops = { - .open = snd_au1000_playback_open, - .close = snd_au1000_playback_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = snd_au1000_hw_params, - .hw_free = snd_au1000_hw_free, - .prepare = snd_au1000_playback_prepare, - .trigger = snd_au1000_trigger, - .pointer = snd_au1000_pointer, -}; - -static struct snd_pcm_ops snd_card_au1000_capture_ops = { - .open = snd_au1000_capture_open, - .close = snd_au1000_capture_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = snd_au1000_hw_params, - .hw_free = snd_au1000_hw_free, - .prepare = snd_au1000_capture_prepare, - .trigger = snd_au1000_trigger, - .pointer = snd_au1000_pointer, -}; - -static int -snd_au1000_pcm_new(struct snd_au1000 *au1000) -{ - struct snd_pcm *pcm; - int err; - unsigned long flags; - - if ((err = snd_pcm_new(au1000->card, "AU1000 AC97 PCM", 0, 1, 1, &pcm)) < 0) - return err; - - snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS, - snd_dma_continuous_data(GFP_KERNEL), 128*1024, 128*1024); - - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, - &snd_card_au1000_playback_ops); - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, - &snd_card_au1000_capture_ops); - - pcm->private_data = au1000; - pcm->info_flags = 0; - strcpy(pcm->name, "Au1000 AC97 PCM"); - - spin_lock_init(&au1000->stream[PLAYBACK]->dma_lock); - spin_lock_init(&au1000->stream[CAPTURE]->dma_lock); - - flags = claim_dma_lock(); - au1000->stream[PLAYBACK]->dma = request_au1000_dma(au1000->dmaid[0], - "AC97 TX", au1000_dma_interrupt, 0, - au1000->stream[PLAYBACK]); - if (au1000->stream[PLAYBACK]->dma < 0) { - release_dma_lock(flags); - return -EBUSY; - } - au1000->stream[CAPTURE]->dma = request_au1000_dma(au1000->dmaid[1], - "AC97 RX", au1000_dma_interrupt, 0, - au1000->stream[CAPTURE]); - if (au1000->stream[CAPTURE]->dma < 0){ - release_dma_lock(flags); - return -EBUSY; - } - /* enable DMA coherency in read/write DMA channels */ - set_dma_mode(au1000->stream[PLAYBACK]->dma, - get_dma_mode(au1000->stream[PLAYBACK]->dma) & ~DMA_NC); - set_dma_mode(au1000->stream[CAPTURE]->dma, - get_dma_mode(au1000->stream[CAPTURE]->dma) & ~DMA_NC); - release_dma_lock(flags); - au1000->pcm = pcm; - return 0; -} - - -/*-------------------------- AC97 CODEC Control ------------------------------*/ - -static unsigned short -snd_au1000_ac97_read(struct snd_ac97 *ac97, unsigned short reg) -{ - struct snd_au1000 *au1000 = ac97->private_data; - u32 volatile cmd; - u16 volatile data; - int i; - - spin_lock(&au1000->ac97_lock); -/* would rather use the interrupt than this polling but it works and I can't -get the interrupt driven case to work efficiently */ - for (i = 0; i < 0x5000; i++) - if (!(au1000->ac97_ioport->status & AC97C_CP)) - break; - if (i == 0x5000) - printk(KERN_ERR "au1000 AC97: AC97 command read timeout\n"); - - cmd = (u32) reg & AC97C_INDEX_MASK; - cmd |= AC97C_READ; - au1000->ac97_ioport->cmd = cmd; - - /* now wait for the data */ - for (i = 0; i < 0x5000; i++) - if (!(au1000->ac97_ioport->status & AC97C_CP)) - break; - if (i == 0x5000) { - printk(KERN_ERR "au1000 AC97: AC97 command read timeout\n"); - spin_unlock(&au1000->ac97_lock); - return 0; - } - - data = au1000->ac97_ioport->cmd & 0xffff; - spin_unlock(&au1000->ac97_lock); - - return data; - -} - - -static void -snd_au1000_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short val) -{ - struct snd_au1000 *au1000 = ac97->private_data; - u32 cmd; - int i; - - spin_lock(&au1000->ac97_lock); -/* would rather use the interrupt than this polling but it works and I can't -get the interrupt driven case to work efficiently */ - for (i = 0; i < 0x5000; i++) - if (!(au1000->ac97_ioport->status & AC97C_CP)) - break; - if (i == 0x5000) - printk(KERN_ERR "au1000 AC97: AC97 command write timeout\n"); - - cmd = (u32) reg & AC97C_INDEX_MASK; - cmd &= ~AC97C_READ; - cmd |= ((u32) val << AC97C_WD_BIT); - au1000->ac97_ioport->cmd = cmd; - spin_unlock(&au1000->ac97_lock); -} - -/*------------------------------ Setup / Destroy ----------------------------*/ - -static void snd_au1000_free(struct snd_card *card) -{ - struct snd_au1000 *au1000 = card->private_data; - - if (au1000->stream[PLAYBACK]) { - if (au1000->stream[PLAYBACK]->dma >= 0) - free_au1000_dma(au1000->stream[PLAYBACK]->dma); - kfree(au1000->stream[PLAYBACK]); - } - - if (au1000->stream[CAPTURE]) { - if (au1000->stream[CAPTURE]->dma >= 0) - free_au1000_dma(au1000->stream[CAPTURE]->dma); - kfree(au1000->stream[CAPTURE]); - } - - if (au1000->ac97_res_port) { - /* put internal AC97 block into reset */ - if (au1000->ac97_ioport) { - au1000->ac97_ioport->cntrl = AC97C_RS; - iounmap(au1000->ac97_ioport); - au1000->ac97_ioport = NULL; - } - release_and_free_resource(au1000->ac97_res_port); - au1000->ac97_res_port = NULL; - } -} - -static struct snd_ac97_bus_ops ops = { - .write = snd_au1000_ac97_write, - .read = snd_au1000_ac97_read, -}; - -static int au1000_ac97_probe(struct platform_device *pdev) -{ - int err; - void __iomem *io; - struct resource *r; - struct snd_card *card; - struct snd_au1000 *au1000; - struct snd_ac97_bus *pbus; - struct snd_ac97_template ac97; - - err = snd_card_new(&pdev->dev, -1, "AC97", THIS_MODULE, - sizeof(struct snd_au1000), &card); - if (err < 0) - return err; - - au1000 = card->private_data; - au1000->card = card; - spin_lock_init(&au1000->ac97_lock); - - /* from here on let ALSA call the special freeing function */ - card->private_free = snd_au1000_free; - - /* TX DMA ID */ - r = platform_get_resource(pdev, IORESOURCE_DMA, 0); - if (!r) { - err = -ENODEV; - snd_printk(KERN_INFO "no TX DMA platform resource!\n"); - goto out; - } - au1000->dmaid[0] = r->start; - - /* RX DMA ID */ - r = platform_get_resource(pdev, IORESOURCE_DMA, 1); - if (!r) { - err = -ENODEV; - snd_printk(KERN_INFO "no RX DMA platform resource!\n"); - goto out; - } - au1000->dmaid[1] = r->start; - - au1000->stream[PLAYBACK] = kmalloc(sizeof(struct audio_stream), - GFP_KERNEL); - if (!au1000->stream[PLAYBACK]) { - err = -ENOMEM; - goto out; - } - au1000->stream[PLAYBACK]->dma = -1; - - au1000->stream[CAPTURE] = kmalloc(sizeof(struct audio_stream), - GFP_KERNEL); - if (!au1000->stream[CAPTURE]) { - err = -ENOMEM; - goto out; - } - au1000->stream[CAPTURE]->dma = -1; - - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!r) { - err = -ENODEV; - goto out; - } - - err = -EBUSY; - au1000->ac97_res_port = request_mem_region(r->start, resource_size(r), - pdev->name); - if (!au1000->ac97_res_port) { - snd_printk(KERN_ERR "ALSA AC97: can't grab AC97 port\n"); - goto out; - } - - io = ioremap(r->start, resource_size(r)); - if (!io) - goto out; - - au1000->ac97_ioport = (struct au1000_ac97_reg *)io; - - /* configure pins for AC'97 - TODO: move to board_setup.c */ - au_writel(au_readl(SYS_PINFUNC) & ~0x02, SYS_PINFUNC); - - /* Initialise Au1000's AC'97 Control Block */ - au1000->ac97_ioport->cntrl = AC97C_RS | AC97C_CE; - udelay(10); - au1000->ac97_ioport->cntrl = AC97C_CE; - udelay(10); - - /* Initialise External CODEC -- cold reset */ - au1000->ac97_ioport->config = AC97C_RESET; - udelay(10); - au1000->ac97_ioport->config = 0x0; - mdelay(5); - - /* Initialise AC97 middle-layer */ - err = snd_ac97_bus(au1000->card, 0, &ops, au1000, &pbus); - if (err < 0) - goto out; - - memset(&ac97, 0, sizeof(ac97)); - ac97.private_data = au1000; - err = snd_ac97_mixer(pbus, &ac97, &au1000->ac97); - if (err < 0) - goto out; - - err = snd_au1000_pcm_new(au1000); - if (err < 0) - goto out; - - strcpy(card->driver, "Au1000-AC97"); - strcpy(card->shortname, "AMD Au1000-AC97"); - sprintf(card->longname, "AMD Au1000--AC97 ALSA Driver"); - - err = snd_card_register(card); - if (err < 0) - goto out; - - printk(KERN_INFO "ALSA AC97: Driver Initialized\n"); - - platform_set_drvdata(pdev, card); - - return 0; - - out: - snd_card_free(card); - return err; -} - -static int au1000_ac97_remove(struct platform_device *pdev) -{ - return snd_card_free(platform_get_drvdata(pdev)); -} - -struct platform_driver au1000_ac97c_driver = { - .driver = { - .name = "au1000-ac97c", - .owner = THIS_MODULE, - }, - .probe = au1000_ac97_probe, - .remove = au1000_ac97_remove, -}; - -module_platform_driver(au1000_ac97c_driver); diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig index 8f6594a7d37f..32151d8c6bb8 100644 --- a/sound/pci/Kconfig +++ b/sound/pci/Kconfig @@ -866,7 +866,7 @@ config SND_VIRTUOSO select SND_OXYGEN_LIB select SND_PCM select SND_MPU401_UART - select SND_JACK if INPUT=y || INPUT=SND + select SND_JACK help Say Y here to include support for sound cards based on the Asus AV66/AV100/AV200 chips, i.e., Xonar D1, DX, D2, D2X, DS, DSX, diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index e94cfd5c69f7..bb02c2d48fd5 100644 --- a/sound/pci/hda/Kconfig +++ b/sound/pci/hda/Kconfig @@ -4,7 +4,7 @@ config SND_HDA tristate select SND_PCM select SND_VMASTER - select SND_JACK if INPUT=y || INPUT=SND + select SND_JACK select SND_HDA_CORE config SND_HDA_INTEL diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index e5240cb3749f..2624cfe98884 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -2145,7 +2145,7 @@ static int azx_probe_continue(struct azx *chip) azx_add_card_list(chip); snd_hda_set_power_save(&chip->bus, power_save * 1000); if (azx_has_pm_runtime(chip) || hda->use_vga_switcheroo) - pm_runtime_put_noidle(&pci->dev); + pm_runtime_put_autosuspend(&pci->dev); out_free: if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index bcbc4ee10130..fe4141c43ddb 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -75,6 +75,8 @@ struct hdmi_spec_per_cvt { struct hdmi_spec_per_pin { hda_nid_t pin_nid; + /* pin idx, different device entries on the same pin use the same idx */ + int pin_nid_idx; int num_mux_nids; hda_nid_t mux_nids[HDA_MAX_CONNECTIONS]; int mux_idx; @@ -84,8 +86,8 @@ struct hdmi_spec_per_pin { struct hdmi_eld sink_eld; struct mutex lock; struct delayed_work work; - struct snd_kcontrol *eld_ctl; - struct snd_jack *acomp_jack; /* jack via audio component */ + struct hdmi_pcm *pcm; /* pointer to spec->pcm_rec[n] dynamically*/ + int pcm_idx; /* which pcm is attached. -1 means no pcm is attached */ int repoll_count; bool setup; /* the stream has been set up by prepare callback */ int channels; /* current number of channels */ @@ -130,6 +132,12 @@ struct hdmi_ops { int (*chmap_validate)(int ca, int channels, unsigned char *chmap); }; +struct hdmi_pcm { + struct hda_pcm *pcm; + struct snd_jack *jack; + struct snd_kcontrol *eld_ctl; +}; + struct hdmi_spec { int num_cvts; struct snd_array cvts; /* struct hdmi_spec_per_cvt */ @@ -137,14 +145,23 @@ struct hdmi_spec { int num_pins; struct snd_array pins; /* struct hdmi_spec_per_pin */ - struct hda_pcm *pcm_rec[16]; + struct hdmi_pcm pcm_rec[16]; + struct mutex pcm_lock; + /* pcm_bitmap means which pcms have been assigned to pins*/ + unsigned long pcm_bitmap; + int pcm_used; /* counter of pcm_rec[] */ + /* bitmap shows whether the pcm is opened in user space + * bit 0 means the first playback PCM (PCM3); + * bit 1 means the second playback PCM, and so on. + */ + unsigned long pcm_in_use; unsigned int channels_max; /* max over all cvts */ struct hdmi_eld temp_eld; struct hdmi_ops ops; bool dyn_pin_out; - + bool dyn_pcm_assign; /* * Non-generic VIA/NVIDIA specific */ @@ -370,7 +387,10 @@ static struct cea_channel_speaker_allocation channel_allocations[] = { ((struct hdmi_spec_per_pin *)snd_array_elem(&spec->pins, idx)) #define get_cvt(spec, idx) \ ((struct hdmi_spec_per_cvt *)snd_array_elem(&spec->cvts, idx)) -#define get_pcm_rec(spec, idx) ((spec)->pcm_rec[idx]) +/* obtain hdmi_pcm object assigned to idx */ +#define get_hdmi_pcm(spec, idx) (&(spec)->pcm_rec[idx]) +/* obtain hda_pcm object assigned to idx */ +#define get_pcm_rec(spec, idx) (get_hdmi_pcm(spec, idx)->pcm) static int pin_nid_to_pin_index(struct hda_codec *codec, hda_nid_t pin_nid) { @@ -385,20 +405,52 @@ static int pin_nid_to_pin_index(struct hda_codec *codec, hda_nid_t pin_nid) return -EINVAL; } +static int hinfo_to_pcm_index(struct hda_codec *codec, + struct hda_pcm_stream *hinfo) +{ + struct hdmi_spec *spec = codec->spec; + int pcm_idx; + + for (pcm_idx = 0; pcm_idx < spec->pcm_used; pcm_idx++) + if (get_pcm_rec(spec, pcm_idx)->stream == hinfo) + return pcm_idx; + + codec_warn(codec, "HDMI: hinfo %p not registered\n", hinfo); + return -EINVAL; +} + static int hinfo_to_pin_index(struct hda_codec *codec, struct hda_pcm_stream *hinfo) { struct hdmi_spec *spec = codec->spec; + struct hdmi_spec_per_pin *per_pin; int pin_idx; - for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) - if (get_pcm_rec(spec, pin_idx)->stream == hinfo) + for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { + per_pin = get_pin(spec, pin_idx); + if (per_pin->pcm && + per_pin->pcm->pcm->stream == hinfo) return pin_idx; + } - codec_warn(codec, "HDMI: hinfo %p not registered\n", hinfo); + codec_dbg(codec, "HDMI: hinfo %p not registered\n", hinfo); return -EINVAL; } +static struct hdmi_spec_per_pin *pcm_idx_to_pin(struct hdmi_spec *spec, + int pcm_idx) +{ + int i; + struct hdmi_spec_per_pin *per_pin; + + for (i = 0; i < spec->num_pins; i++) { + per_pin = get_pin(spec, i); + if (per_pin->pcm_idx == pcm_idx) + return per_pin; + } + return NULL; +} + static int cvt_nid_to_cvt_index(struct hda_codec *codec, hda_nid_t cvt_nid) { struct hdmi_spec *spec = codec->spec; @@ -419,17 +471,22 @@ static int hdmi_eld_ctl_info(struct snd_kcontrol *kcontrol, struct hdmi_spec *spec = codec->spec; struct hdmi_spec_per_pin *per_pin; struct hdmi_eld *eld; - int pin_idx; + int pcm_idx; uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; - pin_idx = kcontrol->private_value; - per_pin = get_pin(spec, pin_idx); + pcm_idx = kcontrol->private_value; + mutex_lock(&spec->pcm_lock); + per_pin = pcm_idx_to_pin(spec, pcm_idx); + if (!per_pin) { + /* no pin is bound to the pcm */ + uinfo->count = 0; + mutex_unlock(&spec->pcm_lock); + return 0; + } eld = &per_pin->sink_eld; - - mutex_lock(&per_pin->lock); uinfo->count = eld->eld_valid ? eld->eld_size : 0; - mutex_unlock(&per_pin->lock); + mutex_unlock(&spec->pcm_lock); return 0; } @@ -441,16 +498,23 @@ static int hdmi_eld_ctl_get(struct snd_kcontrol *kcontrol, struct hdmi_spec *spec = codec->spec; struct hdmi_spec_per_pin *per_pin; struct hdmi_eld *eld; - int pin_idx; - - pin_idx = kcontrol->private_value; - per_pin = get_pin(spec, pin_idx); + int pcm_idx; + + pcm_idx = kcontrol->private_value; + mutex_lock(&spec->pcm_lock); + per_pin = pcm_idx_to_pin(spec, pcm_idx); + if (!per_pin) { + /* no pin is bound to the pcm */ + memset(ucontrol->value.bytes.data, 0, + ARRAY_SIZE(ucontrol->value.bytes.data)); + mutex_unlock(&spec->pcm_lock); + return 0; + } eld = &per_pin->sink_eld; - mutex_lock(&per_pin->lock); if (eld->eld_size > ARRAY_SIZE(ucontrol->value.bytes.data) || eld->eld_size > ELD_MAX_SIZE) { - mutex_unlock(&per_pin->lock); + mutex_unlock(&spec->pcm_lock); snd_BUG(); return -EINVAL; } @@ -460,7 +524,7 @@ static int hdmi_eld_ctl_get(struct snd_kcontrol *kcontrol, if (eld->eld_valid) memcpy(ucontrol->value.bytes.data, eld->eld_buffer, eld->eld_size); - mutex_unlock(&per_pin->lock); + mutex_unlock(&spec->pcm_lock); return 0; } @@ -473,7 +537,7 @@ static struct snd_kcontrol_new eld_bytes_ctl = { .get = hdmi_eld_ctl_get, }; -static int hdmi_create_eld_ctl(struct hda_codec *codec, int pin_idx, +static int hdmi_create_eld_ctl(struct hda_codec *codec, int pcm_idx, int device) { struct snd_kcontrol *kctl; @@ -483,14 +547,17 @@ static int hdmi_create_eld_ctl(struct hda_codec *codec, int pin_idx, kctl = snd_ctl_new1(&eld_bytes_ctl, codec); if (!kctl) return -ENOMEM; - kctl->private_value = pin_idx; + kctl->private_value = pcm_idx; kctl->id.device = device; - err = snd_hda_ctl_add(codec, get_pin(spec, pin_idx)->pin_nid, kctl); + /* no pin nid is associated with the kctl now + * tbd: associate pin nid to eld ctl later + */ + err = snd_hda_ctl_add(codec, 0, kctl); if (err < 0) return err; - get_pin(spec, pin_idx)->eld_ctl = kctl; + get_hdmi_pcm(spec, pcm_idx)->eld_ctl = kctl; return 0; } @@ -1338,6 +1405,11 @@ static int hdmi_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid, return 0; } +/* Try to find an available converter + * If pin_idx is less then zero, just try to find an available converter. + * Otherwise, try to find an available converter and get the cvt mux index + * of the pin. + */ static int hdmi_choose_cvt(struct hda_codec *codec, int pin_idx, int *cvt_id, int *mux_id) { @@ -1346,7 +1418,11 @@ static int hdmi_choose_cvt(struct hda_codec *codec, struct hdmi_spec_per_cvt *per_cvt = NULL; int cvt_idx, mux_idx = 0; - per_pin = get_pin(spec, pin_idx); + /* pin_idx < 0 means no pin will be bound to the converter */ + if (pin_idx < 0) + per_pin = NULL; + else + per_pin = get_pin(spec, pin_idx); /* Dynamically assign converter to stream */ for (cvt_idx = 0; cvt_idx < spec->num_cvts; cvt_idx++) { @@ -1355,6 +1431,8 @@ static int hdmi_choose_cvt(struct hda_codec *codec, /* Must not already be assigned */ if (per_cvt->assigned) continue; + if (per_pin == NULL) + break; /* Must be in pin's mux's list of converters */ for (mux_idx = 0; mux_idx < per_pin->num_mux_nids; mux_idx++) if (per_pin->mux_nids[mux_idx] == per_cvt->cvt_nid) @@ -1367,9 +1445,10 @@ static int hdmi_choose_cvt(struct hda_codec *codec, /* No free converters */ if (cvt_idx == spec->num_cvts) - return -ENODEV; + return -EBUSY; - per_pin->mux_idx = mux_idx; + if (per_pin != NULL) + per_pin->mux_idx = mux_idx; if (cvt_id) *cvt_id = cvt_idx; @@ -1395,6 +1474,20 @@ static void intel_verify_pin_cvt_connect(struct hda_codec *codec, mux_idx); } +/* get the mux index for the converter of the pins + * converter's mux index is the same for all pins on Intel platform + */ +static int intel_cvt_id_to_mux_idx(struct hdmi_spec *spec, + hda_nid_t cvt_nid) +{ + int i; + + for (i = 0; i < spec->num_cvts; i++) + if (spec->cvt_nids[i] == cvt_nid) + return i; + return -EINVAL; +} + /* Intel HDMI workaround to fix audio routing issue: * For some Intel display codecs, pins share the same connection list. * So a conveter can be selected by multiple pins and playback on any of these @@ -1446,6 +1539,74 @@ static void intel_not_share_assigned_cvt(struct hda_codec *codec, } } +/* A wrapper of intel_not_share_asigned_cvt() */ +static void intel_not_share_assigned_cvt_nid(struct hda_codec *codec, + hda_nid_t pin_nid, hda_nid_t cvt_nid) +{ + 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 + * share the converter. + */ + mux_idx = intel_cvt_id_to_mux_idx(spec, cvt_nid); + if (mux_idx >= 0) + intel_not_share_assigned_cvt(codec, pin_nid, mux_idx); +} + +/* called in hdmi_pcm_open when no pin is assigned to the PCM + * in dyn_pcm_assign mode. + */ +static int hdmi_pcm_open_no_pin(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct hdmi_spec *spec = codec->spec; + struct snd_pcm_runtime *runtime = substream->runtime; + int cvt_idx, pcm_idx; + struct hdmi_spec_per_cvt *per_cvt = NULL; + int err; + + pcm_idx = hinfo_to_pcm_index(codec, hinfo); + if (pcm_idx < 0) + return -EINVAL; + + err = hdmi_choose_cvt(codec, -1, &cvt_idx, NULL); + if (err) + return err; + + per_cvt = get_cvt(spec, cvt_idx); + per_cvt->assigned = 1; + hinfo->nid = per_cvt->cvt_nid; + + intel_not_share_assigned_cvt_nid(codec, 0, per_cvt->cvt_nid); + + set_bit(pcm_idx, &spec->pcm_in_use); + /* todo: setup spdif ctls assign */ + + /* Initially set the converter's capabilities */ + hinfo->channels_min = per_cvt->channels_min; + hinfo->channels_max = per_cvt->channels_max; + hinfo->rates = per_cvt->rates; + hinfo->formats = per_cvt->formats; + hinfo->maxbps = per_cvt->maxbps; + + /* Store the updated parameters */ + runtime->hw.channels_min = hinfo->channels_min; + runtime->hw.channels_max = hinfo->channels_max; + runtime->hw.formats = hinfo->formats; + runtime->hw.rates = hinfo->rates; + + snd_pcm_hw_constraint_step(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_CHANNELS, 2); + return 0; +} + /* * HDA PCM callbacks */ @@ -1455,26 +1616,47 @@ 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, mux_idx = 0; + int pin_idx, cvt_idx, pcm_idx, mux_idx = 0; struct hdmi_spec_per_pin *per_pin; struct hdmi_eld *eld; struct hdmi_spec_per_cvt *per_cvt = NULL; int err; /* Validate hinfo */ - pin_idx = hinfo_to_pin_index(codec, hinfo); - if (snd_BUG_ON(pin_idx < 0)) + pcm_idx = hinfo_to_pcm_index(codec, hinfo); + if (pcm_idx < 0) return -EINVAL; - per_pin = get_pin(spec, pin_idx); - eld = &per_pin->sink_eld; + + mutex_lock(&spec->pcm_lock); + pin_idx = hinfo_to_pin_index(codec, hinfo); + if (!spec->dyn_pcm_assign) { + if (snd_BUG_ON(pin_idx < 0)) { + mutex_unlock(&spec->pcm_lock); + return -EINVAL; + } + } else { + /* no pin is assigned to the PCM + * PA need pcm open successfully when probe + */ + if (pin_idx < 0) { + err = hdmi_pcm_open_no_pin(hinfo, codec, substream); + mutex_unlock(&spec->pcm_lock); + return err; + } + } err = hdmi_choose_cvt(codec, pin_idx, &cvt_idx, &mux_idx); - if (err < 0) + if (err < 0) { + mutex_unlock(&spec->pcm_lock); return err; + } per_cvt = get_cvt(spec, cvt_idx); /* Claim converter */ per_cvt->assigned = 1; + + set_bit(pcm_idx, &spec->pcm_in_use); + per_pin = get_pin(spec, pin_idx); per_pin->cvt_nid = per_cvt->cvt_nid; hinfo->nid = per_cvt->cvt_nid; @@ -1486,7 +1668,7 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo, if (is_haswell_plus(codec) || is_valleyview_plus(codec)) intel_not_share_assigned_cvt(codec, per_pin->pin_nid, mux_idx); - snd_hda_spdif_ctls_assign(codec, pin_idx, per_cvt->cvt_nid); + snd_hda_spdif_ctls_assign(codec, pcm_idx, per_cvt->cvt_nid); /* Initially set the converter's capabilities */ hinfo->channels_min = per_cvt->channels_min; @@ -1495,6 +1677,7 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo, hinfo->formats = per_cvt->formats; hinfo->maxbps = per_cvt->maxbps; + eld = &per_pin->sink_eld; /* Restrict capabilities by ELD if this isn't disabled */ if (!static_hdmi_pcm && eld->eld_valid) { snd_hdmi_eld_update_pcm_info(&eld->info, hinfo); @@ -1502,11 +1685,13 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo, !hinfo->rates || !hinfo->formats) { per_cvt->assigned = 0; hinfo->nid = 0; - snd_hda_spdif_ctls_unassign(codec, pin_idx); + snd_hda_spdif_ctls_unassign(codec, pcm_idx); + mutex_unlock(&spec->pcm_lock); return -ENODEV; } } + mutex_unlock(&spec->pcm_lock); /* Store the updated parameters */ runtime->hw.channels_min = hinfo->channels_min; runtime->hw.channels_max = hinfo->channels_max; @@ -1541,6 +1726,125 @@ static int hdmi_read_pin_conn(struct hda_codec *codec, int pin_idx) return 0; } +static int hdmi_find_pcm_slot(struct hdmi_spec *spec, + struct hdmi_spec_per_pin *per_pin) +{ + int i; + + /* try the prefer PCM */ + if (!test_bit(per_pin->pin_nid_idx, &spec->pcm_bitmap)) + return per_pin->pin_nid_idx; + + /* have a second try; check the "reserved area" over num_pins */ + for (i = spec->num_pins; i < spec->pcm_used; i++) { + if (!test_bit(i, &spec->pcm_bitmap)) + return i; + } + + /* the last try; check the empty slots in pins */ + for (i = 0; i < spec->num_pins; i++) { + if (!test_bit(i, &spec->pcm_bitmap)) + return i; + } + return -EBUSY; +} + +static void hdmi_attach_hda_pcm(struct hdmi_spec *spec, + struct hdmi_spec_per_pin *per_pin) +{ + int idx; + + /* pcm already be attached to the pin */ + if (per_pin->pcm) + return; + idx = hdmi_find_pcm_slot(spec, per_pin); + if (idx == -EBUSY) + return; + per_pin->pcm_idx = idx; + per_pin->pcm = get_hdmi_pcm(spec, idx); + set_bit(idx, &spec->pcm_bitmap); +} + +static void hdmi_detach_hda_pcm(struct hdmi_spec *spec, + struct hdmi_spec_per_pin *per_pin) +{ + int idx; + + /* pcm already be detached from the pin */ + if (!per_pin->pcm) + return; + idx = per_pin->pcm_idx; + per_pin->pcm_idx = -1; + per_pin->pcm = NULL; + if (idx >= 0 && idx < spec->pcm_used) + clear_bit(idx, &spec->pcm_bitmap); +} + +static int hdmi_get_pin_cvt_mux(struct hdmi_spec *spec, + struct hdmi_spec_per_pin *per_pin, hda_nid_t cvt_nid) +{ + int mux_idx; + + for (mux_idx = 0; mux_idx < per_pin->num_mux_nids; mux_idx++) + if (per_pin->mux_nids[mux_idx] == cvt_nid) + break; + return mux_idx; +} + +static bool check_non_pcm_per_cvt(struct hda_codec *codec, hda_nid_t cvt_nid); + +static void hdmi_pcm_setup_pin(struct hdmi_spec *spec, + struct hdmi_spec_per_pin *per_pin) +{ + struct hda_codec *codec = per_pin->codec; + struct hda_pcm *pcm; + struct hda_pcm_stream *hinfo; + struct snd_pcm_substream *substream; + int mux_idx; + bool non_pcm; + + if (per_pin->pcm_idx >= 0 && per_pin->pcm_idx < spec->pcm_used) + pcm = get_pcm_rec(spec, per_pin->pcm_idx); + else + return; + if (!test_bit(per_pin->pcm_idx, &spec->pcm_in_use)) + return; + + /* hdmi audio only uses playback and one substream */ + hinfo = pcm->stream; + substream = pcm->pcm->streams[0].substream; + + per_pin->cvt_nid = hinfo->nid; + + mux_idx = hdmi_get_pin_cvt_mux(spec, per_pin, hinfo->nid); + if (mux_idx < per_pin->num_mux_nids) + snd_hda_codec_write_cache(codec, per_pin->pin_nid, 0, + AC_VERB_SET_CONNECT_SEL, + mux_idx); + snd_hda_spdif_ctls_assign(codec, per_pin->pcm_idx, hinfo->nid); + + non_pcm = check_non_pcm_per_cvt(codec, hinfo->nid); + if (substream->runtime) + per_pin->channels = substream->runtime->channels; + per_pin->setup = true; + per_pin->mux_idx = mux_idx; + + hdmi_setup_audio_infoframe(codec, per_pin, non_pcm); +} + +static void hdmi_pcm_reset_pin(struct hdmi_spec *spec, + struct hdmi_spec_per_pin *per_pin) +{ + if (per_pin->pcm_idx >= 0 && per_pin->pcm_idx < spec->pcm_used) + snd_hda_spdif_ctls_unassign(per_pin->codec, per_pin->pcm_idx); + + per_pin->chmap_set = false; + memset(per_pin->chmap, 0, sizeof(per_pin->chmap)); + + per_pin->setup = false; + per_pin->channels = 0; +} + /* update per_pin ELD from the given new ELD; * setup info frame and notification accordingly */ @@ -1549,8 +1853,27 @@ static void update_eld(struct hda_codec *codec, struct hdmi_eld *eld) { struct hdmi_eld *pin_eld = &per_pin->sink_eld; + struct hdmi_spec *spec = codec->spec; bool old_eld_valid = pin_eld->eld_valid; bool eld_changed; + int pcm_idx = -1; + + /* for monitor disconnection, save pcm_idx firstly */ + pcm_idx = per_pin->pcm_idx; + if (spec->dyn_pcm_assign) { + if (eld->eld_valid) { + hdmi_attach_hda_pcm(spec, per_pin); + hdmi_pcm_setup_pin(spec, per_pin); + } else { + hdmi_pcm_reset_pin(spec, per_pin); + hdmi_detach_hda_pcm(spec, per_pin); + } + } + /* if pcm_idx == -1, it means this is in monitor connection event + * we can get the correct pcm_idx now. + */ + if (pcm_idx == -1) + pcm_idx = per_pin->pcm_idx; if (eld->eld_valid) snd_hdmi_show_eld(codec, &eld->info); @@ -1584,11 +1907,11 @@ static void update_eld(struct hda_codec *codec, hdmi_setup_audio_infoframe(codec, per_pin, per_pin->non_pcm); } - if (eld_changed) + if (eld_changed && pcm_idx >= 0) snd_ctl_notify(codec->card, SNDRV_CTL_EVENT_MASK_VALUE | SNDRV_CTL_EVENT_MASK_INFO, - &per_pin->eld_ctl->id); + &get_hdmi_pcm(spec, pcm_idx)->eld_ctl->id); } /* update ELD and jack state via HD-audio verbs */ @@ -1656,12 +1979,36 @@ static bool hdmi_present_sense_via_verbs(struct hdmi_spec_per_pin *per_pin, return ret; } +static struct snd_jack *pin_idx_to_jack(struct hda_codec *codec, + struct hdmi_spec_per_pin *per_pin) +{ + struct hdmi_spec *spec = codec->spec; + struct snd_jack *jack = NULL; + struct hda_jack_tbl *jack_tbl; + + /* if !dyn_pcm_assign, get jack from hda_jack_tbl + * in !dyn_pcm_assign case, spec->pcm_rec[].jack is not + * NULL even after snd_hda_jack_tbl_clear() is called to + * free snd_jack. This may cause access invalid memory + * when calling snd_jack_report + */ + if (per_pin->pcm_idx >= 0 && spec->dyn_pcm_assign) + jack = spec->pcm_rec[per_pin->pcm_idx].jack; + else if (!spec->dyn_pcm_assign) { + jack_tbl = snd_hda_jack_tbl_get(codec, per_pin->pin_nid); + if (jack_tbl) + jack = jack_tbl->jack; + } + return jack; +} + /* update ELD and jack state via audio component */ static void sync_eld_via_acomp(struct hda_codec *codec, struct hdmi_spec_per_pin *per_pin) { struct hdmi_spec *spec = codec->spec; struct hdmi_eld *eld = &spec->temp_eld; + struct snd_jack *jack = NULL; int size; mutex_lock(&per_pin->lock); @@ -1685,8 +2032,16 @@ static void sync_eld_via_acomp(struct hda_codec *codec, eld->eld_size = 0; } + /* pcm_idx >=0 before update_eld() means it is in monitor + * disconnected event. Jack must be fetched before update_eld() + */ + jack = pin_idx_to_jack(codec, per_pin); update_eld(codec, per_pin, eld); - snd_jack_report(per_pin->acomp_jack, + if (jack == NULL) + jack = pin_idx_to_jack(codec, per_pin); + if (jack == NULL) + goto unlock; + snd_jack_report(jack, eld->monitor_present ? SND_JACK_AVOUT : 0); unlock: mutex_unlock(&per_pin->lock); @@ -1695,13 +2050,19 @@ static void sync_eld_via_acomp(struct hda_codec *codec, static bool hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll) { struct hda_codec *codec = per_pin->codec; + struct hdmi_spec *spec = codec->spec; + int ret; + mutex_lock(&spec->pcm_lock); if (codec_has_acomp(codec)) { sync_eld_via_acomp(codec, per_pin); - return false; /* don't call snd_hda_jack_report_sync() */ + ret = false; /* don't call snd_hda_jack_report_sync() */ } else { - return hdmi_present_sense_via_verbs(per_pin, repoll); + ret = hdmi_present_sense_via_verbs(per_pin, repoll); } + mutex_unlock(&spec->pcm_lock); + + return ret; } static void hdmi_repoll_eld(struct work_struct *work) @@ -1745,6 +2106,13 @@ static int hdmi_add_pin(struct hda_codec *codec, hda_nid_t pin_nid) per_pin->pin_nid = pin_nid; per_pin->non_pcm = false; + if (spec->dyn_pcm_assign) + per_pin->pcm_idx = -1; + else { + per_pin->pcm = get_hdmi_pcm(spec, pin_idx); + per_pin->pcm_idx = pin_idx; + } + per_pin->pin_nid_idx = pin_idx; err = hdmi_read_pin_conn(codec, pin_idx); if (err < 0) @@ -1851,13 +2219,34 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo, { hda_nid_t cvt_nid = hinfo->nid; struct hdmi_spec *spec = codec->spec; - int pin_idx = hinfo_to_pin_index(codec, hinfo); - struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx); - hda_nid_t pin_nid = per_pin->pin_nid; + int pin_idx; + struct hdmi_spec_per_pin *per_pin; + hda_nid_t pin_nid; struct snd_pcm_runtime *runtime = substream->runtime; bool non_pcm; int pinctl; + int err; + + mutex_lock(&spec->pcm_lock); + pin_idx = hinfo_to_pin_index(codec, hinfo); + if (spec->dyn_pcm_assign && pin_idx < 0) { + /* when dyn_pcm_assign and pcm is not bound to a pin + * skip pin setup and return 0 to make audio playback + * be ongoing + */ + intel_not_share_assigned_cvt_nid(codec, 0, cvt_nid); + snd_hda_codec_setup_stream(codec, cvt_nid, + stream_tag, 0, format); + mutex_unlock(&spec->pcm_lock); + return 0; + } + if (snd_BUG_ON(pin_idx < 0)) { + mutex_unlock(&spec->pcm_lock); + return -EINVAL; + } + 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 @@ -1882,7 +2271,6 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo, hdmi_setup_audio_infoframe(codec, per_pin, non_pcm); mutex_unlock(&per_pin->lock); - if (spec->dyn_pin_out) { pinctl = snd_hda_codec_read(codec, pin_nid, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0); @@ -1891,7 +2279,10 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo, pinctl | PIN_OUT); } - return spec->ops.setup_stream(codec, cvt_nid, pin_nid, stream_tag, format); + err = spec->ops.setup_stream(codec, cvt_nid, pin_nid, + stream_tag, format); + mutex_unlock(&spec->pcm_lock); + return err; } static int generic_hdmi_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, @@ -1907,12 +2298,15 @@ static int hdmi_pcm_close(struct hda_pcm_stream *hinfo, struct snd_pcm_substream *substream) { struct hdmi_spec *spec = codec->spec; - int cvt_idx, pin_idx; + int cvt_idx, pin_idx, pcm_idx; struct hdmi_spec_per_cvt *per_cvt; struct hdmi_spec_per_pin *per_pin; int pinctl; if (hinfo->nid) { + pcm_idx = hinfo_to_pcm_index(codec, hinfo); + if (snd_BUG_ON(pcm_idx < 0)) + return -EINVAL; cvt_idx = cvt_nid_to_cvt_index(codec, hinfo->nid); if (snd_BUG_ON(cvt_idx < 0)) return -EINVAL; @@ -1922,9 +2316,19 @@ static int hdmi_pcm_close(struct hda_pcm_stream *hinfo, per_cvt->assigned = 0; hinfo->nid = 0; + mutex_lock(&spec->pcm_lock); + snd_hda_spdif_ctls_unassign(codec, pcm_idx); + clear_bit(pcm_idx, &spec->pcm_in_use); pin_idx = hinfo_to_pin_index(codec, hinfo); - if (snd_BUG_ON(pin_idx < 0)) + if (spec->dyn_pcm_assign && pin_idx < 0) { + mutex_unlock(&spec->pcm_lock); + return 0; + } + + if (snd_BUG_ON(pin_idx < 0)) { + mutex_unlock(&spec->pcm_lock); return -EINVAL; + } per_pin = get_pin(spec, pin_idx); if (spec->dyn_pin_out) { @@ -1935,8 +2339,6 @@ static int hdmi_pcm_close(struct hda_pcm_stream *hinfo, pinctl & ~PIN_OUT); } - snd_hda_spdif_ctls_unassign(codec, pin_idx); - mutex_lock(&per_pin->lock); per_pin->chmap_set = false; memset(per_pin->chmap, 0, sizeof(per_pin->chmap)); @@ -1944,6 +2346,7 @@ static int hdmi_pcm_close(struct hda_pcm_stream *hinfo, per_pin->setup = false; per_pin->channels = 0; mutex_unlock(&per_pin->lock); + mutex_unlock(&spec->pcm_lock); } return 0; @@ -2055,10 +2458,16 @@ static int hdmi_chmap_ctl_get(struct snd_kcontrol *kcontrol, struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); struct hda_codec *codec = info->private_data; struct hdmi_spec *spec = codec->spec; - int pin_idx = kcontrol->private_value; - struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx); + int pcm_idx = kcontrol->private_value; + struct hdmi_spec_per_pin *per_pin = pcm_idx_to_pin(spec, pcm_idx); int i; + if (!per_pin) { + for (i = 0; i < spec->channels_max; i++) + ucontrol->value.integer.value[i] = 0; + return 0; + } + for (i = 0; i < ARRAY_SIZE(per_pin->chmap); i++) ucontrol->value.integer.value[i] = per_pin->chmap[i]; return 0; @@ -2070,13 +2479,19 @@ static int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol, struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); struct hda_codec *codec = info->private_data; struct hdmi_spec *spec = codec->spec; - int pin_idx = kcontrol->private_value; - struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx); + int pcm_idx = kcontrol->private_value; + struct hdmi_spec_per_pin *per_pin = pcm_idx_to_pin(spec, pcm_idx); unsigned int ctl_idx; struct snd_pcm_substream *substream; unsigned char chmap[8]; int i, err, ca, prepared = 0; + /* No monitor is connected in dyn_pcm_assign. + * It's invalid to setup the chmap + */ + if (!per_pin) + return 0; + ctl_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); substream = snd_pcm_chmap_substream(info, ctl_idx); if (!substream || !substream->runtime) @@ -2126,7 +2541,9 @@ static int generic_hdmi_build_pcms(struct hda_codec *codec) info = snd_hda_codec_pcm_new(codec, "HDMI %d", pin_idx); if (!info) return -ENOMEM; - spec->pcm_rec[pin_idx] = info; + + spec->pcm_rec[pin_idx].pcm = info; + spec->pcm_used++; info->pcm_type = HDA_PCM_TYPE_HDMI; info->own_chmap = true; @@ -2139,15 +2556,16 @@ static int generic_hdmi_build_pcms(struct hda_codec *codec) return 0; } -static void free_acomp_jack_priv(struct snd_jack *jack) +static void free_hdmi_jack_priv(struct snd_jack *jack) { - struct hdmi_spec_per_pin *per_pin = jack->private_data; + struct hdmi_pcm *pcm = jack->private_data; - per_pin->acomp_jack = NULL; + pcm->jack = NULL; } -static int add_acomp_jack_kctl(struct hda_codec *codec, - struct hdmi_spec_per_pin *per_pin, +static int add_hdmi_jack_kctl(struct hda_codec *codec, + struct hdmi_spec *spec, + int pcm_idx, const char *name) { struct snd_jack *jack; @@ -2157,77 +2575,110 @@ static int add_acomp_jack_kctl(struct hda_codec *codec, true, false); if (err < 0) return err; - per_pin->acomp_jack = jack; - jack->private_data = per_pin; - jack->private_free = free_acomp_jack_priv; + + spec->pcm_rec[pcm_idx].jack = jack; + jack->private_data = &spec->pcm_rec[pcm_idx]; + jack->private_free = free_hdmi_jack_priv; return 0; } -static int generic_hdmi_build_jack(struct hda_codec *codec, int pin_idx) +static int generic_hdmi_build_jack(struct hda_codec *codec, int pcm_idx) { char hdmi_str[32] = "HDMI/DP"; struct hdmi_spec *spec = codec->spec; - struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx); - int pcmdev = get_pcm_rec(spec, pin_idx)->device; + struct hdmi_spec_per_pin *per_pin; + struct hda_jack_tbl *jack; + int pcmdev = get_pcm_rec(spec, pcm_idx)->device; bool phantom_jack; + int ret; if (pcmdev > 0) sprintf(hdmi_str + strlen(hdmi_str), ",pcm=%d", pcmdev); - if (codec_has_acomp(codec)) - return add_acomp_jack_kctl(codec, per_pin, hdmi_str); + + if (spec->dyn_pcm_assign) + return add_hdmi_jack_kctl(codec, spec, pcm_idx, hdmi_str); + + /* for !dyn_pcm_assign, we still use hda_jack for compatibility */ + /* if !dyn_pcm_assign, it must be non-MST mode. + * This means pcms and pins are statically mapped. + * And pcm_idx is pin_idx. + */ + per_pin = get_pin(spec, pcm_idx); phantom_jack = !is_jack_detectable(codec, per_pin->pin_nid); if (phantom_jack) strncat(hdmi_str, " Phantom", sizeof(hdmi_str) - strlen(hdmi_str) - 1); - - return snd_hda_jack_add_kctl(codec, per_pin->pin_nid, hdmi_str, - phantom_jack); + ret = snd_hda_jack_add_kctl(codec, per_pin->pin_nid, hdmi_str, + phantom_jack); + if (ret < 0) + return ret; + jack = snd_hda_jack_tbl_get(codec, per_pin->pin_nid); + if (jack == NULL) + return 0; + /* assign jack->jack to pcm_rec[].jack to + * align with dyn_pcm_assign mode + */ + spec->pcm_rec[pcm_idx].jack = jack->jack; + return 0; } static int generic_hdmi_build_controls(struct hda_codec *codec) { struct hdmi_spec *spec = codec->spec; int err; - int pin_idx; + int pin_idx, pcm_idx; - for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { - struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx); - err = generic_hdmi_build_jack(codec, pin_idx); + for (pcm_idx = 0; pcm_idx < spec->pcm_used; pcm_idx++) { + err = generic_hdmi_build_jack(codec, pcm_idx); if (err < 0) return err; - err = snd_hda_create_dig_out_ctls(codec, + /* create the spdif for each pcm + * pin will be bound when monitor is connected + */ + if (spec->dyn_pcm_assign) + err = snd_hda_create_dig_out_ctls(codec, + 0, spec->cvt_nids[0], + HDA_PCM_TYPE_HDMI); + else { + struct hdmi_spec_per_pin *per_pin = + get_pin(spec, pcm_idx); + err = snd_hda_create_dig_out_ctls(codec, per_pin->pin_nid, per_pin->mux_nids[0], HDA_PCM_TYPE_HDMI); + } if (err < 0) return err; - snd_hda_spdif_ctls_unassign(codec, pin_idx); + snd_hda_spdif_ctls_unassign(codec, pcm_idx); /* add control for ELD Bytes */ - err = hdmi_create_eld_ctl(codec, pin_idx, - get_pcm_rec(spec, pin_idx)->device); - + err = hdmi_create_eld_ctl(codec, pcm_idx, + get_pcm_rec(spec, pcm_idx)->device); if (err < 0) return err; + } + + for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { + struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx); hdmi_present_sense(per_pin, 0); } /* add channel maps */ - for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { + for (pcm_idx = 0; pcm_idx < spec->pcm_used; pcm_idx++) { struct hda_pcm *pcm; struct snd_pcm_chmap *chmap; struct snd_kcontrol *kctl; int i; - pcm = spec->pcm_rec[pin_idx]; + pcm = get_pcm_rec(spec, pcm_idx); if (!pcm || !pcm->pcm) break; err = snd_pcm_add_chmap_ctls(pcm->pcm, SNDRV_PCM_STREAM_PLAYBACK, - NULL, 0, pin_idx, &chmap); + NULL, 0, pcm_idx, &chmap); if (err < 0) return err; /* override handlers */ @@ -2293,18 +2744,25 @@ static void hdmi_array_free(struct hdmi_spec *spec) static void generic_hdmi_free(struct hda_codec *codec) { struct hdmi_spec *spec = codec->spec; - int pin_idx; + int pin_idx, pcm_idx; if (codec_has_acomp(codec)) snd_hdac_i915_register_notifier(NULL); for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx); - cancel_delayed_work_sync(&per_pin->work); eld_proc_free(per_pin); - if (per_pin->acomp_jack) - snd_device_free(codec->card, per_pin->acomp_jack); + } + + for (pcm_idx = 0; pcm_idx < spec->pcm_used; pcm_idx++) { + if (spec->pcm_rec[pcm_idx].jack == NULL) + continue; + if (spec->dyn_pcm_assign) + snd_device_free(codec->card, + spec->pcm_rec[pcm_idx].jack); + else + spec->pcm_rec[pcm_idx].jack = NULL; } if (spec->i915_bound) @@ -2453,6 +2911,7 @@ static int patch_generic_hdmi(struct hda_codec *codec) return -ENOMEM; spec->ops = generic_standard_hdmi_ops; + mutex_init(&spec->pcm_lock); codec->spec = spec; hdmi_array_init(spec, 4); @@ -2510,6 +2969,7 @@ static int patch_generic_hdmi(struct hda_codec *codec) snd_hdac_i915_register_notifier(&spec->i915_audio_ops); } + WARN_ON(spec->dyn_pcm_assign && !codec_has_acomp(codec)); return 0; } @@ -2532,7 +2992,7 @@ static int simple_playback_build_pcms(struct hda_codec *codec) info = snd_hda_codec_pcm_new(codec, "HDMI 0"); if (!info) return -ENOMEM; - spec->pcm_rec[0] = info; + spec->pcm_rec[0].pcm = info; info->pcm_type = HDA_PCM_TYPE_HDMI; pstr = &info->stream[SNDRV_PCM_STREAM_PLAYBACK]; *pstr = spec->pcm_playback; diff --git a/sound/pci/hda/thinkpad_helper.c b/sound/pci/hda/thinkpad_helper.c index 0a4ad5feb82e..59ab6cee1ad8 100644 --- a/sound/pci/hda/thinkpad_helper.c +++ b/sound/pci/hda/thinkpad_helper.c @@ -10,23 +10,10 @@ static int (*led_set_func)(int, bool); static void (*old_vmaster_hook)(void *, int); -static acpi_status acpi_check_cb(acpi_handle handle, u32 lvl, void *context, - void **rv) -{ - bool *found = context; - *found = true; - return AE_OK; -} - static bool is_thinkpad(struct hda_codec *codec) { - bool found = false; - if (codec->core.subsystem_id >> 16 != 0x17aa) - return false; - if (ACPI_SUCCESS(acpi_get_devices("LEN0068", acpi_check_cb, &found, NULL)) && found) - return true; - found = false; - return ACPI_SUCCESS(acpi_get_devices("IBM0068", acpi_check_cb, &found, NULL)) && found; + return (codec->core.subsystem_id >> 16 == 0x17aa) && + (acpi_dev_present("LEN0068") || acpi_dev_present("IBM0068")); } static void update_tpacpi_mute_led(void *private_data, int enabled) diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index 7ea66ee3653f..182d92efc7c8 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig @@ -6,7 +6,7 @@ menuconfig SND_SOC tristate "ALSA for SoC audio support" select SND_PCM select AC97_BUS if SND_SOC_AC97_BUS - select SND_JACK if INPUT=y || INPUT=SND + select SND_JACK select REGMAP_I2C if I2C select REGMAP_SPI if SPI_MASTER ---help--- diff --git a/sound/usb/card.c b/sound/usb/card.c index 1f09d9591276..3fc63583a537 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -82,6 +82,7 @@ static int pid[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = -1 }; static int device_setup[SNDRV_CARDS]; /* device parameter for this card */ static bool ignore_ctl_error; static bool autoclock = true; +static char *quirk_alias[SNDRV_CARDS]; module_param_array(index, int, NULL, 0444); MODULE_PARM_DESC(index, "Index value for the USB audio adapter."); @@ -100,6 +101,8 @@ MODULE_PARM_DESC(ignore_ctl_error, "Ignore errors from USB controller for mixer interfaces."); module_param(autoclock, bool, 0444); MODULE_PARM_DESC(autoclock, "Enable auto-clock selection for UAC2 devices (default: yes)."); +module_param_array(quirk_alias, charp, NULL, 0444); +MODULE_PARM_DESC(quirk_alias, "Quirk aliases, e.g. 0123abcd:5678beef."); /* * we keep the snd_usb_audio_t instances by ourselves for merging @@ -171,8 +174,9 @@ static int snd_usb_create_stream(struct snd_usb_audio *chip, int ctrlif, int int if ((altsd->bInterfaceClass == USB_CLASS_AUDIO || altsd->bInterfaceClass == USB_CLASS_VENDOR_SPEC) && altsd->bInterfaceSubClass == USB_SUBCLASS_MIDISTREAMING) { - int err = snd_usbmidi_create(chip->card, iface, - &chip->midi_list, NULL); + int err = __snd_usbmidi_create(chip->card, iface, + &chip->midi_list, NULL, + chip->usb_id); if (err < 0) { dev_err(&dev->dev, "%u:%d: cannot create sequencer device\n", @@ -311,6 +315,7 @@ static int snd_usb_audio_free(struct snd_usb_audio *chip) snd_usb_endpoint_free(ep); mutex_destroy(&chip->mutex); + dev_set_drvdata(&chip->dev->dev, NULL); kfree(chip); return 0; } @@ -455,6 +460,48 @@ static int snd_usb_audio_create(struct usb_interface *intf, return 0; } +/* look for a matching quirk alias id */ +static bool get_alias_id(struct usb_device *dev, unsigned int *id) +{ + int i; + unsigned int src, dst; + + for (i = 0; i < ARRAY_SIZE(quirk_alias); i++) { + if (!quirk_alias[i] || + sscanf(quirk_alias[i], "%x:%x", &src, &dst) != 2 || + src != *id) + continue; + dev_info(&dev->dev, + "device (%04x:%04x): applying quirk alias %04x:%04x\n", + USB_ID_VENDOR(*id), USB_ID_PRODUCT(*id), + USB_ID_VENDOR(dst), USB_ID_PRODUCT(dst)); + *id = dst; + return true; + } + + return false; +} + +static struct usb_device_id usb_audio_ids[]; /* defined below */ + +/* look for the corresponding quirk */ +static const struct snd_usb_audio_quirk * +get_alias_quirk(struct usb_device *dev, unsigned int id) +{ + const struct usb_device_id *p; + + for (p = usb_audio_ids; p->match_flags; p++) { + /* FIXME: this checks only vendor:product pair in the list */ + if ((p->match_flags & USB_DEVICE_ID_MATCH_DEVICE) == + USB_DEVICE_ID_MATCH_DEVICE && + p->idVendor == USB_ID_VENDOR(id) && + p->idProduct == USB_ID_PRODUCT(id)) + return (const struct snd_usb_audio_quirk *)p->driver_info; + } + + return NULL; +} + /* * probe the active usb device * @@ -481,10 +528,12 @@ static int usb_audio_probe(struct usb_interface *intf, ifnum = get_iface_desc(alts)->bInterfaceNumber; id = USB_ID(le16_to_cpu(dev->descriptor.idVendor), le16_to_cpu(dev->descriptor.idProduct)); + if (get_alias_id(dev, &id)) + quirk = get_alias_quirk(dev, id); if (quirk && quirk->ifnum >= 0 && ifnum != quirk->ifnum) return -ENXIO; - err = snd_usb_apply_boot_quirk(dev, intf, quirk); + err = snd_usb_apply_boot_quirk(dev, intf, quirk, id); if (err < 0) return err; @@ -503,6 +552,7 @@ static int usb_audio_probe(struct usb_interface *intf, goto __error; } chip = usb_chip[i]; + dev_set_drvdata(&dev->dev, chip); atomic_inc(&chip->active); /* avoid autopm */ break; } diff --git a/sound/usb/midi.c b/sound/usb/midi.c index 007cf5831121..47de8af42f16 100644 --- a/sound/usb/midi.c +++ b/sound/usb/midi.c @@ -2320,10 +2320,11 @@ EXPORT_SYMBOL(snd_usbmidi_resume); /* * Creates and registers everything needed for a MIDI streaming interface. */ -int snd_usbmidi_create(struct snd_card *card, - struct usb_interface *iface, - struct list_head *midi_list, - const struct snd_usb_audio_quirk *quirk) +int __snd_usbmidi_create(struct snd_card *card, + struct usb_interface *iface, + struct list_head *midi_list, + const struct snd_usb_audio_quirk *quirk, + unsigned int usb_id) { struct snd_usb_midi *umidi; struct snd_usb_midi_endpoint_info endpoints[MIDI_MAX_ENDPOINTS]; @@ -2341,8 +2342,10 @@ int snd_usbmidi_create(struct snd_card *card, spin_lock_init(&umidi->disc_lock); init_rwsem(&umidi->disc_rwsem); mutex_init(&umidi->mutex); - umidi->usb_id = USB_ID(le16_to_cpu(umidi->dev->descriptor.idVendor), + if (!usb_id) + usb_id = USB_ID(le16_to_cpu(umidi->dev->descriptor.idVendor), le16_to_cpu(umidi->dev->descriptor.idProduct)); + umidi->usb_id = usb_id; setup_timer(&umidi->error_timer, snd_usbmidi_error_timer, (unsigned long)umidi); @@ -2463,4 +2466,4 @@ int snd_usbmidi_create(struct snd_card *card, list_add_tail(&umidi->list, midi_list); return 0; } -EXPORT_SYMBOL(snd_usbmidi_create); +EXPORT_SYMBOL(__snd_usbmidi_create); diff --git a/sound/usb/midi.h b/sound/usb/midi.h index ad8a3211f8e7..5e25a3fd6c1d 100644 --- a/sound/usb/midi.h +++ b/sound/usb/midi.h @@ -39,10 +39,20 @@ struct snd_usb_midi_endpoint_info { /* for QUIRK_MIDI_AKAI, data is NULL */ -int snd_usbmidi_create(struct snd_card *card, +int __snd_usbmidi_create(struct snd_card *card, + struct usb_interface *iface, + struct list_head *midi_list, + const struct snd_usb_audio_quirk *quirk, + unsigned int usb_id); + +static inline int snd_usbmidi_create(struct snd_card *card, struct usb_interface *iface, struct list_head *midi_list, - const struct snd_usb_audio_quirk *quirk); + const struct snd_usb_audio_quirk *quirk) +{ + return __snd_usbmidi_create(card, iface, midi_list, quirk, 0); +} + void snd_usbmidi_input_stop(struct list_head *p); void snd_usbmidi_input_start(struct list_head *p); void snd_usbmidi_disconnect(struct list_head *p); diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index c458d60d5030..5b03296a5bd9 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -446,8 +446,9 @@ static int create_uaxx_quirk(struct snd_usb_audio *chip, const struct snd_usb_audio_quirk *quirk = chip->usb_id == USB_ID(0x0582, 0x002b) ? &ua700_quirk : &uaxx_quirk; - return snd_usbmidi_create(chip->card, iface, - &chip->midi_list, quirk); + return __snd_usbmidi_create(chip->card, iface, + &chip->midi_list, quirk, + chip->usb_id); } if (altsd->bNumEndpoints != 1) @@ -974,11 +975,9 @@ int snd_usb_apply_interface_quirk(struct snd_usb_audio *chip, int snd_usb_apply_boot_quirk(struct usb_device *dev, struct usb_interface *intf, - const struct snd_usb_audio_quirk *quirk) + const struct snd_usb_audio_quirk *quirk, + unsigned int id) { - u32 id = USB_ID(le16_to_cpu(dev->descriptor.idVendor), - le16_to_cpu(dev->descriptor.idProduct)); - switch (id) { case USB_ID(0x041e, 0x3000): /* SB Extigy needs special boot-up sequence */ @@ -1184,7 +1183,7 @@ void snd_usb_endpoint_start_quirk(struct snd_usb_endpoint *ep) * "Playback Design" products send bogus feedback data at the start * of the stream. Ignore them. */ - if ((le16_to_cpu(ep->chip->dev->descriptor.idVendor) == 0x23ba) && + if (USB_ID_VENDOR(ep->chip->usb_id) == 0x23ba && ep->type == SND_USB_ENDPOINT_TYPE_SYNC) ep->skip_packets = 4; @@ -1203,11 +1202,15 @@ void snd_usb_endpoint_start_quirk(struct snd_usb_endpoint *ep) void snd_usb_set_interface_quirk(struct usb_device *dev) { + struct snd_usb_audio *chip = dev_get_drvdata(&dev->dev); + + if (!chip) + return; /* * "Playback Design" products need a 50ms delay after setting the * USB interface. */ - switch (le16_to_cpu(dev->descriptor.idVendor)) { + switch (USB_ID_VENDOR(chip->usb_id)) { case 0x23ba: /* Playback Design */ case 0x0644: /* TEAC Corp. */ mdelay(50); @@ -1215,15 +1218,20 @@ void snd_usb_set_interface_quirk(struct usb_device *dev) } } +/* quirk applied after snd_usb_ctl_msg(); not applied during boot quirks */ void snd_usb_ctl_msg_quirk(struct usb_device *dev, unsigned int pipe, __u8 request, __u8 requesttype, __u16 value, __u16 index, void *data, __u16 size) { + struct snd_usb_audio *chip = dev_get_drvdata(&dev->dev); + + if (!chip) + return; /* * "Playback Design" products need a 20ms delay after each * class compliant request */ - if ((le16_to_cpu(dev->descriptor.idVendor) == 0x23ba) && + if (USB_ID_VENDOR(chip->usb_id) == 0x23ba && (requesttype & USB_TYPE_MASK) == USB_TYPE_CLASS) mdelay(20); @@ -1231,23 +1239,21 @@ void snd_usb_ctl_msg_quirk(struct usb_device *dev, unsigned int pipe, * "TEAC Corp." products need a 20ms delay after each * class compliant request */ - if ((le16_to_cpu(dev->descriptor.idVendor) == 0x0644) && + if (USB_ID_VENDOR(chip->usb_id) == 0x0644 && (requesttype & USB_TYPE_MASK) == USB_TYPE_CLASS) mdelay(20); /* Marantz/Denon devices with USB DAC functionality need a delay * after each class compliant request */ - if (is_marantz_denon_dac(USB_ID(le16_to_cpu(dev->descriptor.idVendor), - le16_to_cpu(dev->descriptor.idProduct))) + if (is_marantz_denon_dac(chip->usb_id) && (requesttype & USB_TYPE_MASK) == USB_TYPE_CLASS) mdelay(20); /* Zoom R16/24 needs a tiny delay here, otherwise requests like * get/set frequency return as failed despite actually succeeding. */ - if ((le16_to_cpu(dev->descriptor.idVendor) == 0x1686) && - (le16_to_cpu(dev->descriptor.idProduct) == 0x00dd) && + if (chip->usb_id == USB_ID(0x1686, 0x00dd) && (requesttype & USB_TYPE_MASK) == USB_TYPE_CLASS) mdelay(1); } @@ -1264,7 +1270,7 @@ u64 snd_usb_interface_dsd_format_quirks(struct snd_usb_audio *chip, unsigned int sample_bytes) { /* Playback Designs */ - if (le16_to_cpu(chip->dev->descriptor.idVendor) == 0x23ba) { + if (USB_ID_VENDOR(chip->usb_id) == 0x23ba) { switch (fp->altsetting) { case 1: fp->dsd_dop = true; diff --git a/sound/usb/quirks.h b/sound/usb/quirks.h index 2cd71ed1201f..192ff5ce9452 100644 --- a/sound/usb/quirks.h +++ b/sound/usb/quirks.h @@ -16,7 +16,8 @@ int snd_usb_apply_interface_quirk(struct snd_usb_audio *chip, int snd_usb_apply_boot_quirk(struct usb_device *dev, struct usb_interface *intf, - const struct snd_usb_audio_quirk *quirk); + const struct snd_usb_audio_quirk *quirk, + unsigned int usb_id); void snd_usb_set_format_quirk(struct snd_usb_substream *subs, struct audioformat *fmt); |