Forwarded to the list as requested by Andreas Mohr
<[EMAIL PROTECTED]>

-- 
Daniel Ribeiro

Hello all,

after having figured out that the FM amplifier contained in the E680 is
a National Semiconductor LM4857 (quite some detective work with 3 puzzles
to solve!), I was able to find that the Neo1973 was already using
the same chip, albeit with a combined driver for a different codec, not
PCAP.

Thus I decided to isolate the LM4857 parts into a standalone, _generic_
driver
for LM4857 (using latest very modular 2.6.x i2c API!),
useable by all devices containing an LM4857.

Apart from the existing combined driver by Wolfson Microelectronics
(thanks!),
it also helped that Harald Welte had already written an LM4857
power management patch for the Neo1973, too.

After quite some trouble getting i2c comm to work (turned out I had simply
probed the wrong bus number, which then locked up everything as seems usual
on i2c), this driver now seems to work fine on my E680 Hong Kong,
providing ample sound over headphones, call speaker, stereo speakers
or whatever, with fully adjustable levels.
I also had to override an "invalid i2c device id" check in the kernel,
since the amplifier sadly uses a reserved ID (this has been done by another
patch for the Neo already, so it was easy to figure out what had to be
done).

To test it, install e.g. OpenMoko rootfs (unstable branch) on Motorola E680,
build or get a 2.6.24 kernel with OpenEZX quilt patchset,
run Media Player and run alsamixer or amixer either via ssh login
or possibly via OpenMoko terminal.

Feel free to give me any feedback you might come up with concerning my
driver layout / placement, mixer control map, test results
or future development.

Patch against a .24 kernel patched with a slightly oldish quilt patchset
(about a week old, shouldn't matter). Note that I have NOT tested the
suspend qualities of this driver yet, I merely added support for it.

Thanks a lot for all your hard OpenEZX work,

Andreas Mohr

Hello all,

after having figured out that the FM amplifier contained in the E680 is
a National Semiconductor LM4857 (quite some detective work with 3 puzzles
to solve!), I was able to find that the Neo1973 was already using
the same chip, albeit with a combined driver for a different codec, not
PCAP.

Thus I decided to isolate the LM4857 parts into a standalone, _generic_ driver
for LM4857 (using latest very modular 2.6.x i2c API!),
useable by all devices containing an LM4857.

Apart from the existing combined driver by Wolfson Microelectronics (thanks!),
it also helped that Harald Welte had already written an LM4857
power management patch for the Neo1973, too.

After quite some trouble getting i2c comm to work (turned out I had simply
probed the wrong bus number, which then locked up everything as seems usual
on i2c), this driver now seems to work fine on my E680 Hong Kong,
providing ample sound over headphones, call speaker, stereo speakers
or whatever, with fully adjustable levels.
I also had to override an "invalid i2c device id" check in the kernel,
since the amplifier sadly uses a reserved ID (this has been done by another
patch for the Neo already, so it was easy to figure out what had to be
done).

To test it, install e.g. OpenMoko rootfs (unstable branch) on Motorola E680,
build or get a 2.6.24 kernel with OpenEZX quilt patchset,
run Media Player and run alsamixer or amixer either via ssh login
or possibly via OpenMoko terminal.

Feel free to give me any feedback you might come up with concerning my
driver layout / placement, mixer control map, test results
or future development.

Patch against a .24 kernel patched with a slightly oldish quilt patchset
(about a week old, shouldn't matter). Note that I have NOT tested the
suspend qualities of this driver yet, I merely added support for it.

Thanks a lot for all your hard OpenEZX work,

Andreas Mohr

Signed-off-by: Andreas Mohr <[EMAIL PROTECTED]>

diff -urN linux-2.6.24.patched_clean/arch/arm/mach-pxa/ezx-e680.c linux-2.6.24.patched_lm4857/arch/arm/mach-pxa/ezx-e680.c
--- linux-2.6.24.patched_clean/arch/arm/mach-pxa/ezx-e680.c	2008-02-15 23:59:18.000000000 +0100
+++ linux-2.6.24.patched_lm4857/arch/arm/mach-pxa/ezx-e680.c	2008-02-17 11:56:36.000000000 +0100
@@ -17,6 +17,7 @@
 #include <linux/irq.h>
 #include <linux/input.h>
 #include <linux/gpio_keys.h>
+#include <linux/i2c.h>
 
 #include <asm/mach-types.h>
 #include <asm/mach/arch.h>
@@ -240,6 +241,11 @@
 	.resource	= e680_emu_resources,
 };
 
+struct platform_device e680_fm_amp_device = {
+	.name		= "e680-fm_amplifier",
+	.id		= -1,
+};
+
 static unsigned char e680_keycode[] = {
        /* row 0 */
        KEY_UP, KEY_RIGHT, KEY_RESERVED, KEY_PHONE,
@@ -349,10 +355,24 @@
 	&e680_pcap_device,
 	&e680_emu_device,
 	&pcap_ts_device,
+	&e680_fm_amp_device,
 	&e680locksw_device,
 	&e680led_device,
 };
 
+#ifdef CONFIG_I2C_BOARDINFO
+static struct i2c_board_info __initdata e680_i2c_board_info[] = {
+	{
+		I2C_BOARD_INFO("lm4857", 0x7c),
+		.type = "lm4857",
+	},
+	/* TODO when driver support is ready:
+	 * - E680 FM radio
+	 * - ... etc
+	 */
+};
+#endif
+
 static void __init e680_init(void)
 {
 	/* setup sleep mode values */
@@ -376,6 +396,11 @@
 	pxa_gpio_mode(GPIO_EMU_MUX2|GPIO_OUT);
 	clr_GPIO(GPIO_EMU_MUX2);
 
+#ifdef CONFIG_I2C_BOARDINFO
+	i2c_register_board_info(0, e680_i2c_board_info,
+				ARRAY_SIZE(e680_i2c_board_info));
+#endif
+
 	platform_add_devices(devices, ARRAY_SIZE(devices));
 }
 
diff -urN linux-2.6.24.patched_clean/drivers/i2c/i2c-core.c linux-2.6.24.patched_lm4857/drivers/i2c/i2c-core.c
--- linux-2.6.24.patched_clean/drivers/i2c/i2c-core.c	2008-02-04 17:12:35.000000000 +0100
+++ linux-2.6.24.patched_lm4857/drivers/i2c/i2c-core.c	2008-02-17 12:07:14.000000000 +0100
@@ -944,7 +944,10 @@
 	if (addr < 0x03 || addr > 0x77) {
 		dev_warn(&adapter->dev, "Invalid probe address 0x%02x\n",
 			 addr);
-		return -EINVAL;
+		/* however at least LM4857 (ab-)uses 0x7c,
+		 * thus need to specifically grant that */
+		if (addr != 0x7c)
+			return -EINVAL;
 	}
 
 	/* Skip if already in use */
diff -urN linux-2.6.24.patched_clean/sound/i2c/amp/lm4857.c linux-2.6.24.patched_lm4857/sound/i2c/amp/lm4857.c
--- linux-2.6.24.patched_clean/sound/i2c/amp/lm4857.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.24.patched_lm4857/sound/i2c/amp/lm4857.c	2008-02-17 11:52:12.000000000 +0100
@@ -0,0 +1,381 @@
+/*
+ * lm4857.c  -- driver for LM4857 I2C FM amplifier by National Semiconductors
+ *              as used e.g. in Motorola E680 and Neo1973 mobile phones.
+ * Copyright 2008 Andreas Mohr
+ *
+ * Heavily based on LM4857 portions of sound/soc/s3c24xx/neo1973_wm8753.c
+ * (Copyright 2007 Wolfson Microelectronics PLC.)
+ * and on asoc-neo1973_wm8753-power.patch (by Harald Welte)
+ * Thanks!
+ *
+ *  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
+ *    15th Feb 2008   Initial version.
+ *
+ * Notes:
+ * This driver does not use the ALSA i2c layer (instead we intend to use
+ * current kernel layer), however we might eventually be forced
+ * to convert it to use ALSA i2c creation if we need specific sound support.
+ *
+ * TODO:
+ * - verify driver dependencies / kbuild
+ * - (re-)activate Neo Mode? (neo1973_get_scenario etc.)
+ * - have a look at neo1973_wm8753.c to integrate more functionality
+ * - adapt neo1973_wm8753.c to use our isolated driver instead of
+ *   commingling disparate functionality
+ * - add very specific probing to make sure we actually have
+ *   an LM4857 at this address
+ * - mutex needed? (e.g. snd_i2c_lock as used by ALSA-created i2c drivers)
+ * - remove debugging printks
+ * - remove all FIXMEs
+ * - use per-instance data object instead of global data
+ */
+
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/i2c.h>
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+
+#include "lm4857.h"
+
+static const u8 lm4857_regs_template[4] = {0x00, 0x40, 0x80, 0xC0};
+
+struct lm4857_data {
+	struct i2c_client *client;
+	u8 regs[4];
+	u8 power_state;
+};
+
+/* FIXME: I'd really like to know how to make per-instance data work
+   with the newish i2c driver concept, but after lots of research I'm still
+   not sure where I should be getting my driver client reference from...
+   Sure, we probably have to stick it into kcontrol and get it via
+   private data, but how do we get the driver instance reference upon
+   kcontrol creation?? */
+#define LM4857_PER_INSTANCE 0
+#if LM4857_PER_INSTANCE == 0
+static struct lm4857_data *lm4857;
+#endif
+
+static void lm4857_write_regs(struct lm4857_data *lm4857)
+{
+	if (i2c_master_send(lm4857->client,
+		lm4857->regs, sizeof(lm4857->regs)) != sizeof(lm4857->regs))
+		printk(KERN_ERR "lm4857: i2c write failed\n");
+}
+
+static int lm4857_get_reg(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+#if LM4857_PER_INSTANCE
+	struct lm4857_data *lm4857 = snd_kcontrol_chip(kcontrol);
+#endif
+	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)
+{
+#if LM4857_PER_INSTANCE
+	struct lm4857_data *lm4857 = snd_kcontrol_chip(kcontrol);
+#endif
+	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(lm4857);
+	return 1;
+}
+
+static int lm4857_get_mode(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+#if LM4857_PER_INSTANCE
+	struct lm4857_data *lm4857 = snd_kcontrol_chip(kcontrol);
+#endif
+	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)
+{
+#if LM4857_PER_INSTANCE
+	struct lm4857_data *lm4857 = snd_kcontrol_chip(kcontrol);
+#endif
+	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(lm4857);
+	return 1;
+}
+
+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(ARRAY_SIZE(lm4857_mode), lm4857_mode),
+};
+
+/* lm4857_controls was taken straight from Neo1973 driver,
+ * however it may easily be that we don't need to add
+ * any Motorola-E680-specific variant here anyway since this map
+ * may happen to be fine for all devices on the market. */
+static const struct snd_kcontrol_new lm4857_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", neo_scenario_enum[0],
+		neo1973_get_scenario, neo1973_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),
+};
+
+/*
+ * Externally visible function to let the driver of a hardware-connected codec
+ * append our LM4857-specific kcontrols to its existing kcontrols.
+ * This function might need to be updated to take an external kcontrols struct
+ * in case audio hardware support between machine models differs.
+ */
+int lm4857_create_controls(struct snd_soc_codec *codec)
+{
+	int i, err;
+
+	printk(KERN_DEBUG "LM4857 init controls!\n");
+
+	if (!lm4857) {
+		printk(KERN_ERR "attempted to add mixer controls"
+		" despite driver not initialized yet, aborting!\n");
+		return -ENODEV;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(lm4857_controls); i++) {
+		err = snd_ctl_add(codec->card,
+				snd_soc_cnew(&lm4857_controls[i],
+				codec, NULL));
+		if (err < 0)
+			return err;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(lm4857_create_controls);
+
+static struct attribute *lm4857_attributes[] = {
+	/* FIXME */
+	NULL
+};
+
+
+static const struct attribute_group lm4857_attr_group = {
+	.attrs = lm4857_attributes,
+};
+
+static int __devinit lm4857_probe(struct i2c_client *client)
+{
+	int err = 0;
+	struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
+#if LM4857_PER_INSTANCE
+	struct lm4857_data *lm4857;
+#endif
+
+	/* happens to probe wrong bus number for you?
+	 * --> make sure to use correct bus upon i2c_register_board_info()! */
+	printk(KERN_DEBUG "lm4857_probe, adapter %s!\n", adapter->name);
+
+	/*if (!(adapter->class & I2C_CLASS_ALL)) FIXME anything to do here?
+		return 0; */
+
+#if LM4857_PER_INSTANCE == 0
+	if (lm4857) {
+		printk(KERN_ERR "driver already used, exiting!\n");
+		err = -EIO;
+		goto ERROR0;
+	}
+#endif
+
+	/* Let's see whether this adapter can support what we need. */
+	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_QUICK |
+						I2C_FUNC_SMBUS_WRITE_BYTE)) {
+		printk(KERN_ERR "insufficient adapter functionality!\n");
+		err = -EIO;
+		goto ERROR0;
+	}
+
+	/* FIXME: we should add a check here whether we're actually dealing with
+	 * an LM4857 at this i2c address, since this is a very generic driver.
+	 * However I couldn't get reading from the device to work,
+	 * possibly because it is a write-only chip. */
+
+#if 0 /* debug code, currently doesn't work */
+	{
+		int i = 0;
+		unsigned char buf[4] = { 0, 0, 0, 0};
+
+		for (i = 0; i < sizeof(buf); i++)
+			buf[i] = i2c_smbus_read_byte_data(client, i);
+		/* i2c_master_recv(client, buf, sizeof(buf)); */
+		if (i2c_smbus_xfer(adapter, addr, 0, 0, 0,
+				I2C_SMBUS_QUICK, NULL) < 0)
+			return 0;
+
+		printk(KERN_DEBUG "device data is %x %x %x %x\n",
+				buf[0], buf[1], buf[2], buf[3]);
+	}
+#endif
+
+	lm4857 = kzalloc(sizeof(*lm4857), GFP_KERNEL);
+	if (!lm4857) {
+		err = -ENOMEM;
+		goto ERROR0;
+	}
+	memcpy(lm4857->regs, lm4857_regs_template, sizeof(lm4857->regs));
+	lm4857->client = client;
+	i2c_set_clientdata(client, lm4857);
+
+	lm4857_write_regs(lm4857);
+
+	/* Register sysfs hooks */
+	err = sysfs_create_group(&client->dev.kobj, &lm4857_attr_group);
+	if (err)
+		goto exit_kfree;
+
+	return err;
+
+exit_kfree:
+	kfree(client);
+
+ERROR0:
+	return err;
+}
+
+static int __devexit lm4857_remove(struct i2c_client *client)
+{
+	printk(KERN_DEBUG "lm4857_remove\n");
+	sysfs_remove_group(&client->dev.kobj, &lm4857_attr_group);
+	kfree(i2c_get_clientdata(client));
+	return 0;
+}
+
+static int lm4857_suspend(struct i2c_client *dev, pm_message_t state)
+{
+#if LM4857_PER_INSTANCE
+	struct lm4857_data *lm4857 = i2c_get_clientdata(dev);
+#endif
+	dev_dbg(&dev->dev, "lm4857_suspend\n");
+	lm4857->power_state = lm4857->regs[LM4857_CTRL] & 0xf;
+	if (lm4857->power_state) {
+		lm4857->regs[LM4857_CTRL] &= 0xf0;
+		lm4857_write_regs(lm4857);
+	}
+	return 0;
+}
+
+static int lm4857_resume(struct i2c_client *dev)
+{
+#if LM4857_PER_INSTANCE
+	struct lm4857_data *lm4857 = i2c_get_clientdata(dev);
+#endif
+	if (lm4857->power_state) {
+		lm4857->regs[LM4857_CTRL] |= (lm4857->power_state & 0x0f);
+		lm4857_write_regs(lm4857);
+	}
+	return 0;
+}
+
+static void lm4857_shutdown(struct i2c_client *dev)
+{
+#if LM4857_PER_INSTANCE
+	struct lm4857_data *lm4857 = i2c_get_clientdata(dev);
+#endif
+	dev_dbg(&dev->dev, "lm4857_shutdown\n");
+	lm4857->regs[LM4857_CTRL] &= 0xf0;
+	lm4857_write_regs(lm4857);
+}
+
+
+/* LM4857 i2c codec control layer */
+static struct i2c_driver lm4857_driver = {
+	.driver = {
+		.name = "lm4857",
+		.owner = THIS_MODULE,
+	},
+	.id =             I2C_DRIVERID_LM4857,
+	.probe =	  lm4857_probe,
+	.remove =  	  lm4857_remove,
+	.suspend =        lm4857_suspend,
+	.resume =         lm4857_resume,
+	.shutdown =       lm4857_shutdown,
+	.command       =  NULL,
+};
+
+static int __init lm4857_module_init(void)
+{
+	int res;
+
+	printk(KERN_DEBUG "lm4857_module_init\n");
+	res = i2c_add_driver(&lm4857_driver);
+	if (res != 0)
+		printk(KERN_ERR "can't add i2c driver\n");
+
+	return res;
+}
+
+static void __exit lm4857_module_exit(void)
+{
+	i2c_del_driver(&lm4857_driver);
+}
+
+module_init(lm4857_module_init);
+module_exit(lm4857_module_exit);
+
+/* Module information */
+MODULE_AUTHOR("Andreas Mohr, [EMAIL PROTECTED]");
+MODULE_DESCRIPTION("LM4857 audio amplifier (I2C)");
+MODULE_LICENSE("GPL");
diff -urN linux-2.6.24.patched_clean/sound/i2c/amp/lm4857.h linux-2.6.24.patched_lm4857/sound/i2c/amp/lm4857.h
--- linux-2.6.24.patched_clean/sound/i2c/amp/lm4857.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.24.patched_lm4857/sound/i2c/amp/lm4857.h	2008-02-08 16:34:55.000000000 +0100
@@ -0,0 +1,34 @@
+/*
+ * lm4857.h  --  ALSA Soc Audio Layer
+ *
+ * Copyright 2007 Wolfson Microelectronics PLC.
+ * Author: Graeme Gregory
+ *         [EMAIL PROTECTED] or [EMAIL PROTECTED]
+ *
+ *  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
+ *    18th Jun 2007   Initial version.
+ */
+
+#ifndef LM4857_H_
+#define LM4857_H_
+
+/* The register offsets in the cache array */
+#define LM4857_MVOL 0
+#define LM4857_LVOL 1
+#define LM4857_RVOL 2
+#define LM4857_CTRL 3
+
+/* the shifts required to set these bits */
+#define LM4857_3D 5
+#define LM4857_WAKEUP 5
+#define LM4857_EPGAIN 4
+
+int lm4857_create_controls(struct snd_soc_codec *codec);
+
+#endif /*LM4857_H_*/
+
diff -urN linux-2.6.24.patched_clean/sound/i2c/amp/Makefile linux-2.6.24.patched_lm4857/sound/i2c/amp/Makefile
--- linux-2.6.24.patched_clean/sound/i2c/amp/Makefile	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.24.patched_lm4857/sound/i2c/amp/Makefile	2008-02-10 21:14:55.000000000 +0100
@@ -0,0 +1,9 @@
+#
+# Makefile for ALSA
+# Copyright (c) 2001 by Jaroslav Kysela <[EMAIL PROTECTED]>
+#
+
+snd-lm4857-objs := lm4857.o
+
+# Toplevel Module Dependency
+obj-$(CONFIG_SND_PXA2XX_SOC_EZX) += snd-lm4857.o
diff -urN linux-2.6.24.patched_clean/sound/i2c/Makefile linux-2.6.24.patched_lm4857/sound/i2c/Makefile
--- linux-2.6.24.patched_clean/sound/i2c/Makefile	2008-02-04 17:13:10.000000000 +0100
+++ linux-2.6.24.patched_lm4857/sound/i2c/Makefile	2008-02-08 19:41:32.000000000 +0100
@@ -9,6 +9,7 @@
 
 obj-$(CONFIG_L3) += l3/
 
+obj-$(CONFIG_SND) += amp/
 obj-$(CONFIG_SND) += other/
 
 # Toplevel Module Dependency
diff -urN linux-2.6.24.patched_clean/sound/soc/pxa/ezx.c linux-2.6.24.patched_lm4857/sound/soc/pxa/ezx.c
--- linux-2.6.24.patched_clean/sound/soc/pxa/ezx.c	2008-02-15 23:59:19.000000000 +0100
+++ linux-2.6.24.patched_lm4857/sound/soc/pxa/ezx.c	2008-02-17 12:05:55.000000000 +0100
@@ -28,6 +28,9 @@
 #include <asm/arch/ezx-pcap.h>
 
 #include "../codecs/pcap2.h"
+#if CONFIG_PXA_EZX_E680
+#include "../../i2c/amp/lm4857.h" /* FIXME: move to include/sound/ ? */
+#endif
 #include "pxa2xx-pcm.h"
 #include "pxa2xx-ssp.h"
 
@@ -206,7 +209,7 @@
  */
 static int ezx_machine_init(struct snd_soc_codec *codec)
 {
-	int i;
+	int i, err;
 	/* mark unused codec pins as NC */
 //	snd_soc_dapm_set_endpoint(codec, "FIXME", 0);
 	control_codec = codec;
@@ -226,6 +229,16 @@
 		snd_soc_dapm_connect_input(codec, audio_map[i][0], audio_map[i][1], audio_map[i][2]);
 	}
 
+#if CONFIG_PXA_EZX_E680
+	/* append the mixer controls of the LM4857 audio amplifier driver
+	 * in case of a Motorola E680. */
+	printk(KERN_DEBUG "about to call lm4857_create_controls\n");
+	err = lm4857_create_controls(codec);
+	if (err < 0)
+		return err;
+	printk(KERN_DEBUG "called lm4857_create_controls\n");
+#endif
+
 	/* synchronise subsystem */
 	snd_soc_dapm_sync_endpoints(codec);
 	return 0;
@@ -305,6 +318,7 @@
 static int __init ezx_init(void)
 {
 	int ret;
+
 	ezx_snd_device = platform_device_alloc("soc-audio", -1);
 	if (!ezx_snd_device)
 		return -ENOMEM;
@@ -315,6 +329,7 @@
 
 	if (ret)
 		platform_device_put(ezx_snd_device);
+
 	/* configure gpio for ssp3 */
 	pxa_gpio_mode(GPIO83_SFRM3_MD);	/* SFRM */
 	pxa_gpio_mode(GPIO81_STXD3_MD);	/* TXD  */


Reply via email to