Index: linux-2.6/include/sound/asound.h
===================================================================
--- linux-2.6.orig/include/sound/asound.h
+++ linux-2.6/include/sound/asound.h
@@ -818,6 +818,12 @@ struct snd_ctl_elem_value {
         unsigned char reserved[128-sizeof(struct timespec)];
 };
 
+struct snd_ctl_scenario {
+	char name[16];
+	char source[32];
+	char sink[32];
+};
+
 enum {
 	SNDRV_CTL_IOCTL_PVERSION = _IOR('U', 0x00, int),
 	SNDRV_CTL_IOCTL_CARD_INFO = _IOR('U', 0x01, struct snd_ctl_card_info),
@@ -839,6 +845,8 @@ enum {
 	SNDRV_CTL_IOCTL_RAWMIDI_NEXT_DEVICE = _IOWR('U', 0x40, int),
 	SNDRV_CTL_IOCTL_RAWMIDI_INFO = _IOWR('U', 0x41, struct snd_rawmidi_info),
 	SNDRV_CTL_IOCTL_RAWMIDI_PREFER_SUBDEVICE = _IOW('U', 0x42, int),
+	SNDRV_CTL_IOCTL_SCENARIO_SET_PATH = _IOW('U', 0x50, struct snd_ctl_scenario),
+	SNDRV_CTL_IOCTL_SCENARIO_FLUSH = _IOW('U', 0x51, int),
 	SNDRV_CTL_IOCTL_POWER = _IOWR('U', 0xd0, int),
 	SNDRV_CTL_IOCTL_POWER_STATE = _IOR('U', 0xd1, int),
 };
Index: linux-2.6/include/sound/core.h
===================================================================
--- linux-2.6.orig/include/sound/core.h
+++ linux-2.6/include/sound/core.h
@@ -66,6 +66,7 @@ typedef int __bitwise snd_device_cmd_t;
 #define	SNDRV_DEV_CMD_POST	((__force snd_device_cmd_t) 2)
 
 struct snd_device;
+struct snd_soc_device;
 
 struct snd_device_ops {
 	int (*dev_free)(struct snd_device *dev);
@@ -134,6 +135,7 @@ struct snd_card {
 	wait_queue_head_t shutdown_sleep;
 	struct work_struct free_workq;	/* for free in workqueue */
 	struct device *dev;
+	struct snd_soc_device *socdev;	/* SoC device */
 
 #ifdef CONFIG_PM
 	unsigned int power_state;	/* power state */
Index: linux-2.6/include/sound/soc-dapm.h
===================================================================
--- linux-2.6.orig/include/sound/soc-dapm.h
+++ linux-2.6/include/sound/soc-dapm.h
@@ -221,6 +221,7 @@ struct snd_soc_dapm_path {
 	/* status */
 	int connect:1;	/* source and sink widgets are connected */
 	int walked:1;	/* path has been walked */
+	int length:6;	/* scenario path length */
 
 	struct list_head list_source;
 	struct list_head list_sink;
@@ -240,6 +241,7 @@ struct snd_soc_dapm_widget {
 	unsigned char shift;			/* bits to shift */
 	unsigned int saved_value;		/* widget saved value */
 	unsigned int value;				/* widget current value */
+	unsigned int hops;				/* scenario hops */
 	unsigned char power:1;			/* block power status */
 	unsigned char invert:1;			/* invert the power bit */
 	unsigned char active:1;			/* active stream on DAC, ADC's */
Index: linux-2.6/include/sound/soc-scenario.h
===================================================================
--- /dev/null
+++ linux-2.6/include/sound/soc-scenario.h
@@ -0,0 +1,24 @@
+/*
+ * linux/sound/soc-scenario.h -- ALSA SoC Scenario Support
+ *
+ * Author:		Liam Girdwood
+ * Created:		Aug 11th 2005
+ * Copyright:	Wolfson Microelectronics. PLC.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __LINUX_SND_SOC_SCENARIO_H
+#define __LINUX_SND_SOC_SCENARIO_H
+
+struct snd_soc_codec;
+struct snd_ctl_scenario;
+
+int snd_soc_scenario_flush(struct snd_soc_codec *codec);
+
+int snd_soc_scenario_set_path(struct snd_soc_codec *codec,
+	struct snd_ctl_scenario *scenario);
+
+#endif
Index: linux-2.6/sound/soc/Makefile
===================================================================
--- linux-2.6.orig/sound/soc/Makefile
+++ linux-2.6/sound/soc/Makefile
@@ -1,4 +1,4 @@
-snd-soc-core-objs := soc-core.o soc-dapm.o
+snd-soc-core-objs := soc-core.o soc-dapm.o soc-scenario.o
 
 obj-$(CONFIG_SND_SOC)	+= snd-soc-core.o
 obj-$(CONFIG_SND_SOC)	+= pxa/ codecs/
Index: linux-2.6/sound/soc/pxa/mainstone_wm9713.c
===================================================================
--- linux-2.6.orig/sound/soc/pxa/mainstone_wm9713.c
+++ linux-2.6/sound/soc/pxa/mainstone_wm9713.c
@@ -29,6 +29,7 @@
 #include <sound/pcm.h>
 #include <sound/soc.h>
 #include <sound/soc-dapm.h>
+#include <sound/soc-scenario.h>
 
 #include <asm/arch/pxa-regs.h>
 #include <asm/arch/mainstone.h>
@@ -77,10 +78,16 @@ static int get_test(snd_kcontrol_t * kco
 static int set_test(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
 {
 	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct snd_ctl_scenario scen;
 
 	test = ucontrol->value.integer.value[0];
 	if(test) {
-
+		strcpy(scen.source, "Left DAC");
+		strcpy(scen.sink, "HPL");
+		snd_soc_scenario_set_path(codec, &scen);
+		strcpy(scen.source, "Right DAC");
+		strcpy(scen.sink, "HPR");
+		snd_soc_scenario_set_path(codec, &scen);
 	} else {
 
 	}
Index: linux-2.6/sound/soc/soc-core.c
===================================================================
--- linux-2.6.orig/sound/soc/soc-core.c
+++ linux-2.6/sound/soc/soc-core.c
@@ -36,6 +36,7 @@
 #include <sound/pcm_params.h>
 #include <sound/soc.h>
 #include <sound/soc-dapm.h>
+#include <sound/soc-scenario.h>
 #include <sound/initval.h>
 
 /* debug */
@@ -855,6 +856,23 @@ static snd_pcm_ops_t soc_pcm_ops = {
 	.prepare	= soc_pcm_prepare,
 };
 
+/* scenario ioctls */
+static int snd_soc_scenario_ioctl(struct snd_card *card,
+				 struct snd_ctl_file *control,
+				 unsigned int cmd, unsigned long arg)
+{
+	void __user *argp = (void __user *)arg;
+
+	switch (cmd) {
+	case SNDRV_CTL_IOCTL_SCENARIO_SET_PATH:
+		return snd_soc_scenario_set_path(card->socdev->codec, argp);
+	case SNDRV_CTL_IOCTL_SCENARIO_FLUSH:
+		return snd_soc_scenario_flush(card->socdev->codec);
+	}
+
+	return -ENOTTY;
+}
+
 #ifdef CONFIG_PM
 /* powers down audio subsystem for suspend */
 static int soc_suspend(struct platform_device *pdev, pm_message_t state)
@@ -1023,6 +1041,7 @@ static int soc_remove(struct platform_de
 	struct snd_soc_platform *platform = socdev->platform;
 	struct snd_soc_codec_device *codec_dev = socdev->codec_dev;
 
+	snd_ctl_unregister_ioctl(snd_soc_scenario_ioctl);
 	if(soc_workq)
 		destroy_workqueue(soc_workq);
 
@@ -1281,6 +1300,7 @@ int snd_soc_new_pcms(struct snd_soc_devi
 		return -ENODEV;
 	}
 
+	codec->card->socdev = socdev;
 	codec->card->dev = socdev->dev;
 	codec->card->private_data = codec;
 	strncpy(codec->card->driver, codec->name, sizeof(codec->card->driver));
Index: linux-2.6/sound/soc/soc-scenario.c
===================================================================
--- /dev/null
+++ linux-2.6/sound/soc/soc-scenario.c
@@ -0,0 +1,357 @@
+/*
+ * soc-scenario.c  --  ALSA SoC Audio Layer
+ *
+ * Copyright 2006 Wolfson Microelectronics PLC.
+ * Author: Liam Girdwood
+ *         liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.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.
+ *
+ *  Revision history
+ *    16th Jul 2006   Initial version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/bitops.h>
+#include <linux/platform_device.h>
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/soc-scenario.h>
+#include <sound/initval.h>
+
+/* debug */
+#define SOC_DEBUG 1
+#if SOC_DEBUG
+#define dbg(format, arg...) printk(format, ## arg)
+#else
+#define dbg(format, arg...)
+#endif
+
+#define MAX_HOPS 16
+
+static int paths = 0;
+
+static void scenario_clear_paths(struct snd_soc_codec *codec)
+{
+	struct snd_soc_dapm_path *p;
+	struct snd_soc_dapm_widget *w;
+	struct list_head *l;
+
+	list_for_each(l, &codec->dapm_paths)
+	{
+		p = list_entry(l, struct snd_soc_dapm_path, list);
+		p->length = 0;
+	}
+	list_for_each(l, &codec->dapm_widgets)
+	{
+		w = list_entry(l, struct snd_soc_dapm_widget, list);
+		w->hops = 0;
+	}
+}
+
+/*
+ * find all the paths between source and sink
+ */
+static int scenario_find_paths (struct snd_soc_dapm_widget *source,
+	struct snd_soc_dapm_widget *sink, int hops)
+{
+	struct list_head *lp;
+	struct snd_soc_dapm_path *path;
+	int dist = 0;
+
+	if(hops > MAX_HOPS)
+		return 0;
+
+	if(source == sink) {
+		dbg("soc: found route with length %d\n", hops);
+		paths++;
+		return 1;
+	}
+
+	if(source->hops && source->hops <= hops)
+		return 0;
+	source->hops = hops;
+
+	list_for_each(lp, &source->sinks)
+	{
+		path = list_entry(lp, struct snd_soc_dapm_path, list_source);
+
+		if(path->length && path->length <= hops)
+			continue;
+
+		if (path->sink && scenario_find_paths(path->sink, sink, hops + 1)) {
+			path->length = hops;
+			if(!dist || dist > path->length)
+				dist = path->length;
+		}
+	}
+
+	return dist;
+}
+
+/*
+ * traverse the tree from sink to source via the shortest path
+ */
+static int scenario_get_paths(struct snd_soc_dapm_widget *source,
+	struct snd_soc_dapm_widget *sink)
+{
+	paths = 0;
+	scenario_find_paths(source, sink, 1);
+	return paths;
+}
+
+static int scenario_set_mux_path(struct snd_soc_dapm_path *path)
+{
+	struct snd_soc_dapm_widget *widget = path->sink;
+	int mask, bitmask, val, mux = 0, i, ret;
+	struct soc_enum *e;
+	snd_kcontrol_t *kcontrol;
+
+	if(!path->kcontrol)
+		return -EINVAL;
+
+	kcontrol = path->kcontrol;
+	e = (struct soc_enum *)kcontrol->private_value;
+
+	/* find the correct control value */
+	for(i = 0; i < e->mask; i++) {
+		if(!strcmp(path->name, e->texts[i])) {
+			mux = i;
+			break;
+		}
+	}
+	for (bitmask = 1; bitmask < e->mask; bitmask <<= 1)
+		;
+
+	val = mux << e->shift_l;
+	mask = (bitmask - 1) << e->shift_l;
+	widget->value = val;
+	dbg("mux reg %x val %x mask %x val %x\n", e->reg, val, mask, val);
+
+	/* update the path, power and any events */
+	dapm_mux_update_power(widget, kcontrol, mask, mux, e);
+	if(widget->event) {
+		if(widget->event_flags & SND_SOC_DAPM_PRE_REG)
+			if((ret = widget->event(widget, SND_SOC_DAPM_PRE_REG)) < 0)
+				return ret;
+		ret = snd_soc_update_bits(widget->codec, e->reg, mask, val);
+		if(widget->event_flags & SND_SOC_DAPM_POST_REG)
+			ret = widget->event(widget, SND_SOC_DAPM_POST_REG);
+	} else
+		ret = snd_soc_update_bits(widget->codec, e->reg, mask, val);
+
+	return ret;
+}
+
+static int scenario_set_mixer_path(struct snd_soc_dapm_path *path)
+{
+	struct snd_soc_dapm_widget *widget = path->sink;
+	int reg, shift, mask, invert, val_mask, val, ret;
+	snd_kcontrol_t *kcontrol;
+
+	if(!path->kcontrol)
+		return -EINVAL;
+
+	kcontrol = path->kcontrol;
+
+	reg = kcontrol->private_value & 0xff;
+	shift = (kcontrol->private_value >> 8) & 0x0f;
+	mask = (kcontrol->private_value >> 16) & 0xff;
+	invert = (kcontrol->private_value >> 24) & 0x01;
+
+	val_mask = mask << shift;
+	val = val_mask;
+	if(invert)
+		val = 0;
+	dbg("mixer reg %x shift %x vmask %x mask %x, val %x invert %d\n",
+		reg, shift, val_mask, mask, val, invert);
+
+	dapm_mixer_update_power(widget, kcontrol, reg, val_mask, val, invert);
+	if(widget->event) {
+		if(widget->event_flags & SND_SOC_DAPM_PRE_REG)
+			if ((ret = widget->event(widget, SND_SOC_DAPM_PRE_REG )) < 0)
+				return ret;
+		ret = snd_soc_update_bits(widget->codec, reg, val_mask, val);
+		if(widget->event_flags & SND_SOC_DAPM_POST_REG)
+			ret = widget->event(widget, SND_SOC_DAPM_POST_REG);
+	} else
+		ret = snd_soc_update_bits(widget->codec, reg, val_mask, val);
+
+	return ret;
+}
+
+/* travel from sink to source and set the valid audio path */
+static int scenario_set_path(struct snd_soc_dapm_widget *source,
+	struct snd_soc_dapm_widget *sink)
+{
+	struct list_head *lp;
+	struct snd_soc_dapm_path *path, *vpath = NULL;
+	int dist = 64;
+
+	if(source == sink)
+		return 1;
+
+	list_for_each(lp, &sink->sources)
+	{
+		path = list_entry(lp, struct snd_soc_dapm_path, list_sink);
+
+		if(path->length && path->length < dist) {
+			dist = path->length;
+			vpath = path;
+			continue;
+		}
+	}
+
+	if(!vpath)
+		return 0;
+	else {
+		switch(vpath->sink->id) {
+		case snd_soc_dapm_switch:
+		case snd_soc_dapm_mixer:
+			scenario_set_mixer_path(vpath);
+			break;
+		case snd_soc_dapm_mux:
+			scenario_set_mux_path(vpath);
+			break;
+		case snd_soc_dapm_adc:
+		case snd_soc_dapm_dac:
+		case snd_soc_dapm_pga:
+		case snd_soc_dapm_input:
+		case snd_soc_dapm_output:
+		case snd_soc_dapm_micbias:
+		case snd_soc_dapm_mute:
+		case snd_soc_dapm_spk:
+		case snd_soc_dapm_hp:
+		case snd_soc_dapm_mic:
+		case snd_soc_dapm_line:
+			break;
+		}
+	}
+	return scenario_set_path(source, vpath->source);
+}
+
+static void scenario_flush_mux(struct snd_soc_dapm_widget *w)
+{
+}
+
+static void scenario_flush_mixer(struct snd_soc_dapm_widget *w)
+{
+}
+
+/**
+ * snd_soc_scenario_flush - flush all scenario paths
+ * @codec: the soc codec
+ *
+ * Flushes all scenario paths to their default states.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_scenario_flush(struct snd_soc_codec *codec)
+{
+	struct snd_soc_dapm_widget *w = NULL;
+	struct list_head *l = NULL;
+
+	list_for_each(l, &codec->dapm_widgets)
+	{
+		w = list_entry(l, struct snd_soc_dapm_widget, list);
+		switch(w->id) {
+			case snd_soc_dapm_switch:
+			case snd_soc_dapm_mixer:
+				scenario_flush_mixer(w);
+				break;
+			case snd_soc_dapm_mux:
+				scenario_flush_mux(w);
+			case snd_soc_dapm_adc:
+			case snd_soc_dapm_dac:
+			case snd_soc_dapm_pga:
+			case snd_soc_dapm_input:
+			case snd_soc_dapm_output:
+			case snd_soc_dapm_micbias:
+			case snd_soc_dapm_mute:
+			case snd_soc_dapm_spk:
+			case snd_soc_dapm_hp:
+			case snd_soc_dapm_mic:
+			case snd_soc_dapm_line:
+				break;
+			}
+	}
+
+	snd_soc_dapm_sync_endpoints(codec);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_scenario_flush);
+
+
+/**
+ * snd_soc_scenario_set_path - set new scenario path
+ * @codec: the soc codec
+ * @scenario: the sceanrio path
+ *
+ * Sets up a new audio path within the audio susbsytem.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_scenario_set_path(struct snd_soc_codec *codec,
+	struct snd_ctl_scenario *scenario)
+{
+	struct snd_soc_dapm_widget *sink = NULL, *source = NULL;
+	struct list_head *l = NULL;
+
+	/* find source */
+	list_for_each(l, &codec->dapm_widgets)
+	{
+		struct snd_soc_dapm_widget *w;
+		w = list_entry(l, struct snd_soc_dapm_widget, list);
+
+		if(!source && !strncmp(w->name, scenario->source, 16)) {
+			source = w;
+			continue;
+		}
+		if(!sink && !strncmp(w->name, scenario->sink, 16)) {
+			sink = w;
+		}
+	}
+
+	if(!source) {
+		printk(KERN_ERR "soc: invalid scenario source %s\n", scenario->source);
+		return -EINVAL;
+	}
+	if(!sink) {
+		printk(KERN_ERR "soc: invalid scenario sink %s\n", scenario->sink);
+		return -EINVAL;
+	}
+
+	down(&codec->sem);
+	if(!scenario_get_paths(source, sink)) {
+		printk(KERN_ERR "soc: invalid path %s\n", scenario->name);
+		scenario_clear_paths(codec);
+		up(&codec->sem);
+		return -EINVAL;
+	}
+
+	if(!scenario_set_path(source, sink)) {
+		printk(KERN_ERR "soc: could not set path %s\n", scenario->name);
+		scenario_clear_paths(codec);
+		up(&codec->sem);
+		return -EINVAL;
+	}
+
+	scenario_clear_paths(codec);
+	up(&codec->sem);
+	return snd_soc_dapm_sync_endpoints(source->codec);
+}
+EXPORT_SYMBOL_GPL(snd_soc_scenario_set_path);

