[alsa-devel] [PATCH] ALSA SOC driver for s3c24xx with uda1341

Ben Dooks ben-linux at fluff.org
Mon Nov 10 23:09:59 CET 2008


On Sat, Nov 08, 2008 at 08:43:55AM +0100, Christian Pellegrin wrote:
> This patch adds support for the UDA1341 codec and a sound card
> definition for one of these UDAs connected to the s3c24xx. It is
> *heavily* based on the one made by Zoltan Devai but:
> 
> 1 since the UDA is the only use of the L3 protocol I can find I just
> embedded a stripped-down L3 bit-banging algorithm from the original
> RMK work. It is really small.
> 
> 2 the driver has the possibility to specify the pins used by codec
> via platform data so it can work on SMDK2410, SMDK2440 or any custom
> design.

And, iirc, the h1940 ipaq.
 
> 3 it tries to guess the right clock source and divider so it is not tied
> to a particular crystal used.
> 
> 4 it fixes some bugs.
> 
> Thank you for reviews/comments.
> 
> Generated on  20081108  against v2.6.27
> 
> Signed-off-by: Christian Pellegrin <chripell at fsfe.org>
> ---
>  include/sound/s3c24xx_uda1341.h                  |   41 ++
>  sound/soc/codecs/Kconfig                         |    3 +
>  sound/soc/codecs/Makefile                        |    1 +
>  sound/soc/codecs/uda1341/Makefile                |    3 +
>  sound/soc/codecs/uda1341/l3.c                    |  106 +++++
>  sound/soc/codecs/uda1341/l3.h                    |    8 +
>  sound/soc/codecs/uda1341/uda1341.c               |  537 ++++++++++++++++++++++
>  sound/soc/codecs/uda1341/uda1341.h               |   47 ++
>  sound/soc/codecs/uda1341/uda1341_platform_data.h |   18 +
>  sound/soc/s3c24xx/Kconfig                        |    7 +-
>  sound/soc/s3c24xx/Makefile                       |    2 +
>  sound/soc/s3c24xx/s3c24xx_uda1341.c              |  314 +++++++++++++
>  12 files changed, 1086 insertions(+), 1 deletions(-)
> 
> diff --git a/include/sound/s3c24xx_uda1341.h b/include/sound/s3c24xx_uda1341.h
> new file mode 100644
> index 0000000..bd80bd3
> --- /dev/null
> +++ b/include/sound/s3c24xx_uda1341.h
> @@ -0,0 +1,41 @@
> +#ifndef _S3C24XX_UDA1341_H_
> +#define _S3C24XX_UDA1341_H_ 1
> +
> +/*
> +
> +Example usage (pins for SMDK2410). Put something like this in your
> +machine file:
> +
> +...
> +
> +static struct s3c24xx_uda1341_platform_data s3c24xx_uda1341_data = {
> +	.l3_clk = S3C2410_GPB4,
> +	.l3_data = S3C2410_GPB3,
> +	.l3_mode = S3C2410_GPB2,
> +};
> +
> +static struct platform_device s3c24xx_uda1341 = {
> +	.name		  = "s3c24xx_uda1341",
> +	.dev = {
> +		.platform_data	  = &s3c24xx_uda1341_data,
> +	}
> +};
> +
> +...
> +
> +static struct platform_device *smdk2410_devices[] __initdata = {
> +...
> +	&s3c24xx_uda1341,
> +...
> +};
> +
> + */

example into a doc file please, or point at an implementation of
this.

> +struct s3c24xx_uda1341_platform_data {
> +	int l3_clk;
> +	int l3_mode;
> +	int l3_data;
> +	void (*power) (int);
> +};
>
> +#endif
> diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
> index 1db04a2..4b483b7 100644
> --- a/sound/soc/codecs/Kconfig
> +++ b/sound/soc/codecs/Kconfig
> @@ -50,3 +50,6 @@ config SND_SOC_CS4270_VD33_ERRATA
>  config SND_SOC_TLV320AIC3X
>  	tristate
>  	depends on I2C
> +
> +config SND_SOC_UDA1341
> +	tristate
> diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
> index d7b97ab..cbace60 100644
> --- a/sound/soc/codecs/Makefile
> +++ b/sound/soc/codecs/Makefile
> @@ -23,3 +23,4 @@ obj-$(CONFIG_SND_SOC_WM9712)	+= snd-soc-wm9712.o
>  obj-$(CONFIG_SND_SOC_WM9713)	+= snd-soc-wm9713.o
>  obj-$(CONFIG_SND_SOC_CS4270)	+= snd-soc-cs4270.o
>  obj-$(CONFIG_SND_SOC_TLV320AIC3X)	+= snd-soc-tlv320aic3x.o
> +obj-$(CONFIG_SND_SOC_UDA1341)	+= uda1341/
> diff --git a/sound/soc/codecs/uda1341/Makefile b/sound/soc/codecs/uda1341/Makefile
> new file mode 100644
> index 0000000..f9869ba
> --- /dev/null
> +++ b/sound/soc/codecs/uda1341/Makefile
> @@ -0,0 +1,3 @@
> +snd-soc-uda1341-objs := l3.o uda1341.o
> +
> +obj-$(CONFIG_SND_SOC_UDA1341)	+= snd-soc-uda1341.o
> \ No newline at end of file
> diff --git a/sound/soc/codecs/uda1341/l3.c b/sound/soc/codecs/uda1341/l3.c
> new file mode 100644
> index 0000000..b65b352
> --- /dev/null
> +++ b/sound/soc/codecs/uda1341/l3.c
> @@ -0,0 +1,106 @@
> +/*
> + * L3 code
> + *
> + *  Copyright (C) 2008, Christian Pellegrin <chripell at evolware.org>
> + *
> + * 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.
> + *
> + *
> + * based on:
> + *
> + * L3 bus algorithm module.
> + *
> + *  Copyright (C) 2001 Russell King, All Rights Reserved.
> + *
> + *
> + */
> +
> +#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 "l3.h"
> +
> +/*#define L3_DEBUG 1*/
> +#ifdef L3_DEBUG
> +#define DBG(format, arg...) \
> +	printk(KERN_DEBUG "L3: %s:" format "\n" , __func__, ## arg)
> +#else
> +#define DBG(format, arg...) do {} while (0)
> +#endif
> +
> +#define setdat(adap, val)	(adap->setdat(adap, val))
> +#define setclk(adap, val)	(adap->setclk(adap, val))
> +#define setmode(adap, val)	(adap->setmode(adap, val))

inline functions are better than #define.

> +/*
> + * 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 uda1341_platform_data *adap, unsigned int byte)
> +{
> +	int i;
> +
> +	DBG("%02x", 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 uda1341_platform_data *adap, const u8 *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]);
> +	}
> +}
> +
> +int l3_write(struct uda1341_platform_data *adap, u8 addr, u8 *data, int len)
> +{
> +	setclk(adap, 1);
> +	setdat(adap, 1);
> +	setmode(adap, 1);
> +	udelay(adap->mode);
> +
> +	setmode(adap, 0);
> +	udelay(adap->mode_setup);
> +	sendbyte(adap, addr);
> +	udelay(adap->mode_hold);
> +
> +	sendbytes(adap, data, len);
> +
> +	setclk(adap, 1);
> +	setdat(adap, 1);
> +	setmode(adap, 0);
> +
> +	return len;
> +}
> +
> +
> diff --git a/sound/soc/codecs/uda1341/l3.h b/sound/soc/codecs/uda1341/l3.h
> new file mode 100644
> index 0000000..2c31e0c
> --- /dev/null
> +++ b/sound/soc/codecs/uda1341/l3.h
> @@ -0,0 +1,8 @@
> +#ifndef _L3_H_
> +#define _L3_H_ 1
> +
> +#include "uda1341_platform_data.h"

please find as better name for that file.

> +int l3_write(struct uda1341_platform_data *adap, u8 addr, u8 *data, int len);
> +
> +#endif
> diff --git a/sound/soc/codecs/uda1341/uda1341.c b/sound/soc/codecs/uda1341/uda1341.c
> new file mode 100644
> index 0000000..93cadf0
> --- /dev/null
> +++ b/sound/soc/codecs/uda1341/uda1341.c
> @@ -0,0 +1,537 @@
> +/*
> + * uda1341.c  --  UDA1341 ALSA SoC Codec driver
> + *
> + * Modifications by Christian Pellegrin <chripell at evolware.org>
> + *
> + * Copyright 2007 Dension Audio Systems Ltd.
> + * Author: Zoltan Devai
> + *
> + * Based on the WM87xx drivers by Liam Girdwood and Richard Purdie
> + *
> + * 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.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/delay.h>
> +#include <sound/pcm.h>
> +#include <sound/pcm_params.h>
> +#include <sound/soc.h>
> +#include <sound/soc-dapm.h>
> +#include <sound/initval.h>
> +
> +#include "uda1341.h"
> +#include "l3.h"
> +
> +/*#define UDA1341_DEBUG 1*/
> +#ifdef UDA1341_DEBUG
> +#define DBG(format, arg...) \
> +	printk(KERN_DEBUG "UDA1341: %s:" format "\n" , __func__, ## arg)
> +#else
> +#define DBG(format, arg...) do {} while (0)
> +#endif
> +
> +#define UDA1341_RATES SNDRV_PCM_RATE_8000_48000
> +#define UDA1341_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | \
> +		SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S20_3LE)
> +
> +struct uda1341_priv {
> +	int sysclk;
> +	int dai_fmt;
> +};
> +
> +/* In-data addresses are hard-coded into the reg-cache values */
> +static const char uda1341_reg[UDA1341_REGS_NUM] = {
> +	/* Extended address registers */
> +	0x04, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
> +	/* Status, data regs */
> +	0x00, 0x83, 0x00, 0x40, 0x80, 0x00,
> +};
> +
> +/*
> + * The codec has no support for reading its registers except for peak level...
> + */
> +static inline unsigned int uda1341_read_reg_cache(struct snd_soc_codec *codec,
> +	unsigned int reg)
> +{
> +	u8 *cache = codec->reg_cache;
> +
> +	if (reg >= UDA1341_REGS_NUM)
> +		return -1;
> +	return cache[reg];
> +}
> +
> +/*
> + * Write the register cache
> + */
> +static inline void uda1341_write_reg_cache(struct snd_soc_codec *codec,
> +	u8 reg, unsigned int value)
> +{
> +	u8 *cache = codec->reg_cache;
> +
> +	if (reg >= UDA1341_REGS_NUM)
> +		return;
> +	cache[reg] = value;
> +}
> +
> +/*
> + * Write to the uda1341 registers
> + *
> + */
> +static int uda1341_write(struct snd_soc_codec *codec, unsigned int reg,
> +	unsigned int value)
> +{
> +	int ret;
> +	u8 addr;
> +	u8 data = value;
> +
> +	DBG("reg: %02X, value:%02X", reg, value);
> +
> +	if (reg >= UDA1341_REGS_NUM) {
> +		DBG("Unkown register: reg: %d", reg);
> +		return -EINVAL;
> +	}
> +
> +	uda1341_write_reg_cache(codec, reg, value);
> +
> +	switch (reg) {
> +	case UDA1341_STATUS0:
> +	case UDA1341_STATUS1:
> +		addr = UDA1341_STATUS_ADDR;
> +		break;
> +	case UDA1341_DATA000:
> +	case UDA1341_DATA001:
> +	case UDA1341_DATA010:
> +		addr = UDA1341_DATA0_ADDR;
> +		break;
> +	case UDA1341_DATA1:
> +		addr = UDA1341_DATA1_ADDR;
> +		break;
> +	default:
> +		/* It's an extended address register */
> +		addr =  (reg | UDA1341_EXTADDR_PREFIX);
> +
> +		ret = l3_write((struct uda1341_platform_data *)
> +			       codec->control_data,
> +			       UDA1341_DATA0_ADDR, &addr, 1);
> +		if (ret != 1)
> +			return -EIO;
> +
> +		addr = UDA1341_DATA0_ADDR;
> +		data = (value | UDA1341_EXTDATA_PREFIX);
> +		break;
> +	}
> +
> +	ret = l3_write((struct uda1341_platform_data *)  codec->control_data,
> +		       addr, &data, 1);
> +	if (ret != 1)
> +		return -EIO;
> +
> +	return 0;
> +}
> +
> +static inline void uda1341_reset(struct snd_soc_codec *codec)
> +{
> +	u8 reset_reg = uda1341_read_reg_cache(codec, UDA1341_STATUS0);
> +	uda1341_write(codec, UDA1341_STATUS0, reset_reg | (1<<6));
> +	mdelay(1);
> +	uda1341_write(codec, UDA1341_STATUS0, reset_reg & ~(1<<6));
> +}

do you really want to be busy-waiting the cpu for that long? how
about msleep?

> +static int uda1341_mute(struct snd_soc_dai *dai, int mute)
> +{
> +	struct snd_soc_codec *codec = dai->codec;
> +	u8 mute_reg = uda1341_read_reg_cache(codec, UDA1341_DATA010);
> +
> +	DBG("mute: %d", mute);
> +
> +	if (mute)
> +		mute_reg |= (1<<2);
> +	else
> +		mute_reg &= ~(1<<2);
> +
> +	uda1341_write(codec, UDA1341_DATA010, mute_reg & ~(1<<2));
> +
> +	return 0;
> +}
> +
> +static int uda1341_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_device *socdev = rtd->socdev;
> +	struct snd_soc_codec *codec = socdev->codec;
> +	struct uda1341_priv *uda1341 = codec->private_data;
> +
> +	u8 hw_params = uda1341_read_reg_cache(codec, UDA1341_STATUS0);
> +	hw_params &= STATUS0_SYSCLK_MASK;
> +	hw_params &= STATUS0_DAIFMT_MASK;
> +
> +	DBG("sysclk: %d, rate:%d", uda1341->sysclk, params_rate(params));
> +
> +	/* set SYSCLK / fs ratio */
> +	switch (uda1341->sysclk / params_rate(params)) {
> +	case 512:
> +		break;
> +	case 384:
> +		hw_params |= (1<<4);
> +		break;
> +	case 256:
> +		hw_params |= (1<<5);
> +		break;
> +	default:
> +		return -EINVAL;
> +		break;
> +	}
> +
> +	DBG("dai_fmt: %d, params_format:%d", uda1341->dai_fmt,
> +						params_format(params));
> +
> +
> +	/* set DAI format and word length */
> +	switch (uda1341->dai_fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
> +	case SND_SOC_DAIFMT_I2S:
> +		break;
> +	case SND_SOC_DAIFMT_RIGHT_J:
> +		switch (params_format(params)) {
> +		case SNDRV_PCM_FORMAT_S16_LE:
> +			hw_params |= (1<<1);
> +			break;
> +		case SNDRV_PCM_FORMAT_S18_3LE:
> +			hw_params |= (1<<2);
> +			break;
> +		case SNDRV_PCM_FORMAT_S20_3LE:
> +			hw_params |= ((1<<2) | (1<<1));
> +			break;
> +		default:
> +			return -EINVAL;
> +		}
> +		break;
> +	case SND_SOC_DAIFMT_LEFT_J:
> +		hw_params |= (1<<3);
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	uda1341_write(codec, UDA1341_STATUS0, hw_params);
> +
> +	return 0;
> +}
> +
> +static int uda1341_set_dai_sysclk(struct snd_soc_dai *codec_dai,
> +				  int clk_id, unsigned int freq, int dir)
> +{
> +	struct snd_soc_codec *codec = codec_dai->codec;
> +	struct uda1341_priv *uda1341 = codec->private_data;
> +
> +	DBG("clk_id: %d, freq: %d, dir: %d", clk_id, freq, dir);
> +
> +	/* Anything between 256fs*8Khz and 512fs*48Khz should be acceptable
> +	we'll error out on set_hw_params if it's not OK */
> +	if ((freq >= (256 * 8000)) && (freq <= (512 * 48000))) {
> +		uda1341->sysclk = freq;
> +		return 0;
> +	}
> +
> +	return -EINVAL;
> +}
> +
> +static int uda1341_set_dai_fmt(struct snd_soc_dai *codec_dai,
> +			       unsigned int fmt)
> +{
> +	struct snd_soc_codec *codec = codec_dai->codec;
> +	struct uda1341_priv *uda1341 = codec->private_data;
> +
> +	DBG("fmt: %08X", fmt);
> +
> +	/* codec supports only full slave mode */
> +	if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS)
> +		return -EINVAL;
> +
> +	/* no support for clock inversion */
> +	if ((fmt & SND_SOC_DAIFMT_INV_MASK) != SND_SOC_DAIFMT_NB_NF)
> +		return -EINVAL;
> +
> +	/* We can't setup DAI format here as it depends on the word bit num */
> +	/* so let's just store the value for later */
> +	uda1341->dai_fmt = fmt;
> +
> +	return 0;
> +}
> +
> +static int uda1341_dapm_event(struct snd_soc_codec *codec, int event)
> +{
> +	u8 reg;
> +
> +	DBG("event: %08X", event);
> +
> +	switch (event) {
> +	case SNDRV_CTL_POWER_D0: /* full On */
> +		/* ADC, DAC on */
> +		reg = uda1341_read_reg_cache(codec, UDA1341_STATUS1);
> +		uda1341_write(codec, UDA1341_STATUS1, reg | 0x03);
> +		break;
> +	case SNDRV_CTL_POWER_D1: /* partial On */
> +	case SNDRV_CTL_POWER_D2: /* partial On */
> +	case SNDRV_CTL_POWER_D3hot: /* Off, with power */
> +		break;
> +	case SNDRV_CTL_POWER_D3cold: /* Off, without power */
> +		/* mute, ADC, DAC power off */
> +		reg = uda1341_read_reg_cache(codec, UDA1341_STATUS1);
> +		uda1341_write(codec, UDA1341_STATUS1, reg & ~(0x03));
> +		break;
> +	}
> +	return 0;
> +}
> +
> +static const char *uda1341_dsp_setting[] = {"Flat", "Minimum1",
> +					    "Minimum2", "Maximum"};
> +static const char *uda1341_deemph[] = {"None", "32Khz", "44.1Khz", "48Khz"};
> +static const char *uda1341_mixmode[] = {"DD", "Input 2",
> +					"Input 2", "Digital mixer"};
> +
> +static const struct soc_enum uda1341_mixer_enum[] = {
> +SOC_ENUM_SINGLE(UDA1341_DATA010, 0, 0x03, uda1341_dsp_setting),
> +SOC_ENUM_SINGLE(UDA1341_DATA010, 3, 0x03, uda1341_deemph),
> +SOC_ENUM_SINGLE(UDA1341_EA010, 0, 0x03, uda1341_mixmode),
> +};
> +
> +static const struct snd_kcontrol_new uda1341_snd_controls[] = {
> +SOC_SINGLE("Playback Volume", UDA1341_DATA000, 0, 0x3F, 1),
> +SOC_SINGLE("Mic gain", UDA1341_EA010, 2, 0x07, 0),
> +SOC_SINGLE("Channel 1 mixer gain", UDA1341_EA000, 0, 0x1F, 1),
> +SOC_SINGLE("Channgel 2 mixer gain", UDA1341_EA001, 0, 0x1F, 1),
> +SOC_SINGLE("Input channel 2 amp gain", UDA1341_EA101, 0, 0x1F, 0),
> +
> +SOC_SINGLE("Bass boost", UDA1341_DATA001, 2, 0xF, 0),
> +SOC_SINGLE("Treble", UDA1341_DATA001, 0, 3, 0),
> +
> +SOC_ENUM("DSP setting", uda1341_mixer_enum[0]),
> +SOC_ENUM("Playback De-emphasis", uda1341_mixer_enum[1]),
> +SOC_ENUM("Mixer mode", uda1341_mixer_enum[2]),
> +
> +/* This should be an ext control with own handler, if one wants
> +	   to set the values in 0.5dB steps instead of 3dB */
> +SOC_SINGLE("AGC output level", UDA1341_EA110, 0, 0x03, 1),
> +SOC_SINGLE("AGC time const", UDA1341_EA110, 2, 0x07, 0),
> +
> +SOC_SINGLE("DAC Gain switch", UDA1341_STATUS1, 6, 1, 0),
> +SOC_SINGLE("ADC Gain switch", UDA1341_STATUS1, 5, 1, 0),
> +SOC_SINGLE("ADC Polarity switch", UDA1341_STATUS1, 4, 1, 0),
> +SOC_SINGLE("DAC Polarity switch", UDA1341_STATUS1, 3, 1, 0),
> +SOC_SINGLE("Double speed playback switch", UDA1341_STATUS1, 2, 1, 0),
> +};
> +
> +static int uda1341_add_controls(struct snd_soc_codec *codec)
> +{
> +	int err, i;
> +
> +	for (i = 0; i < ARRAY_SIZE(uda1341_snd_controls); i++) {
> +		err = snd_ctl_add(codec->card,
> +				  snd_soc_cnew(&uda1341_snd_controls[i],
> +					       codec, NULL));
> +		if (err < 0)
> +			return err;
> +	}
> +
> +	return 0;
> +}
> +
> +struct snd_soc_dai uda1341_dai = {
> +	.name = "UDA1341",
> +	/* playback capabilities */
> +	.playback = {
> +		.stream_name = "Playback",
> +		.channels_min = 1,
> +		.channels_max = 2,
> +		.rates = UDA1341_RATES,
> +		.formats = UDA1341_FORMATS,
> +	},
> +	/* capture capabilities */
> +	.capture = {
> +		.stream_name = "Capture",
> +		.channels_min = 1,
> +		.channels_max = 2,
> +		.rates = UDA1341_RATES,
> +		.formats = UDA1341_FORMATS,
> +	},
> +	/* pcm operations */
> +	.ops = {
> +		.hw_params = uda1341_hw_params,
> +	},
> +	/* DAI operations */
> +	.dai_ops = {
> +		.digital_mute = uda1341_mute,
> +		.set_sysclk = uda1341_set_dai_sysclk,
> +		.set_fmt = uda1341_set_dai_fmt,
> +	}
> +};
> +EXPORT_SYMBOL(uda1341_dai);
> +
> +
> +static int uda1341_soc_probe(struct platform_device *pdev)
> +{
> +	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
> +	struct snd_soc_codec *codec;
> +	struct uda1341_priv *uda1341;
> +	void *codec_setup_data = socdev->codec_data;
> +	int ret = -ENOMEM;
> +	struct uda1341_platform_data *pd;
> +
> +	printk(KERN_INFO "UDA1341 SoC Audio Codec\n");
> +
> +	socdev->codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
> +	if (socdev->codec == NULL)
> +		return ret;
> +
> +	codec = socdev->codec;
> +
> +	uda1341 = kzalloc(sizeof(struct uda1341_priv), GFP_KERNEL);
> +	if (uda1341 == NULL)
> +		goto priv_err;
> +
> +	codec->private_data = uda1341;
> +
> +	codec->reg_cache = kmemdup(uda1341_reg, sizeof(uda1341_reg),
> +				   GFP_KERNEL);
> +
> +	if (codec->reg_cache == NULL)
> +		goto reg_err;
> +
> +	mutex_init(&codec->mutex);
> +
> +	codec->reg_cache_size = sizeof(uda1341_reg);
> +	codec->reg_cache_step = 1;
> +
> +	codec->name = "UDA1341";
> +	codec->owner = THIS_MODULE;
> +	codec->dai = &uda1341_dai;
> +	codec->num_dai = 1;
> +	codec->read = uda1341_read_reg_cache;
> +	codec->write = uda1341_write;
> +	INIT_LIST_HEAD(&codec->dapm_widgets);
> +	INIT_LIST_HEAD(&codec->dapm_paths);
> +
> +	if (!codec_setup_data) {
> +		printk(KERN_ERR "UDA1341 SoC codec: "
> +		       "missing L3 bitbang function\n");
> +		ret = -ENODEV;
> +		goto pcm_err;
> +	}
> +
> +	codec->control_data = codec_setup_data;
> +	pd = (struct uda1341_platform_data *) codec_setup_data;
> +
> +	if (pd->power)
> +		pd->power(1);
> +
> +	uda1341_reset(codec);
> +
> +	/* register pcms */
> +	ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
> +	if (ret < 0) {
> +		printk(KERN_ERR "UDA1341: failed to register pcms\n");
> +		goto pcm_err;
> +	}
> +
> +	ret = uda1341_add_controls(codec);
> +	if (ret < 0) {
> +		printk(KERN_ERR "UDA1341: failed to register controls\n");
> +		goto pcm_err;
> +	}
> +
> +	ret = snd_soc_register_card(socdev);
> +	if (ret < 0) {
> +		printk(KERN_ERR "UDA1341: failed to register card\n");
> +		goto card_err;
> +	}
> +
> +	return 0;
> +
> +card_err:
> +	snd_soc_free_pcms(socdev);
> +	snd_soc_dapm_free(socdev);
> +pcm_err:
> +	kfree(codec->reg_cache);
> +reg_err:
> +	kfree(codec->private_data);
> +priv_err:
> +	kfree(codec);
> +	return ret;
> +}
> +
> +/* power down chip */
> +static int uda1341_soc_remove(struct platform_device *pdev)
> +{
> +	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
> +	struct snd_soc_codec *codec = socdev->codec;
> +	struct uda1341_platform_data *pd;
> +
> +	uda1341_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
> +
> +	pd = (struct uda1341_platform_data *) codec->control_data;
> +	if (pd->power)
> +		pd->power(0);
> +
> +	snd_soc_free_pcms(socdev);
> +	snd_soc_dapm_free(socdev);
> +
> +	kfree(codec->private_data);
> +	kfree(codec->reg_cache);
> +	kfree(codec);
> +
> +	return 0;
> +}
> +
> +#if defined(CONFIG_PM)
> +static int uda1341_soc_suspend(struct platform_device *pdev,
> +						pm_message_t state)
> +{
> +	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
> +	struct snd_soc_codec *codec = socdev->codec;
> +	struct uda1341_platform_data *pd;
> +
> +	uda1341_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
> +
> +	pd = (struct uda1341_platform_data *) codec->control_data;
> +	if (pd->power)
> +		pd->power(0);
> +
> +	return 0;
> +}
> +
> +static int uda1341_soc_resume(struct platform_device *pdev)
> +{
> +	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
> +	struct snd_soc_codec *codec = socdev->codec;
> +	struct uda1341_platform_data *pd;
> +	int i;
> +	u8 *cache = codec->reg_cache;
> +
> +	pd = (struct uda1341_platform_data *) codec->control_data;
> +	if (pd->power)
> +		pd->power(1);
> +
> +	/* Sync reg_cache with the hardware */
> +	for (i = 0; i < ARRAY_SIZE(uda1341_reg); i++)
> +		codec->write(codec, i, *cache++);
> +	uda1341_dapm_event(codec, SNDRV_CTL_POWER_D0);
> +	return 0;
> +}
> +#endif /* CONFIG_PM */
> +
> +struct snd_soc_codec_device soc_codec_dev_uda1341 = {
> +	.probe =        uda1341_soc_probe,
> +	.remove =       uda1341_soc_remove,
> +#if defined(CONFIG_PM)
> +	.suspend =      uda1341_soc_suspend,
> +	.resume =       uda1341_soc_resume,
> +#endif /* CONFIG_PM */
> +};
> +EXPORT_SYMBOL(soc_codec_dev_uda1341);
> +
> +MODULE_DESCRIPTION("UDA1341 ALSA soc codec driver");
> +MODULE_AUTHOR("Zoltan Devai, Christian Pellegrin <chripell at evolware.org>");
> +MODULE_LICENSE("GPL");
> diff --git a/sound/soc/codecs/uda1341/uda1341.h b/sound/soc/codecs/uda1341/uda1341.h
> new file mode 100644
> index 0000000..5eccfc4
> --- /dev/null
> +++ b/sound/soc/codecs/uda1341/uda1341.h
> @@ -0,0 +1,47 @@
> +/*
> + * uda1341.h  --  UDA1341 ALSA SoC Codec driver
> + *
> + * Copyright 2007 Dension Audio Systems Ltd.
> + * Author: Zoltan Devai
> + *
> + * 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 _UDA1341_H
> +#define _UDA1341_H
> +
> +#define UDA1341_L3ADDR	5
> +#define UDA1341_DATA0_ADDR	((UDA1341_L3ADDR << 2) | 0)
> +#define UDA1341_DATA1_ADDR	((UDA1341_L3ADDR << 2) | 1)
> +#define UDA1341_STATUS_ADDR	((UDA1341_L3ADDR << 2) | 2)
> +
> +#define UDA1341_EXTADDR_PREFIX	0xC0
> +#define UDA1341_EXTDATA_PREFIX	0xE0
> +
> +/* UDA1341 registers */
> +#define UDA1341_EA000	0
> +#define UDA1341_EA001	1
> +#define UDA1341_EA010	2
> +#define UDA1341_EA011	3
> +#define UDA1341_EA100	4
> +#define UDA1341_EA101	5
> +#define UDA1341_EA110	6
> +#define UDA1341_EA111	7
> +#define UDA1341_STATUS0 8
> +#define UDA1341_STATUS1 9
> +#define UDA1341_DATA000 10
> +#define UDA1341_DATA001 11
> +#define UDA1341_DATA010 12
> +#define UDA1341_DATA1	13
> +
> +#define UDA1341_REGS_NUM 14
> +
> +#define STATUS0_DAIFMT_MASK (~(7<<1))
> +#define STATUS0_SYSCLK_MASK (~(3<<4))
> +
> +extern struct snd_soc_dai uda1341_dai;
> +extern struct snd_soc_codec_device soc_codec_dev_uda1341;
> +
> +#endif /* _UDA1341_H */
> diff --git a/sound/soc/codecs/uda1341/uda1341_platform_data.h b/sound/soc/codecs/uda1341/uda1341_platform_data.h
> new file mode 100644
> index 0000000..26081cb
> --- /dev/null
> +++ b/sound/soc/codecs/uda1341/uda1341_platform_data.h
> @@ -0,0 +1,18 @@
> +#ifndef _UDA1341_PLATFORM_DATA_H_
> +#define _UDA1341_PLATFORM_DATA_H_ 1
> +
> +struct uda1341_platform_data {
> +	void (*setdat) (struct uda1341_platform_data *, int);
> +	void (*setclk) (struct uda1341_platform_data *, int);
> +	void (*setmode) (struct uda1341_platform_data *, int);
no spaces              ^

> +	int data_hold;
> +	int data_setup;
> +	int clock_high;
> +	int mode_hold;
> +	int mode;
> +	int mode_setup;
> +	void *priv;
> +	void (*power) (int);
> +};
> +
> +#endif
> diff --git a/sound/soc/s3c24xx/Kconfig b/sound/soc/s3c24xx/Kconfig
> index b9f2353..cc8fa5e 100644
> --- a/sound/soc/s3c24xx/Kconfig
> +++ b/sound/soc/s3c24xx/Kconfig
> @@ -16,7 +16,7 @@ config SND_S3C2443_SOC_AC97
>  	tristate
>  	select AC97_BUS
>  	select SND_SOC_AC97_BUS
> -	
> +
>  config SND_S3C24XX_SOC_NEO1973_WM8753
>  	tristate "SoC I2S Audio support for NEO1973 - WM8753"
>  	depends on SND_S3C24XX_SOC && MACH_NEO1973_GTA01
> @@ -44,3 +44,8 @@ config SND_S3C24XX_SOC_LN2440SBC_ALC650
>  	  Say Y if you want to add support for SoC audio on ln2440sbc
>  	  with the ALC650.
>  
> +config SND_S3C24XX_SOC_S3C24XX_UDA1341
> +       tristate "SoC I2S Audio support UDA1341 wired to a S3C24XX"
> +       depends on SND_S3C24XX_SOC
> +       select SND_S3C24XX_SOC_I2S
> +       select SND_SOC_UDA1341
> \ No newline at end of file
> diff --git a/sound/soc/s3c24xx/Makefile b/sound/soc/s3c24xx/Makefile
> index 0aa5fb0..155f5a4 100644
> --- a/sound/soc/s3c24xx/Makefile
> +++ b/sound/soc/s3c24xx/Makefile
> @@ -13,7 +13,9 @@ obj-$(CONFIG_SND_S3C2412_SOC_I2S) += snd-soc-s3c2412-i2s.o
>  snd-soc-neo1973-wm8753-objs := neo1973_wm8753.o
>  snd-soc-smdk2443-wm9710-objs := smdk2443_wm9710.o
>  snd-soc-ln2440sbc-alc650-objs := ln2440sbc_alc650.o
> +snd-soc-s3c24xx-uda1341-objs := s3c24xx_uda1341.o
>  
>  obj-$(CONFIG_SND_S3C24XX_SOC_NEO1973_WM8753) += snd-soc-neo1973-wm8753.o
>  obj-$(CONFIG_SND_S3C24XX_SOC_SMDK2443_WM9710) += snd-soc-smdk2443-wm9710.o
>  obj-$(CONFIG_SND_S3C24XX_SOC_LN2440SBC_ALC650) += snd-soc-ln2440sbc-alc650.o
> +obj-$(CONFIG_SND_S3C24XX_SOC_S3C24XX_UDA1341) += snd-soc-s3c24xx-uda1341.o
> diff --git a/sound/soc/s3c24xx/s3c24xx_uda1341.c b/sound/soc/s3c24xx/s3c24xx_uda1341.c
> new file mode 100644
> index 0000000..fc3c13e
> --- /dev/null
> +++ b/sound/soc/s3c24xx/s3c24xx_uda1341.c
> @@ -0,0 +1,314 @@
> +/*
> + * Modifications by Christian Pellegrin <chripell at evolware.org>
> + *
> + * s3c24xx_uda1341.c  --  S3C24XX_UDA1341 ALSA SoC Audio board driver
> + *
> + * Copyright 2007 Dension Audio Systems Ltd.
> + * Author: Zoltan Devai
> + *
> + * 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.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/clk.h>
> +#include <sound/pcm.h>
> +#include <sound/pcm_params.h>
> +#include <sound/soc.h>
> +#include <sound/soc-dapm.h>
> +#include <sound/s3c24xx_uda1341.h>
> +
> +#include <asm/mach-types.h>
> +#include <asm/plat-s3c24xx/regs-iis.h>
> +#include <mach/regs-gpio.h>
> +#include <mach/regs-gpioj.h>
nope, don't include these as noted above.

> +#include <mach/hardware.h>
> +
> +#include "../codecs/uda1341/uda1341.h"
> +#include "../codecs/uda1341/uda1341_platform_data.h"

find somewhere for these to live that can be included without
resorting to knowing the directory layout.

> +#include "s3c24xx-pcm.h"
> +#include "s3c24xx-i2s.h"
> +
> +/*#define S3C24XX_UDA1341_DEBUG 1*/
> +#ifdef S3C24XX_UDA1341_DEBUG
> +#define DBG(x...) printk(KERN_DEBUG "s3c24xx-i2s: " x)
> +#else
> +#define DBG(x...)
> +#endif
> +
> +static struct clk *xtal;
> +static struct clk *pclk;
> +
> +static unsigned long s3c24xx_uda1341_calc_error(unsigned long rate,
> +						unsigned long clk_rate,
> +						unsigned int div,
> +						unsigned int fs)
> +{
> +	long err;
> +
> +	err = clk_rate / (div * fs);
> +	err -= rate;
> +	if (err < 0)
> +		err = -err;
> +	DBG("rate %lu clk %lu div %u fs %u err %ld\n",
> +	    rate, clk_rate, div, fs, err);
> +	return err;
> +}
> +
> +static int s3c24xx_uda1341_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_dai *codec_dai = rtd->dai->codec_dai;
> +	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
> +	unsigned int clk = 0;
> +	unsigned int div = 0, cdiv;
> +	int ret = 0;
> +	int clk_source, fs_mode;
> +	unsigned long mpllin_rate = clk_get_rate(xtal);
> +	unsigned long pclk_rate = clk_get_rate(pclk);
> +	unsigned long rate = params_rate(params);
> +	unsigned long err, cerr;
> +
> +	DBG("mpllin %ld pclk %ld rate %lu\n", mpllin_rate, pclk_rate, rate);
> +
> +	div = pclk_rate / (256 * rate);
> +	if (div == 0)
> +		div = 1;
> +	if (div > 32)
> +		div = 32;
> +	err = s3c24xx_uda1341_calc_error(rate, pclk_rate, div, 256);
> +	fs_mode = S3C2410_IISMOD_256FS;
> +	clk_source = S3C24XX_CLKSRC_PCLK;
> +
> +	if (div < 32) {
> +		cdiv = div + 1;
> +		cerr = s3c24xx_uda1341_calc_error(rate, pclk_rate, cdiv, 256);
> +		if (cerr < err) {
> +			err = cerr;
> +			div = cdiv;
> +		}
> +	}
> +
> +	cdiv = pclk_rate / (384 * rate);
> +	if (cdiv == 0)
> +		cdiv = 1;
> +	if (cdiv > 32)
> +		cdiv = 32;
> +	cerr = s3c24xx_uda1341_calc_error(rate, pclk_rate, cdiv, 384);
> +	if (cerr < err) {
> +		err = cerr;
> +		div = cdiv;
> +		fs_mode = S3C2410_IISMOD_384FS;
> +	}
> +
> +	if (cdiv < 32) {
> +		cdiv = cdiv + 1;
> +		cerr = s3c24xx_uda1341_calc_error(rate, pclk_rate, cdiv, 384);
> +		if (cerr < err) {
> +			err = cerr;
> +			div = cdiv;
> +			fs_mode = S3C2410_IISMOD_384FS;
> +		}
> +	}
> +
> +	cerr = s3c24xx_uda1341_calc_error(rate, mpllin_rate, 1, 256);
> +	if (cerr < err) {
> +		err = cerr;
> +		fs_mode = S3C2410_IISMOD_256FS;
> +		clk_source = S3C24XX_CLKSRC_MPLL;
> +	}
> +
> +	cerr = s3c24xx_uda1341_calc_error(rate, mpllin_rate, 1, 384);
> +	if (cerr < err) {
> +		err = cerr;
> +		fs_mode = S3C2410_IISMOD_384FS;
> +		clk_source = S3C24XX_CLKSRC_MPLL;
> +	}
> +
> +	clk = (fs_mode == S3C2410_IISMOD_384FS ? 384 : 256) * rate;
> +	div = div - 1;
> +	DBG("Will use: %s %s %d sysclk %d err %ld\n",
> +	    fs_mode == S3C2410_IISMOD_384FS ? "384FS" : "256FS",
> +	    clk_source == S3C24XX_CLKSRC_MPLL ? "MPLLin" : "PCLK",
> +	    div, clk, err);
> +
> +	ret = codec_dai->dai_ops.set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
> +			SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = cpu_dai->dai_ops.set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
> +			SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = cpu_dai->dai_ops.set_sysclk(cpu_dai, clk_source , clk,
> +						SND_SOC_CLOCK_IN);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = cpu_dai->dai_ops.set_clkdiv(cpu_dai, S3C24XX_DIV_MCLK,
> +						fs_mode);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = cpu_dai->dai_ops.set_clkdiv(cpu_dai, S3C24XX_DIV_BCLK,
> +						S3C2410_IISMOD_32FS);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = cpu_dai->dai_ops.set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER,
> +					  S3C24XX_PRESCALE(div, div));
> +	if (ret < 0)
> +		return ret;
> +
> +	/* set the codec system clock for DAC and ADC */
> +	ret = codec_dai->dai_ops.set_sysclk(codec_dai, 0, clk,
> +						SND_SOC_CLOCK_OUT);
> +	if (ret < 0)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +static struct snd_soc_ops s3c24xx_uda1341_ops = {
> +	.hw_params = s3c24xx_uda1341_hw_params,
> +};
> +
> +static struct snd_soc_dai_link s3c24xx_uda1341_dai_link = {
> +	.name = "UDA1341",
> +	.stream_name = "UDA1341",
> +	.codec_dai = &uda1341_dai,
> +	.cpu_dai = &s3c24xx_i2s_dai,
> +	.ops = &s3c24xx_uda1341_ops,
> +};
> +
> +static struct snd_soc_machine snd_soc_machine_s3c24xx_uda1341 = {
> +	.name = "S3C24XX_UDA1341",
> +	.dai_link = &s3c24xx_uda1341_dai_link,
> +	.num_links = 1,
> +};
> +
> +static struct s3c24xx_uda1341_platform_data *s3c24xx_uda1341_l3_pins;
> +
> +static void setdat(struct uda1341_platform_data *p, int v)
> +{
> +	s3c2410_gpio_setpin(s3c24xx_uda1341_l3_pins->l3_data, v > 0);
> +	s3c2410_gpio_cfgpin(s3c24xx_uda1341_l3_pins->l3_data,
> +			    S3C2410_GPIO_OUTPUT);
> +}
> +
> +static void setclk(struct uda1341_platform_data *p, int v)
> +{
> +	s3c2410_gpio_setpin(s3c24xx_uda1341_l3_pins->l3_clk, v > 0);
> +	s3c2410_gpio_cfgpin(s3c24xx_uda1341_l3_pins->l3_clk,
> +			    S3C2410_GPIO_OUTPUT);
> +}
> +
> +static void setmode(struct uda1341_platform_data *p, int v)
> +{
> +	s3c2410_gpio_setpin(s3c24xx_uda1341_l3_pins->l3_mode, v > 0);
> +	s3c2410_gpio_cfgpin(s3c24xx_uda1341_l3_pins->l3_mode,
> +			    S3C2410_GPIO_OUTPUT);
> +}

please use the generic gpio layer, the s3c24xx functions are going
to be deprecated and removed as soon as possible, probably after
2.6.28.

> +static void s3c24xx_uda1341_power(int en)
> +{
> +	if (s3c24xx_uda1341_l3_pins->power)
> +		s3c24xx_uda1341_l3_pins->power(en);
> +}
> +
> +static struct uda1341_platform_data s3c24xx_uda1341 = {
> +	.setdat = setdat,
> +	.setclk = setclk,
> +	.setmode = setmode,
> +	.data_hold = 1,
> +	.data_setup = 1,
> +	.clock_high = 1,
> +	.mode_hold = 1,
> +	.mode = 1,
> +	.mode_setup = 1,
> +	.power = s3c24xx_uda1341_power,
> +};
> +
> +static struct snd_soc_device s3c24xx_uda1341_snd_devdata = {
> +	.machine = &snd_soc_machine_s3c24xx_uda1341,
> +	.platform = &s3c24xx_soc_platform,
> +	.codec_dev = &soc_codec_dev_uda1341,
> +	.codec_data = &s3c24xx_uda1341,
> +};
> +
> +static struct platform_device *s3c24xx_uda1341_snd_device;
> +
> +static int s3c24xx_uda1341_probe(struct platform_device *pdev)
> +{
> +	int ret;
> +
> +	printk(KERN_INFO "S3C24XX_UDA1341 SoC Audio driver\n");
> +
> +	s3c24xx_uda1341_l3_pins = pdev->dev.platform_data;
> +	if (s3c24xx_uda1341_l3_pins == NULL) {
> +		printk(KERN_ERR "S3C24XX_UDA1341 SoC Audio: "
> +		       "unable to find platform data\n");
> +		return -ENODEV;
> +	}
> +
> +	s3c24xx_uda1341_snd_device = platform_device_alloc("soc-audio", -1);
> +	if (!s3c24xx_uda1341_snd_device) {
> +		printk(KERN_ERR "S3C24XX_UDA1341 SoC Audio: "
> +		       "Unable to register\n");
> +		return -ENOMEM;
> +	}
> +
> +	platform_set_drvdata(s3c24xx_uda1341_snd_device,
> +			     &s3c24xx_uda1341_snd_devdata);
> +	s3c24xx_uda1341_snd_devdata.dev = &s3c24xx_uda1341_snd_device->dev;
> +	ret = platform_device_add(s3c24xx_uda1341_snd_device);
> +
> +	if (ret) {
> +		printk(KERN_ERR "S3C24XX_UDA1341 SoC Audio: Unable to add\n");
> +		platform_device_put(s3c24xx_uda1341_snd_device);
> +	}
> +
> +	xtal = clk_get(NULL, "xtal");
> +	pclk = clk_get(NULL, "pclk");

check for errors. really you should be passing a device for pclk.

> +	return ret;
> +}
> +
> +static int s3c24xx_uda1341_remove(struct platform_device *pdev)
> +{
> +	platform_device_unregister(s3c24xx_uda1341_snd_device);
> +	clk_put(xtal);
> +	clk_put(pclk);
> +	return 0;
> +}
> +
> +static struct platform_driver s3c24xx_uda1341_driver = {
> +	.probe  = s3c24xx_uda1341_probe,
> +	.remove = s3c24xx_uda1341_remove,
> +	.driver = {
> +		.name = "s3c24xx_uda1341",
> +		.owner = THIS_MODULE,
> +	},
> +};
> +
> +static int __init s3c24xx_uda1341_init(void)
> +{
> +	return platform_driver_register(&s3c24xx_uda1341_driver);
> +}
> +
> +static void __exit s3c24xx_uda1341_exit(void)
> +{
> +	platform_driver_unregister(&s3c24xx_uda1341_driver);
> +}
> +
> +
> +module_init(s3c24xx_uda1341_init);
> +module_exit(s3c24xx_uda1341_exit);
> +
> +MODULE_AUTHOR("Zoltan Devai, Christian Pellegrin <chripell at evolware.org>");
> +MODULE_DESCRIPTION("S3C24XX_UDA1341 ALSA SoC audio driver");
> +MODULE_LICENSE("GPL");

-- 
Ben

Q:      What's a light-year?
A:      One-third less calories than a regular year.



More information about the Alsa-devel mailing list