Index: linux-2.6.21/sound/soc/codecs/wm8976.c
===================================================================
--- linux-2.6.21.orig/sound/soc/codecs/wm8976.c	2007-05-15 11:27:22.000000000 +0800
+++ linux-2.6.21/sound/soc/codecs/wm8976.c	2007-05-15 13:26:23.000000000 +0800
@@ -248,7 +248,7 @@
 }
 
 /* Left Output Mixer */
-static const snd_kcontrol_new_t wm8976_left_mixer_controls[] = {
+static const struct snd_kcontrol_new wm8976_left_mixer_controls[] = {
 SOC_DAPM_SINGLE("Right PCM Playback Switch", WM8976_OUTPUT, 6, 1, 1),
 SOC_DAPM_SINGLE("Left PCM Playback Switch", WM8976_MIXL, 0, 1, 1),
 SOC_DAPM_SINGLE("Line Bypass Switch", WM8976_MIXL, 1, 1, 0),
@@ -256,7 +256,7 @@
 };
 
 /* Right Output Mixer */
-static const snd_kcontrol_new_t wm8976_right_mixer_controls[] = {
+static const struct snd_kcontrol_new wm8976_right_mixer_controls[] = {
 SOC_DAPM_SINGLE("Left PCM Playback Switch", WM8976_OUTPUT, 5, 1, 1),
 SOC_DAPM_SINGLE("Right PCM Playback Switch", WM8976_MIXR, 0, 1, 1),
 SOC_DAPM_SINGLE("Line Bypass Switch", WM8976_MIXR, 1, 1, 0),
@@ -264,23 +264,23 @@
 };
 
 /* Left AUX Input boost vol */
-static const snd_kcontrol_new_t wm8976_laux_boost_controls =
+static const struct snd_kcontrol_new wm8976_laux_boost_controls =
 SOC_DAPM_SINGLE("Aux Volume", WM8976_ADCBOOST, 0, 3, 0);
 
 /* Left Input boost vol */
-static const snd_kcontrol_new_t wm8976_lmic_boost_controls =
+static const struct snd_kcontrol_new wm8976_lmic_boost_controls =
 SOC_DAPM_SINGLE("Input Volume", WM8976_ADCBOOST, 4, 3, 0);
 
 /* Left Aux In to PGA */
-static const snd_kcontrol_new_t wm8976_laux_capture_boost_controls =
+static const struct snd_kcontrol_new wm8976_laux_capture_boost_controls =
 SOC_DAPM_SINGLE("Capture Switch", WM8976_ADCBOOST,  8, 1, 0);
 
 /* Left Input P In to PGA */
-static const snd_kcontrol_new_t wm8976_lmicp_capture_boost_controls =
+static const struct snd_kcontrol_new wm8976_lmicp_capture_boost_controls =
 SOC_DAPM_SINGLE("Input P Capture Boost Switch", WM8976_INPUT,  0, 1, 0);
 
 /* Left Input N In to PGA */
-static const snd_kcontrol_new_t wm8976_lmicn_capture_boost_controls =
+static const struct snd_kcontrol_new wm8976_lmicn_capture_boost_controls =
 SOC_DAPM_SINGLE("Input N Capture Boost Switch", WM8976_INPUT,  1, 1, 0);
 
 // TODO Widgets
@@ -379,28 +379,57 @@
 	return 0;
 }
 
-struct pll_ {
-	unsigned int in_hz, out_hz;
+struct _pll_div {
 	unsigned int pre:4; /* prescale - 1 */
 	unsigned int n:4;
 	unsigned int k;
 };
 
-struct pll_ pll[] = {
-	{12000000, 11289600, 0, 7, 0x86c220},
-	{12000000, 12288000, 0, 8, 0x3126e8},
-	{13000000, 11289600, 0, 6, 0xf28bd4},
-	{13000000, 12288000, 0, 7, 0x8fd525},
-	{12288000, 11289600, 0, 7, 0x59999a},
-	{11289600, 12288000, 0, 8, 0x80dee9},
-	/* TODO: liam - add more entries */
-};
+static struct _pll_div pll_div;
+
+/* The size in bits of the pll divide multiplied by 10
+ * to allow rounding later */
+#define FIXED_PLL_SIZE ((1 << 24) * 10)
+
+static void pll_factors(unsigned int target, unsigned int source)
+{
+	unsigned long long Kpart;
+	unsigned int K, Ndiv, Nmod;
+
+	Ndiv = target / source;
+	if (Ndiv < 6) {
+		source >>= 1;
+		pll_div.pre = 1;
+		Ndiv = target / source;
+	} else
+		pll_div.pre = 0;
+
+	if ((Ndiv < 6) || (Ndiv > 12))
+		printk(KERN_WARNING
+			"WM8976 N value outwith recommended range! N = %d\n",Ndiv);
+
+	pll_div.n = Ndiv;
+	Nmod = target % source;
+	Kpart = FIXED_PLL_SIZE * (long long)Nmod;
+
+	do_div(Kpart, source);
+
+	K = Kpart & 0xFFFFFFFF;
+
+	/* Check if we need to round */
+	if ((K % 10) >= 5)
+		K += 5;
+
+	/* Move down to proper range now rounding is done */
+	K /= 10;
+
+	pll_div.k = K;
+}
 
 static int wm8976_set_dai_pll(struct snd_soc_codec_dai *codec_dai,
 		int pll_id, unsigned int freq_in, unsigned int freq_out)
 {
 	struct snd_soc_codec *codec = codec_dai->codec;
-	int i;
 	u16 reg;
 
 	if(freq_in == 0 || freq_out == 0) {
@@ -409,18 +438,16 @@
 		return 0;
 	}
 
-	for(i = 0; i < ARRAY_SIZE(pll); i++) {
-		if (freq_in == pll[i].in_hz && freq_out == pll[i].out_hz) {
-			wm8976_write(codec, WM8976_PLLN, (pll[i].pre << 4) | pll[i].n);
-			wm8976_write(codec, WM8976_PLLK1, pll[i].k >> 18);
-			wm8976_write(codec, WM8976_PLLK1, (pll[i].k >> 9) && 0x1ff);
-			wm8976_write(codec, WM8976_PLLK1, pll[i].k && 0x1ff);
-			reg = wm8976_read_reg_cache(codec, WM8976_POWER1);
-			wm8976_write(codec, WM8976_POWER1, reg | 0x020);
-			return 0;
-		}
-	}
-	return -EINVAL;
+	pll_factors(freq_out * 8, freq_in);
+
+	wm8976_write(codec, WM8976_PLLN, (pll_div.pre << 4) | pll_div.n);
+	wm8976_write(codec, WM8976_PLLK1, pll_div.k >> 18);
+	wm8976_write(codec, WM8976_PLLK1, (pll_div.k >> 9) && 0x1ff);
+	wm8976_write(codec, WM8976_PLLK1, pll_div.k && 0x1ff);
+	reg = wm8976_read_reg_cache(codec, WM8976_POWER1);
+	wm8976_write(codec, WM8976_POWER1, reg | 0x020);
+
+	return 0;
 }
 
 static int wm8976_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
@@ -476,6 +503,7 @@
 	}
 
 	wm8976_write(codec, WM8976_IFACE, iface);
+	wm8976_write(codec, WM8976_CLOCK, clk);
 	return 0;
 }
 
@@ -676,6 +704,7 @@
 {
 	struct snd_soc_codec *codec = socdev->codec;
 	int ret = 0;
+	u16 reg;
 
 	codec->name = "WM8976";
 	codec->owner = THIS_MODULE;
@@ -684,17 +713,21 @@
 	codec->dapm_event = wm8976_dapm_event;
 	codec->dai = &wm8976_dai;
 	codec->num_dai = 1;
-	codec->reg_cache_size = ARRAY_SIZE(wm8976_reg);
+	codec->reg_cache_size = sizeof(wm8976_reg);
 	codec->reg_cache =
-			kzalloc(sizeof(u16) * ARRAY_SIZE(wm8976_reg), GFP_KERNEL);
+			kzalloc(sizeof(u16) * sizeof(wm8976_reg), GFP_KERNEL);
 	if (codec->reg_cache == NULL)
 		return -ENOMEM;
 	memcpy(codec->reg_cache, wm8976_reg,
-		sizeof(u16) * ARRAY_SIZE(wm8976_reg));
-	codec->reg_cache_size = sizeof(u16) * ARRAY_SIZE(wm8976_reg);
+		sizeof(u16) * sizeof(wm8976_reg));
+	codec->reg_cache_size = sizeof(u16) * sizeof(wm8976_reg);
 
 	wm8976_reset(codec);
 
+	/* Setup Mono Device Operation of WM8976. */
+	reg = wm8976_read_reg_cache(codec, WM8976_IFACE) & 0x1FF;
+	wm8976_write(codec, WM8976_IFACE, reg | 0x0001);
+
 	/* register pcms */
 	ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
 	if(ret < 0) {
@@ -708,9 +741,9 @@
 	wm8976_add_widgets(codec);
 	ret = snd_soc_register_card(socdev);
 	if (ret < 0) {
-      	printk(KERN_ERR "wm8976: failed to register card\n");
+		printk(KERN_ERR "wm8976: failed to register card\n");
 		goto card_err;
-    }
+	}
 	return ret;
 
 card_err:
Index: linux-2.6.21/sound/soc/s3c24xx/hxd8_wm8976.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.21/sound/soc/s3c24xx/hxd8_wm8976.c	2007-05-15 11:26:49.000000000 +0800
@@ -0,0 +1,527 @@
+/*
+ * hxd8_wm8976.c  --  SoC audio for HXD8
+ *
+ * Copyright 2007 Wolfson Microelectronics PLC.
+ * Author: Graeme Gregory
+ *         graeme.gregory@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
+ *    09th Apr 2007   Initial version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include <asm/mach-types.h>
+#include <asm/hardware/scoop.h>
+#include <asm/arch/regs-iis.h>
+#include <asm/arch/regs-clock.h>
+#include <asm/arch/regs-gpio.h>
+#include <asm/hardware.h>
+#include <asm/arch/audio.h>
+#include <asm/io.h>
+#include <asm/arch/spi-gpio.h>
+#include "../codecs/wm8976.h"
+#include "lm4857.h"
+#include "s3c24xx-pcm.h"
+#include "s3c24xx-i2s.h"
+
+#define HXD8_DEBUG 0
+#if HXD8_DEBUG
+#define DBG(x...) printk(KERN_DEBUG x)
+#else
+#define DBG(x...)
+#endif
+
+/* define the scenarios */
+#define HXD8_AUDIO_OFF			0
+#define HXD8_GSM_CALL_AUDIO_HANDSET	1
+#define HXD8_STEREO_TO_SPEAKERS		2
+#define HXD8_CAPTURE_HANDSET		3
+
+static struct snd_soc_machine hxd8;
+static struct i2c_client *i2c;
+
+static int hxd8_hifi_hw_params(struct snd_pcm_substream *substream,
+	struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
+	struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+	unsigned int pll_out = 0, bclk = 0;
+	int ret = 0;
+	unsigned long iis_clkrate;
+
+	iis_clkrate = s3c24xx_i2s_get_clockrate();
+
+	switch (params_rate(params)) {
+	case 8000:
+	case 16000:
+		pll_out = 12288000;
+		break;
+	case 48000:
+		bclk = WM8976_BCLK_DIV_4;
+		pll_out = 12288000;
+		break;
+	case 96000:
+		bclk = WM8976_BCLK_DIV_2;
+		pll_out = 12288000;
+		break;
+	case 11025:
+		bclk = WM8976_BCLK_DIV_16;
+		pll_out = 11289600;
+		break;
+	case 22050:
+		bclk = WM8976_BCLK_DIV_8;
+		pll_out = 11289600;
+		break;
+	case 44100:
+		bclk = WM8976_BCLK_DIV_4;
+		pll_out = 11289600;
+		break;
+	case 88200:
+		bclk = WM8976_BCLK_DIV_2;
+		pll_out = 11289600;
+		break;
+	}
+
+	/* set codec DAI configuration */
+	ret = codec_dai->dai_ops.set_fmt(codec_dai,
+		SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
+	if (ret < 0)
+		return ret;
+
+	/* set cpu DAI configuration */
+	ret = cpu_dai->dai_ops.set_fmt(cpu_dai,
+		SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
+	if (ret < 0)
+		return ret;
+
+    /* set MCLK division for sample rate */
+    ret = cpu_dai->dai_ops.set_clkdiv(cpu_dai, S3C24XX_DIV_MCLK,
+        S3C2410_IISMOD_32FS );
+    if (ret < 0)
+        return ret;
+
+	/* set codec BCLK division for sample rate */
+	ret = codec_dai->dai_ops.set_clkdiv(codec_dai, WM8976_BCLKDIV, bclk);
+	if (ret < 0)
+		return ret;
+
+	/* set prescaler division for sample rate */
+	ret = cpu_dai->dai_ops.set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER,
+		S3C24XX_PRESCALE(4,4));
+	if (ret < 0)
+		return ret;
+
+	/* codec PLL input is PCLK/4 */
+	ret = codec_dai->dai_ops.set_pll(codec_dai, 0, iis_clkrate/4,
+		pll_out);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static int hxd8_hifi_hw_free(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
+
+	/* disable the PLL */
+	return codec_dai->dai_ops.set_pll(codec_dai, 0, 0, 0);
+}
+
+/*
+ * Hxd8 WM8976 HiFi DAI opserations.
+ */
+static struct snd_soc_ops hxd8_hifi_ops = {
+	.hw_params = hxd8_hifi_hw_params,
+	.hw_free = hxd8_hifi_hw_free,
+};
+
+/* Detail the scenarios that the hxd8 is expected to be used in
+ */
+
+static int hxd8_scenario = 0;
+
+static int hxd8_get_scenario(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	ucontrol->value.integer.value[0] = hxd8_scenario;
+	return 0;
+}
+
+static int set_scenario_endpoints(struct snd_soc_codec *codec, int scenario)
+{
+	switch(hxd8_scenario) {
+	case HXD8_AUDIO_OFF:
+		snd_soc_dapm_set_endpoint(codec, "Audio Out",    0);
+		snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 0);
+		snd_soc_dapm_set_endpoint(codec, "Handset Mic",  0);
+		break;
+	case HXD8_GSM_CALL_AUDIO_HANDSET:
+		snd_soc_dapm_set_endpoint(codec, "Audio Out",    1);
+		snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 1);
+		snd_soc_dapm_set_endpoint(codec, "Handset Mic",  1);
+		break;
+	case HXD8_STEREO_TO_SPEAKERS:
+		snd_soc_dapm_set_endpoint(codec, "Audio Out",    1);
+		snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 0);
+		snd_soc_dapm_set_endpoint(codec, "Handset Mic",  0);
+		break;
+	case HXD8_CAPTURE_HANDSET:
+		snd_soc_dapm_set_endpoint(codec, "Audio Out",    0);
+		snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 0);
+		snd_soc_dapm_set_endpoint(codec, "Handset Mic",  1);
+		break;
+	}
+
+	snd_soc_dapm_sync_endpoints(codec);
+
+	return 0;
+}
+
+static int hxd8_set_scenario(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+
+	if (hxd8_scenario == ucontrol->value.integer.value[0])
+		return 0;
+
+	hxd8_scenario = ucontrol->value.integer.value[0];
+
+	set_scenario_endpoints(codec, hxd8_scenario);
+
+	return 1;
+}
+
+static u8 lm4857_regs[4] = {0x00, 0x40, 0x80, 0xC0};
+
+static void lm4857_write_regs( void )
+{
+	if( i2c_master_send(i2c, lm4857_regs, 4) != 4)
+		printk(KERN_WARNING "lm4857: i2c write failed\n");
+}
+
+static int lm4857_get_reg(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	int reg=kcontrol->private_value & 0xFF;
+	int shift = (kcontrol->private_value >> 8) & 0x0F;
+	int mask = (kcontrol->private_value >> 16) & 0xFF;
+
+	ucontrol->value.integer.value[0] = (lm4857_regs[reg] >> shift) & mask;
+
+	return 0;
+}
+
+static int lm4857_set_reg(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	int reg = kcontrol->private_value & 0xFF;
+	int shift = (kcontrol->private_value >> 8) & 0x0F;
+	int mask = (kcontrol->private_value >> 16) & 0xFF;
+
+	if (((lm4857_regs[reg] >> shift ) & mask) ==
+		ucontrol->value.integer.value[0])
+		return 0;
+
+	lm4857_regs[reg] &= ~ (mask << shift);
+	lm4857_regs[reg] |= ucontrol->value.integer.value[0] << shift;
+
+	lm4857_write_regs();
+	return 1;
+}
+
+static int lm4857_get_mode(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	u8 value = lm4857_regs[LM4857_CTRL] & 0x0F;
+
+	if (value)
+		value -= 5;
+
+	ucontrol->value.integer.value[0] = value;
+	return 0;
+}
+
+static int lm4857_set_mode(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	u8 value = ucontrol->value.integer.value[0];
+
+	if (value)
+		value += 5;
+
+	if ((lm4857_regs[LM4857_CTRL] & 0x0F) == value)
+		return 0;
+
+	lm4857_regs[LM4857_CTRL] &= 0xF0;
+	lm4857_regs[LM4857_CTRL] |= value;
+
+	lm4857_write_regs();
+	return 1;
+}
+
+static const struct snd_soc_dapm_widget wm8976_dapm_widgets[] = {
+	SND_SOC_DAPM_LINE("Audio Out", NULL),
+	SND_SOC_DAPM_LINE("GSM Line Out", NULL),
+	SND_SOC_DAPM_LINE("GSM Line In", NULL),
+	SND_SOC_DAPM_MIC("Headset Mic", NULL),
+	SND_SOC_DAPM_MIC("Call Mic", NULL),
+};
+
+/* example machine audio_mapnections */
+static const char* audio_map[][3] = {
+
+	/* Connections to the lm4857 amp */
+	{"Audio Out", NULL, "LOUT2"},
+	{"Audio Out", NULL, "ROUT2"},
+
+	/* Connections to the GSM Module */
+	{"GSM Line Out", NULL, "OUT3"},
+
+	/* Connections to Headset */
+	{"AUXL", NULL, "Handset Mic"},
+	{"AUXR", NULL, "Handset Mic"},
+
+	{NULL, NULL, NULL},
+};
+
+static const char *lm4857_mode[] = {
+	"Off",
+	"Call Speaker",
+	"Stereo Speakers",
+	"Stereo Speakers + Headphones",
+	"Headphones"
+};
+
+static const struct soc_enum lm4857_mode_enum[] = {
+	SOC_ENUM_SINGLE_EXT(5, lm4857_mode),
+};
+
+static const char *hxd8_scenarios[] = {
+	"Off",
+	"GSM Handset",
+	"Speakers",
+	"Capture Handset"
+};
+
+static const struct soc_enum hxd8_scenario_enum[] = {
+	SOC_ENUM_SINGLE_EXT(4,hxd8_scenarios),
+};
+
+static const struct snd_kcontrol_new wm8976_hxd8_controls[] = {
+	SOC_SINGLE_EXT("Amp Left Playback Volume", LM4857_LVOL, 0, 31, 0,
+		lm4857_get_reg, lm4857_set_reg),
+	SOC_SINGLE_EXT("Amp Right Playback Volume", LM4857_RVOL, 0, 31, 0,
+		lm4857_get_reg, lm4857_set_reg),
+	SOC_SINGLE_EXT("Amp Mono Playback Volume", LM4857_MVOL, 0, 31, 0,
+		lm4857_get_reg, lm4857_set_reg),
+	SOC_ENUM_EXT("Amp Mode", lm4857_mode_enum[0],
+		lm4857_get_mode, lm4857_set_mode),
+	SOC_ENUM_EXT("Neo Mode", hxd8_scenario_enum[0],
+		hxd8_get_scenario, hxd8_set_scenario),
+	SOC_SINGLE_EXT("Amp Spk 3D Playback Switch", LM4857_LVOL, 5, 1, 0,
+		lm4857_get_reg, lm4857_set_reg),
+	SOC_SINGLE_EXT("Amp HP 3d Playback Switch", LM4857_RVOL, 5, 1, 0,
+		lm4857_get_reg, lm4857_set_reg),
+	SOC_SINGLE_EXT("Amp Fast Wakeup Playback Switch", LM4857_CTRL, 5, 1, 0,
+		lm4857_get_reg, lm4857_set_reg),
+	SOC_SINGLE_EXT("Amp Earpiece 6dB Playback Switch", LM4857_CTRL, 4, 1, 0,
+		lm4857_get_reg, lm4857_set_reg),
+};
+
+/*
+ * This is an example machine initialisation for a wm8976 connected to a
+ * hxd8.
+ */
+
+static int hxd8_wm8976_init(struct snd_soc_codec *codec)
+{
+	int i, err;
+
+	/* set up NC codec pins */
+	snd_soc_dapm_set_endpoint(codec, "MICBIAS", 0);
+	snd_soc_dapm_set_endpoint(codec, "OUT4", 0);
+	snd_soc_dapm_set_endpoint(codec, "ROUT1",  0);
+	snd_soc_dapm_set_endpoint(codec, "LOUT1",  0);
+	snd_soc_dapm_set_endpoint(codec, "LIP", 0);
+	snd_soc_dapm_set_endpoint(codec, "LIN", 0);
+	snd_soc_dapm_set_endpoint(codec, "L2", 0);
+
+
+	/* set endpoints to default mode */
+	set_scenario_endpoints(codec, HXD8_AUDIO_OFF);
+
+	/* Add hxd8 specific widgets */
+	for(i = 0; i < ARRAY_SIZE(wm8976_dapm_widgets); i++)
+		snd_soc_dapm_new_control(codec, &wm8976_dapm_widgets[i]);
+
+	/* add hxd8 specific controls */
+	for (i = 0; i < ARRAY_SIZE(wm8976_hxd8_controls); i++) {
+		if ((err = snd_ctl_add(codec->card,
+				snd_soc_cnew(&wm8976_hxd8_controls[i],codec, NULL))) < 0)
+			return err;
+	}
+
+	/* set up hxd8 specific audio path audio_mapnects */
+	for(i = 0; audio_map[i][0] != NULL; i++) {
+		snd_soc_dapm_connect_input(codec, audio_map[i][0], audio_map[i][1],
+			audio_map[i][2]);
+	}
+
+	snd_soc_dapm_sync_endpoints(codec);
+	return 0;
+}
+
+static struct snd_soc_dai_link hxd8_dai[] = {
+{ /* Hifi Playback */
+	.name = "WM8976",
+	.stream_name = "WM8976 HiFi",
+	.cpu_dai = &s3c24xx_i2s_dai,
+	.codec_dai = &wm8976_dai,
+	.init = hxd8_wm8976_init,
+	.ops = &hxd8_hifi_ops,
+},
+};
+
+static struct snd_soc_machine hxd8 = {
+	.name = "hxd8",
+	.dai_link = hxd8_dai,
+	.num_links = ARRAY_SIZE(hxd8_dai),
+};
+
+static struct wm8976_setup_data hxd8_wm8976_setup = {
+	.i2c_address = 0x1a,
+};
+
+static struct snd_soc_device hxd8_snd_devdata = {
+	.machine = &hxd8,
+	.platform = &s3c24xx_soc_platform,
+	.codec_dev = &soc_codec_dev_wm8976,
+	.codec_data = &hxd8_wm8976_setup,
+};
+
+static struct i2c_client client_template;
+
+static unsigned short normal_i2c[] = { 0x7C, I2C_CLIENT_END };
+
+/* Magic definition of all other variables and things */
+I2C_CLIENT_INSMOD;
+
+static int lm4857_amp_probe(struct i2c_adapter *adap, int addr, int kind)
+{
+	int ret;
+
+	client_template.adapter = adap;
+	client_template.addr = addr;
+
+	DBG("Entering %s\n", __FUNCTION__);
+
+	i2c = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
+	if (i2c == NULL){
+		return -ENOMEM;
+	}
+	memcpy(i2c, &client_template, sizeof(struct i2c_client));
+
+	ret = i2c_attach_client(i2c);
+	if (ret < 0) {
+		DBG("failed to attach codec at addr %x\n", addr);
+		goto exit_err;
+	}
+
+	lm4857_write_regs();
+
+	return ret;
+
+exit_err:
+	kfree(i2c);
+	return ret;
+}
+
+static int lm4857_i2c_detach(struct i2c_client *client)
+{
+	i2c_detach_client(client);
+	kfree(client);
+	return 0;
+}
+
+static int lm4857_i2c_attach(struct i2c_adapter *adap)
+{
+	return i2c_probe(adap, &addr_data, lm4857_amp_probe);
+}
+
+#define I2C_DRIVERID_LM4857 0xA5A5 /* liam -  need a proper id */
+
+/* corgi i2c codec control layer */
+static struct i2c_driver lm4857_i2c_driver = {
+	.driver = {
+		.name = "LM4857 I2C Amp",
+		.owner = THIS_MODULE,
+	},
+	.id =             I2C_DRIVERID_LM4857,
+	.attach_adapter = lm4857_i2c_attach,
+	.detach_client =  lm4857_i2c_detach,
+	.command =        NULL,
+};
+
+static struct i2c_client client_template = {
+	.name =   "LM4857",
+	.driver = &lm4857_i2c_driver,
+};
+
+static struct platform_device *hxd8_snd_device;
+
+static int __init hxd8_init(void)
+{
+	int ret;
+
+	hxd8_snd_device = platform_device_alloc("soc-audio", -1);
+	if (!hxd8_snd_device)
+		return -ENOMEM;
+
+	platform_set_drvdata(hxd8_snd_device, &hxd8_snd_devdata);
+	hxd8_snd_devdata.dev = &hxd8_snd_device->dev;
+	ret = platform_device_add(hxd8_snd_device);
+
+	if (ret)
+		platform_device_put(hxd8_snd_device);
+
+	ret = i2c_add_driver(&lm4857_i2c_driver);
+	if (ret != 0)
+		printk(KERN_ERR "can't add i2c driver");
+
+	return ret;
+}
+
+static void __exit hxd8_exit(void)
+{
+	platform_device_unregister(hxd8_snd_device);
+}
+
+module_init(hxd8_init);
+module_exit(hxd8_exit);
+
+/* Module information */
+MODULE_AUTHOR("Graeme Gregory, graeme.gregory@wolfsonmicro.com, www.wolfsonmicro.com");
+MODULE_DESCRIPTION("ALSA SoC WM8976 Hxd8");
+MODULE_LICENSE("GPL");
+
Index: linux-2.6.21/sound/soc/s3c24xx/Kconfig
===================================================================
--- linux-2.6.21.orig/sound/soc/s3c24xx/Kconfig	2007-05-15 12:33:54.000000000 +0800
+++ linux-2.6.21/sound/soc/s3c24xx/Kconfig	2007-05-15 12:36:22.000000000 +0800
@@ -28,5 +28,15 @@
 	help
 	  Say Y if you want to add support for SoC audio on FIC Neo1973
 	  with the WM8753.
+
+config SND_S3C24XX_SOC_HXD8_WM8976
+	tristate "SoC I2S Audio support for HXD8 - WM8976"
+	depends on SND_S3C24XX_SOC && MACH_HXD8
+	select SND_S3C24XX_SOC_I2S
+	select SND_SOC_WM8976
+	help
+	  Say Y if you want to add support for SoC audio on HXD8
+          with the WM8976
+
 endmenu
 
Index: linux-2.6.21/sound/soc/s3c24xx/Makefile
===================================================================
--- linux-2.6.21.orig/sound/soc/s3c24xx/Makefile	2007-05-15 12:36:30.000000000 +0800
+++ linux-2.6.21/sound/soc/s3c24xx/Makefile	2007-05-15 13:46:30.000000000 +0800
@@ -8,6 +8,8 @@
 # S3C24XX Machine Support
 snd-soc-smdk2440-objs := smdk2440.o
 snd-soc-neo1973-wm8753-objs := neo1973_wm8753.o
+snd-soc-hxd8-wm8976-objs := hxd8_wm8976.o
 
 obj-$(CONFIG_SND_S3C24XX_SOC_SMDK2440) += snd-soc-smdk2440.o
 obj-$(CONFIG_SND_S3C24XX_SOC_NEO1973_WM8753) += snd-soc-neo1973-wm8753.o
+obj-$(CONFIG_SND_S3C24XX_SOC_HXD8_WM8976) += snd-soc-hxd8-wm8976.o
