diff options
Diffstat (limited to 'sound')
43 files changed, 2420 insertions, 2502 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/compress_offload.c b/sound/core/compress_offload.c index 7fac3cae8abd..a9933c07a6bf 100644 --- a/sound/core/compress_offload.c +++ b/sound/core/compress_offload.c @@ -69,11 +69,14 @@ struct snd_compr_file { /* * a note on stream states used: - * we use follwing states in the compressed core + * we use following states in the compressed core * SNDRV_PCM_STATE_OPEN: When stream has been opened. * SNDRV_PCM_STATE_SETUP: When stream has been initialized. This is done by - * calling SNDRV_COMPRESS_SET_PARAMS. running streams will come to this + * calling SNDRV_COMPRESS_SET_PARAMS. Running streams will come to this * state at stop by calling SNDRV_COMPRESS_STOP, or at end of drain. + * SNDRV_PCM_STATE_PREPARED: When a stream has been written to (for + * playback only). User after setting up stream writes the data buffer + * before starting the stream. * SNDRV_PCM_STATE_RUNNING: When stream has been started and is * decoding/encoding and rendering/capturing data. * SNDRV_PCM_STATE_DRAINING: When stream is draining current data. This is done @@ -286,6 +289,7 @@ static ssize_t snd_compr_write(struct file *f, const char __user *buf, mutex_lock(&stream->device->lock); /* write is allowed when stream is running or has been steup */ if (stream->runtime->state != SNDRV_PCM_STATE_SETUP && + stream->runtime->state != SNDRV_PCM_STATE_PREPARED && stream->runtime->state != SNDRV_PCM_STATE_RUNNING) { mutex_unlock(&stream->device->lock); return -EBADFD; @@ -700,7 +704,7 @@ static int snd_compress_wait_for_drain(struct snd_compr_stream *stream) /* * We are called with lock held. So drop the lock while we wait for - * drain complete notfication from the driver + * drain complete notification from the driver * * It is expected that driver will notify the drain completion and then * stream will be moved to SETUP state, even if draining resulted in an @@ -755,7 +759,7 @@ static int snd_compr_next_track(struct snd_compr_stream *stream) if (stream->runtime->state != SNDRV_PCM_STATE_RUNNING) return -EPERM; - /* you can signal next track isf this is intended to be a gapless stream + /* you can signal next track if this is intended to be a gapless stream * and current track metadata is set */ if (stream->metadata_set == false) 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/pcm_lib.c b/sound/core/pcm_lib.c index 6b5a811e01a5..3a9b66c6e09c 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -322,7 +322,7 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, char name[16]; snd_pcm_debug_name(substream, name, sizeof(name)); pcm_err(substream->pcm, - "BUG: %s, pos = %ld, buffer size = %ld, period size = %ld\n", + "invalid position: %s, pos = %ld, buffer size = %ld, period size = %ld\n", name, pos, runtime->buffer_size, runtime->period_size); } diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c index 58e79e02f217..d6d9419d8bac 100644 --- a/sound/core/seq/seq_clientmgr.c +++ b/sound/core/seq/seq_clientmgr.c @@ -364,6 +364,7 @@ static int snd_seq_open(struct inode *inode, struct file *file) /* fill client data */ user->file = file; sprintf(client->name, "Client-%d", c); + client->data.user.owner = get_pid(task_pid(current)); /* make others aware this new client */ snd_seq_system_client_ev_client_start(c); @@ -380,6 +381,7 @@ static int snd_seq_release(struct inode *inode, struct file *file) seq_free_client(client); if (client->data.user.fifo) snd_seq_fifo_delete(&client->data.user.fifo); + put_pid(client->data.user.owner); kfree(client); } @@ -1197,6 +1199,17 @@ static void get_client_info(struct snd_seq_client *cptr, info->event_lost = cptr->event_lost; memcpy(info->event_filter, cptr->event_filter, 32); info->num_ports = cptr->num_ports; + + if (cptr->type == USER_CLIENT) + info->pid = pid_vnr(cptr->data.user.owner); + else + info->pid = -1; + + if (cptr->type == KERNEL_CLIENT) + info->card = cptr->data.kernel.card ? cptr->data.kernel.card->number : -1; + else + info->card = -1; + memset(info->reserved, 0, sizeof(info->reserved)); } @@ -2271,6 +2284,7 @@ int snd_seq_create_kernel_client(struct snd_card *card, int client_index, client->accept_input = 1; client->accept_output = 1; + client->data.kernel.card = card; va_start(args, name_fmt); vsnprintf(client->name, sizeof(client->name), name_fmt, args); diff --git a/sound/core/seq/seq_clientmgr.h b/sound/core/seq/seq_clientmgr.h index 20f0a725ec7d..c6614254ef8a 100644 --- a/sound/core/seq/seq_clientmgr.h +++ b/sound/core/seq/seq_clientmgr.h @@ -33,6 +33,7 @@ struct snd_seq_user_client { struct file *file; /* file struct of client */ /* ... */ + struct pid *owner; /* fifo */ struct snd_seq_fifo *fifo; /* queue for incoming events */ @@ -41,6 +42,7 @@ struct snd_seq_user_client { struct snd_seq_kernel_client { /* ... */ + struct snd_card *card; }; 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..a040617505a7 100644 --- a/sound/firewire/dice/dice-midi.c +++ b/sound/firewire/dice/dice-midi.c @@ -52,10 +52,10 @@ static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up) spin_lock_irqsave(&dice->lock, flags); if (up) - amdtp_am824_midi_trigger(&dice->tx_stream, + amdtp_am824_midi_trigger(&dice->tx_stream[0], substrm->number, substrm); else - amdtp_am824_midi_trigger(&dice->tx_stream, + amdtp_am824_midi_trigger(&dice->tx_stream[0], substrm->number, NULL); spin_unlock_irqrestore(&dice->lock, flags); @@ -69,10 +69,10 @@ static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up) spin_lock_irqsave(&dice->lock, flags); if (up) - amdtp_am824_midi_trigger(&dice->rx_stream, + amdtp_am824_midi_trigger(&dice->rx_stream[0], substrm->number, substrm); else - amdtp_am824_midi_trigger(&dice->rx_stream, + amdtp_am824_midi_trigger(&dice->rx_stream[0], substrm->number, NULL); spin_unlock_irqrestore(&dice->lock, flags); @@ -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..4aa0249826fd 100644 --- a/sound/firewire/dice/dice-pcm.c +++ b/sound/firewire/dice/dice-pcm.c @@ -9,99 +9,46 @@ #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, + enum amdtp_stream_direction dir, + unsigned int index, unsigned int size) { struct snd_pcm_hardware *hw = &runtime->hw; - unsigned int i, rate, mode; + struct amdtp_stream *stream; + unsigned int rate; + __be32 reg; + int err; - hw->channels_min = UINT_MAX; - hw->channels_max = 0; + /* + * Retrieve current Multi Bit Linear Audio data channel and limit to + * it. + */ + if (dir == AMDTP_IN_STREAM) { + stream = &dice->tx_stream[index]; + err = snd_dice_transaction_read_tx(dice, + size * index + TX_NUMBER_AUDIO, + ®, sizeof(reg)); + } else { + stream = &dice->rx_stream[index]; + err = snd_dice_transaction_read_rx(dice, + size * index + RX_NUMBER_AUDIO, + ®, 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); - 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) @@ -121,8 +68,10 @@ static int init_hw_info(struct snd_dice *dice, { struct snd_pcm_runtime *runtime = substream->runtime; struct snd_pcm_hardware *hw = &runtime->hw; + enum amdtp_stream_direction dir; struct amdtp_stream *stream; - unsigned int *pcm_channels; + __be32 reg[2]; + unsigned int count, size; int err; hw->info = SNDRV_PCM_INFO_MMAP | @@ -134,38 +83,38 @@ 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; + dir = AMDTP_IN_STREAM; + stream = &dice->tx_stream[substream->pcm->device]; + err = snd_dice_transaction_read_tx(dice, TX_NUMBER, reg, + sizeof(reg)); } else { hw->formats = AM824_OUT_PCM_FORMAT_BITS; - stream = &dice->rx_stream; - pcm_channels = dice->rx_channels; + dir = AMDTP_OUT_STREAM; + stream = &dice->rx_stream[substream->pcm->device]; + err = snd_dice_transaction_read_rx(dice, RX_NUMBER, reg, + sizeof(reg)); } - 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); + return err; + + count = min_t(unsigned int, be32_to_cpu(reg[0]), MAX_STREAMS); + if (substream->pcm->device >= count) + return -ENXIO; + + size = be32_to_cpu(reg[1]) * 4; + err = limit_channels_and_rates(dice, substream->runtime, dir, + substream->pcm->device, size); 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 +125,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; @@ -230,6 +146,7 @@ static int capture_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { struct snd_dice *dice = substream->private_data; + struct amdtp_stream *stream = &dice->tx_stream[substream->pcm->device]; int err; err = snd_pcm_lib_alloc_vmalloc_buffer(substream, @@ -243,7 +160,7 @@ static int capture_hw_params(struct snd_pcm_substream *substream, mutex_unlock(&dice->mutex); } - amdtp_am824_set_pcm_format(&dice->tx_stream, params_format(hw_params)); + amdtp_am824_set_pcm_format(stream, params_format(hw_params)); return 0; } @@ -251,6 +168,7 @@ static int playback_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { struct snd_dice *dice = substream->private_data; + struct amdtp_stream *stream = &dice->rx_stream[substream->pcm->device]; int err; err = snd_pcm_lib_alloc_vmalloc_buffer(substream, @@ -264,7 +182,7 @@ static int playback_hw_params(struct snd_pcm_substream *substream, mutex_unlock(&dice->mutex); } - amdtp_am824_set_pcm_format(&dice->rx_stream, params_format(hw_params)); + amdtp_am824_set_pcm_format(stream, params_format(hw_params)); return 0; } @@ -304,26 +222,28 @@ static int playback_hw_free(struct snd_pcm_substream *substream) static int capture_prepare(struct snd_pcm_substream *substream) { struct snd_dice *dice = substream->private_data; + struct amdtp_stream *stream = &dice->tx_stream[substream->pcm->device]; int err; mutex_lock(&dice->mutex); err = snd_dice_stream_start_duplex(dice, substream->runtime->rate); mutex_unlock(&dice->mutex); if (err >= 0) - amdtp_stream_pcm_prepare(&dice->tx_stream); + amdtp_stream_pcm_prepare(stream); return 0; } static int playback_prepare(struct snd_pcm_substream *substream) { struct snd_dice *dice = substream->private_data; + struct amdtp_stream *stream = &dice->rx_stream[substream->pcm->device]; int err; mutex_lock(&dice->mutex); err = snd_dice_stream_start_duplex(dice, substream->runtime->rate); mutex_unlock(&dice->mutex); if (err >= 0) - amdtp_stream_pcm_prepare(&dice->rx_stream); + amdtp_stream_pcm_prepare(stream); return err; } @@ -331,13 +251,14 @@ static int playback_prepare(struct snd_pcm_substream *substream) static int capture_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_dice *dice = substream->private_data; + struct amdtp_stream *stream = &dice->tx_stream[substream->pcm->device]; switch (cmd) { case SNDRV_PCM_TRIGGER_START: - amdtp_stream_pcm_trigger(&dice->tx_stream, substream); + amdtp_stream_pcm_trigger(stream, substream); break; case SNDRV_PCM_TRIGGER_STOP: - amdtp_stream_pcm_trigger(&dice->tx_stream, NULL); + amdtp_stream_pcm_trigger(stream, NULL); break; default: return -EINVAL; @@ -348,13 +269,14 @@ static int capture_trigger(struct snd_pcm_substream *substream, int cmd) static int playback_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_dice *dice = substream->private_data; + struct amdtp_stream *stream = &dice->rx_stream[substream->pcm->device]; switch (cmd) { case SNDRV_PCM_TRIGGER_START: - amdtp_stream_pcm_trigger(&dice->rx_stream, substream); + amdtp_stream_pcm_trigger(stream, substream); break; case SNDRV_PCM_TRIGGER_STOP: - amdtp_stream_pcm_trigger(&dice->rx_stream, NULL); + amdtp_stream_pcm_trigger(stream, NULL); break; default: return -EINVAL; @@ -366,14 +288,16 @@ static int playback_trigger(struct snd_pcm_substream *substream, int cmd) static snd_pcm_uframes_t capture_pointer(struct snd_pcm_substream *substream) { struct snd_dice *dice = substream->private_data; + struct amdtp_stream *stream = &dice->tx_stream[substream->pcm->device]; - return amdtp_stream_pcm_pointer(&dice->tx_stream); + return amdtp_stream_pcm_pointer(stream); } static snd_pcm_uframes_t playback_pointer(struct snd_pcm_substream *substream) { struct snd_dice *dice = substream->private_data; + struct amdtp_stream *stream = &dice->rx_stream[substream->pcm->device]; - return amdtp_stream_pcm_pointer(&dice->rx_stream); + return amdtp_stream_pcm_pointer(stream); } int snd_dice_create_pcm(struct snd_dice *dice) @@ -402,29 +326,53 @@ 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 i, max_capture, max_playback, capture, playback; int err; - capture = playback = 0; - for (i = 0; i < 3; i++) { - if (dice->tx_channels[i] > 0) + /* Check whether PCM substreams are required. */ + if (dice->force_two_pcms) { + max_capture = max_playback = 2; + } else { + max_capture = max_playback = 0; + err = snd_dice_transaction_read_tx(dice, TX_NUMBER, ®, + sizeof(reg)); + if (err < 0) + return err; + max_capture = min_t(unsigned int, be32_to_cpu(reg), MAX_STREAMS); + + err = snd_dice_transaction_read_rx(dice, RX_NUMBER, ®, + sizeof(reg)); + if (err < 0) + return err; + max_playback = min_t(unsigned int, be32_to_cpu(reg), MAX_STREAMS); + } + + for (i = 0; i < MAX_STREAMS; i++) { + capture = playback = 0; + if (i < max_capture) capture = 1; - if (dice->rx_channels[i] > 0) + if (i < max_playback) playback = 1; - } + if (capture == 0 && playback == 0) + break; - err = snd_pcm_new(dice->card, "DICE", 0, playback, capture, &pcm); - if (err < 0) - return err; - pcm->private_data = dice; - strcpy(pcm->name, dice->card->shortname); + err = snd_pcm_new(dice->card, "DICE", i, playback, capture, + &pcm); + if (err < 0) + return err; + pcm->private_data = dice; + strcpy(pcm->name, dice->card->shortname); - if (capture > 0) - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops); + if (capture > 0) + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, + &capture_ops); - if (playback > 0) - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops); + if (playback > 0) + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, + &playback_ops); + } return 0; } diff --git a/sound/firewire/dice/dice-stream.c b/sound/firewire/dice/dice-stream.c index a6a39f7ef58d..845d5e5884a4 100644 --- a/sound/firewire/dice/dice-stream.c +++ b/sound/firewire/dice/dice-stream.c @@ -10,6 +10,12 @@ #include "dice.h" #define CALLBACK_TIMEOUT 200 +#define NOTIFICATION_TIMEOUT_MS (2 * MSEC_PER_SEC) + +struct reg_params { + unsigned int count; + unsigned int size; +}; const unsigned int snd_dice_rates[SND_DICE_RATES_COUNT] = { /* mode 0 */ @@ -24,96 +30,126 @@ 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; -} -static void release_resources(struct snd_dice *dice, - struct fw_iso_resources *resources) -{ - __be32 channel; - - /* Reset channel number */ - channel = cpu_to_be32((u32)-1); - if (resources == &dice->tx_resources) - snd_dice_transaction_write_tx(dice, TX_ISOCHRONOUS, - &channel, sizeof(channel)); - else - snd_dice_transaction_write_rx(dice, RX_ISOCHRONOUS, - &channel, sizeof(channel)); - - fw_iso_resources_free(resources); + return 0; } -static int keep_resources(struct snd_dice *dice, - struct fw_iso_resources *resources, - unsigned int max_payload_bytes) +static int get_register_params(struct snd_dice *dice, + struct reg_params *tx_params, + struct reg_params *rx_params) { - __be32 channel; + __be32 reg[2]; int err; - err = fw_iso_resources_allocate(resources, max_payload_bytes, - fw_parent_device(dice->unit)->max_speed); + err = snd_dice_transaction_read_tx(dice, TX_NUMBER, reg, sizeof(reg)); if (err < 0) - goto end; + return err; + tx_params->count = + min_t(unsigned int, be32_to_cpu(reg[0]), MAX_STREAMS); + tx_params->size = be32_to_cpu(reg[1]) * 4; - /* Set channel number */ - channel = cpu_to_be32(resources->channel); - if (resources == &dice->tx_resources) - err = snd_dice_transaction_write_tx(dice, TX_ISOCHRONOUS, - &channel, sizeof(channel)); - else - err = snd_dice_transaction_write_rx(dice, RX_ISOCHRONOUS, - &channel, sizeof(channel)); + err = snd_dice_transaction_read_rx(dice, RX_NUMBER, reg, sizeof(reg)); if (err < 0) - release_resources(dice, resources); -end: - return err; + return err; + rx_params->count = + min_t(unsigned int, be32_to_cpu(reg[0]), MAX_STREAMS); + rx_params->size = be32_to_cpu(reg[1]) * 4; + + return 0; } -static void stop_stream(struct snd_dice *dice, struct amdtp_stream *stream) +static void release_resources(struct snd_dice *dice) { - amdtp_stream_pcm_abort(stream); - amdtp_stream_stop(stream); + unsigned int i; - if (stream == &dice->tx_stream) - release_resources(dice, &dice->tx_resources); - else - release_resources(dice, &dice->rx_resources); + for (i = 0; i < MAX_STREAMS; i++) { + if (amdtp_stream_running(&dice->tx_stream[i])) { + amdtp_stream_pcm_abort(&dice->tx_stream[i]); + amdtp_stream_stop(&dice->tx_stream[i]); + } + if (amdtp_stream_running(&dice->rx_stream[i])) { + amdtp_stream_pcm_abort(&dice->rx_stream[i]); + amdtp_stream_stop(&dice->rx_stream[i]); + } + + fw_iso_resources_free(&dice->tx_resources[i]); + fw_iso_resources_free(&dice->rx_resources[i]); + } } -static int start_stream(struct snd_dice *dice, struct amdtp_stream *stream, - unsigned int rate) +static void stop_streams(struct snd_dice *dice, enum amdtp_stream_direction dir, + struct reg_params *params) { + __be32 reg; + unsigned int i; + + for (i = 0; i < params->count; i++) { + reg = cpu_to_be32((u32)-1); + if (dir == AMDTP_IN_STREAM) { + snd_dice_transaction_write_tx(dice, + params->size * i + TX_ISOCHRONOUS, + ®, sizeof(reg)); + } else { + snd_dice_transaction_write_rx(dice, + params->size * i + RX_ISOCHRONOUS, + ®, sizeof(reg)); + } + } +} + +static int keep_resources(struct snd_dice *dice, + enum amdtp_stream_direction dir, unsigned int index, + unsigned int rate, unsigned int pcm_chs, + unsigned int midi_ports) +{ + struct amdtp_stream *stream; struct fw_iso_resources *resources; - unsigned int i, mode, pcm_chs, midi_ports; bool double_pcm_frames; + unsigned int i; 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]; + if (dir == AMDTP_IN_STREAM) { + stream = &dice->tx_stream[index]; + resources = &dice->tx_resources[index]; } else { - resources = &dice->rx_resources; - pcm_chs = dice->rx_channels[mode]; - midi_ports = dice->rx_midi_ports[mode]; + stream = &dice->rx_stream[index]; + resources = &dice->rx_resources[index]; } /* @@ -126,7 +162,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; @@ -135,7 +171,7 @@ static int start_stream(struct snd_dice *dice, struct amdtp_stream *stream, err = amdtp_am824_set_parameters(stream, rate, pcm_chs, midi_ports, double_pcm_frames); if (err < 0) - goto end; + return err; if (double_pcm_frames) { pcm_chs /= 2; @@ -147,158 +183,201 @@ static int start_stream(struct snd_dice *dice, struct amdtp_stream *stream, } } - err = keep_resources(dice, resources, - amdtp_stream_get_max_payload(stream)); - if (err < 0) { - dev_err(&dice->unit->device, - "fail to keep isochronous resources\n"); - goto end; - } - - err = amdtp_stream_start(stream, resources->channel, - fw_parent_device(dice->unit)->max_speed); - if (err < 0) - release_resources(dice, resources); -end: - return err; + return fw_iso_resources_allocate(resources, + amdtp_stream_get_max_payload(stream), + fw_parent_device(dice->unit)->max_speed); } -static int get_sync_mode(struct snd_dice *dice, enum cip_flags *sync_mode) +static int start_streams(struct snd_dice *dice, enum amdtp_stream_direction dir, + unsigned int rate, struct reg_params *params) { - u32 source; - int err; + __be32 reg[2]; + unsigned int i, pcm_chs, midi_ports; + struct amdtp_stream *streams; + struct fw_iso_resources *resources; + int err = 0; - err = snd_dice_transaction_get_clock_source(dice, &source); - if (err < 0) - goto end; + if (dir == AMDTP_IN_STREAM) { + streams = dice->tx_stream; + resources = dice->tx_resources; + } else { + streams = dice->rx_stream; + resources = dice->rx_resources; + } + + for (i = 0; i < params->count; i++) { + if (dir == AMDTP_IN_STREAM) { + err = snd_dice_transaction_read_tx(dice, + params->size * i + TX_NUMBER_AUDIO, + reg, sizeof(reg)); + } else { + err = snd_dice_transaction_read_rx(dice, + params->size * i + RX_NUMBER_AUDIO, + reg, sizeof(reg)); + } + if (err < 0) + return err; + pcm_chs = be32_to_cpu(reg[0]); + midi_ports = be32_to_cpu(reg[1]); + + err = keep_resources(dice, dir, i, rate, pcm_chs, midi_ports); + if (err < 0) + return err; + + reg[0] = cpu_to_be32(resources[i].channel); + if (dir == AMDTP_IN_STREAM) { + err = snd_dice_transaction_write_tx(dice, + params->size * i + TX_ISOCHRONOUS, + reg, sizeof(reg[0])); + } else { + err = snd_dice_transaction_write_rx(dice, + params->size * i + RX_ISOCHRONOUS, + reg, sizeof(reg[0])); + } + if (err < 0) + return err; - 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; + err = amdtp_stream_start(&streams[i], resources[i].channel, + fw_parent_device(dice->unit)->max_speed); + if (err < 0) + return err; } -end: + return err; } +/* + * MEMO: After this function, there're two states of streams: + * - None streams are running. + * - All streams are running. + */ 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; + unsigned int i; + struct reg_params tx_params, rx_params; + bool need_to_start; + int err; if (dice->substreams_counter == 0) - goto end; + return -EIO; - err = get_sync_mode(dice, &sync_mode); + err = get_register_params(dice, &tx_params, &rx_params); 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; - } - - /* Some packet queueing errors. */ - if (amdtp_streaming_error(master) || amdtp_streaming_error(slave)) - stop_stream(dice, master); + return err; - /* Stop stream if rate is different. */ err = snd_dice_transaction_get_rate(dice, &curr_rate); if (err < 0) { dev_err(&dice->unit->device, "fail to get sampling rate\n"); - goto end; + return err; } if (rate == 0) rate = curr_rate; if (rate != curr_rate) - stop_stream(dice, master); + return -EINVAL; + + /* Judge to need to restart streams. */ + for (i = 0; i < MAX_STREAMS; i++) { + if (i < tx_params.count) { + if (amdtp_streaming_error(&dice->tx_stream[i]) || + !amdtp_stream_running(&dice->tx_stream[i])) + break; + } + if (i < rx_params.count) { + if (amdtp_streaming_error(&dice->rx_stream[i]) || + !amdtp_stream_running(&dice->rx_stream[i])) + break; + } + } + need_to_start = (i < MAX_STREAMS); - if (!amdtp_stream_running(master)) { - stop_stream(dice, slave); + if (need_to_start) { + /* Stop transmission. */ snd_dice_transaction_clear_enable(dice); + stop_streams(dice, AMDTP_IN_STREAM, &tx_params); + stop_streams(dice, AMDTP_OUT_STREAM, &rx_params); + release_resources(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"); - goto end; + "fail to ensure phase lock\n"); + return err; } /* Start both streams. */ - err = start_stream(dice, master, rate); - if (err < 0) { - dev_err(&dice->unit->device, - "fail to start AMDTP master stream\n"); - goto end; - } - err = start_stream(dice, slave, rate); - if (err < 0) { - dev_err(&dice->unit->device, - "fail to start AMDTP slave stream\n"); - stop_stream(dice, master); - goto end; - } + err = start_streams(dice, AMDTP_IN_STREAM, rate, &tx_params); + if (err < 0) + goto error; + err = start_streams(dice, AMDTP_OUT_STREAM, rate, &rx_params); + if (err < 0) + goto error; + err = snd_dice_transaction_set_enable(dice); if (err < 0) { dev_err(&dice->unit->device, "fail to enable interface\n"); - stop_stream(dice, master); - stop_stream(dice, slave); - goto end; + goto error; } - /* Wait first callbacks */ - if (!amdtp_stream_wait_callback(master, CALLBACK_TIMEOUT) || - !amdtp_stream_wait_callback(slave, CALLBACK_TIMEOUT)) { - snd_dice_transaction_clear_enable(dice); - stop_stream(dice, master); - stop_stream(dice, slave); - err = -ETIMEDOUT; + for (i = 0; i < MAX_STREAMS; i++) { + if ((i < tx_params.count && + !amdtp_stream_wait_callback(&dice->tx_stream[i], + CALLBACK_TIMEOUT)) || + (i < rx_params.count && + !amdtp_stream_wait_callback(&dice->rx_stream[i], + CALLBACK_TIMEOUT))) { + err = -ETIMEDOUT; + goto error; + } } } -end: + + return err; +error: + snd_dice_transaction_clear_enable(dice); + stop_streams(dice, AMDTP_IN_STREAM, &tx_params); + stop_streams(dice, AMDTP_OUT_STREAM, &rx_params); + release_resources(dice); return err; } +/* + * MEMO: After this function, there're two states of streams: + * - None streams are running. + * - All streams are running. + */ void snd_dice_stream_stop_duplex(struct snd_dice *dice) { + struct reg_params tx_params, rx_params; + if (dice->substreams_counter > 0) return; snd_dice_transaction_clear_enable(dice); - stop_stream(dice, &dice->tx_stream); - stop_stream(dice, &dice->rx_stream); + if (get_register_params(dice, &tx_params, &rx_params) == 0) { + stop_streams(dice, AMDTP_IN_STREAM, &tx_params); + stop_streams(dice, AMDTP_OUT_STREAM, &rx_params); + } + + release_resources(dice); } -static int init_stream(struct snd_dice *dice, struct amdtp_stream *stream) +static int init_stream(struct snd_dice *dice, enum amdtp_stream_direction dir, + unsigned int index) { - int err; + struct amdtp_stream *stream; struct fw_iso_resources *resources; - enum amdtp_stream_direction dir; + int err; - if (stream == &dice->tx_stream) { - resources = &dice->tx_resources; - dir = AMDTP_IN_STREAM; + if (dir == AMDTP_IN_STREAM) { + stream = &dice->tx_stream[index]; + resources = &dice->tx_resources[index]; } else { - resources = &dice->rx_resources; - dir = AMDTP_OUT_STREAM; + stream = &dice->rx_stream[index]; + resources = &dice->rx_resources[index]; } err = fw_iso_resources_init(resources, dice->unit); @@ -319,14 +398,20 @@ end: * This function should be called before starting streams or after stopping * streams. */ -static void destroy_stream(struct snd_dice *dice, struct amdtp_stream *stream) +static void destroy_stream(struct snd_dice *dice, + enum amdtp_stream_direction dir, + unsigned int index) { + struct amdtp_stream *stream; struct fw_iso_resources *resources; - if (stream == &dice->tx_stream) - resources = &dice->tx_resources; - else - resources = &dice->rx_resources; + if (dir == AMDTP_IN_STREAM) { + stream = &dice->tx_stream[index]; + resources = &dice->tx_resources[index]; + } else { + stream = &dice->rx_stream[index]; + resources = &dice->rx_resources[index]; + } amdtp_stream_destroy(stream); fw_iso_resources_destroy(resources); @@ -334,33 +419,51 @@ static void destroy_stream(struct snd_dice *dice, struct amdtp_stream *stream) int snd_dice_stream_init_duplex(struct snd_dice *dice) { - int err; - - dice->substreams_counter = 0; + int i, err; - err = init_stream(dice, &dice->tx_stream); - if (err < 0) - goto end; + for (i = 0; i < MAX_STREAMS; i++) { + err = init_stream(dice, AMDTP_IN_STREAM, i); + if (err < 0) { + for (; i >= 0; i--) + destroy_stream(dice, AMDTP_OUT_STREAM, i); + goto end; + } + } - err = init_stream(dice, &dice->rx_stream); - if (err < 0) - destroy_stream(dice, &dice->tx_stream); + for (i = 0; i < MAX_STREAMS; i++) { + err = init_stream(dice, AMDTP_OUT_STREAM, i); + if (err < 0) { + for (; i >= 0; i--) + destroy_stream(dice, AMDTP_OUT_STREAM, i); + for (i = 0; i < MAX_STREAMS; i++) + destroy_stream(dice, AMDTP_IN_STREAM, i); + break; + } + } end: return err; } void snd_dice_stream_destroy_duplex(struct snd_dice *dice) { + struct reg_params tx_params, rx_params; + snd_dice_transaction_clear_enable(dice); - destroy_stream(dice, &dice->tx_stream); - destroy_stream(dice, &dice->rx_stream); + if (get_register_params(dice, &tx_params, &rx_params) == 0) { + stop_streams(dice, AMDTP_IN_STREAM, &tx_params); + stop_streams(dice, AMDTP_OUT_STREAM, &rx_params); + } + + release_resources(dice); dice->substreams_counter = 0; } void snd_dice_stream_update_duplex(struct snd_dice *dice) { + struct reg_params tx_params, rx_params; + /* * On a bus reset, the DICE firmware disables streaming and then goes * off contemplating its own navel for hundreds of milliseconds before @@ -371,11 +474,10 @@ void snd_dice_stream_update_duplex(struct snd_dice *dice) */ dice->global_enabled = false; - stop_stream(dice, &dice->rx_stream); - stop_stream(dice, &dice->tx_stream); - - fw_iso_resources_update(&dice->rx_resources); - fw_iso_resources_update(&dice->tx_resources); + if (get_register_params(dice, &tx_params, &rx_params) == 0) { + stop_streams(dice, AMDTP_IN_STREAM, &tx_params); + stop_streams(dice, AMDTP_OUT_STREAM, &rx_params); + } } static void dice_lock_changed(struct snd_dice *dice) 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..8b64aef31a86 100644 --- a/sound/firewire/dice/dice.c +++ b/sound/firewire/dice/dice.c @@ -13,6 +13,8 @@ MODULE_LICENSE("GPL v2"); #define OUI_WEISS 0x001c6a #define OUI_LOUD 0x000ff2 +#define OUI_FOCUSRITE 0x00130e +#define OUI_TCELECTRONIC 0x001486 #define DICE_CATEGORY_ID 0x04 #define WEISS_CATEGORY_ID 0x00 @@ -20,6 +22,36 @@ MODULE_LICENSE("GPL v2"); #define PROBE_DELAY_MS (2 * MSEC_PER_SEC) +/* + * Some models support several isochronous channels, while these streams are not + * always available. In this case, add the model name to this list. + */ +static bool force_two_pcm_support(struct fw_unit *unit) +{ + const char *const models[] = { + /* TC Electronic models. */ + "StudioKonnekt48", + /* Focusrite models. */ + "SAFFIRE_PRO_40", + "LIQUID_SAFFIRE_56", + "SAFFIRE_PRO_40_1", + }; + char model[32]; + unsigned int i; + int err; + + err = fw_csr_string(unit->directory, CSR_MODEL, model, sizeof(model)); + if (err < 0) + return false; + + for (i = 0; i < ARRAY_SIZE(models); i++) { + if (strcmp(models[i], model) == 0) + break; + } + + return i < ARRAY_SIZE(models); +} + static int check_dice_category(struct fw_unit *unit) { struct fw_device *device = fw_parent_device(unit); @@ -44,6 +76,12 @@ static int check_dice_category(struct fw_unit *unit) break; } } + + if (vendor == OUI_FOCUSRITE || vendor == OUI_TCELECTRONIC) { + if (force_two_pcm_support(unit)) + return 0; + } + if (vendor == OUI_WEISS) category = WEISS_CATEGORY_ID; else if (vendor == OUI_LOUD) @@ -57,65 +95,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 +116,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; } @@ -211,11 +188,14 @@ static void do_registration(struct work_struct *work) if (err < 0) return; + if (force_two_pcm_support(dice->unit)) + dice->force_two_pcms = true; + err = snd_dice_transaction_init(dice); 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..e6c07857f475 100644 --- a/sound/firewire/dice/dice.h +++ b/sound/firewire/dice/dice.h @@ -39,6 +39,29 @@ #include "../lib.h" #include "dice-interface.h" +/* + * This module support maximum 2 pairs of tx/rx isochronous streams for + * our convinience. + * + * In documents for ASICs called with a name of 'DICE': + * - ASIC for DICE II: + * - Maximum 2 tx and 4 rx are supported. + * - A packet supports maximum 16 data channels. + * - TCD2210/2210-E (so-called 'Dice Mini'): + * - Maximum 2 tx and 2 rx are supported. + * - A packet supports maximum 16 data channels. + * - TCD2220/2220-E (so-called 'Dice Jr.') + * - 2 tx and 2 rx are supported. + * - A packet supports maximum 16 data channels. + * - TCD3070-CH (so-called 'Dice III') + * - Maximum 2 tx and 2 rx are supported. + * - A packet supports maximum 32 data channels. + * + * For the above, MIDI conformant data channel is just on the first isochronous + * stream. + */ +#define MAX_STREAMS 2 + struct snd_dice { struct snd_card *card; struct fw_unit *unit; @@ -56,10 +79,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; @@ -71,13 +90,15 @@ struct snd_dice { wait_queue_head_t hwdep_wait; /* For streaming */ - struct fw_iso_resources tx_resources; - struct fw_iso_resources rx_resources; - struct amdtp_stream tx_stream; - struct amdtp_stream rx_stream; + struct fw_iso_resources tx_resources[MAX_STREAMS]; + struct fw_iso_resources rx_resources[MAX_STREAMS]; + struct amdtp_stream tx_stream[MAX_STREAMS]; + struct amdtp_stream rx_stream[MAX_STREAMS]; bool global_enabled; struct completion clock_accepted; unsigned int substreams_counter; + + bool force_two_pcms; }; enum snd_dice_addr_type { @@ -158,7 +179,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 +189,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/hda/Makefile b/sound/hda/Makefile index 7e999c995cdc..3b9bedee2fa4 100644 --- a/sound/hda/Makefile +++ b/sound/hda/Makefile @@ -1,5 +1,5 @@ snd-hda-core-objs := hda_bus_type.o hdac_bus.o hdac_device.o hdac_sysfs.o \ - hdac_regmap.o hdac_controller.o hdac_stream.o array.o + hdac_regmap.o hdac_controller.o hdac_stream.o array.o hdmi_chmap.o snd-hda-core-objs += trace.o CFLAGS_trace.o := -I$(src) diff --git a/sound/hda/hdac_device.c b/sound/hda/hdac_device.c index e361024eabb6..d1a4d6973330 100644 --- a/sound/hda/hdac_device.c +++ b/sound/hda/hdac_device.c @@ -611,6 +611,22 @@ int snd_hdac_power_up_pm(struct hdac_device *codec) } EXPORT_SYMBOL_GPL(snd_hdac_power_up_pm); +/* like snd_hdac_power_up_pm(), but only increment the pm count when + * already powered up. Returns -1 if not powered up, 1 if incremented + * or 0 if unchanged. Only used in hdac_regmap.c + */ +int snd_hdac_keep_power_up(struct hdac_device *codec) +{ + if (!atomic_inc_not_zero(&codec->in_pm)) { + int ret = pm_runtime_get_if_in_use(&codec->dev); + if (!ret) + return -1; + if (ret < 0) + return 0; + } + return 1; +} + /** * snd_hdac_power_down_pm - power down the codec * @codec: the codec object diff --git a/sound/hda/hdac_i915.c b/sound/hda/hdac_i915.c index f6854dbd7d8d..fb96aead8257 100644 --- a/sound/hda/hdac_i915.c +++ b/sound/hda/hdac_i915.c @@ -126,6 +126,8 @@ EXPORT_SYMBOL_GPL(snd_hdac_get_display_clk); */ static int pin2port(hda_nid_t pin_nid) { + if (WARN_ON(pin_nid < 5 || pin_nid > 7)) + return -1; return pin_nid - 4; } @@ -144,10 +146,14 @@ static int pin2port(hda_nid_t pin_nid) int snd_hdac_sync_audio_rate(struct hdac_bus *bus, hda_nid_t nid, int rate) { struct i915_audio_component *acomp = bus->audio_component; + int port; if (!acomp || !acomp->ops || !acomp->ops->sync_audio_rate) return -ENODEV; - return acomp->ops->sync_audio_rate(acomp->dev, pin2port(nid), rate); + port = pin2port(nid); + if (port < 0) + return -EINVAL; + return acomp->ops->sync_audio_rate(acomp->dev, port, rate); } EXPORT_SYMBOL_GPL(snd_hdac_sync_audio_rate); @@ -175,11 +181,15 @@ int snd_hdac_acomp_get_eld(struct hdac_bus *bus, hda_nid_t nid, bool *audio_enabled, char *buffer, int max_bytes) { struct i915_audio_component *acomp = bus->audio_component; + int port; if (!acomp || !acomp->ops || !acomp->ops->get_eld) return -ENODEV; - return acomp->ops->get_eld(acomp->dev, pin2port(nid), audio_enabled, + port = pin2port(nid); + if (port < 0) + return -EINVAL; + return acomp->ops->get_eld(acomp->dev, port, audio_enabled, buffer, max_bytes); } EXPORT_SYMBOL_GPL(snd_hdac_acomp_get_eld); diff --git a/sound/hda/hdac_regmap.c b/sound/hda/hdac_regmap.c index eb8f7c30cb09..bdbcd6b75ff6 100644 --- a/sound/hda/hdac_regmap.c +++ b/sound/hda/hdac_regmap.c @@ -21,13 +21,16 @@ #include <sound/hdaudio.h> #include <sound/hda_regmap.h> -#ifdef CONFIG_PM -#define codec_is_running(codec) \ - (atomic_read(&(codec)->in_pm) || \ - !pm_runtime_suspended(&(codec)->dev)) -#else -#define codec_is_running(codec) true -#endif +static int codec_pm_lock(struct hdac_device *codec) +{ + return snd_hdac_keep_power_up(codec); +} + +static void codec_pm_unlock(struct hdac_device *codec, int lock) +{ + if (lock == 1) + snd_hdac_power_down_pm(codec); +} #define get_verb(reg) (((reg) >> 8) & 0xfff) @@ -238,20 +241,28 @@ static int hda_reg_read(void *context, unsigned int reg, unsigned int *val) struct hdac_device *codec = context; int verb = get_verb(reg); int err; + int pm_lock = 0; - if (!codec_is_running(codec) && verb != AC_VERB_GET_POWER_STATE) - return -EAGAIN; + if (verb != AC_VERB_GET_POWER_STATE) { + pm_lock = codec_pm_lock(codec); + if (pm_lock < 0) + return -EAGAIN; + } reg |= (codec->addr << 28); - if (is_stereo_amp_verb(reg)) - return hda_reg_read_stereo_amp(codec, reg, val); - if (verb == AC_VERB_GET_PROC_COEF) - return hda_reg_read_coef(codec, reg, val); + if (is_stereo_amp_verb(reg)) { + err = hda_reg_read_stereo_amp(codec, reg, val); + goto out; + } + if (verb == AC_VERB_GET_PROC_COEF) { + err = hda_reg_read_coef(codec, reg, val); + goto out; + } if ((verb & 0x700) == AC_VERB_SET_AMP_GAIN_MUTE) reg &= ~AC_AMP_FAKE_MUTE; err = snd_hdac_exec_verb(codec, reg, 0, val); if (err < 0) - return err; + goto out; /* special handling for asymmetric reads */ if (verb == AC_VERB_GET_POWER_STATE) { if (*val & AC_PWRST_ERROR) @@ -259,7 +270,9 @@ static int hda_reg_read(void *context, unsigned int reg, unsigned int *val) else /* take only the actual state */ *val = (*val >> 4) & 0x0f; } - return 0; + out: + codec_pm_unlock(codec, pm_lock); + return err; } static int hda_reg_write(void *context, unsigned int reg, unsigned int val) @@ -267,6 +280,7 @@ static int hda_reg_write(void *context, unsigned int reg, unsigned int val) struct hdac_device *codec = context; unsigned int verb; int i, bytes, err; + int pm_lock = 0; if (codec->caps_overwriting) return 0; @@ -275,14 +289,21 @@ static int hda_reg_write(void *context, unsigned int reg, unsigned int val) reg |= (codec->addr << 28); verb = get_verb(reg); - if (!codec_is_running(codec) && verb != AC_VERB_SET_POWER_STATE) - return codec->lazy_cache ? 0 : -EAGAIN; + if (verb != AC_VERB_SET_POWER_STATE) { + pm_lock = codec_pm_lock(codec); + if (pm_lock < 0) + return codec->lazy_cache ? 0 : -EAGAIN; + } - if (is_stereo_amp_verb(reg)) - return hda_reg_write_stereo_amp(codec, reg, val); + if (is_stereo_amp_verb(reg)) { + err = hda_reg_write_stereo_amp(codec, reg, val); + goto out; + } - if (verb == AC_VERB_SET_PROC_COEF) - return hda_reg_write_coef(codec, reg, val); + if (verb == AC_VERB_SET_PROC_COEF) { + err = hda_reg_write_coef(codec, reg, val); + goto out; + } switch (verb & 0xf00) { case AC_VERB_SET_AMP_GAIN_MUTE: @@ -319,10 +340,12 @@ static int hda_reg_write(void *context, unsigned int reg, unsigned int val) reg |= (verb + i) << 8 | ((val >> (8 * i)) & 0xff); err = snd_hdac_exec_verb(codec, reg, 0, NULL); if (err < 0) - return err; + goto out; } - return 0; + out: + codec_pm_unlock(codec, pm_lock); + return err; } static const struct regmap_config hda_regmap_cfg = { diff --git a/sound/hda/hdmi_chmap.c b/sound/hda/hdmi_chmap.c new file mode 100644 index 000000000000..d7ec86263828 --- /dev/null +++ b/sound/hda/hdmi_chmap.c @@ -0,0 +1,791 @@ +/* + * HDMI Channel map support helpers + */ + +#include <linux/module.h> +#include <sound/control.h> +#include <sound/tlv.h> +#include <sound/hda_chmap.h> + +/* + * CEA speaker placement: + * + * FLH FCH FRH + * FLW FL FLC FC FRC FR FRW + * + * LFE + * TC + * + * RL RLC RC RRC RR + * + * The Left/Right Surround channel _notions_ LS/RS in SMPTE 320M corresponds to + * CEA RL/RR; The SMPTE channel _assignment_ C/LFE is swapped to CEA LFE/FC. + */ +enum cea_speaker_placement { + FL = (1 << 0), /* Front Left */ + FC = (1 << 1), /* Front Center */ + FR = (1 << 2), /* Front Right */ + FLC = (1 << 3), /* Front Left Center */ + FRC = (1 << 4), /* Front Right Center */ + RL = (1 << 5), /* Rear Left */ + RC = (1 << 6), /* Rear Center */ + RR = (1 << 7), /* Rear Right */ + RLC = (1 << 8), /* Rear Left Center */ + RRC = (1 << 9), /* Rear Right Center */ + LFE = (1 << 10), /* Low Frequency Effect */ + FLW = (1 << 11), /* Front Left Wide */ + FRW = (1 << 12), /* Front Right Wide */ + FLH = (1 << 13), /* Front Left High */ + FCH = (1 << 14), /* Front Center High */ + FRH = (1 << 15), /* Front Right High */ + TC = (1 << 16), /* Top Center */ +}; + +static const char * const cea_speaker_allocation_names[] = { + /* 0 */ "FL/FR", + /* 1 */ "LFE", + /* 2 */ "FC", + /* 3 */ "RL/RR", + /* 4 */ "RC", + /* 5 */ "FLC/FRC", + /* 6 */ "RLC/RRC", + /* 7 */ "FLW/FRW", + /* 8 */ "FLH/FRH", + /* 9 */ "TC", + /* 10 */ "FCH", +}; + +/* + * ELD SA bits in the CEA Speaker Allocation data block + */ +static int eld_speaker_allocation_bits[] = { + [0] = FL | FR, + [1] = LFE, + [2] = FC, + [3] = RL | RR, + [4] = RC, + [5] = FLC | FRC, + [6] = RLC | RRC, + /* the following are not defined in ELD yet */ + [7] = FLW | FRW, + [8] = FLH | FRH, + [9] = TC, + [10] = FCH, +}; + +/* + * ALSA sequence is: + * + * surround40 surround41 surround50 surround51 surround71 + * ch0 front left = = = = + * ch1 front right = = = = + * ch2 rear left = = = = + * ch3 rear right = = = = + * ch4 LFE center center center + * ch5 LFE LFE + * ch6 side left + * ch7 side right + * + * surround71 = {FL, FR, RLC, RRC, FC, LFE, RL, RR} + */ +static int hdmi_channel_mapping[0x32][8] = { + /* stereo */ + [0x00] = { 0x00, 0x11, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7 }, + /* 2.1 */ + [0x01] = { 0x00, 0x11, 0x22, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7 }, + /* Dolby Surround */ + [0x02] = { 0x00, 0x11, 0x23, 0xf2, 0xf4, 0xf5, 0xf6, 0xf7 }, + /* surround40 */ + [0x08] = { 0x00, 0x11, 0x24, 0x35, 0xf3, 0xf2, 0xf6, 0xf7 }, + /* 4ch */ + [0x03] = { 0x00, 0x11, 0x23, 0x32, 0x44, 0xf5, 0xf6, 0xf7 }, + /* surround41 */ + [0x09] = { 0x00, 0x11, 0x24, 0x35, 0x42, 0xf3, 0xf6, 0xf7 }, + /* surround50 */ + [0x0a] = { 0x00, 0x11, 0x24, 0x35, 0x43, 0xf2, 0xf6, 0xf7 }, + /* surround51 */ + [0x0b] = { 0x00, 0x11, 0x24, 0x35, 0x43, 0x52, 0xf6, 0xf7 }, + /* 7.1 */ + [0x13] = { 0x00, 0x11, 0x26, 0x37, 0x43, 0x52, 0x64, 0x75 }, +}; + +/* + * This is an ordered list! + * + * The preceding ones have better chances to be selected by + * hdmi_channel_allocation(). + */ +static struct hdac_cea_channel_speaker_allocation channel_allocations[] = { +/* channel: 7 6 5 4 3 2 1 0 */ +{ .ca_index = 0x00, .speakers = { 0, 0, 0, 0, 0, 0, FR, FL } }, + /* 2.1 */ +{ .ca_index = 0x01, .speakers = { 0, 0, 0, 0, 0, LFE, FR, FL } }, + /* Dolby Surround */ +{ .ca_index = 0x02, .speakers = { 0, 0, 0, 0, FC, 0, FR, FL } }, + /* surround40 */ +{ .ca_index = 0x08, .speakers = { 0, 0, RR, RL, 0, 0, FR, FL } }, + /* surround41 */ +{ .ca_index = 0x09, .speakers = { 0, 0, RR, RL, 0, LFE, FR, FL } }, + /* surround50 */ +{ .ca_index = 0x0a, .speakers = { 0, 0, RR, RL, FC, 0, FR, FL } }, + /* surround51 */ +{ .ca_index = 0x0b, .speakers = { 0, 0, RR, RL, FC, LFE, FR, FL } }, + /* 6.1 */ +{ .ca_index = 0x0f, .speakers = { 0, RC, RR, RL, FC, LFE, FR, FL } }, + /* surround71 */ +{ .ca_index = 0x13, .speakers = { RRC, RLC, RR, RL, FC, LFE, FR, FL } }, + +{ .ca_index = 0x03, .speakers = { 0, 0, 0, 0, FC, LFE, FR, FL } }, +{ .ca_index = 0x04, .speakers = { 0, 0, 0, RC, 0, 0, FR, FL } }, +{ .ca_index = 0x05, .speakers = { 0, 0, 0, RC, 0, LFE, FR, FL } }, +{ .ca_index = 0x06, .speakers = { 0, 0, 0, RC, FC, 0, FR, FL } }, +{ .ca_index = 0x07, .speakers = { 0, 0, 0, RC, FC, LFE, FR, FL } }, +{ .ca_index = 0x0c, .speakers = { 0, RC, RR, RL, 0, 0, FR, FL } }, +{ .ca_index = 0x0d, .speakers = { 0, RC, RR, RL, 0, LFE, FR, FL } }, +{ .ca_index = 0x0e, .speakers = { 0, RC, RR, RL, FC, 0, FR, FL } }, +{ .ca_index = 0x10, .speakers = { RRC, RLC, RR, RL, 0, 0, FR, FL } }, +{ .ca_index = 0x11, .speakers = { RRC, RLC, RR, RL, 0, LFE, FR, FL } }, +{ .ca_index = 0x12, .speakers = { RRC, RLC, RR, RL, FC, 0, FR, FL } }, +{ .ca_index = 0x14, .speakers = { FRC, FLC, 0, 0, 0, 0, FR, FL } }, +{ .ca_index = 0x15, .speakers = { FRC, FLC, 0, 0, 0, LFE, FR, FL } }, +{ .ca_index = 0x16, .speakers = { FRC, FLC, 0, 0, FC, 0, FR, FL } }, +{ .ca_index = 0x17, .speakers = { FRC, FLC, 0, 0, FC, LFE, FR, FL } }, +{ .ca_index = 0x18, .speakers = { FRC, FLC, 0, RC, 0, 0, FR, FL } }, +{ .ca_index = 0x19, .speakers = { FRC, FLC, 0, RC, 0, LFE, FR, FL } }, +{ .ca_index = 0x1a, .speakers = { FRC, FLC, 0, RC, FC, 0, FR, FL } }, +{ .ca_index = 0x1b, .speakers = { FRC, FLC, 0, RC, FC, LFE, FR, FL } }, +{ .ca_index = 0x1c, .speakers = { FRC, FLC, RR, RL, 0, 0, FR, FL } }, +{ .ca_index = 0x1d, .speakers = { FRC, FLC, RR, RL, 0, LFE, FR, FL } }, +{ .ca_index = 0x1e, .speakers = { FRC, FLC, RR, RL, FC, 0, FR, FL } }, +{ .ca_index = 0x1f, .speakers = { FRC, FLC, RR, RL, FC, LFE, FR, FL } }, +{ .ca_index = 0x20, .speakers = { 0, FCH, RR, RL, FC, 0, FR, FL } }, +{ .ca_index = 0x21, .speakers = { 0, FCH, RR, RL, FC, LFE, FR, FL } }, +{ .ca_index = 0x22, .speakers = { TC, 0, RR, RL, FC, 0, FR, FL } }, +{ .ca_index = 0x23, .speakers = { TC, 0, RR, RL, FC, LFE, FR, FL } }, +{ .ca_index = 0x24, .speakers = { FRH, FLH, RR, RL, 0, 0, FR, FL } }, +{ .ca_index = 0x25, .speakers = { FRH, FLH, RR, RL, 0, LFE, FR, FL } }, +{ .ca_index = 0x26, .speakers = { FRW, FLW, RR, RL, 0, 0, FR, FL } }, +{ .ca_index = 0x27, .speakers = { FRW, FLW, RR, RL, 0, LFE, FR, FL } }, +{ .ca_index = 0x28, .speakers = { TC, RC, RR, RL, FC, 0, FR, FL } }, +{ .ca_index = 0x29, .speakers = { TC, RC, RR, RL, FC, LFE, FR, FL } }, +{ .ca_index = 0x2a, .speakers = { FCH, RC, RR, RL, FC, 0, FR, FL } }, +{ .ca_index = 0x2b, .speakers = { FCH, RC, RR, RL, FC, LFE, FR, FL } }, +{ .ca_index = 0x2c, .speakers = { TC, FCH, RR, RL, FC, 0, FR, FL } }, +{ .ca_index = 0x2d, .speakers = { TC, FCH, RR, RL, FC, LFE, FR, FL } }, +{ .ca_index = 0x2e, .speakers = { FRH, FLH, RR, RL, FC, 0, FR, FL } }, +{ .ca_index = 0x2f, .speakers = { FRH, FLH, RR, RL, FC, LFE, FR, FL } }, +{ .ca_index = 0x30, .speakers = { FRW, FLW, RR, RL, FC, 0, FR, FL } }, +{ .ca_index = 0x31, .speakers = { FRW, FLW, RR, RL, FC, LFE, FR, FL } }, +}; + +static int hdmi_pin_set_slot_channel(struct hdac_device *codec, + hda_nid_t pin_nid, int asp_slot, int channel) +{ + return snd_hdac_codec_write(codec, pin_nid, 0, + AC_VERB_SET_HDMI_CHAN_SLOT, + (channel << 4) | asp_slot); +} + +static int hdmi_pin_get_slot_channel(struct hdac_device *codec, + hda_nid_t pin_nid, int asp_slot) +{ + return (snd_hdac_codec_read(codec, pin_nid, 0, + AC_VERB_GET_HDMI_CHAN_SLOT, + asp_slot) & 0xf0) >> 4; +} + +static int hdmi_get_channel_count(struct hdac_device *codec, hda_nid_t cvt_nid) +{ + return 1 + snd_hdac_codec_read(codec, cvt_nid, 0, + AC_VERB_GET_CVT_CHAN_COUNT, 0); +} + +static void hdmi_set_channel_count(struct hdac_device *codec, + hda_nid_t cvt_nid, int chs) +{ + if (chs != hdmi_get_channel_count(codec, cvt_nid)) + snd_hdac_codec_write(codec, cvt_nid, 0, + AC_VERB_SET_CVT_CHAN_COUNT, chs - 1); +} + +/* + * Channel mapping routines + */ + +/* + * Compute derived values in channel_allocations[]. + */ +static void init_channel_allocations(void) +{ + int i, j; + struct hdac_cea_channel_speaker_allocation *p; + + for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) { + p = channel_allocations + i; + p->channels = 0; + p->spk_mask = 0; + for (j = 0; j < ARRAY_SIZE(p->speakers); j++) + if (p->speakers[j]) { + p->channels++; + p->spk_mask |= p->speakers[j]; + } + } +} + +static int get_channel_allocation_order(int ca) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) { + if (channel_allocations[i].ca_index == ca) + break; + } + return i; +} + +void snd_hdac_print_channel_allocation(int spk_alloc, char *buf, int buflen) +{ + int i, j; + + for (i = 0, j = 0; i < ARRAY_SIZE(cea_speaker_allocation_names); i++) { + if (spk_alloc & (1 << i)) + j += snprintf(buf + j, buflen - j, " %s", + cea_speaker_allocation_names[i]); + } + buf[j] = '\0'; /* necessary when j == 0 */ +} +EXPORT_SYMBOL_GPL(snd_hdac_print_channel_allocation); + +/* + * The transformation takes two steps: + * + * eld->spk_alloc => (eld_speaker_allocation_bits[]) => spk_mask + * spk_mask => (channel_allocations[]) => ai->CA + * + * TODO: it could select the wrong CA from multiple candidates. +*/ +static int hdmi_channel_allocation_spk_alloc_blk(struct hdac_device *codec, + int spk_alloc, int channels) +{ + int i; + int ca = 0; + int spk_mask = 0; + char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE]; + + /* + * CA defaults to 0 for basic stereo audio + */ + if (channels <= 2) + return 0; + + /* + * expand ELD's speaker allocation mask + * + * ELD tells the speaker mask in a compact(paired) form, + * expand ELD's notions to match the ones used by Audio InfoFrame. + */ + for (i = 0; i < ARRAY_SIZE(eld_speaker_allocation_bits); i++) { + if (spk_alloc & (1 << i)) + spk_mask |= eld_speaker_allocation_bits[i]; + } + + /* search for the first working match in the CA table */ + for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) { + if (channels == channel_allocations[i].channels && + (spk_mask & channel_allocations[i].spk_mask) == + channel_allocations[i].spk_mask) { + ca = channel_allocations[i].ca_index; + break; + } + } + + if (!ca) { + /* + * if there was no match, select the regular ALSA channel + * allocation with the matching number of channels + */ + for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) { + if (channels == channel_allocations[i].channels) { + ca = channel_allocations[i].ca_index; + break; + } + } + } + + snd_hdac_print_channel_allocation(spk_alloc, buf, sizeof(buf)); + dev_dbg(&codec->dev, "HDMI: select CA 0x%x for %d-channel allocation: %s\n", + ca, channels, buf); + + return ca; +} + +static void hdmi_debug_channel_mapping(struct hdac_chmap *chmap, + hda_nid_t pin_nid) +{ +#ifdef CONFIG_SND_DEBUG_VERBOSE + int i; + int channel; + + for (i = 0; i < 8; i++) { + channel = chmap->ops.pin_get_slot_channel( + chmap->hdac, pin_nid, i); + dev_dbg(&chmap->hdac->dev, "HDMI: ASP channel %d => slot %d\n", + channel, i); + } +#endif +} + +static void hdmi_std_setup_channel_mapping(struct hdac_chmap *chmap, + hda_nid_t pin_nid, + bool non_pcm, + int ca) +{ + struct hdac_cea_channel_speaker_allocation *ch_alloc; + int i; + int err; + int order; + int non_pcm_mapping[8]; + + order = get_channel_allocation_order(ca); + ch_alloc = &channel_allocations[order]; + + if (hdmi_channel_mapping[ca][1] == 0) { + int hdmi_slot = 0; + /* fill actual channel mappings in ALSA channel (i) order */ + for (i = 0; i < ch_alloc->channels; i++) { + while (!ch_alloc->speakers[7 - hdmi_slot] && !WARN_ON(hdmi_slot >= 8)) + hdmi_slot++; /* skip zero slots */ + + hdmi_channel_mapping[ca][i] = (i << 4) | hdmi_slot++; + } + /* fill the rest of the slots with ALSA channel 0xf */ + for (hdmi_slot = 0; hdmi_slot < 8; hdmi_slot++) + if (!ch_alloc->speakers[7 - hdmi_slot]) + hdmi_channel_mapping[ca][i++] = (0xf << 4) | hdmi_slot; + } + + if (non_pcm) { + for (i = 0; i < ch_alloc->channels; i++) + non_pcm_mapping[i] = (i << 4) | i; + for (; i < 8; i++) + non_pcm_mapping[i] = (0xf << 4) | i; + } + + for (i = 0; i < 8; i++) { + int slotsetup = non_pcm ? non_pcm_mapping[i] : hdmi_channel_mapping[ca][i]; + int hdmi_slot = slotsetup & 0x0f; + int channel = (slotsetup & 0xf0) >> 4; + + err = chmap->ops.pin_set_slot_channel(chmap->hdac, + pin_nid, hdmi_slot, channel); + if (err) { + dev_dbg(&chmap->hdac->dev, "HDMI: channel mapping failed\n"); + break; + } + } +} + +struct channel_map_table { + unsigned char map; /* ALSA API channel map position */ + int spk_mask; /* speaker position bit mask */ +}; + +static struct channel_map_table map_tables[] = { + { SNDRV_CHMAP_FL, FL }, + { SNDRV_CHMAP_FR, FR }, + { SNDRV_CHMAP_RL, RL }, + { SNDRV_CHMAP_RR, RR }, + { SNDRV_CHMAP_LFE, LFE }, + { SNDRV_CHMAP_FC, FC }, + { SNDRV_CHMAP_RLC, RLC }, + { SNDRV_CHMAP_RRC, RRC }, + { SNDRV_CHMAP_RC, RC }, + { SNDRV_CHMAP_FLC, FLC }, + { SNDRV_CHMAP_FRC, FRC }, + { SNDRV_CHMAP_TFL, FLH }, + { SNDRV_CHMAP_TFR, FRH }, + { SNDRV_CHMAP_FLW, FLW }, + { SNDRV_CHMAP_FRW, FRW }, + { SNDRV_CHMAP_TC, TC }, + { SNDRV_CHMAP_TFC, FCH }, + {} /* terminator */ +}; + +/* from ALSA API channel position to speaker bit mask */ +int snd_hdac_chmap_to_spk_mask(unsigned char c) +{ + struct channel_map_table *t = map_tables; + + for (; t->map; t++) { + if (t->map == c) + return t->spk_mask; + } + return 0; +} +EXPORT_SYMBOL_GPL(snd_hdac_chmap_to_spk_mask); + +/* from ALSA API channel position to CEA slot */ +static int to_cea_slot(int ordered_ca, unsigned char pos) +{ + int mask = snd_hdac_chmap_to_spk_mask(pos); + int i; + + if (mask) { + for (i = 0; i < 8; i++) { + if (channel_allocations[ordered_ca].speakers[7 - i] == mask) + return i; + } + } + + return -1; +} + +/* from speaker bit mask to ALSA API channel position */ +int snd_hdac_spk_to_chmap(int spk) +{ + struct channel_map_table *t = map_tables; + + for (; t->map; t++) { + if (t->spk_mask == spk) + return t->map; + } + return 0; +} +EXPORT_SYMBOL_GPL(snd_hdac_spk_to_chmap); + +/* from CEA slot to ALSA API channel position */ +static int from_cea_slot(int ordered_ca, unsigned char slot) +{ + int mask = channel_allocations[ordered_ca].speakers[7 - slot]; + + return snd_hdac_spk_to_chmap(mask); +} + +/* get the CA index corresponding to the given ALSA API channel map */ +static int hdmi_manual_channel_allocation(int chs, unsigned char *map) +{ + int i, spks = 0, spk_mask = 0; + + for (i = 0; i < chs; i++) { + int mask = snd_hdac_chmap_to_spk_mask(map[i]); + + if (mask) { + spk_mask |= mask; + spks++; + } + } + + for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) { + if ((chs == channel_allocations[i].channels || + spks == channel_allocations[i].channels) && + (spk_mask & channel_allocations[i].spk_mask) == + channel_allocations[i].spk_mask) + return channel_allocations[i].ca_index; + } + return -1; +} + +/* set up the channel slots for the given ALSA API channel map */ +static int hdmi_manual_setup_channel_mapping(struct hdac_chmap *chmap, + hda_nid_t pin_nid, + int chs, unsigned char *map, + int ca) +{ + int ordered_ca = get_channel_allocation_order(ca); + int alsa_pos, hdmi_slot; + int assignments[8] = {[0 ... 7] = 0xf}; + + for (alsa_pos = 0; alsa_pos < chs; alsa_pos++) { + + hdmi_slot = to_cea_slot(ordered_ca, map[alsa_pos]); + + if (hdmi_slot < 0) + continue; /* unassigned channel */ + + assignments[hdmi_slot] = alsa_pos; + } + + for (hdmi_slot = 0; hdmi_slot < 8; hdmi_slot++) { + int err; + + err = chmap->ops.pin_set_slot_channel(chmap->hdac, + pin_nid, hdmi_slot, assignments[hdmi_slot]); + if (err) + return -EINVAL; + } + return 0; +} + +/* store ALSA API channel map from the current default map */ +static void hdmi_setup_fake_chmap(unsigned char *map, int ca) +{ + int i; + int ordered_ca = get_channel_allocation_order(ca); + + for (i = 0; i < 8; i++) { + if (i < channel_allocations[ordered_ca].channels) + map[i] = from_cea_slot(ordered_ca, hdmi_channel_mapping[ca][i] & 0x0f); + else + map[i] = 0; + } +} + +void snd_hdac_setup_channel_mapping(struct hdac_chmap *chmap, + hda_nid_t pin_nid, bool non_pcm, int ca, + int channels, unsigned char *map, + bool chmap_set) +{ + if (!non_pcm && chmap_set) { + hdmi_manual_setup_channel_mapping(chmap, pin_nid, + channels, map, ca); + } else { + hdmi_std_setup_channel_mapping(chmap, pin_nid, non_pcm, ca); + hdmi_setup_fake_chmap(map, ca); + } + + hdmi_debug_channel_mapping(chmap, pin_nid); +} +EXPORT_SYMBOL_GPL(snd_hdac_setup_channel_mapping); + +int snd_hdac_get_active_channels(int ca) +{ + int ordered_ca = get_channel_allocation_order(ca); + + return channel_allocations[ordered_ca].channels; +} +EXPORT_SYMBOL_GPL(snd_hdac_get_active_channels); + +struct hdac_cea_channel_speaker_allocation *snd_hdac_get_ch_alloc_from_ca(int ca) +{ + return &channel_allocations[get_channel_allocation_order(ca)]; +} +EXPORT_SYMBOL_GPL(snd_hdac_get_ch_alloc_from_ca); + +int snd_hdac_channel_allocation(struct hdac_device *hdac, int spk_alloc, + int channels, bool chmap_set, bool non_pcm, unsigned char *map) +{ + int ca; + + if (!non_pcm && chmap_set) + ca = hdmi_manual_channel_allocation(channels, map); + else + ca = hdmi_channel_allocation_spk_alloc_blk(hdac, + spk_alloc, channels); + + if (ca < 0) + ca = 0; + + return ca; +} +EXPORT_SYMBOL_GPL(snd_hdac_channel_allocation); + +/* + * ALSA API channel-map control callbacks + */ +static int hdmi_chmap_ctl_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); + struct hdac_chmap *chmap = info->private_data; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = chmap->channels_max; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = SNDRV_CHMAP_LAST; + return 0; +} + +static int hdmi_chmap_cea_alloc_validate_get_type(struct hdac_chmap *chmap, + struct hdac_cea_channel_speaker_allocation *cap, int channels) +{ + /* If the speaker allocation matches the channel count, it is OK.*/ + if (cap->channels != channels) + return -1; + + /* all channels are remappable freely */ + return SNDRV_CTL_TLVT_CHMAP_VAR; +} + +static void hdmi_cea_alloc_to_tlv_chmap(struct hdac_chmap *hchmap, + struct hdac_cea_channel_speaker_allocation *cap, + unsigned int *chmap, int channels) +{ + int count = 0; + int c; + + for (c = 7; c >= 0; c--) { + int spk = cap->speakers[c]; + + if (!spk) + continue; + + chmap[count++] = snd_hdac_spk_to_chmap(spk); + } + + WARN_ON(count != channels); +} + +static int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag, + unsigned int size, unsigned int __user *tlv) +{ + struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); + struct hdac_chmap *chmap = info->private_data; + unsigned int __user *dst; + int chs, count = 0; + + if (size < 8) + return -ENOMEM; + if (put_user(SNDRV_CTL_TLVT_CONTAINER, tlv)) + return -EFAULT; + size -= 8; + dst = tlv + 2; + for (chs = 2; chs <= chmap->channels_max; chs++) { + int i; + struct hdac_cea_channel_speaker_allocation *cap; + + cap = channel_allocations; + for (i = 0; i < ARRAY_SIZE(channel_allocations); i++, cap++) { + int chs_bytes = chs * 4; + int type = chmap->ops.chmap_cea_alloc_validate_get_type( + chmap, cap, chs); + unsigned int tlv_chmap[8]; + + if (type < 0) + continue; + if (size < 8) + return -ENOMEM; + if (put_user(type, dst) || + put_user(chs_bytes, dst + 1)) + return -EFAULT; + dst += 2; + size -= 8; + count += 8; + if (size < chs_bytes) + return -ENOMEM; + size -= chs_bytes; + count += chs_bytes; + chmap->ops.cea_alloc_to_tlv_chmap(chmap, cap, + tlv_chmap, chs); + if (copy_to_user(dst, tlv_chmap, chs_bytes)) + return -EFAULT; + dst += chs; + } + } + if (put_user(count, tlv + 1)) + return -EFAULT; + return 0; +} + +static int hdmi_chmap_ctl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); + struct hdac_chmap *chmap = info->private_data; + int pcm_idx = kcontrol->private_value; + unsigned char pcm_chmap[8]; + int i; + + memset(pcm_chmap, 0, sizeof(pcm_chmap)); + chmap->ops.get_chmap(chmap->hdac, pcm_idx, pcm_chmap); + + for (i = 0; i < sizeof(chmap); i++) + ucontrol->value.integer.value[i] = pcm_chmap[i]; + + return 0; +} + +static int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); + struct hdac_chmap *hchmap = info->private_data; + int pcm_idx = kcontrol->private_value; + unsigned int ctl_idx; + struct snd_pcm_substream *substream; + unsigned char chmap[8], per_pin_chmap[8]; + int i, err, ca, prepared = 0; + + /* No monitor is connected in dyn_pcm_assign. + * It's invalid to setup the chmap + */ + if (!hchmap->ops.is_pcm_attached(hchmap->hdac, pcm_idx)) + return 0; + + ctl_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); + substream = snd_pcm_chmap_substream(info, ctl_idx); + if (!substream || !substream->runtime) + return 0; /* just for avoiding error from alsactl restore */ + switch (substream->runtime->status->state) { + case SNDRV_PCM_STATE_OPEN: + case SNDRV_PCM_STATE_SETUP: + break; + case SNDRV_PCM_STATE_PREPARED: + prepared = 1; + break; + default: + return -EBUSY; + } + memset(chmap, 0, sizeof(chmap)); + for (i = 0; i < ARRAY_SIZE(chmap); i++) + chmap[i] = ucontrol->value.integer.value[i]; + + hchmap->ops.get_chmap(hchmap->hdac, pcm_idx, per_pin_chmap); + if (!memcmp(chmap, per_pin_chmap, sizeof(chmap))) + return 0; + ca = hdmi_manual_channel_allocation(ARRAY_SIZE(chmap), chmap); + if (ca < 0) + return -EINVAL; + if (hchmap->ops.chmap_validate) { + err = hchmap->ops.chmap_validate(hchmap, ca, + ARRAY_SIZE(chmap), chmap); + if (err) + return err; + } + + hchmap->ops.set_chmap(hchmap->hdac, pcm_idx, chmap, prepared); + + return 0; +} + +static const struct hdac_chmap_ops chmap_ops = { + .chmap_cea_alloc_validate_get_type = hdmi_chmap_cea_alloc_validate_get_type, + .cea_alloc_to_tlv_chmap = hdmi_cea_alloc_to_tlv_chmap, + .pin_get_slot_channel = hdmi_pin_get_slot_channel, + .pin_set_slot_channel = hdmi_pin_set_slot_channel, + .set_channel_count = hdmi_set_channel_count, +}; + +void snd_hdac_register_chmap_ops(struct hdac_device *hdac, + struct hdac_chmap *chmap) +{ + chmap->ops = chmap_ops; + chmap->hdac = hdac; + init_channel_allocations(); +} +EXPORT_SYMBOL_GPL(snd_hdac_register_chmap_ops); + +int snd_hdac_add_chmap_ctls(struct snd_pcm *pcm, int pcm_idx, + struct hdac_chmap *hchmap) +{ + struct snd_pcm_chmap *chmap; + struct snd_kcontrol *kctl; + int err, i; + + err = snd_pcm_add_chmap_ctls(pcm, + SNDRV_PCM_STREAM_PLAYBACK, + NULL, 0, pcm_idx, &chmap); + if (err < 0) + return err; + /* override handlers */ + chmap->private_data = hchmap; + kctl = chmap->kctl; + for (i = 0; i < kctl->count; i++) + kctl->vd[i].access |= SNDRV_CTL_ELEM_ACCESS_WRITE; + kctl->info = hdmi_chmap_ctl_info; + kctl->get = hdmi_chmap_ctl_get; + kctl->put = hdmi_chmap_ctl_put; + kctl->tlv.c = hdmi_chmap_ctl_tlv; + + return 0; +} +EXPORT_SYMBOL_GPL(snd_hdac_add_chmap_ctls); 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_eld.c b/sound/pci/hda/hda_eld.c index bc2e08257c2e..ba7fe9b6655c 100644 --- a/sound/pci/hda/hda_eld.c +++ b/sound/pci/hda/hda_eld.c @@ -26,6 +26,7 @@ #include <linux/slab.h> #include <sound/core.h> #include <asm/unaligned.h> +#include <sound/hda_chmap.h> #include "hda_codec.h" #include "hda_local.h" @@ -42,20 +43,6 @@ enum cea_edid_versions { CEA_EDID_VER_RESERVED = 4, }; -static const char * const cea_speaker_allocation_names[] = { - /* 0 */ "FL/FR", - /* 1 */ "LFE", - /* 2 */ "FC", - /* 3 */ "RL/RR", - /* 4 */ "RC", - /* 5 */ "FLC/FRC", - /* 6 */ "RLC/RRC", - /* 7 */ "FLW/FRW", - /* 8 */ "FLH/FRH", - /* 9 */ "TC", - /* 10 */ "FCH", -}; - static const char * const eld_connection_type_names[4] = { "HDMI", "DisplayPort", @@ -419,18 +406,6 @@ static void hdmi_show_short_audio_desc(struct hda_codec *codec, a->channels, buf, buf2); } -void snd_print_channel_allocation(int spk_alloc, char *buf, int buflen) -{ - int i, j; - - for (i = 0, j = 0; i < ARRAY_SIZE(cea_speaker_allocation_names); i++) { - if (spk_alloc & (1 << i)) - j += snprintf(buf + j, buflen - j, " %s", - cea_speaker_allocation_names[i]); - } - buf[j] = '\0'; /* necessary when j == 0 */ -} - void snd_hdmi_show_eld(struct hda_codec *codec, struct parsed_hdmi_eld *e) { int i; @@ -441,7 +416,7 @@ void snd_hdmi_show_eld(struct hda_codec *codec, struct parsed_hdmi_eld *e) if (e->spk_alloc) { char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE]; - snd_print_channel_allocation(e->spk_alloc, buf, sizeof(buf)); + snd_hdac_print_channel_allocation(e->spk_alloc, buf, sizeof(buf)); codec_dbg(codec, "HDMI: available speakers:%s\n", buf); } @@ -516,7 +491,7 @@ void snd_hdmi_print_eld_info(struct hdmi_eld *eld, snd_iprintf(buffer, "support_ai\t\t%d\n", e->support_ai); snd_iprintf(buffer, "audio_sync_delay\t%d\n", e->aud_synch_delay); - snd_print_channel_allocation(e->spk_alloc, buf, sizeof(buf)); + snd_hdac_print_channel_allocation(e->spk_alloc, buf, sizeof(buf)); snd_iprintf(buffer, "speakers\t\t[0x%x]%s\n", e->spk_alloc, buf); snd_iprintf(buffer, "sad_count\t\t%d\n", e->sad_count); 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 b90e4f3967de..4447aa57c42c 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -39,6 +39,7 @@ #include <sound/tlv.h> #include <sound/hdaudio.h> #include <sound/hda_i915.h> +#include <sound/hda_chmap.h> #include "hda_codec.h" #include "hda_local.h" #include "hda_jack.h" @@ -75,6 +76,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 +87,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 */ @@ -97,19 +100,11 @@ struct hdmi_spec_per_pin { #endif }; -struct cea_channel_speaker_allocation; - /* operations used by generic code that can be overridden by patches */ struct hdmi_ops { int (*pin_get_eld)(struct hda_codec *codec, hda_nid_t pin_nid, unsigned char *buf, int *eld_size); - /* get and set channel assigned to each HDMI ASP (audio sample packet) slot */ - int (*pin_get_slot_channel)(struct hda_codec *codec, hda_nid_t pin_nid, - int asp_slot); - int (*pin_set_slot_channel)(struct hda_codec *codec, hda_nid_t pin_nid, - int asp_slot, int channel); - void (*pin_setup_infoframe)(struct hda_codec *codec, hda_nid_t pin_nid, int ca, int active_channels, int conn_type); @@ -119,15 +114,12 @@ struct hdmi_ops { int (*setup_stream)(struct hda_codec *codec, hda_nid_t cvt_nid, hda_nid_t pin_nid, u32 stream_tag, int format); - /* Helpers for producing the channel map TLVs. These can be overridden - * for devices that have non-standard mapping requirements. */ - int (*chmap_cea_alloc_validate_get_type)(struct cea_channel_speaker_allocation *cap, - int channels); - void (*cea_alloc_to_tlv_chmap)(struct cea_channel_speaker_allocation *cap, - unsigned int *chmap, int channels); +}; - /* check that the user-given chmap is supported */ - 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 { @@ -137,14 +129,22 @@ struct hdmi_spec { int num_pins; struct snd_array pins; /* struct hdmi_spec_per_pin */ - struct hda_pcm *pcm_rec[16]; - unsigned int channels_max; /* max over all cvts */ + 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; struct hdmi_eld temp_eld; struct hdmi_ops ops; bool dyn_pin_out; - + bool dyn_pcm_assign; /* * Non-generic VIA/NVIDIA specific */ @@ -154,6 +154,8 @@ struct hdmi_spec { /* i915/powerwell (Haswell+/Valleyview+) specific */ struct i915_audio_component_audio_ops i915_audio_ops; bool i915_bound; /* was i915 bound in this driver? */ + + struct hdac_chmap chmap; }; #ifdef CONFIG_SND_HDA_I915 @@ -196,173 +198,6 @@ union audio_infoframe { }; /* - * CEA speaker placement: - * - * FLH FCH FRH - * FLW FL FLC FC FRC FR FRW - * - * LFE - * TC - * - * RL RLC RC RRC RR - * - * The Left/Right Surround channel _notions_ LS/RS in SMPTE 320M corresponds to - * CEA RL/RR; The SMPTE channel _assignment_ C/LFE is swapped to CEA LFE/FC. - */ -enum cea_speaker_placement { - FL = (1 << 0), /* Front Left */ - FC = (1 << 1), /* Front Center */ - FR = (1 << 2), /* Front Right */ - FLC = (1 << 3), /* Front Left Center */ - FRC = (1 << 4), /* Front Right Center */ - RL = (1 << 5), /* Rear Left */ - RC = (1 << 6), /* Rear Center */ - RR = (1 << 7), /* Rear Right */ - RLC = (1 << 8), /* Rear Left Center */ - RRC = (1 << 9), /* Rear Right Center */ - LFE = (1 << 10), /* Low Frequency Effect */ - FLW = (1 << 11), /* Front Left Wide */ - FRW = (1 << 12), /* Front Right Wide */ - FLH = (1 << 13), /* Front Left High */ - FCH = (1 << 14), /* Front Center High */ - FRH = (1 << 15), /* Front Right High */ - TC = (1 << 16), /* Top Center */ -}; - -/* - * ELD SA bits in the CEA Speaker Allocation data block - */ -static int eld_speaker_allocation_bits[] = { - [0] = FL | FR, - [1] = LFE, - [2] = FC, - [3] = RL | RR, - [4] = RC, - [5] = FLC | FRC, - [6] = RLC | RRC, - /* the following are not defined in ELD yet */ - [7] = FLW | FRW, - [8] = FLH | FRH, - [9] = TC, - [10] = FCH, -}; - -struct cea_channel_speaker_allocation { - int ca_index; - int speakers[8]; - - /* derived values, just for convenience */ - int channels; - int spk_mask; -}; - -/* - * ALSA sequence is: - * - * surround40 surround41 surround50 surround51 surround71 - * ch0 front left = = = = - * ch1 front right = = = = - * ch2 rear left = = = = - * ch3 rear right = = = = - * ch4 LFE center center center - * ch5 LFE LFE - * ch6 side left - * ch7 side right - * - * surround71 = {FL, FR, RLC, RRC, FC, LFE, RL, RR} - */ -static int hdmi_channel_mapping[0x32][8] = { - /* stereo */ - [0x00] = { 0x00, 0x11, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7 }, - /* 2.1 */ - [0x01] = { 0x00, 0x11, 0x22, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7 }, - /* Dolby Surround */ - [0x02] = { 0x00, 0x11, 0x23, 0xf2, 0xf4, 0xf5, 0xf6, 0xf7 }, - /* surround40 */ - [0x08] = { 0x00, 0x11, 0x24, 0x35, 0xf3, 0xf2, 0xf6, 0xf7 }, - /* 4ch */ - [0x03] = { 0x00, 0x11, 0x23, 0x32, 0x44, 0xf5, 0xf6, 0xf7 }, - /* surround41 */ - [0x09] = { 0x00, 0x11, 0x24, 0x35, 0x42, 0xf3, 0xf6, 0xf7 }, - /* surround50 */ - [0x0a] = { 0x00, 0x11, 0x24, 0x35, 0x43, 0xf2, 0xf6, 0xf7 }, - /* surround51 */ - [0x0b] = { 0x00, 0x11, 0x24, 0x35, 0x43, 0x52, 0xf6, 0xf7 }, - /* 7.1 */ - [0x13] = { 0x00, 0x11, 0x26, 0x37, 0x43, 0x52, 0x64, 0x75 }, -}; - -/* - * This is an ordered list! - * - * The preceding ones have better chances to be selected by - * hdmi_channel_allocation(). - */ -static struct cea_channel_speaker_allocation channel_allocations[] = { -/* channel: 7 6 5 4 3 2 1 0 */ -{ .ca_index = 0x00, .speakers = { 0, 0, 0, 0, 0, 0, FR, FL } }, - /* 2.1 */ -{ .ca_index = 0x01, .speakers = { 0, 0, 0, 0, 0, LFE, FR, FL } }, - /* Dolby Surround */ -{ .ca_index = 0x02, .speakers = { 0, 0, 0, 0, FC, 0, FR, FL } }, - /* surround40 */ -{ .ca_index = 0x08, .speakers = { 0, 0, RR, RL, 0, 0, FR, FL } }, - /* surround41 */ -{ .ca_index = 0x09, .speakers = { 0, 0, RR, RL, 0, LFE, FR, FL } }, - /* surround50 */ -{ .ca_index = 0x0a, .speakers = { 0, 0, RR, RL, FC, 0, FR, FL } }, - /* surround51 */ -{ .ca_index = 0x0b, .speakers = { 0, 0, RR, RL, FC, LFE, FR, FL } }, - /* 6.1 */ -{ .ca_index = 0x0f, .speakers = { 0, RC, RR, RL, FC, LFE, FR, FL } }, - /* surround71 */ -{ .ca_index = 0x13, .speakers = { RRC, RLC, RR, RL, FC, LFE, FR, FL } }, - -{ .ca_index = 0x03, .speakers = { 0, 0, 0, 0, FC, LFE, FR, FL } }, -{ .ca_index = 0x04, .speakers = { 0, 0, 0, RC, 0, 0, FR, FL } }, -{ .ca_index = 0x05, .speakers = { 0, 0, 0, RC, 0, LFE, FR, FL } }, -{ .ca_index = 0x06, .speakers = { 0, 0, 0, RC, FC, 0, FR, FL } }, -{ .ca_index = 0x07, .speakers = { 0, 0, 0, RC, FC, LFE, FR, FL } }, -{ .ca_index = 0x0c, .speakers = { 0, RC, RR, RL, 0, 0, FR, FL } }, -{ .ca_index = 0x0d, .speakers = { 0, RC, RR, RL, 0, LFE, FR, FL } }, -{ .ca_index = 0x0e, .speakers = { 0, RC, RR, RL, FC, 0, FR, FL } }, -{ .ca_index = 0x10, .speakers = { RRC, RLC, RR, RL, 0, 0, FR, FL } }, -{ .ca_index = 0x11, .speakers = { RRC, RLC, RR, RL, 0, LFE, FR, FL } }, -{ .ca_index = 0x12, .speakers = { RRC, RLC, RR, RL, FC, 0, FR, FL } }, -{ .ca_index = 0x14, .speakers = { FRC, FLC, 0, 0, 0, 0, FR, FL } }, -{ .ca_index = 0x15, .speakers = { FRC, FLC, 0, 0, 0, LFE, FR, FL } }, -{ .ca_index = 0x16, .speakers = { FRC, FLC, 0, 0, FC, 0, FR, FL } }, -{ .ca_index = 0x17, .speakers = { FRC, FLC, 0, 0, FC, LFE, FR, FL } }, -{ .ca_index = 0x18, .speakers = { FRC, FLC, 0, RC, 0, 0, FR, FL } }, -{ .ca_index = 0x19, .speakers = { FRC, FLC, 0, RC, 0, LFE, FR, FL } }, -{ .ca_index = 0x1a, .speakers = { FRC, FLC, 0, RC, FC, 0, FR, FL } }, -{ .ca_index = 0x1b, .speakers = { FRC, FLC, 0, RC, FC, LFE, FR, FL } }, -{ .ca_index = 0x1c, .speakers = { FRC, FLC, RR, RL, 0, 0, FR, FL } }, -{ .ca_index = 0x1d, .speakers = { FRC, FLC, RR, RL, 0, LFE, FR, FL } }, -{ .ca_index = 0x1e, .speakers = { FRC, FLC, RR, RL, FC, 0, FR, FL } }, -{ .ca_index = 0x1f, .speakers = { FRC, FLC, RR, RL, FC, LFE, FR, FL } }, -{ .ca_index = 0x20, .speakers = { 0, FCH, RR, RL, FC, 0, FR, FL } }, -{ .ca_index = 0x21, .speakers = { 0, FCH, RR, RL, FC, LFE, FR, FL } }, -{ .ca_index = 0x22, .speakers = { TC, 0, RR, RL, FC, 0, FR, FL } }, -{ .ca_index = 0x23, .speakers = { TC, 0, RR, RL, FC, LFE, FR, FL } }, -{ .ca_index = 0x24, .speakers = { FRH, FLH, RR, RL, 0, 0, FR, FL } }, -{ .ca_index = 0x25, .speakers = { FRH, FLH, RR, RL, 0, LFE, FR, FL } }, -{ .ca_index = 0x26, .speakers = { FRW, FLW, RR, RL, 0, 0, FR, FL } }, -{ .ca_index = 0x27, .speakers = { FRW, FLW, RR, RL, 0, LFE, FR, FL } }, -{ .ca_index = 0x28, .speakers = { TC, RC, RR, RL, FC, 0, FR, FL } }, -{ .ca_index = 0x29, .speakers = { TC, RC, RR, RL, FC, LFE, FR, FL } }, -{ .ca_index = 0x2a, .speakers = { FCH, RC, RR, RL, FC, 0, FR, FL } }, -{ .ca_index = 0x2b, .speakers = { FCH, RC, RR, RL, FC, LFE, FR, FL } }, -{ .ca_index = 0x2c, .speakers = { TC, FCH, RR, RL, FC, 0, FR, FL } }, -{ .ca_index = 0x2d, .speakers = { TC, FCH, RR, RL, FC, LFE, FR, FL } }, -{ .ca_index = 0x2e, .speakers = { FRH, FLH, RR, RL, FC, 0, FR, FL } }, -{ .ca_index = 0x2f, .speakers = { FRH, FLH, RR, RL, FC, LFE, FR, FL } }, -{ .ca_index = 0x30, .speakers = { FRW, FLW, RR, RL, FC, 0, FR, FL } }, -{ .ca_index = 0x31, .speakers = { FRW, FLW, RR, RL, FC, LFE, FR, FL } }, -}; - - -/* * HDMI routines */ @@ -370,7 +205,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 +223,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 +289,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 +316,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 +342,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 +355,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 +365,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; } @@ -547,20 +432,6 @@ static void hdmi_init_pin(struct hda_codec *codec, hda_nid_t pin_nid) AC_VERB_SET_PIN_WIDGET_CONTROL, pin_out); } -static int hdmi_get_channel_count(struct hda_codec *codec, hda_nid_t cvt_nid) -{ - return 1 + snd_hda_codec_read(codec, cvt_nid, 0, - AC_VERB_GET_CVT_CHAN_COUNT, 0); -} - -static void hdmi_set_channel_count(struct hda_codec *codec, - hda_nid_t cvt_nid, int chs) -{ - if (chs != hdmi_get_channel_count(codec, cvt_nid)) - snd_hda_codec_write(codec, cvt_nid, 0, - AC_VERB_SET_CVT_CHAN_COUNT, chs - 1); -} - /* * ELD proc files */ @@ -625,339 +496,6 @@ static inline void eld_proc_free(struct hdmi_spec_per_pin *per_pin) #endif /* - * Channel mapping routines - */ - -/* - * Compute derived values in channel_allocations[]. - */ -static void init_channel_allocations(void) -{ - int i, j; - struct cea_channel_speaker_allocation *p; - - for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) { - p = channel_allocations + i; - p->channels = 0; - p->spk_mask = 0; - for (j = 0; j < ARRAY_SIZE(p->speakers); j++) - if (p->speakers[j]) { - p->channels++; - p->spk_mask |= p->speakers[j]; - } - } -} - -static int get_channel_allocation_order(int ca) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) { - if (channel_allocations[i].ca_index == ca) - break; - } - return i; -} - -/* - * The transformation takes two steps: - * - * eld->spk_alloc => (eld_speaker_allocation_bits[]) => spk_mask - * spk_mask => (channel_allocations[]) => ai->CA - * - * TODO: it could select the wrong CA from multiple candidates. -*/ -static int hdmi_channel_allocation(struct hda_codec *codec, - struct hdmi_eld *eld, int channels) -{ - int i; - int ca = 0; - int spk_mask = 0; - char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE]; - - /* - * CA defaults to 0 for basic stereo audio - */ - if (channels <= 2) - return 0; - - /* - * expand ELD's speaker allocation mask - * - * ELD tells the speaker mask in a compact(paired) form, - * expand ELD's notions to match the ones used by Audio InfoFrame. - */ - for (i = 0; i < ARRAY_SIZE(eld_speaker_allocation_bits); i++) { - if (eld->info.spk_alloc & (1 << i)) - spk_mask |= eld_speaker_allocation_bits[i]; - } - - /* search for the first working match in the CA table */ - for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) { - if (channels == channel_allocations[i].channels && - (spk_mask & channel_allocations[i].spk_mask) == - channel_allocations[i].spk_mask) { - ca = channel_allocations[i].ca_index; - break; - } - } - - if (!ca) { - /* if there was no match, select the regular ALSA channel - * allocation with the matching number of channels */ - for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) { - if (channels == channel_allocations[i].channels) { - ca = channel_allocations[i].ca_index; - break; - } - } - } - - snd_print_channel_allocation(eld->info.spk_alloc, buf, sizeof(buf)); - codec_dbg(codec, "HDMI: select CA 0x%x for %d-channel allocation: %s\n", - ca, channels, buf); - - return ca; -} - -static void hdmi_debug_channel_mapping(struct hda_codec *codec, - hda_nid_t pin_nid) -{ -#ifdef CONFIG_SND_DEBUG_VERBOSE - struct hdmi_spec *spec = codec->spec; - int i; - int channel; - - for (i = 0; i < 8; i++) { - channel = spec->ops.pin_get_slot_channel(codec, pin_nid, i); - codec_dbg(codec, "HDMI: ASP channel %d => slot %d\n", - channel, i); - } -#endif -} - -static void hdmi_std_setup_channel_mapping(struct hda_codec *codec, - hda_nid_t pin_nid, - bool non_pcm, - int ca) -{ - struct hdmi_spec *spec = codec->spec; - struct cea_channel_speaker_allocation *ch_alloc; - int i; - int err; - int order; - int non_pcm_mapping[8]; - - order = get_channel_allocation_order(ca); - ch_alloc = &channel_allocations[order]; - - if (hdmi_channel_mapping[ca][1] == 0) { - int hdmi_slot = 0; - /* fill actual channel mappings in ALSA channel (i) order */ - for (i = 0; i < ch_alloc->channels; i++) { - while (!ch_alloc->speakers[7 - hdmi_slot] && !WARN_ON(hdmi_slot >= 8)) - hdmi_slot++; /* skip zero slots */ - - hdmi_channel_mapping[ca][i] = (i << 4) | hdmi_slot++; - } - /* fill the rest of the slots with ALSA channel 0xf */ - for (hdmi_slot = 0; hdmi_slot < 8; hdmi_slot++) - if (!ch_alloc->speakers[7 - hdmi_slot]) - hdmi_channel_mapping[ca][i++] = (0xf << 4) | hdmi_slot; - } - - if (non_pcm) { - for (i = 0; i < ch_alloc->channels; i++) - non_pcm_mapping[i] = (i << 4) | i; - for (; i < 8; i++) - non_pcm_mapping[i] = (0xf << 4) | i; - } - - for (i = 0; i < 8; i++) { - int slotsetup = non_pcm ? non_pcm_mapping[i] : hdmi_channel_mapping[ca][i]; - int hdmi_slot = slotsetup & 0x0f; - int channel = (slotsetup & 0xf0) >> 4; - err = spec->ops.pin_set_slot_channel(codec, pin_nid, hdmi_slot, channel); - if (err) { - codec_dbg(codec, "HDMI: channel mapping failed\n"); - break; - } - } -} - -struct channel_map_table { - unsigned char map; /* ALSA API channel map position */ - int spk_mask; /* speaker position bit mask */ -}; - -static struct channel_map_table map_tables[] = { - { SNDRV_CHMAP_FL, FL }, - { SNDRV_CHMAP_FR, FR }, - { SNDRV_CHMAP_RL, RL }, - { SNDRV_CHMAP_RR, RR }, - { SNDRV_CHMAP_LFE, LFE }, - { SNDRV_CHMAP_FC, FC }, - { SNDRV_CHMAP_RLC, RLC }, - { SNDRV_CHMAP_RRC, RRC }, - { SNDRV_CHMAP_RC, RC }, - { SNDRV_CHMAP_FLC, FLC }, - { SNDRV_CHMAP_FRC, FRC }, - { SNDRV_CHMAP_TFL, FLH }, - { SNDRV_CHMAP_TFR, FRH }, - { SNDRV_CHMAP_FLW, FLW }, - { SNDRV_CHMAP_FRW, FRW }, - { SNDRV_CHMAP_TC, TC }, - { SNDRV_CHMAP_TFC, FCH }, - {} /* terminator */ -}; - -/* from ALSA API channel position to speaker bit mask */ -static int to_spk_mask(unsigned char c) -{ - struct channel_map_table *t = map_tables; - for (; t->map; t++) { - if (t->map == c) - return t->spk_mask; - } - return 0; -} - -/* from ALSA API channel position to CEA slot */ -static int to_cea_slot(int ordered_ca, unsigned char pos) -{ - int mask = to_spk_mask(pos); - int i; - - if (mask) { - for (i = 0; i < 8; i++) { - if (channel_allocations[ordered_ca].speakers[7 - i] == mask) - return i; - } - } - - return -1; -} - -/* from speaker bit mask to ALSA API channel position */ -static int spk_to_chmap(int spk) -{ - struct channel_map_table *t = map_tables; - for (; t->map; t++) { - if (t->spk_mask == spk) - return t->map; - } - return 0; -} - -/* from CEA slot to ALSA API channel position */ -static int from_cea_slot(int ordered_ca, unsigned char slot) -{ - int mask = channel_allocations[ordered_ca].speakers[7 - slot]; - - return spk_to_chmap(mask); -} - -/* get the CA index corresponding to the given ALSA API channel map */ -static int hdmi_manual_channel_allocation(int chs, unsigned char *map) -{ - int i, spks = 0, spk_mask = 0; - - for (i = 0; i < chs; i++) { - int mask = to_spk_mask(map[i]); - if (mask) { - spk_mask |= mask; - spks++; - } - } - - for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) { - if ((chs == channel_allocations[i].channels || - spks == channel_allocations[i].channels) && - (spk_mask & channel_allocations[i].spk_mask) == - channel_allocations[i].spk_mask) - return channel_allocations[i].ca_index; - } - return -1; -} - -/* set up the channel slots for the given ALSA API channel map */ -static int hdmi_manual_setup_channel_mapping(struct hda_codec *codec, - hda_nid_t pin_nid, - int chs, unsigned char *map, - int ca) -{ - struct hdmi_spec *spec = codec->spec; - int ordered_ca = get_channel_allocation_order(ca); - int alsa_pos, hdmi_slot; - int assignments[8] = {[0 ... 7] = 0xf}; - - for (alsa_pos = 0; alsa_pos < chs; alsa_pos++) { - - hdmi_slot = to_cea_slot(ordered_ca, map[alsa_pos]); - - if (hdmi_slot < 0) - continue; /* unassigned channel */ - - assignments[hdmi_slot] = alsa_pos; - } - - for (hdmi_slot = 0; hdmi_slot < 8; hdmi_slot++) { - int err; - - err = spec->ops.pin_set_slot_channel(codec, pin_nid, hdmi_slot, - assignments[hdmi_slot]); - if (err) - return -EINVAL; - } - return 0; -} - -/* store ALSA API channel map from the current default map */ -static void hdmi_setup_fake_chmap(unsigned char *map, int ca) -{ - int i; - int ordered_ca = get_channel_allocation_order(ca); - for (i = 0; i < 8; i++) { - if (i < channel_allocations[ordered_ca].channels) - map[i] = from_cea_slot(ordered_ca, hdmi_channel_mapping[ca][i] & 0x0f); - else - map[i] = 0; - } -} - -static void hdmi_setup_channel_mapping(struct hda_codec *codec, - hda_nid_t pin_nid, bool non_pcm, int ca, - int channels, unsigned char *map, - bool chmap_set) -{ - if (!non_pcm && chmap_set) { - hdmi_manual_setup_channel_mapping(codec, pin_nid, - channels, map, ca); - } else { - hdmi_std_setup_channel_mapping(codec, pin_nid, non_pcm, ca); - hdmi_setup_fake_chmap(map, ca); - } - - hdmi_debug_channel_mapping(codec, pin_nid); -} - -static int hdmi_pin_set_slot_channel(struct hda_codec *codec, hda_nid_t pin_nid, - int asp_slot, int channel) -{ - return snd_hda_codec_write(codec, pin_nid, 0, - AC_VERB_SET_HDMI_CHAN_SLOT, - (channel << 4) | asp_slot); -} - -static int hdmi_pin_get_slot_channel(struct hda_codec *codec, hda_nid_t pin_nid, - int asp_slot) -{ - return (snd_hda_codec_read(codec, pin_nid, 0, - AC_VERB_GET_HDMI_CHAN_SLOT, - asp_slot) & 0xf0) >> 4; -} - -/* * Audio InfoFrame routines */ @@ -1132,11 +670,12 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, bool non_pcm) { struct hdmi_spec *spec = codec->spec; + struct hdac_chmap *chmap = &spec->chmap; hda_nid_t pin_nid = per_pin->pin_nid; int channels = per_pin->channels; int active_channels; struct hdmi_eld *eld; - int ca, ordered_ca; + int ca; if (!channels) return; @@ -1148,25 +687,22 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, eld = &per_pin->sink_eld; - if (!non_pcm && per_pin->chmap_set) - ca = hdmi_manual_channel_allocation(channels, per_pin->chmap); - else - ca = hdmi_channel_allocation(codec, eld, channels); - if (ca < 0) - ca = 0; + ca = snd_hdac_channel_allocation(&codec->core, + eld->info.spk_alloc, channels, + per_pin->chmap_set, non_pcm, per_pin->chmap); - ordered_ca = get_channel_allocation_order(ca); - active_channels = channel_allocations[ordered_ca].channels; + active_channels = snd_hdac_get_active_channels(ca); - hdmi_set_channel_count(codec, per_pin->cvt_nid, active_channels); + chmap->ops.set_channel_count(&codec->core, per_pin->cvt_nid, + active_channels); /* * always configure channel mapping, it may have been changed by the * user in the meantime */ - hdmi_setup_channel_mapping(codec, pin_nid, non_pcm, ca, - channels, per_pin->chmap, - per_pin->chmap_set); + snd_hdac_setup_channel_mapping(&spec->chmap, + pin_nid, non_pcm, ca, channels, + per_pin->chmap, per_pin->chmap_set); spec->ops.pin_setup_infoframe(codec, pin_nid, ca, active_channels, eld->info.conn_type); @@ -1338,6 +874,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 +887,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 +900,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 +914,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 +943,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 +1008,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 +1085,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 +1137,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 +1146,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 +1154,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 +1195,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 +1322,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 +1376,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 +1448,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 +1501,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 +1519,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 +1575,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) @@ -1773,8 +1610,8 @@ static int hdmi_add_cvt(struct hda_codec *codec, hda_nid_t cvt_nid) per_cvt->channels_min = 2; if (chans <= 16) { per_cvt->channels_max = chans; - if (chans > spec->channels_max) - spec->channels_max = chans; + if (chans > spec->chmap.channels_max) + spec->chmap.channels_max = chans; } err = snd_hda_query_supported_pcm(codec, cvt_nid, @@ -1851,13 +1688,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 +1740,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 +1748,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 +1767,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 +1785,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 +1808,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 +1815,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; @@ -1956,162 +1828,42 @@ static const struct hda_pcm_ops generic_ops = { .cleanup = generic_hdmi_playback_pcm_cleanup, }; -/* - * ALSA API channel-map control callbacks - */ -static int hdmi_chmap_ctl_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) +static void hdmi_get_chmap(struct hdac_device *hdac, int pcm_idx, + unsigned char *chmap) { - struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); - struct hda_codec *codec = info->private_data; + struct hda_codec *codec = container_of(hdac, struct hda_codec, core); struct hdmi_spec *spec = codec->spec; - uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - uinfo->count = spec->channels_max; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = SNDRV_CHMAP_LAST; - return 0; -} - -static int hdmi_chmap_cea_alloc_validate_get_type(struct cea_channel_speaker_allocation *cap, - int channels) -{ - /* If the speaker allocation matches the channel count, it is OK.*/ - if (cap->channels != channels) - return -1; - - /* all channels are remappable freely */ - return SNDRV_CTL_TLVT_CHMAP_VAR; -} - -static void hdmi_cea_alloc_to_tlv_chmap(struct cea_channel_speaker_allocation *cap, - unsigned int *chmap, int channels) -{ - int count = 0; - int c; + struct hdmi_spec_per_pin *per_pin = pcm_idx_to_pin(spec, pcm_idx); - for (c = 7; c >= 0; c--) { - int spk = cap->speakers[c]; - if (!spk) - continue; - - chmap[count++] = spk_to_chmap(spk); - } - - WARN_ON(count != channels); -} - -static int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag, - unsigned int size, unsigned int __user *tlv) -{ - struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); - struct hda_codec *codec = info->private_data; - struct hdmi_spec *spec = codec->spec; - unsigned int __user *dst; - int chs, count = 0; + /* chmap is already set to 0 in caller */ + if (!per_pin) + return; - if (size < 8) - return -ENOMEM; - if (put_user(SNDRV_CTL_TLVT_CONTAINER, tlv)) - return -EFAULT; - size -= 8; - dst = tlv + 2; - for (chs = 2; chs <= spec->channels_max; chs++) { - int i; - struct cea_channel_speaker_allocation *cap; - cap = channel_allocations; - for (i = 0; i < ARRAY_SIZE(channel_allocations); i++, cap++) { - int chs_bytes = chs * 4; - int type = spec->ops.chmap_cea_alloc_validate_get_type(cap, chs); - unsigned int tlv_chmap[8]; - - if (type < 0) - continue; - if (size < 8) - return -ENOMEM; - if (put_user(type, dst) || - put_user(chs_bytes, dst + 1)) - return -EFAULT; - dst += 2; - size -= 8; - count += 8; - if (size < chs_bytes) - return -ENOMEM; - size -= chs_bytes; - count += chs_bytes; - spec->ops.cea_alloc_to_tlv_chmap(cap, tlv_chmap, chs); - if (copy_to_user(dst, tlv_chmap, chs_bytes)) - return -EFAULT; - dst += chs; - } - } - if (put_user(count, tlv + 1)) - return -EFAULT; - return 0; + memcpy(chmap, per_pin->chmap, ARRAY_SIZE(per_pin->chmap)); } -static int hdmi_chmap_ctl_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +static void hdmi_set_chmap(struct hdac_device *hdac, int pcm_idx, + unsigned char *chmap, int prepared) { - struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); - struct hda_codec *codec = info->private_data; + struct hda_codec *codec = container_of(hdac, struct hda_codec, core); 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 i; - - for (i = 0; i < ARRAY_SIZE(per_pin->chmap); i++) - ucontrol->value.integer.value[i] = per_pin->chmap[i]; - return 0; -} + struct hdmi_spec_per_pin *per_pin = pcm_idx_to_pin(spec, pcm_idx); -static int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - 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); - unsigned int ctl_idx; - struct snd_pcm_substream *substream; - unsigned char chmap[8]; - int i, err, ca, prepared = 0; - - ctl_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); - substream = snd_pcm_chmap_substream(info, ctl_idx); - if (!substream || !substream->runtime) - return 0; /* just for avoiding error from alsactl restore */ - switch (substream->runtime->status->state) { - case SNDRV_PCM_STATE_OPEN: - case SNDRV_PCM_STATE_SETUP: - break; - case SNDRV_PCM_STATE_PREPARED: - prepared = 1; - break; - default: - return -EBUSY; - } - memset(chmap, 0, sizeof(chmap)); - for (i = 0; i < ARRAY_SIZE(chmap); i++) - chmap[i] = ucontrol->value.integer.value[i]; - if (!memcmp(chmap, per_pin->chmap, sizeof(chmap))) - return 0; - ca = hdmi_manual_channel_allocation(ARRAY_SIZE(chmap), chmap); - if (ca < 0) - return -EINVAL; - if (spec->ops.chmap_validate) { - err = spec->ops.chmap_validate(ca, ARRAY_SIZE(chmap), chmap); - if (err) - return err; - } mutex_lock(&per_pin->lock); per_pin->chmap_set = true; - memcpy(per_pin->chmap, chmap, sizeof(chmap)); + memcpy(per_pin->chmap, chmap, ARRAY_SIZE(per_pin->chmap)); if (prepared) hdmi_setup_audio_infoframe(codec, per_pin, per_pin->non_pcm); mutex_unlock(&per_pin->lock); +} - return 0; +static bool is_hdmi_pcm_attached(struct hdac_device *hdac, int pcm_idx) +{ + struct hda_codec *codec = container_of(hdac, struct hda_codec, core); + struct hdmi_spec *spec = codec->spec; + struct hdmi_spec_per_pin *per_pin = pcm_idx_to_pin(spec, pcm_idx); + + return per_pin ? true:false; } static int generic_hdmi_build_pcms(struct hda_codec *codec) @@ -2126,7 +1878,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 +1893,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,88 +1912,107 @@ 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); + err = snd_hdac_add_chmap_ctls(pcm->pcm, pcm_idx, &spec->chmap); if (err < 0) return err; - /* override handlers */ - chmap->private_data = codec; - kctl = chmap->kctl; - for (i = 0; i < kctl->count; i++) - kctl->vd[i].access |= SNDRV_CTL_ELEM_ACCESS_WRITE; - kctl->info = hdmi_chmap_ctl_info; - kctl->get = hdmi_chmap_ctl_get; - kctl->put = hdmi_chmap_ctl_put; - kctl->tlv.c = hdmi_chmap_ctl_tlv; } return 0; @@ -2293,18 +2067,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) @@ -2343,16 +2124,11 @@ static const struct hda_codec_ops generic_hdmi_patch_ops = { static const struct hdmi_ops generic_standard_hdmi_ops = { .pin_get_eld = snd_hdmi_get_eld, - .pin_get_slot_channel = hdmi_pin_get_slot_channel, - .pin_set_slot_channel = hdmi_pin_set_slot_channel, .pin_setup_infoframe = hdmi_pin_setup_infoframe, .pin_hbr_setup = hdmi_pin_hbr_setup, .setup_stream = hdmi_setup_stream, - .chmap_cea_alloc_validate_get_type = hdmi_chmap_cea_alloc_validate_get_type, - .cea_alloc_to_tlv_chmap = hdmi_cea_alloc_to_tlv_chmap, }; - static void intel_haswell_fixup_connect_list(struct hda_codec *codec, hda_nid_t nid) { @@ -2457,6 +2233,13 @@ static int patch_generic_hdmi(struct hda_codec *codec) return -ENOMEM; spec->ops = generic_standard_hdmi_ops; + mutex_init(&spec->pcm_lock); + snd_hdac_register_chmap_ops(&codec->core, &spec->chmap); + + spec->chmap.ops.get_chmap = hdmi_get_chmap; + spec->chmap.ops.set_chmap = hdmi_set_chmap; + spec->chmap.ops.is_pcm_attached = is_hdmi_pcm_attached; + codec->spec = spec; hdmi_array_init(spec, 4); @@ -2500,7 +2283,6 @@ static int patch_generic_hdmi(struct hda_codec *codec) generic_hdmi_init_per_pins(codec); - init_channel_allocations(); if (codec_has_acomp(codec)) { codec->depop_delay = 0; @@ -2514,6 +2296,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; } @@ -2536,7 +2319,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; @@ -3047,16 +2830,18 @@ static int patch_nvhdmi_8ch_7x(struct hda_codec *codec) * - 0x10de0015 * - 0x10de0040 */ -static int nvhdmi_chmap_cea_alloc_validate_get_type(struct cea_channel_speaker_allocation *cap, - int channels) +static int nvhdmi_chmap_cea_alloc_validate_get_type(struct hdac_chmap *chmap, + struct hdac_cea_channel_speaker_allocation *cap, int channels) { if (cap->ca_index == 0x00 && channels == 2) return SNDRV_CTL_TLVT_CHMAP_FIXED; - return hdmi_chmap_cea_alloc_validate_get_type(cap, channels); + return chmap->ops.chmap_cea_alloc_validate_get_type( + chmap, cap, channels); } -static int nvhdmi_chmap_validate(int ca, int chs, unsigned char *map) +static int nvhdmi_chmap_validate(struct hdac_chmap *chmap, + int ca, int chs, unsigned char *map) { if (ca == 0x00 && (map[0] != SNDRV_CHMAP_FL || map[1] != SNDRV_CHMAP_FR)) return -EINVAL; @@ -3076,9 +2861,9 @@ static int patch_nvhdmi(struct hda_codec *codec) spec = codec->spec; spec->dyn_pin_out = true; - spec->ops.chmap_cea_alloc_validate_get_type = + spec->chmap.ops.chmap_cea_alloc_validate_get_type = nvhdmi_chmap_cea_alloc_validate_get_type; - spec->ops.chmap_validate = nvhdmi_chmap_validate; + spec->chmap.ops.chmap_validate = nvhdmi_chmap_validate; return 0; } @@ -3326,16 +3111,17 @@ static int atihdmi_paired_swap_fc_lfe(int pos) return pos; } -static int atihdmi_paired_chmap_validate(int ca, int chs, unsigned char *map) +static int atihdmi_paired_chmap_validate(struct hdac_chmap *chmap, + int ca, int chs, unsigned char *map) { - struct cea_channel_speaker_allocation *cap; + struct hdac_cea_channel_speaker_allocation *cap; int i, j; /* check that only channel pairs need to be remapped on old pre-rev3 ATI/AMD */ - cap = &channel_allocations[get_channel_allocation_order(ca)]; + cap = snd_hdac_get_ch_alloc_from_ca(ca); for (i = 0; i < chs; ++i) { - int mask = to_spk_mask(map[i]); + int mask = snd_hdac_chmap_to_spk_mask(map[i]); bool ok = false; bool companion_ok = false; @@ -3351,7 +3137,7 @@ static int atihdmi_paired_chmap_validate(int ca, int chs, unsigned char *map) if (i % 2 == 0 && i + 1 < chs) { /* even channel, check the odd companion */ int comp_chan_idx = 7 - atihdmi_paired_swap_fc_lfe(j + 1); - int comp_mask_req = to_spk_mask(map[i+1]); + int comp_mask_req = snd_hdac_chmap_to_spk_mask(map[i+1]); int comp_mask_act = cap->speakers[comp_chan_idx]; if (comp_mask_req == comp_mask_act) @@ -3373,9 +3159,10 @@ static int atihdmi_paired_chmap_validate(int ca, int chs, unsigned char *map) return 0; } -static int atihdmi_pin_set_slot_channel(struct hda_codec *codec, hda_nid_t pin_nid, - int hdmi_slot, int stream_channel) +static int atihdmi_pin_set_slot_channel(struct hdac_device *hdac, + hda_nid_t pin_nid, int hdmi_slot, int stream_channel) { + struct hda_codec *codec = container_of(hdac, struct hda_codec, core); int verb; int ati_channel_setup = 0; @@ -3408,9 +3195,10 @@ static int atihdmi_pin_set_slot_channel(struct hda_codec *codec, hda_nid_t pin_n return snd_hda_codec_write(codec, pin_nid, 0, verb, ati_channel_setup); } -static int atihdmi_pin_get_slot_channel(struct hda_codec *codec, hda_nid_t pin_nid, - int asp_slot) +static int atihdmi_pin_get_slot_channel(struct hdac_device *hdac, + hda_nid_t pin_nid, int asp_slot) { + struct hda_codec *codec = container_of(hdac, struct hda_codec, core); bool was_odd = false; int ati_asp_slot = asp_slot; int verb; @@ -3437,8 +3225,10 @@ static int atihdmi_pin_get_slot_channel(struct hda_codec *codec, hda_nid_t pin_n return ((ati_channel_setup & 0xf0) >> 4) + !!was_odd; } -static int atihdmi_paired_chmap_cea_alloc_validate_get_type(struct cea_channel_speaker_allocation *cap, - int channels) +static int atihdmi_paired_chmap_cea_alloc_validate_get_type( + struct hdac_chmap *chmap, + struct hdac_cea_channel_speaker_allocation *cap, + int channels) { int c; @@ -3465,8 +3255,9 @@ static int atihdmi_paired_chmap_cea_alloc_validate_get_type(struct cea_channel_s return SNDRV_CTL_TLVT_CHMAP_PAIRED; } -static void atihdmi_paired_cea_alloc_to_tlv_chmap(struct cea_channel_speaker_allocation *cap, - unsigned int *chmap, int channels) +static void atihdmi_paired_cea_alloc_to_tlv_chmap(struct hdac_chmap *hchmap, + struct hdac_cea_channel_speaker_allocation *cap, + unsigned int *chmap, int channels) { /* produce paired maps for pre-rev3 ATI/AMD codecs */ int count = 0; @@ -3483,7 +3274,7 @@ static void atihdmi_paired_cea_alloc_to_tlv_chmap(struct cea_channel_speaker_all continue; } - chmap[count++] = spk_to_chmap(spk); + chmap[count++] = snd_hdac_spk_to_chmap(spk); } WARN_ON(count != channels); @@ -3577,18 +3368,21 @@ static int patch_atihdmi(struct hda_codec *codec) spec = codec->spec; spec->ops.pin_get_eld = atihdmi_pin_get_eld; - spec->ops.pin_get_slot_channel = atihdmi_pin_get_slot_channel; - spec->ops.pin_set_slot_channel = atihdmi_pin_set_slot_channel; spec->ops.pin_setup_infoframe = atihdmi_pin_setup_infoframe; spec->ops.pin_hbr_setup = atihdmi_pin_hbr_setup; spec->ops.setup_stream = atihdmi_setup_stream; if (!has_amd_full_remap_support(codec)) { /* override to ATI/AMD-specific versions with pairwise mapping */ - spec->ops.chmap_cea_alloc_validate_get_type = + spec->chmap.ops.chmap_cea_alloc_validate_get_type = atihdmi_paired_chmap_cea_alloc_validate_get_type; - spec->ops.cea_alloc_to_tlv_chmap = atihdmi_paired_cea_alloc_to_tlv_chmap; - spec->ops.chmap_validate = atihdmi_paired_chmap_validate; + spec->chmap.ops.cea_alloc_to_tlv_chmap = + atihdmi_paired_cea_alloc_to_tlv_chmap; + spec->chmap.ops.chmap_validate = atihdmi_paired_chmap_validate; + spec->chmap.ops.pin_get_slot_channel = + atihdmi_pin_get_slot_channel; + spec->chmap.ops.pin_set_slot_channel = + atihdmi_pin_set_slot_channel; } /* ATI/AMD converters do not advertise all of their capabilities */ @@ -3600,7 +3394,7 @@ static int patch_atihdmi(struct hda_codec *codec) per_cvt->maxbps = max(per_cvt->maxbps, 24u); } - spec->channels_max = max(spec->channels_max, 8u); + spec->chmap.channels_max = max(spec->chmap.channels_max, 8u); return 0; } 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); |