summaryrefslogtreecommitdiff
path: root/cras/src/server/cras_iodev_list.c
diff options
context:
space:
mode:
Diffstat (limited to 'cras/src/server/cras_iodev_list.c')
-rw-r--r--cras/src/server/cras_iodev_list.c48
1 files changed, 36 insertions, 12 deletions
diff --git a/cras/src/server/cras_iodev_list.c b/cras/src/server/cras_iodev_list.c
index 29ed64b6..41850f34 100644
--- a/cras/src/server/cras_iodev_list.c
+++ b/cras/src/server/cras_iodev_list.c
@@ -682,6 +682,11 @@ static int init_and_attach_streams(struct cras_iodev *dev)
if (!can_attach)
continue;
+ /*
+ * Note that the stream list is descending ordered by channel
+ * count, which guarantees the first attachable stream will have
+ * the highest channel count.
+ */
rc = init_device(dev, stream);
if (rc) {
syslog(LOG_ERR, "Enable %s failed, rc = %d",
@@ -806,6 +811,7 @@ static int stream_added_cb(struct cras_rstream *rstream)
struct cras_iodev *iodevs[10];
unsigned int num_iodevs;
int rc;
+ bool iodev_reopened;
if (stream_list_suspended)
return 0;
@@ -816,24 +822,42 @@ static int stream_added_cb(struct cras_rstream *rstream)
/* Add the new stream to all enabled iodevs at once to avoid offset
* in shm level between different ouput iodevs. */
num_iodevs = 0;
+ iodev_reopened = false;
DL_FOREACH (enabled_devs[rstream->direction], edev) {
if (num_iodevs >= ARRAY_SIZE(iodevs)) {
syslog(LOG_ERR, "too many enabled devices");
break;
}
- rc = init_device(edev->dev, rstream);
- if (rc) {
- /* Error log but don't return error here, because
- * stopping audio could block video playback.
+ if (cras_iodev_is_open(edev->dev) &&
+ (rstream->format.num_channels >
+ edev->dev->format->num_channels)) {
+ /* Re-open the device with the format of the attached
+ * stream if it has higher channel count than the
+ * current format of the device. Fallback device will
+ * be transciently enabled during the device re-opening.
*/
- syslog(LOG_ERR, "Init %s failed, rc = %d",
- edev->dev->info.name, rc);
- schedule_init_device_retry(edev->dev);
- continue;
- }
+ syslog(LOG_INFO, "re-open %s for higher channel count",
+ edev->dev->info.name);
+ possibly_enable_fallback(rstream->direction, false);
+ cras_iodev_list_suspend_dev(edev->dev->info.idx);
+ cras_iodev_list_resume_dev(edev->dev->info.idx);
+ possibly_disable_fallback(rstream->direction);
+ iodev_reopened = true;
+ } else {
+ rc = init_device(edev->dev, rstream);
+ if (rc) {
+ /* Error log but don't return error here, because
+ * stopping audio could block video playback.
+ */
+ syslog(LOG_ERR, "Init %s failed, rc = %d",
+ edev->dev->info.name, rc);
+ schedule_init_device_retry(edev->dev);
+ continue;
+ }
- iodevs[num_iodevs++] = edev->dev;
+ iodevs[num_iodevs++] = edev->dev;
+ }
}
if (num_iodevs) {
rc = add_stream_to_open_devs(rstream, iodevs, num_iodevs);
@@ -841,9 +865,9 @@ static int stream_added_cb(struct cras_rstream *rstream)
syslog(LOG_ERR, "adding stream to thread fail");
return rc;
}
- } else {
+ } else if (!iodev_reopened) {
/* Enable fallback device if no other iodevs can be initialized
- * successfully.
+ * or re-opened successfully.
* For error codes like EAGAIN and ENOENT, a new iodev will be
* enabled soon so streams are going to route there. As for the
* rest of the error cases, silence will be played or recorded