--- /dev/null
+++ b/drivers/l3/l3-core.c
@@ -0,0 +1,211 @@
+/*
+ *  linux/drivers/l3/l3-core.c
+ *
+ *  Copyright (C) 2001 Russell King
+ *
+ *  General structure taken from i2c-core.c by Simon G. Vogl
+ *
+ * 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.
+ *
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/proc_fs.h>
+#include <linux/kmod.h>
+#include <linux/init.h>
+#include <linux/l3.h>
+
+static DECLARE_MUTEX(adapter_lock);
+static LIST_HEAD(adapter_list);
+
+static DECLARE_MUTEX(driver_lock);
+static LIST_HEAD(driver_list);
+
+/**
+ * l3_add_adapter - register a new L3 bus adapter
+ * @adap: l3_adapter structure for the registering adapter
+ *
+ * Make the adapter available for use by clients using name adap->name.
+ * The adap->adapters list is initialised by this function.
+ *
+ * Returns 0;
+ */
+int l3_add_adapter(struct l3_adapter *adap)
+{
+	printk("L3 add adapter\n");
+
+	down(&adapter_lock);
+	list_add(&adap->adapters, &adapter_list);
+	up(&adapter_lock);
+	return 0;	
+}
+
+/**
+ * l3_del_adapter - unregister a L3 bus adapter
+ * @adap: l3_adapter structure to unregister
+ *
+ * Remove an adapter from the list of available L3 Bus adapters.
+ *
+ * Returns 0;
+ */
+int l3_del_adapter(struct l3_adapter *adap)
+{
+	down(&adapter_lock);
+	list_del(&adap->adapters);
+	up(&adapter_lock);
+	return 0;
+}
+
+static struct l3_adapter *__l3_get_adapter(const char *name)
+{
+	struct list_head *l;
+
+	list_for_each(l, &adapter_list) {
+		struct l3_adapter *adap = list_entry(l, struct l3_adapter, adapters);
+
+		if (strcmp(adap->name, name) == 0)
+			return adap;
+	}
+
+	return NULL;
+}
+
+/**
+ * l3_get_adapter - get a reference to an adapter
+ * @name: driver name
+ *
+ * Obtain a l3_adapter structure for the specified adapter.  If the adapter
+ * is not currently load, then load it.  The adapter will be locked in core
+ * until all references are released via l3_put_adapter.
+ */
+struct l3_adapter *l3_get_adapter(const char *name)
+{
+	struct l3_adapter *adap;
+	int try;
+
+	for (try = 0; try < 2; try ++) {
+		down(&adapter_lock);
+		adap = __l3_get_adapter(name);
+		if (adap && !try_module_get(adap->owner))
+			adap = NULL;
+		up(&adapter_lock);
+
+		if (adap)
+			break;
+
+		if (try == 0)
+			request_module(name);
+	}
+
+	return adap;
+}
+
+/**
+ * l3_put_adapter - release a reference to an adapter
+ * @adap: driver to release reference
+ *
+ * Indicate to the L3 core that you no longer require the adapter reference.
+ * The adapter module may be unloaded when there are no references to its
+ * data structure.
+ *
+ * You must not use the reference after calling this function.
+ */
+void l3_put_adapter(struct l3_adapter *adap)
+{
+	if (adap && adap->owner)
+		module_put(adap->owner);
+}
+
+
+/**
+ * l3_transfer - transfer information on an L3 bus
+ * @adap: adapter structure to perform transfer on
+ * @msgs: array of l3_msg structures describing transfer
+ * @num: number of l3_msg structures
+ *
+ * Transfer the specified messages to/from a device on the L3 bus.
+ *
+ * Returns number of messages successfully transferred, otherwise negative
+ * error code.
+ */
+int l3_transfer(struct l3_adapter *adap, struct l3_msg msgs[], int num)
+{
+	int ret = -ENOSYS;
+
+	printk("L3 transfer\n");
+
+	if (adap->algo->xfer) {
+		down(adap->lock);
+		ret = adap->algo->xfer(adap, msgs, num);
+		up(adap->lock);
+	}
+	return ret;
+}
+
+/**
+ * l3_write - send data to a device on an L3 bus
+ * @adap: L3 bus adapter
+ * @addr: L3 bus address
+ * @buf: buffer for bytes to send
+ * @len: number of bytes to send
+ *
+ * Send len bytes pointed to by buf to device address addr on the L3 bus
+ * described by client.
+ *
+ * Returns the number of bytes transferred, or negative error code.
+ */
+int l3_write(struct l3_adapter *adap, int addr, const char *buf, int len)
+{
+	struct l3_msg msg;
+	int ret;
+
+	printk("L3 write\n");
+
+	msg.addr = addr;
+	msg.flags = 0;
+	msg.buf = (char *)buf;
+	msg.len = len;
+
+	ret = l3_transfer(adap, &msg, 1);
+	return ret == 1 ? len : ret;
+}
+
+/**
+ * l3_read - receive data from a device on an L3 bus
+ * @adap: L3 bus adapter
+ * @addr: L3 bus address
+ * @buf: buffer for bytes to receive
+ * @len: number of bytes to receive
+ *
+ * Receive len bytes from device address addr on the L3 bus described by
+ * client to a buffer pointed to by buf.
+ *
+ * Returns the number of bytes transferred, or negative error code.
+ */
+int l3_read(struct l3_adapter *adap, int addr, char *buf, int len)
+{
+	struct l3_msg msg;
+	int ret;
+
+	printk("L3 read\n");
+
+	msg.addr = addr;
+	msg.flags = L3_M_RD;
+	msg.buf = buf;
+	msg.len = len;
+
+	ret = l3_transfer(adap, &msg, 1);
+	return ret == 1 ? len : ret;
+}
+
+EXPORT_SYMBOL(l3_add_adapter);
+EXPORT_SYMBOL(l3_del_adapter);
+EXPORT_SYMBOL(l3_get_adapter);
+EXPORT_SYMBOL(l3_put_adapter);
+EXPORT_SYMBOL(l3_transfer);
+EXPORT_SYMBOL(l3_write);
+EXPORT_SYMBOL(l3_read);
--- /dev/null
+++ b/drivers/l3/l3-algo-bit.c
@@ -0,0 +1,177 @@
+/*
+ * L3 bus algorithm module.
+ *
+ *  Copyright (C) 2001 Russell King, All Rights Reserved.
+ *
+ * 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.
+ *
+ *  Note that L3 buses can share the same pins as I2C buses, so we must
+ *  _not_ generate an I2C start condition.  An I2C start condition is
+ *  defined as a high-to-low transition of the data line while the clock
+ *  is high.  Therefore, we must only change the data line while the
+ *  clock is low.
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/l3.h>
+#include <linux/l3-algo-bit.h>
+
+#define setdat(adap,val)	adap->setdat(adap->data, val)
+#define setclk(adap,val)	adap->setclk(adap->data, val)
+#define setmode(adap,val)	adap->setmode(adap->data, val)
+#define setdatin(adap)		adap->setdir(adap->data, 1)
+#define setdatout(adap)		adap->setdir(adap->data, 0)
+#define getdat(adap)		adap->getdat(adap->data)
+
+/*
+ * Send one byte of data to the chip.  Data is latched into the chip on
+ * the rising edge of the clock.
+ */
+static void sendbyte(struct l3_algo_bit_data *adap, unsigned int byte)
+{
+	int i;
+
+	printk("L3 sendbyte: %02X\n",byte);
+
+	for (i = 0; i < 8; i++) {
+		setclk(adap, 0);
+		udelay(adap->data_hold);
+		setdat(adap, byte & 1);
+		udelay(adap->data_setup);
+		setclk(adap, 1);
+		udelay(adap->clock_high);
+		byte >>= 1;
+	}
+}
+
+/*
+ * Send a set of bytes to the chip.  We need to pulse the MODE line
+ * between each byte, but never at the start nor at the end of the
+ * transfer.
+ */
+static void sendbytes(struct l3_algo_bit_data *adap, const char *buf, int len)
+{
+	int i;
+
+	for (i = 0; i < len; i++) {
+		if (i) {
+			udelay(adap->mode_hold);
+			setmode(adap, 0);
+			udelay(adap->mode);
+		}
+		setmode(adap, 1);
+		udelay(adap->mode_setup);
+		sendbyte(adap, buf[i]);
+	}
+}
+
+/*
+ * Read one byte of data from the chip.  Data is latched into the chip on
+ * the rising edge of the clock.
+ */
+static unsigned int readbyte(struct l3_algo_bit_data *adap)
+{
+	unsigned int byte = 0;
+	int i;
+
+	for (i = 0; i < 8; i++) {
+		setclk(adap, 0);
+		udelay(adap->data_hold + adap->data_setup);
+		setclk(adap, 1);
+		if (getdat(adap))
+			byte |= 1 << i;
+		udelay(adap->clock_high);
+	}
+
+	return byte;
+}
+
+/*
+ * Read a set of bytes from the chip.  We need to pulse the MODE line
+ * between each byte, but never at the start nor at the end of the
+ * transfer.
+ */
+static void readbytes(struct l3_algo_bit_data *adap, char *buf, int len)
+{
+	int i;
+
+	for (i = 0; i < len; i++) {
+		if (i) {
+			udelay(adap->mode_hold);
+			setmode(adap, 0);
+		}
+		setmode(adap, 1);
+		udelay(adap->mode_setup);
+		buf[i] = readbyte(adap);
+	}
+}
+
+static int l3_xfer(struct l3_adapter *l3_adap, struct l3_msg msgs[], int num)
+{
+	struct l3_algo_bit_data *adap = l3_adap->algo_data;
+	int i;
+
+	/*
+	 * If we share an I2C bus, ensure that it is in STOP mode
+	 */
+	setclk(adap, 1);
+	setdat(adap, 1);
+	setmode(adap, 1);
+	setdatout(adap);
+	udelay(adap->mode);
+
+	for (i = 0; i < num; i++) {
+		struct l3_msg *pmsg = &msgs[i];
+
+		if (!(pmsg->flags & L3_M_NOADDR)) {
+			setmode(adap, 0);
+			udelay(adap->mode_setup);
+			sendbyte(adap, pmsg->addr);
+			udelay(adap->mode_hold);
+		}
+
+		if (pmsg->flags & L3_M_RD) {
+			setdatin(adap);
+			readbytes(adap, pmsg->buf, pmsg->len);
+		} else {
+			setdatout(adap);
+			sendbytes(adap, pmsg->buf, pmsg->len);
+		}
+	}
+
+	/*
+	 * Ensure that we leave the bus in I2C stop mode.
+	 */
+	setclk(adap, 1);
+	setdat(adap, 1);
+	setmode(adap, 0);
+	setdatin(adap);
+
+	return num;
+}
+
+static struct l3_algorithm l3_bit_algo = {
+	name:	"L3 bit-shift algorithm",
+	xfer:	l3_xfer,
+};
+
+int l3_bit_add_bus(struct l3_adapter *adap)
+{
+	adap->algo = &l3_bit_algo;
+	return l3_add_adapter(adap);
+}
+
+int l3_bit_del_bus(struct l3_adapter *adap)
+{
+	return l3_del_adapter(adap);
+}
+
+EXPORT_SYMBOL(l3_bit_add_bus);
+EXPORT_SYMBOL(l3_bit_del_bus);
--- /dev/null
+++ b/drivers/l3/s3c24xx.c
@@ -0,0 +1,152 @@
+/*
+ * Code transformed into separate module by Zoltan Devai
+ * to support UDA1341 codec
+ * 
+ * Original code made by Christian Pellegrin, 
+ * see http://www.evolware.org/chri/paciugo/asound.patch
+ *
+ * License status unclear
+ */
+
+
+#include <linux/autoconf.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+
+#include <linux/l3-algo-bit.h>
+
+#include <asm/semaphore.h>
+#include <asm/hardware.h>
+#include <asm/arch/regs-gpio.h>
+
+struct bit_data {
+	unsigned int sda;
+	unsigned int scl;
+	unsigned int l3_mode;
+};
+
+static struct bit_data bit_data;
+
+static int getsda(void *data)
+{
+	struct bit_data *bits = data;
+
+	return s3c2410_gpio_getpin(bits->sda);
+}
+
+static DECLARE_MUTEX(l3_lock);
+#define LOCK	&l3_lock
+
+static void l3_setscl(void *data, int state)
+{
+	struct bit_data *bits = data;
+	unsigned long flags;
+
+	local_irq_save(flags);
+	
+	if (state)
+		s3c2410_gpio_setpin(bits->scl,1);
+	else
+		s3c2410_gpio_setpin(bits->scl,0);
+	
+	s3c2410_gpio_cfgpin(bits->scl, S3C2410_GPIO_OUTPUT);
+	
+	local_irq_restore(flags);
+}
+
+static void l3_setsda(void *data, int state)
+{
+	struct bit_data *bits = data;
+
+	if (state)
+		s3c2410_gpio_setpin(bits->sda,1);
+	else
+		s3c2410_gpio_setpin(bits->sda,0);
+}
+
+static void l3_setdir(void *data, int in)
+{
+	struct bit_data *bits = data;
+	unsigned long flags;
+
+	local_irq_save(flags);
+	
+	if (in)
+		s3c2410_gpio_cfgpin(bits->sda, S3C2410_GPIO_INPUT);
+	else
+		s3c2410_gpio_cfgpin(bits->sda, S3C2410_GPIO_OUTPUT);
+
+	local_irq_restore(flags);
+}
+
+static void l3_setmode(void *data, int state)
+{
+	struct bit_data *bits = data;
+
+	if (state)
+		s3c2410_gpio_setpin(bits->l3_mode,1);
+	else
+		s3c2410_gpio_setpin(bits->l3_mode,0);
+}
+
+static struct l3_algo_bit_data l3_bit_data = {
+	.data= NULL,
+	.setdat= l3_setsda,
+	.setclk= l3_setscl,
+	.setmode= l3_setmode,
+	.setdir= l3_setdir,
+	.getdat= getsda,
+	.data_hold= 1,
+	.data_setup= 1,
+	.clock_high= 1,
+	.mode_hold= 1,
+	.mode_setup= 1,
+};
+
+static struct l3_adapter l3_adapter = {
+	.owner= THIS_MODULE,
+	.name= "s3c24xx-l3",
+	.algo_data= &l3_bit_data,
+	.lock= LOCK,
+};
+
+static int __init s3c24xx_l3_bus_init(void)
+{
+	struct bit_data *bit = &bit_data;
+	unsigned long flags;
+
+	printk(KERN_INFO "S3C24XX L3-bus driver\n");
+
+#if defined(CONFIG_ARCH_SMDK2410) || defined(CONFIG_SMDK2440_CPU2440)
+	bit->scl     = S3C2410_GPB4;
+	bit->sda     = S3C2410_GPB3;
+	bit->l3_mode = S3C2410_GPB2;
+#else
+#error The L3 driver currently does not support your board
+#endif
+
+	/*
+	 * Default level for L3 mode is low.
+	 * We set SCL and SDA high (i2c idle state).
+	 */
+	local_irq_save(flags);
+	s3c2410_gpio_cfgpin(bit->scl, S3C2410_GPIO_OUTPUT);
+	s3c2410_gpio_cfgpin(bit->sda, S3C2410_GPIO_OUTPUT);
+	s3c2410_gpio_cfgpin(bit->l3_mode, S3C2410_GPIO_OUTPUT);
+	local_irq_restore(flags);
+
+	/* if needed  */
+
+	l3_bit_data.data = bit;
+
+	return l3_bit_add_bus(&l3_adapter);
+}
+
+static void __exit s3c24xx_l3_bus_exit(void)
+{
+	l3_bit_del_bus(&l3_adapter);
+}
+
+module_init(s3c24xx_l3_bus_init);
+module_exit(s3c24xx_l3_bus_exit);
--- /dev/null
+++ b/drivers/l3/Makefile
@@ -0,0 +1,11 @@
+#
+# Makefile for the L3 bus driver.
+#
+
+# Link order:
+#  (core, adapters, algorithms, drivers) then clients
+
+l3-$(CONFIG_L3_ALGOBIT)		+= l3-algo-bit.o
+l3-$(CONFIG_BIT_S3C24XX)	+= s3c24xx.o
+
+obj-$(CONFIG_L3)		+= l3-core.o $(l3-y) $(l3-drv-y)
--- /dev/null
+++ b/drivers/l3/Kconfig
@@ -0,0 +1,18 @@
+#
+# L3 bus configuration
+#
+
+menu "L3 serial bus support"
+
+config L3
+	tristate "L3 support"
+
+config L3_ALGOBIT
+	bool "L3 bit-banging interfaces"
+	depends on L3=y
+
+config BIT_S3C24XX
+	bool "L3 bus support for the S3C24xx arch"
+	depends on L3_ALGOBIT && ARCH_S3C2410
+
+endmenu
--- /dev/null
+++ b/include/linux/l3.h
@@ -0,0 +1,95 @@
+/*
+ *  linux/include/linux/l3/l3.h
+ *
+ *  Copyright (C) 2001 Russell King, All Rights Reserved.
+ *
+ * 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.
+ *
+ * Derived from i2c.h by Simon G. Vogl
+ */
+#ifndef L3_H
+#define L3_H
+
+struct l3_msg {
+	unsigned char	addr;	/* slave address	*/
+	unsigned char	flags;		
+#define L3_M_RD		0x01
+#define L3_M_NOADDR	0x02
+	unsigned short	len;	/* msg length		*/
+	unsigned char	*buf;	/* pointer to msg data	*/
+};
+
+#ifdef __KERNEL__
+
+#include <linux/types.h>
+#include <linux/list.h>
+
+struct l3_adapter;
+
+struct l3_algorithm {
+	/* textual description */
+	char name[32];
+
+	/* perform bus transactions */
+	int (*xfer)(struct l3_adapter *, struct l3_msg msgs[], int num);
+};
+
+struct semaphore;
+
+/*
+ * l3_adapter is the structure used to identify a physical L3 bus along
+ * with the access algorithms necessary to access it.
+ */
+struct l3_adapter {
+	/*
+	 * This name is used to uniquely identify the adapter.
+	 * It should be the same as the module name.
+	 */
+	char			name[32];
+
+	/*
+	 * the algorithm to access the bus
+	 */
+	struct l3_algorithm	*algo;
+
+	/*
+	 * Algorithm specific data
+	 */
+	void			*algo_data;
+
+	/*
+	 * This may be NULL, or should point to the module struct
+	 */
+	struct module		*owner;
+
+	/*
+	 * private data for the adapter
+	 */
+	void			*data;
+
+	/*
+	 * Our lock.  Unlike the i2c layer, we allow this to be used for
+	 * other stuff, like the i2c layer lock.  Some people implement
+	 * i2c stuff using the same signals as the l3 bus.
+	 */
+	struct semaphore	*lock;
+
+	/*
+	 * List of all adapters.
+	 */
+	struct list_head	adapters;
+};
+
+extern int l3_add_adapter(struct l3_adapter *);
+extern int l3_del_adapter(struct l3_adapter *);
+extern void l3_put_adapter(struct l3_adapter *);
+extern struct l3_adapter *l3_get_adapter(const char *name);
+
+extern int l3_write(struct l3_adapter *, int, const char *, int);
+extern int l3_read(struct l3_adapter *, int, char *, int);
+
+#endif
+
+#endif /* L3_H */
--- /dev/null
+++ b/include/linux/l3-algo-bit.h
@@ -0,0 +1,39 @@
+/*
+ *  linux/include/linux/l3/algo-bit.h
+ *
+ *  Copyright (C) 2001 Russell King, All Rights Reserved.
+ *
+ * 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.
+ *
+ * L3 Bus bit-banging algorithm.  Derived from i2c-algo-bit.h by
+ * Simon G. Vogl.
+ */
+#ifndef L3_ALGO_BIT_H
+#define L3_ALGO_BIT_H 1
+
+#include <linux/l3.h>
+
+struct l3_algo_bit_data {
+	void (*setdat) (void *data, int state);
+	void (*setclk) (void *data, int state);
+	void (*setmode)(void *data, int state);
+	void (*setdir) (void *data, int in);	/* set data direction */
+	int  (*getdat) (void *data);
+
+	void *data;
+
+	/* bus timings (us) */
+	int	data_hold;
+	int	data_setup;
+	int	clock_high;
+	int	mode_hold;
+	int	mode_setup;
+	int	mode;
+};
+
+int l3_bit_add_bus(struct l3_adapter *);
+int l3_bit_del_bus(struct l3_adapter *);
+
+#endif

--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -1011,7 +1011,7 @@ source "drivers/w1/Kconfig"
 
 source "drivers/hwmon/Kconfig"
 
-#source "drivers/l3/Kconfig"
+source "drivers/l3/Kconfig"
 
 source "drivers/misc/Kconfig"

--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -60,6 +60,7 @@ obj-$(CONFIG_INPUT)		+= input/
 obj-$(CONFIG_I2O)		+= message/
 obj-$(CONFIG_RTC_LIB)		+= rtc/
 obj-y				+= i2c/
+obj-$(CONFIG_L3)		+= l3/
 obj-$(CONFIG_W1)		+= w1/
 obj-$(CONFIG_HWMON)		+= hwmon/
 obj-$(CONFIG_PHONE)		+= telephony/
