All of lore.kernel.org
 help / color / mirror / Atom feed
From: Liam Girdwood <lrg@slimlogic.co.uk>
To: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
Cc: alsa-devel@alsa-project.org,
	Kuninori Morimoto <morimoto.kuninori@renesas.com>,
	Magnus Damm <damm@opensource.se>,
	Mark Brown <broonie@opensource.wolfsonmicro.com>,
	linux-sh@vger.kernel.org
Subject: Re: [alsa-devel] [PATCH 2/4] ASoC: add DAI and platform drivers for SH SIU and support for the Migo-R board
Date: Tue, 19 Jan 2010 11:13:16 +0000	[thread overview]
Message-ID: <1263899596.3089.81.camel@odin> (raw)
In-Reply-To: <Pine.LNX.4.64.1001190902000.4607@axis700.grange>

On Tue, 2010-01-19 at 09:09 +0100, Guennadi Liakhovetski wrote:
> Several SuperH platforms, including sh7722, sh7343, sh7354, sh7367 include a
> Sound Interface Unit (SIU). This patch adds drivers for this interface and
> support for the sh7722 Migo-R board.
> 

Had a quick look wrt the ALSA parts. Comments below.

> Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
> ---
> 
> As mentioned in the introduction mail, this driver requires firmware to be 
> loaded from user-space using the standard hotplug functionality.
> 
>  arch/sh/include/asm/siu.h |   26 ++
>  sound/soc/sh/Kconfig      |   16 +
>  sound/soc/sh/Makefile     |    4 +
>  sound/soc/sh/migor.c      |  261 ++++++++++++++
>  sound/soc/sh/siu.h        |  217 ++++++++++++
>  sound/soc/sh/siu_dai.c    |  833 +++++++++++++++++++++++++++++++++++++++++++++
>  sound/soc/sh/siu_pcm.c    |  716 ++++++++++++++++++++++++++++++++++++++
>  7 files changed, 2073 insertions(+), 0 deletions(-)
>  create mode 100644 arch/sh/include/asm/siu.h
>  create mode 100644 sound/soc/sh/migor.c
>  create mode 100644 sound/soc/sh/siu.h
>  create mode 100644 sound/soc/sh/siu_dai.c
>  create mode 100644 sound/soc/sh/siu_pcm.c
> 
> diff --git a/arch/sh/include/asm/siu.h b/arch/sh/include/asm/siu.h
> new file mode 100644
> index 0000000..57565a3
> --- /dev/null
> +++ b/arch/sh/include/asm/siu.h
> @@ -0,0 +1,26 @@
> +/*
> + * platform header for the SIU ASoC driver
> + *
> + * Copyright (C) 2009-2010 Guennadi Liakhovetski <g.liakhovetski@gmx.de>
> + *
> + * 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 ASM_SIU_H
> +#define ASM_SIU_H
> +
> +#include <asm/dma-sh.h>
> +
> +struct device;
> +
> +struct siu_platform {
> +	struct device *dma_dev;
> +	enum sh_dmae_slave_chan_id dma_slave_tx_a;
> +	enum sh_dmae_slave_chan_id dma_slave_rx_a;
> +	enum sh_dmae_slave_chan_id dma_slave_tx_b;
> +	enum sh_dmae_slave_chan_id dma_slave_rx_b;
> +};
> +
> +#endif /* ASM_SIU_H */
> diff --git a/sound/soc/sh/Kconfig b/sound/soc/sh/Kconfig
> index 8072a6d..eec6fe5 100644
> --- a/sound/soc/sh/Kconfig
> +++ b/sound/soc/sh/Kconfig
> @@ -26,6 +26,14 @@ config SND_SOC_SH4_FSI
>  	help
>  	  This option enables FSI sound support
>  
> +config SND_SOC_SH4_SIU
> +	tristate "SH4 SIU support"
> +	depends on CPU_SUBTYPE_SH7722
> +	select DMADEVICES
> +	select SH_DMAE
> +	help
> +	  This option enables SIU sound support
> +
>  ##
>  ## Boards
>  ##
> @@ -55,4 +63,12 @@ config SND_FSI_DA7210
>  	  This option enables generic sound support for the
>  	  FSI - DA7210 unit
>  
> +config SND_SIU_MIGOR
> +	tristate "SIU sound support on Migo-R"
> +	depends on SND_SOC_SH4_SIU && SH_MIGOR
> +	select SND_SOC_WM8978
> +	help
> +	  This option enables generic sound support for the
> +	  SH7722 Migo-R board
> +
>  endmenu
> diff --git a/sound/soc/sh/Makefile b/sound/soc/sh/Makefile
> index 1d0ec0a..8a5a192 100644
> --- a/sound/soc/sh/Makefile
> +++ b/sound/soc/sh/Makefile
> @@ -6,15 +6,19 @@ obj-$(CONFIG_SND_SOC_PCM_SH7760)	+= snd-soc-dma-sh7760.o
>  snd-soc-hac-objs	:= hac.o
>  snd-soc-ssi-objs	:= ssi.o
>  snd-soc-fsi-objs	:= fsi.o
> +snd-soc-siu-objs	:= siu_pcm.o siu_dai.o
>  obj-$(CONFIG_SND_SOC_SH4_HAC)	+= snd-soc-hac.o
>  obj-$(CONFIG_SND_SOC_SH4_SSI)	+= snd-soc-ssi.o
>  obj-$(CONFIG_SND_SOC_SH4_FSI)	+= snd-soc-fsi.o
> +obj-$(CONFIG_SND_SOC_SH4_SIU)	+= snd-soc-siu.o
>  
>  ## boards
>  snd-soc-sh7760-ac97-objs	:= sh7760-ac97.o
>  snd-soc-fsi-ak4642-objs		:= fsi-ak4642.o
>  snd-soc-fsi-da7210-objs		:= fsi-da7210.o
> +snd-soc-migor-objs		:= migor.o
>  
>  obj-$(CONFIG_SND_SH7760_AC97)	+= snd-soc-sh7760-ac97.o
>  obj-$(CONFIG_SND_FSI_AK4642)	+= snd-soc-fsi-ak4642.o
>  obj-$(CONFIG_SND_FSI_DA7210)	+= snd-soc-fsi-da7210.o
> +obj-$(CONFIG_SND_SIU_MIGOR)	+= snd-soc-migor.o
> diff --git a/sound/soc/sh/migor.c b/sound/soc/sh/migor.c
> new file mode 100644
> index 0000000..507e59e
> --- /dev/null
> +++ b/sound/soc/sh/migor.c
> @@ -0,0 +1,261 @@
> +/*
> + * ALSA SoC driver for Migo-R
> + *
> + * Copyright (C) 2009-2010 Guennadi Liakhovetski <g.liakhovetski@gmx.de>
> + *
> + * 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/device.h>
> +#include <linux/firmware.h>
> +#include <linux/module.h>
> +
> +#include <asm/clock.h>
> +
> +#include <cpu/sh7722.h>
> +
> +#include <sound/core.h>
> +#include <sound/pcm.h>
> +#include <sound/soc.h>
> +#include <sound/soc-dapm.h>
> +
> +#include "../codecs/wm8978.h"
> +#include "siu.h"
> +
> +/* Default 8000Hz sampling frequency */
> +static unsigned long codec_freq = 49152350 / 12;
> +
> +static const int mclk_numerator[]	= {1, 3, 2, 3, 4, 6, 8, 12};
> +static const int mclk_denominator[]	= {1, 2, 1, 1, 1, 1, 1, 1};
> +
> +/* External clock, sourced from the codec at the SIUMCKB pin */
> +static unsigned long siumckb_recalc(struct clk *clk)
> +{
> +	return codec_freq;
> +}
> +
> +static struct clk_ops siumckb_clk_ops = {
> +	.recalc = siumckb_recalc,
> +};
> +
> +static struct clk siumckb_clk = {
> +	.name		= "siumckb_clk",
> +	.id		= -1,
> +	.ops		= &siumckb_clk_ops,
> +	.rate		= 0, /* initialised at run-time */
> +};
> +
> +static int migor_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;
> +	unsigned int mclk_div, opclk_div, f2;
> +	int ret, mclk_idx;
> +	unsigned int rate = params_rate(params);
> +
> +	switch (rate) {
> +	case 48000:
> +		mclk_div = 0x40;
> +		opclk_div = 0;
> +		/* f2 = 98304000, was 98304050 */

What does the "was" value represent here ?

> +		break;
> +	case 44100:
> +		mclk_div = 0x40;
> +		opclk_div = 0;
> +		/* f2 = 90316800, was 90317500 */
> +		break;
> +	case 32000:
> +		mclk_div = 0x80;
> +		opclk_div = 0x010;
> +		/* f2 = 131072000, was 131072500 */
> +		break;
> +	case 24000:
> +		mclk_div = 0x80;
> +		opclk_div = 0x010;
> +		/* f2 = 98304000, was 98304700 */
> +		break;
> +	case 22050:
> +		mclk_div = 0x80;
> +		opclk_div = 0x010;
> +		/* f2 = 90316800, was 90317500 */
> +		break;
> +	case 16000:
> +		mclk_div = 0xa0;
> +		opclk_div = 0x020;
> +		/* f2 = 98304000, was 98304700 */
> +		break;
> +	case 11025:
> +		mclk_div = 0x80;
> +		opclk_div = 0x010;
> +		/* f2 = 45158400, was 45158752 */
> +		break;
> +	default:
> +	case 8000:
> +		mclk_div = 0xa0;
> +		opclk_div = 0x020;
> +		/* f2 = 49152000, was 49152350 */
> +		break;
> +	}
> +
> +	mclk_idx = mclk_div >> 5;
> +	/*
> +	 * Calculate f2, according to Figure 40 "PLL and Clock Select Circuit"
> +	 * in WM8978 datasheet
> +	 */
> +	f2 = rate * 256 * 4 * mclk_numerator[mclk_idx] /
> +		mclk_denominator[mclk_idx];
> +
> +	ret = snd_soc_dai_set_clkdiv(codec_dai, WM8978_MCLKDIV,
> +				     mclk_div & 0xe0);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = snd_soc_dai_set_clkdiv(codec_dai, WM8978_OPCLKDIV, opclk_div);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = snd_soc_dai_set_clkdiv(codec_dai, WM8978_DACCLK, 8);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = snd_soc_dai_set_pll(codec_dai, 0, 0, 13000000, f2);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_NB_IF |
> +				  SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = snd_soc_dai_set_fmt(rtd->dai->cpu_dai, SND_SOC_DAIFMT_NB_IF |
> +				  SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS);
> +	if (ret < 0)
> +		return ret;
> +
> +	/* See Figure 40 */
> +	codec_freq = f2 / ((opclk_div >> 4) + 1) >> 2;
> +	/*
> +	 * This propagates the parent frequency change to children and
> +	 * recalculates the frequency table
> +	 */
> +	clk_set_rate(&siumckb_clk, codec_freq);
> +	dev_dbg(codec_dai->dev, "%s: configure %luHz\n", __func__, codec_freq);
> +
> +	snd_soc_dai_set_sysclk(rtd->dai->cpu_dai, CLKB_EXT, codec_freq / 2,
> +			       SND_SOC_CLOCK_IN);
> +
> +	return ret;
> +}
> +
> +static int migor_hw_free(struct snd_pcm_substream *substream)
> +{
> +	struct snd_soc_pcm_runtime *rtd = substream->private_data;
> +	struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
> +
> +	/* disable the PLL */
> +	return snd_soc_dai_set_pll(codec_dai, 0, 0, 0, 0);
> +}
> +
> +static int migor_startup(struct snd_pcm_substream *substream)
> +{
> +	struct snd_soc_pcm_runtime *rtd = substream->private_data;
> +	struct snd_soc_device *socdev = rtd->socdev;
> +	struct snd_soc_codec *codec = socdev->card->codec;
> +	int ret;
> +
> +	/* Activate DAC output routes */
> +	ret = snd_soc_dapm_enable_pin(codec, "Left Speaker Out");
> +	if (ret < 0) {
> +		dev_warn(socdev->dev, "Left err %d\n", ret);
> +		return ret;
> +	}
> +
> +	ret = snd_soc_dapm_enable_pin(codec, "Right Speaker Out");
> +	if (ret < 0) {
> +		dev_warn(socdev->dev, "Right err %d\n", ret);
> +		return ret;
> +	}
> +
> +	snd_soc_dapm_sync(codec);
> +
> +	return 0;
> +}
> +
> +static struct snd_soc_ops migor_dai_ops = {
> +	.hw_params = migor_hw_params,
> +	.hw_free = migor_hw_free,
> +	.startup = migor_startup,
> +};
> +
> +/* migor digital audio interface glue - connects codec <--> CPU */
> +static struct snd_soc_dai_link migor_dai = {
> +	.name = "wm8978",
> +	.stream_name = "WM8978",
> +	.cpu_dai = &siu_i2s_dai,
> +	.codec_dai = &wm8978_dai,
> +	.ops = &migor_dai_ops,
> +};
> +
> +/* migor audio machine driver */
> +static struct snd_soc_card snd_soc_migor = {
> +	.name = "Migo-R",
> +	.platform = &siu_platform,
> +	.dai_link = &migor_dai,
> +	.num_links = 1,
> +};
> +
> +/* migor audio subsystem */
> +static struct snd_soc_device migor_snd_devdata = {
> +	.card = &snd_soc_migor,
> +	.codec_dev = &soc_codec_dev_wm8978,
> +};
> +
> +static struct platform_device *migor_snd_device;
> +
> +static int __init migor_init(void)
> +{
> +	int ret;
> +
> +	ret = clk_register(&siumckb_clk);
> +	if (ret < 0)
> +		return ret;
> +
> +	/* Port number used on this machine: port B */
> +	migor_snd_device = platform_device_alloc("soc-audio", 1);
> +	if (!migor_snd_device) {
> +		ret = -ENOMEM;
> +		goto epdevalloc;
> +	}
> +
> +	platform_set_drvdata(migor_snd_device, &migor_snd_devdata);
> +
> +	migor_snd_devdata.dev = &migor_snd_device->dev;
> +
> +	ret = platform_device_add(migor_snd_device);
> +	if (ret)
> +		goto epdevadd;
> +
> +	return 0;
> +
> +epdevadd:
> +	platform_device_put(migor_snd_device);
> +epdevalloc:
> +	clk_unregister(&siumckb_clk);
> +	return ret;
> +}
> +
> +static void __exit migor_exit(void)
> +{
> +	clk_unregister(&siumckb_clk);
> +	platform_device_unregister(migor_snd_device);
> +}
> +
> +module_init(migor_init);
> +module_exit(migor_exit);
> +
> +MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>");
> +MODULE_DESCRIPTION("ALSA SoC Migor");
> +MODULE_LICENSE("GPL v2");
> diff --git a/sound/soc/sh/siu.h b/sound/soc/sh/siu.h
> new file mode 100644
> index 0000000..e7cba83
> --- /dev/null
> +++ b/sound/soc/sh/siu.h
> @@ -0,0 +1,217 @@
> +/*
> + * siu.h - ALSA SoC driver for Renesas SH7343, SH7722 SIU peripheral.
> + *
> + * Copyright (C) 2009-2010 Guennadi Liakhovetski <g.liakhovetski@gmx.de>
> + * Copyright (C) 2006 Carlos Munoz <carlos@kenati.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.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
> + */
> +
> +#ifndef SIU_H
> +#define SIU_H
> +
> +/* Common kernel and user-space firmware-building defines and types */
> +
> +#define YRAM0_SIZE		(0x0040 / 4)		/* 16 */
> +#define YRAM1_SIZE		(0x0080 / 4)		/* 32 */
> +#define YRAM2_SIZE		(0x0040 / 4)		/* 16 */
> +#define YRAM3_SIZE		(0x0080 / 4)		/* 32 */
> +#define YRAM4_SIZE		(0x0080 / 4)		/* 32 */
> +#define YRAM_DEF_SIZE		(YRAM0_SIZE + YRAM1_SIZE + YRAM2_SIZE + \
> +				 YRAM3_SIZE + YRAM4_SIZE)
> +#define YRAM_FIR_SIZE		(0x0400 / 4)		/* 256 */
> +#define YRAM_IIR_SIZE		(0x0200 / 4)		/* 128 */
> +
> +#define XRAM0_SIZE		(0x0400 / 4)		/* 256 */
> +#define XRAM1_SIZE		(0x0200 / 4)		/* 128 */
> +#define XRAM2_SIZE		(0x0200 / 4)		/* 128 */
> +
> +/* PRAM program array size */
> +#define PRAM0_SIZE		(0x0100 / 4)		/* 64 */
> +#define PRAM1_SIZE		((0x2000 - 0x0100) / 4)	/* 1984 */
> +
> +#include <linux/types.h>
> +
> +struct siu_spb_param {
> +	__u32	ab1a;	/* input FIFO address */
> +	__u32	ab0a;	/* output FIFO address */
> +	__u32	dir;	/* 0=the ather except CPUOUTPUT, 1=CPUINPUT */
> +	__u32	event;	/* SPB program starting conditions */
> +	__u32	stfifo;	/* STFIFO register setting value */
> +	__u32	trdat;	/* TRDAT register setting value */
> +};
> +
> +struct siu_firmware {
> +	__u32			yram_fir_coeff[YRAM_FIR_SIZE];
> +	__u32			pram0[PRAM0_SIZE];
> +	__u32			pram1[PRAM1_SIZE];
> +	__u32			yram0[YRAM0_SIZE];
> +	__u32			yram1[YRAM1_SIZE];
> +	__u32			yram2[YRAM2_SIZE];
> +	__u32			yram3[YRAM3_SIZE];
> +	__u32			yram4[YRAM4_SIZE];
> +	__u32			spbpar_num;
> +	struct siu_spb_param	spbpar[32];
> +};
> +
> +#ifdef __KERNEL__
> +
> +#include <linux/dmaengine.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +
> +#include <asm/dma-sh.h>
> +
> +#include <sound/core.h>
> +#include <sound/pcm.h>
> +#include <sound/soc-dai.h>
> +
> +#define SIU_PORTA		0		/* port A */
> +#define SIU_PORTB		1		/* port B */
> +#define MAX_SIU_PORTS		2
> +
> +/* SIU clock configuration */
> +enum {CLKA_PLL, CLKA_EXT, CLKB_PLL, CLKB_EXT};
> +
> +/* Board specifics */
> +#if defined(CONFIG_CPU_SUBTYPE_SH7722)
> +# define MAX_VOLUME		0x1000
> +#else
> +# define MAX_VOLUME		0x7fff
> +#endif
> +
> +struct siu_info {
> +	int			port_id;
> +	u32 __iomem		*pram;
> +	u32 __iomem		*xram;
> +	u32 __iomem		*yram;
> +	u32 __iomem		*reg;
> +	struct siu_firmware	fw;
> +};
> +
> +#define PRAM_SIZE	0x2000
> +#define XRAM_SIZE	0x800
> +#define YRAM_SIZE	0x800
> +
> +#define XRAM_OFFSET	0x4000
> +#define YRAM_OFFSET	0x6000
> +#define REG_OFFSET	0xc000
> +
> +struct siu_stream {
> +	struct tasklet_struct		tasklet;
> +	struct snd_pcm_substream	*substream;
> +	snd_pcm_format_t		format;
> +	size_t				buf_bytes;
> +	size_t				period_bytes;
> +	int				cur_period;	/* Period currently in dma */
> +	u32				volume;
> +	void				*mono_buf;	/* Mono buffer */
> +	size_t				mono_size;	/* and its size in bytes */
> +	snd_pcm_sframes_t		xfer_cnt;	/* Number of frames */
> +	u8				rw_flg;		/* transfer status */
> +	/* DMA status */
> +	dma_addr_t			mono_dma;
> +	struct dma_chan			*chan;		/* DMA channel */
> +	struct dma_async_tx_descriptor	*tx_desc;
> +	dma_cookie_t			cookie;
> +	struct sh_dmae_slave		param;
> +};
> +
> +struct siu_port {
> +	unsigned long		play_cap;	/* Used to track full duplex */
> +	struct snd_pcm		*pcm;
> +	struct siu_stream	playback;
> +	struct siu_stream	capture;
> +	u32			stfifo;		/* STFIFO value from firmware */
> +	u32			trdat;		/* TRDAT value from firmware */
> +};
> +
> +extern struct siu_port *siu_ports[MAX_SIU_PORTS];
> +
> +static inline struct siu_port *siu_port_info(struct snd_pcm_substream *substream)
> +{
> +	struct platform_device *pdev =
> +		to_platform_device(substream->pcm->card->dev);
> +	return siu_ports[pdev->id];
> +}
> +
> +#define PLAYBACK_ENABLED	1
> +#define CAPTURE_ENABLED		2
> +
> +#define VOLUME_CAPTURE		0
> +#define VOLUME_PLAYBACK		1
> +#define DFLT_VOLUME_LEVEL	0x08000800
> +
> +#define PERIOD_BYTES_MAX	8192		/* DMA transfer/period size */
> +#define PERIOD_BYTES_MIN	256		/* DMA transfer/period size */
> +#define PERIODS_MAX		64		/* Max periods in buffer */
> +#define PERIODS_MIN		4		/* Min periods in buffer */
> +#define BUFFER_BYTES_MAX	(PERIOD_BYTES_MAX * PERIODS_MAX)
> +#define GET_MAX_PERIODS(buf_bytes, period_bytes) \
> +				((buf_bytes) / (period_bytes))
> +#define PERIOD_OFFSET(buf_addr, period_num, period_bytes) \
> +				((buf_addr) + ((period_num) * (period_bytes)))
> +
> +#define RWF_STM_RD		0x01		/* Read in progress */
> +#define RWF_STM_WT		0x02		/* Write in progress */
> +
> +/* Register access */
> +static inline void siu_write32(u32 __iomem *addr, u32 val)
> +{
> +	__raw_writel(val, addr);
> +}
> +
> +static inline u32 siu_read32(u32 __iomem *addr)
> +{
> +	return __raw_readl(addr);
> +}
> +
> +/* SIU registers */
> +#define IFCTL		(0x000 / sizeof(u32))
> +#define SRCTL		(0x004 / sizeof(u32))
> +#define SFORM		(0x008 / sizeof(u32))
> +#define CKCTL		(0x00c / sizeof(u32))
> +#define TRDAT		(0x010 / sizeof(u32))
> +#define STFIFO		(0x014 / sizeof(u32))
> +#define DPAK		(0x01c / sizeof(u32))
> +#define CKREV		(0x020 / sizeof(u32))
> +#define EVNTC		(0x028 / sizeof(u32))
> +#define SBCTL		(0x040 / sizeof(u32))
> +#define SBPSET		(0x044 / sizeof(u32))
> +#define SBFSTS		(0x068 / sizeof(u32))
> +#define SBDVCA		(0x06c / sizeof(u32))
> +#define SBDVCB		(0x070 / sizeof(u32))
> +#define SBACTIV		(0x074 / sizeof(u32))
> +#define DMAIA		(0x090 / sizeof(u32))
> +#define DMAIB		(0x094 / sizeof(u32))
> +#define DMAOA		(0x098 / sizeof(u32))
> +#define DMAOB		(0x09c / sizeof(u32))
> +#define DMAML		(0x0a0 / sizeof(u32))
> +#define SPSTS		(0x0cc / sizeof(u32))
> +#define SPCTL		(0x0d0 / sizeof(u32))
> +#define BRGASEL		(0x100 / sizeof(u32))
> +#define BRRA		(0x104 / sizeof(u32))
> +#define BRGBSEL		(0x108 / sizeof(u32))
> +#define BRRB		(0x10c / sizeof(u32))
> +
> +extern struct snd_soc_platform siu_platform;
> +extern struct snd_soc_dai siu_i2s_dai;
> +
> +int siu_init_port(int port, struct siu_port **port_info, struct snd_card *card);
> +void siu_free_port(struct siu_port *port_info);
> +
> +#endif
> +
> +#endif /* SIU_H */
> diff --git a/sound/soc/sh/siu_dai.c b/sound/soc/sh/siu_dai.c
> new file mode 100644
> index 0000000..e5dbedb
> --- /dev/null
> +++ b/sound/soc/sh/siu_dai.c
> @@ -0,0 +1,833 @@
> +/*
> + * siu_dai.c - ALSA SoC driver for Renesas SH7343, SH7722 SIU peripheral.
> + *
> + * Copyright (C) 2009-2010 Guennadi Liakhovetski <g.liakhovetski@gmx.de>
> + * Copyright (C) 2006 Carlos Munoz <carlos@kenati.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.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/firmware.h>
> +#include <linux/pm_runtime.h>
> +
> +#include <asm/clock.h>
> +#include <asm/siu.h>
> +
> +#include <sound/control.h>
> +#include <sound/soc-dai.h>
> +
> +#include "siu.h"
> +
> +/*
> + * SPDIF is only available on port A and on some SIU implementations it is only
> + * available for input. Due to the lack of hardware to test it, SPDIF is left
> + * disabled in this driver version
> + */
> +struct format_flag {
> +	u32	i2s;
> +	u32	pcm;
> +	u32	spdif;
> +	u32	mask;
> +};
> +
> +struct port_flag {
> +	struct format_flag	playback;
> +	struct format_flag	capture;
> +};
> +
> +static struct port_flag siu_flags[MAX_SIU_PORTS] = {
> +	[SIU_PORTA] = {
> +		.playback = {
> +			.i2s	= 0x50000000,
> +			.pcm	= 0x40000000,
> +			.spdif	= 0x80000000,	/* not on all SIU versions */
> +			.mask	= 0xd0000000,
> +		},
> +		.capture = {
> +			.i2s	= 0x05000000,
> +			.pcm	= 0x04000000,
> +			.spdif	= 0x08000000,
> +			.mask	= 0x0d000000,
> +		},
> +	},
> +	[SIU_PORTB] = {
> +		.playback = {
> +			.i2s	= 0x00500000,
> +			.pcm	= 0x00400000,
> +			.spdif	= 0,		/* impossible - turn off */
> +			.mask	= 0x00500000,
> +		},
> +		.capture = {
> +			.i2s	= 0x00050000,
> +			.pcm	= 0x00040000,
> +			.spdif	= 0,		/* impossible - turn off */
> +			.mask	= 0x00050000,
> +		},
> +	},
> +};
> +
> +static void siu_dai_start(struct siu_port *port_info)
> +{
> +	struct siu_info *info = siu_i2s_dai.private_data;
> +	u32 __iomem *base = info->reg;
> +
> +	dev_dbg(port_info->pcm->card->dev, "%s\n", __func__);
> +
> +	/* Turn on SIU clock */
> +	pm_runtime_get_sync(siu_i2s_dai.dev);
> +
> +	/* Issue software reset to siu */
> +	siu_write32(base + SRCTL, 0);
> +
> +	/* Wait for the reset to take effect */
> +	udelay(1);
> +
> +	port_info->stfifo = 0;
> +	port_info->trdat = 0;
> +
> +	/* portA, portB, SIU operate */
> +	siu_write32(base + SRCTL, 0x301);
> +
> +	/* portA=256fs, portB=256fs */
> +	siu_write32(base + CKCTL, 0x40400000);
> +
> +	/* portA's BRG does not divide SIUCKA */
> +	siu_write32(base + BRGASEL, 0);
> +	siu_write32(base + BRRA, 0);
> +
> +	/* portB's BRG divides SIUCKB by half */
> +	siu_write32(base + BRGBSEL, 1);
> +	siu_write32(base + BRRB, 0);
> +
> +	siu_write32(base + IFCTL, 0x44440000);
> +
> +	/* portA: 32 bit/fs, master; portB: 32 bit/fs, master */
> +	siu_write32(base + SFORM, 0x0c0c0000);
> +
> +	/*
> +	 * Volume levels: looks like the DSP firmware implements volume controls
> +	 * differently from what's described in the datasheet
> +	 */
> +	siu_write32(base + SBDVCA, port_info->playback.volume);
> +	siu_write32(base + SBDVCB, port_info->capture.volume);
> +}
> +
> +static void siu_dai_stop(void)
> +{
> +	struct siu_info *info = siu_i2s_dai.private_data;
> +	u32 __iomem *base = info->reg;
> +
> +	/* SIU software reset */
> +	siu_write32(base + SRCTL, 0);
> +
> +	/* Turn off SIU clock */
> +	pm_runtime_put_sync(siu_i2s_dai.dev);
> +}
> +
> +static void siu_dai_spbAselect(struct siu_port *port_info)
> +{
> +	struct siu_info *info = siu_i2s_dai.private_data;
> +	struct siu_firmware *fw = &info->fw;
> +	u32 *ydef = fw->yram0;
> +	u32 idx;
> +
> +	/* path A use */
> +	if (!info->port_id)
> +		idx = 1;		/* portA */
> +	else
> +		idx = 2;		/* portB */
> +
> +	ydef[0] = (fw->spbpar[idx].ab1a << 16) |
> +		(fw->spbpar[idx].ab0a << 8) |
> +		(fw->spbpar[idx].dir << 7) | 3;
> +	ydef[1] = fw->yram0[1];	/* 0x03000300 */
> +	ydef[2] = (16 / 2) << 24;
> +	ydef[3] = fw->yram0[3];	/* 0 */
> +	ydef[4] = fw->yram0[4];	/* 0 */
> +	ydef[7] = fw->spbpar[idx].event;
> +	port_info->stfifo |= fw->spbpar[idx].stfifo;
> +	port_info->trdat |= fw->spbpar[idx].trdat;
> +}
> +
> +static void siu_dai_spbBselect(struct siu_port *port_info)
> +{
> +	struct siu_info *info = siu_i2s_dai.private_data;
> +	struct siu_firmware *fw = &info->fw;
> +	u32 *ydef = fw->yram0;
> +	u32 idx;
> +
> +	/* path B use */
> +	if (!info->port_id)
> +		idx = 7;		/* portA */
> +	else
> +		idx = 8;		/* portB */
> +
> +	ydef[5] = (fw->spbpar[idx].ab1a << 16) |
> +		(fw->spbpar[idx].ab0a << 8) | 1;
> +	ydef[6] = fw->spbpar[idx].event;
> +	port_info->stfifo |= fw->spbpar[idx].stfifo;
> +	port_info->trdat |= fw->spbpar[idx].trdat;
> +}
> +
> +static void siu_dai_open(struct siu_stream *siu_stream)
> +{
> +	struct siu_info *info = siu_i2s_dai.private_data;
> +	u32 __iomem *base = info->reg;
> +	struct snd_pcm_substream *substream = siu_stream->substream;
> +	struct snd_pcm_runtime *rt = substream->runtime;
> +	u32 srctl, ifctl;
> +
> +	srctl = siu_read32(base + SRCTL);
> +	ifctl = siu_read32(base + IFCTL);
> +
> +	switch (info->port_id) {
> +	case SIU_PORTA:
> +		/* portA operates */
> +		srctl |= 0x200;
> +		ifctl &= ~0xc2;
> +		/* Mono mode is not used, instead, stereo is simulated */
> +		if (rt->channels == 1)
> +			ifctl |= 0x80;
> +		break;
> +	case SIU_PORTB:
> +		/* portB operates */
> +		srctl |= 0x100;
> +		ifctl &= ~0x31;
> +		/* Mono mode is not used, instead, stereo is simulated */
> +		if (rt->channels == 1)
> +			ifctl |= 0x20;
> +		break;
> +	}
> +
> +	siu_write32(base + SRCTL, srctl);
> +	/* Unmute and configure portA */
> +	siu_write32(base + IFCTL, ifctl);
> +}
> +
> +/*
> + * At the moment only fixed Left-upper, Left-lower, Right-upper, Right-lower
> + * packing is supported
> + */
> +static void siu_dai_pcmdatapack(struct siu_stream *siu_stream)
> +{
> +	struct siu_info *info = siu_i2s_dai.private_data;
> +	u32 __iomem *base = info->reg;
> +	u32 dpak;
> +
> +	dpak = siu_read32(base + DPAK);
> +
> +	switch (info->port_id) {
> +	case SIU_PORTA:
> +		dpak &= ~0xc0000000;
> +		break;
> +	case SIU_PORTB:
> +		dpak &= ~0x00c00000;
> +		break;
> +	}
> +
> +	siu_write32(base + DPAK, dpak);
> +}
> +
> +static int siu_dai_spbstart(struct siu_port *port_info)
> +{
> +	struct siu_info *info = siu_i2s_dai.private_data;
> +	u32 __iomem *base = info->reg;
> +	struct siu_firmware *fw = &info->fw;
> +	u32 *ydef = fw->yram0;
> +	int cnt;
> +	u32 __iomem *add;
> +	u32 *ptr;
> +
> +	/* Load SPB Program in PRAM */
> +	ptr = fw->pram0;
> +	add = info->pram;
> +	for (cnt = 0; cnt < PRAM0_SIZE; cnt++, add++, ptr++)
> +		siu_write32(add, *ptr);
> +
> +	ptr = fw->pram1;
> +	add = info->pram + (0x0100 / sizeof(u32));
> +	for (cnt = 0; cnt < PRAM1_SIZE; cnt++, add++, ptr++)
> +		siu_write32(add, *ptr);
> +
> +	/* XRAM initialization */
> +	add = info->xram;
> +	for (cnt = 0; cnt < XRAM0_SIZE + XRAM1_SIZE + XRAM2_SIZE; cnt++, add++)
> +		siu_write32(add, 0);
> +
> +	/* YRAM variable area initialization */
> +	add = info->yram;
> +	for (cnt = 0; cnt < YRAM_DEF_SIZE; cnt++, add++)
> +		siu_write32(add, ydef[cnt]);
> +
> +	/* YRAM FIR coefficient area initialization */
> +	add = info->yram + (0x0200 / sizeof(u32));
> +	for (cnt = 0; cnt < YRAM_FIR_SIZE; cnt++, add++)
> +		siu_write32(add, fw->yram_fir_coeff[cnt]);
> +
> +	/* YRAM IIR coefficient area initialization */
> +	add = info->yram + (0x0600 / sizeof(u32));
> +	for (cnt = 0; cnt < YRAM_IIR_SIZE; cnt++, add++)
> +		siu_write32(add, 0);
> +
> +	siu_write32(base + TRDAT, port_info->trdat);
> +	port_info->trdat = 0x0;
> +
> +
> +	/* SPB start condition: software */
> +	siu_write32(base + SBACTIV, 0);
> +	/* Start SPB */
> +	siu_write32(base + SBCTL, 0xc0000000);
> +	/* Wait for program to halt */
> +	cnt = 0x10000;
> +	while (--cnt && siu_read32(base + SBCTL) != 0x80000000)
> +		cpu_relax();
> +
> +	if (!cnt)
> +		return -EBUSY;
> +
> +	/* SPB program start address setting */
> +	siu_write32(base + SBPSET, 0x00400000);
> +	/* SPB hardware start(FIFOCTL source) */
> +	siu_write32(base + SBACTIV, 0xc0000000);
> +
> +	return 0;
> +}
> +
> +static void siu_dai_spbstop(struct siu_port *port_info)
> +{
> +	struct siu_info *info = siu_i2s_dai.private_data;
> +	u32 __iomem *base = info->reg;
> +
> +	siu_write32(base + SBACTIV, 0);
> +	/* SPB stop */
> +	siu_write32(base + SBCTL, 0);
> +
> +	port_info->stfifo = 0;
> +}
> +
> +/*		API functions		*/
> +
> +/* Playback and capture hardware properties are identical */
> +static struct snd_pcm_hardware siu_dai_pcm_hw = {
> +	.info			= SNDRV_PCM_INFO_INTERLEAVED,
> +	.formats		= SNDRV_PCM_FMTBIT_S16,
> +	.rates			= SNDRV_PCM_RATE_8000_48000,
> +	.rate_min		= 8000,
> +	.rate_max		= 48000,
> +	.channels_min		= 1,

Shouldn't this be 2 as it's stated in siu_dai_open() that mono is not
used.

> +	.channels_max		= 2,
> +	.buffer_bytes_max	= BUFFER_BYTES_MAX,
> +	.period_bytes_min	= PERIOD_BYTES_MIN,
> +	.period_bytes_max	= PERIOD_BYTES_MAX,
> +	.periods_min		= PERIODS_MIN,
> +	.periods_max		= PERIODS_MAX,
> +};
> +
> +static int siu_dai_info_volume(struct snd_kcontrol *kctrl,
> +			       struct snd_ctl_elem_info *uinfo)
> +{
> +	struct siu_port *port_info = snd_kcontrol_chip(kctrl);
> +
> +	dev_dbg(port_info->pcm->card->dev, "%s\n", __func__);
> +
> +	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
> +	uinfo->count = 2;
> +	uinfo->value.integer.min = 0;
> +	uinfo->value.integer.max = MAX_VOLUME;
> +
> +	return 0;
> +}
> +
> +static int siu_dai_get_volume(struct snd_kcontrol *kctrl,
> +			      struct snd_ctl_elem_value *ucontrol)
> +{
> +	struct siu_port *port_info = snd_kcontrol_chip(kctrl);
> +	struct device *dev = port_info->pcm->card->dev;
> +	u32 vol;
> +
> +	dev_dbg(dev, "%s\n", __func__);
> +
> +	switch (kctrl->private_value) {
> +	case VOLUME_PLAYBACK:
> +		/* Playback is always on port 0 */
> +		vol = port_info->playback.volume;
> +		ucontrol->value.integer.value[0] = vol & 0xffff;
> +		ucontrol->value.integer.value[1] = vol >> 16 & 0xffff;
> +		break;
> +	case VOLUME_CAPTURE:
> +		/* Capture is always on port 1 */
> +		vol = port_info->capture.volume;
> +		ucontrol->value.integer.value[0] = vol & 0xffff;
> +		ucontrol->value.integer.value[1] = vol >> 16 & 0xffff;
> +		break;
> +	default:
> +		dev_err(dev, "%s() invalid private_value=%ld\n",
> +			__func__, kctrl->private_value);
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static int siu_dai_put_volume(struct snd_kcontrol *kctrl,
> +			      struct snd_ctl_elem_value *ucontrol)
> +{
> +	struct siu_port *port_info = snd_kcontrol_chip(kctrl);
> +	struct device *dev = port_info->pcm->card->dev;
> +	struct siu_info *info = siu_i2s_dai.private_data;
> +	u32 __iomem *base = info->reg;
> +	u32 new_vol;
> +	u32 cur_vol;
> +
> +	dev_dbg(dev, "%s\n", __func__);
> +
> +	if (ucontrol->value.integer.value[0] < 0 ||
> +	    ucontrol->value.integer.value[0] > MAX_VOLUME ||
> +	    ucontrol->value.integer.value[1] < 0 ||
> +	    ucontrol->value.integer.value[1] > MAX_VOLUME)
> +		return -EINVAL;
> +
> +	new_vol = ucontrol->value.integer.value[0] |
> +		ucontrol->value.integer.value[1] << 16;
> +
> +	/* See comment above - DSP firmware implementation */
> +	switch (kctrl->private_value) {
> +	case VOLUME_PLAYBACK:
> +		/* Playback is always on port 0 */
> +		cur_vol = port_info->playback.volume;
> +		siu_write32(base + SBDVCA, new_vol);
> +		port_info->playback.volume = new_vol;
> +		break;
> +	case VOLUME_CAPTURE:
> +		/* Capture is always on port 1 */
> +		cur_vol = port_info->capture.volume;
> +		siu_write32(base + SBDVCB, new_vol);
> +		port_info->capture.volume = new_vol;
> +		break;
> +	default:
> +		dev_err(dev, "%s() invalid private_value=%ld\n",
> +			__func__, kctrl->private_value);
> +		return -EINVAL;
> +	}
> +
> +	if (cur_vol != new_vol)
> +		return 1;
> +
> +	return 0;
> +}
> +
> +static struct snd_kcontrol_new playback_controls = {
> +	.iface		= SNDRV_CTL_ELEM_IFACE_MIXER,
> +	.name		= "PCM Playback Volume",
> +	.index		= 0,
> +	.info		= siu_dai_info_volume,
> +	.get		= siu_dai_get_volume,
> +	.put		= siu_dai_put_volume,
> +	.private_value	= VOLUME_PLAYBACK,
> +};
> +
> +static struct snd_kcontrol_new capture_controls = {
> +	.iface		= SNDRV_CTL_ELEM_IFACE_MIXER,
> +	.name		= "PCM Capture Volume",
> +	.index		= 0,
> +	.info		= siu_dai_info_volume,
> +	.get		= siu_dai_get_volume,
> +	.put		= siu_dai_put_volume,
> +	.private_value	= VOLUME_CAPTURE,
> +};
> +
> +int siu_init_port(int port, struct siu_port **port_info, struct snd_card *card)
> +{
> +	struct device *dev = card->dev;
> +	struct snd_kcontrol *kctrl;
> +	int ret;
> +
> +	*port_info = kzalloc(sizeof(**port_info), GFP_KERNEL);
> +	if (!*port_info)
> +		return -ENOMEM;
> +
> +	dev_dbg(dev, "%s: port #%d@%p\n", __func__, port, *port_info);
> +
> +	(*port_info)->playback.volume = DFLT_VOLUME_LEVEL;
> +	(*port_info)->capture.volume = DFLT_VOLUME_LEVEL;
> +
> +	/*
> +	 * Add mixer support. The SPB is used to change the volume. Both
> +	 * ports use the same SPB. Therefore, we only register one
> +	 * control instance since it will be used by both channels.
> +	 * In error case we continue without controls.
> +	 */
> +	kctrl = snd_ctl_new1(&playback_controls, *port_info);
> +	ret = snd_ctl_add(card, kctrl);
> +	if (ret < 0)
> +		dev_err(dev,
> +			"failed to add playback controls %p port=%d err=%d\n",
> +			kctrl, port, ret);
> +
> +	kctrl = snd_ctl_new1(&capture_controls, *port_info);
> +	ret = snd_ctl_add(card, kctrl);
> +	if (ret < 0)
> +		dev_err(dev,
> +			"failed to add capture controls %p port=%d err=%d\n",
> +			kctrl, port, ret);
> +
> +	return 0;
> +}
> +
> +void siu_free_port(struct siu_port *port_info)
> +{
> +	kfree(port_info);
> +}
> +
> +static int siu_dai_startup(struct snd_pcm_substream *substream,
> +			   struct snd_soc_dai *dai)
> +{
> +	struct siu_info *info = siu_i2s_dai.private_data;
> +	struct snd_pcm_runtime *rt = substream->runtime;
> +	struct siu_port	*port_info = siu_port_info(substream);
> +	int ret;
> +
> +	dev_dbg(substream->pcm->card->dev, "%s: port=%d@%p\n", __func__,
> +		info->port_id, port_info);
> +
> +	snd_soc_set_runtime_hwparams(substream, &siu_dai_pcm_hw);
> +
> +	ret = snd_pcm_hw_constraint_integer(rt, SNDRV_PCM_HW_PARAM_PERIODS);
> +	if (unlikely(ret < 0))
> +		return ret;
> +
> +	siu_dai_start(port_info);
> +
> +	return 0;
> +}
> +
> +static void siu_dai_shutdown(struct snd_pcm_substream *substream,
> +			     struct snd_soc_dai *dai)
> +{
> +	struct siu_info *info = siu_i2s_dai.private_data;
> +	struct siu_port	*port_info = siu_port_info(substream);
> +
> +	dev_dbg(substream->pcm->card->dev, "%s: port=%d@%p\n", __func__,
> +		info->port_id, port_info);
> +
> +	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
> +		port_info->play_cap &= ~PLAYBACK_ENABLED;
> +	else
> +		port_info->play_cap &= ~CAPTURE_ENABLED;
> +
> +	/* Stop the siu if the other stream is not using it */
> +	if (!port_info->play_cap) {
> +		/* during stmread or stmwrite ? */
> +		BUG_ON(port_info->playback.rw_flg || port_info->capture.rw_flg);
> +		siu_dai_spbstop(port_info);
> +		siu_dai_stop();
> +	}
> +}
> +
> +/* PCM part of siu_dai_playback_prepare() / siu_dai_capture_prepare() */
> +static int siu_dai_prepare(struct snd_pcm_substream *substream,
> +			   struct snd_soc_dai *dai)
> +{
> +	struct siu_info *info = siu_i2s_dai.private_data;
> +	struct snd_pcm_runtime *rt = substream->runtime;
> +	struct siu_port *port_info = siu_port_info(substream);
> +	struct siu_stream *siu_stream;
> +	int self, ret;
> +
> +	dev_dbg(substream->pcm->card->dev,
> +		"%s: port %d, active streams %lx, %d channels\n",
> +		__func__, info->port_id, port_info->play_cap, rt->channels);
> +
> +	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
> +		self = PLAYBACK_ENABLED;
> +		siu_stream = &port_info->playback;
> +	} else {
> +		self = CAPTURE_ENABLED;
> +		siu_stream = &port_info->capture;
> +	}
> +
> +	/* Set up the siu if not already done */
> +	if (!port_info->play_cap) {
> +		siu_stream->rw_flg = 0;	/* stream-data transfer flag */
> +
> +		siu_dai_spbAselect(port_info);
> +		siu_dai_spbBselect(port_info);
> +
> +		siu_dai_open(siu_stream);
> +
> +		siu_dai_pcmdatapack(siu_stream);
> +
> +		ret = siu_dai_spbstart(port_info);
> +		if (ret < 0)
> +			goto fail;
> +	}
> +
> +	port_info->play_cap |= self;
> +
> +fail:
> +	return ret;
> +}
> +
> +/*
> + * SIU can set bus format to I2S / PCM / SPDIF independently for playback and
> + * capture, however, the current API sets the bus format globally for a DAI.
> + */
> +static int siu_dai_set_fmt(struct snd_soc_dai *dai,
> +			   unsigned int fmt)
> +{
> +	struct siu_info *info = siu_i2s_dai.private_data;
> +	u32 __iomem *base = info->reg;
> +	u32 ifctl;
> +
> +	dev_dbg(dai->dev, "%s: fmt 0x%x on port %d\n",
> +		__func__, fmt, info->port_id);
> +
> +	if (info->port_id < 0)
> +		return -ENODEV;
> +
> +	/* Here select between I2S / PCM / SPDIF */
> +	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
> +	case SND_SOC_DAIFMT_I2S:
> +		ifctl = siu_flags[info->port_id].playback.i2s |
> +			siu_flags[info->port_id].capture.i2s;
> +		break;
> +	case SND_SOC_DAIFMT_LEFT_J:
> +		ifctl = siu_flags[info->port_id].playback.pcm |
> +			siu_flags[info->port_id].capture.pcm;
> +		break;
> +	/* SPDIF disabled - see comment at the top */
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	ifctl |= ~(siu_flags[info->port_id].playback.mask |
> +		   siu_flags[info->port_id].capture.mask) &
> +		siu_read32(base + IFCTL);
> +	siu_write32(base + IFCTL, ifctl);
> +
> +	return 0;
> +}
> +
> +static int siu_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id,
> +			      unsigned int freq, int dir)
> +{
> +	struct clk *siu_clk, *parent_clk;
> +	char *siu_name, *parent_name;
> +	int ret;
> +
> +	if (dir != SND_SOC_CLOCK_IN)
> +		return -EINVAL;
> +
> +	dev_dbg(dai->dev, "%s: using clock %d\n", __func__, clk_id);
> +
> +	switch (clk_id) {
> +	case CLKA_PLL:
> +		siu_name = "siua_clk";
> +		parent_name = "pll_clk";
> +		break;
> +	case CLKA_EXT:
> +		siu_name = "siua_clk";
> +		parent_name = "siumcka_clk";
> +		break;
> +	case CLKB_PLL:
> +		siu_name = "siub_clk";
> +		parent_name = "pll_clk";
> +		break;
> +	case CLKB_EXT:
> +		siu_name = "siub_clk";
> +		parent_name = "siumckb_clk";
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	siu_clk = clk_get(siu_i2s_dai.dev, siu_name);
> +	if (IS_ERR(siu_clk))
> +		return PTR_ERR(siu_clk);
> +
> +	parent_clk = clk_get(siu_i2s_dai.dev, parent_name);
> +	if (!IS_ERR(parent_clk)) {
> +		ret = clk_set_parent(siu_clk, parent_clk);
> +		if (!ret)
> +			clk_set_rate(siu_clk, freq);
> +	}
> +
> +	clk_put(parent_clk);
> +	clk_put(siu_clk);
> +
> +	return 0;
> +}
> +
> +static struct snd_soc_dai_ops siu_dai_ops = {
> +	.startup	= siu_dai_startup,
> +	.shutdown	= siu_dai_shutdown,
> +	.prepare	= siu_dai_prepare,
> +	.set_sysclk	= siu_dai_set_sysclk,
> +	.set_fmt	= siu_dai_set_fmt,
> +};
> +
> +struct snd_soc_dai siu_i2s_dai = {
> +	.name = "sh-siu",
> +	.id = 0,
> +	.playback = {
> +		.channels_min = 1,

Shouldn't this also be 2 due to mono not used statement in
siu_dai_open()

> +		.channels_max = 2,
> +		.formats = SNDRV_PCM_FMTBIT_S16,
> +		.rates = SNDRV_PCM_RATE_8000_48000,
> +	},
> +	.capture = {
> +		.channels_min = 1,
> +		.channels_max = 2,
> +		.formats = SNDRV_PCM_FMTBIT_S16,
> +		.rates = SNDRV_PCM_RATE_8000_48000,
> +	 },
> +	.ops = &siu_dai_ops,
> +};
> +EXPORT_SYMBOL_GPL(siu_i2s_dai);
> +
> +static int __devinit siu_probe(struct platform_device *pdev)
> +{
> +	const struct firmware *fw_entry;
> +	struct resource *res, *region;
> +	struct siu_info *info;
> +	int ret;
> +
> +	info = kmalloc(sizeof(*info), GFP_KERNEL);
> +	if (!info)
> +		return -ENOMEM;
> +
> +	ret = request_firmware(&fw_entry, "siu_spb.bin", &pdev->dev);
> +	if (ret)
> +		goto ereqfw;
> +
> +	/*
> +	 * Loaded firmware is "const" - read only, but we have to modify it in
> +	 * snd_siu_sh7343_spbAselect() and snd_siu_sh7343_spbBselect()
> +	 */
> +	memcpy(&info->fw, fw_entry->data, fw_entry->size);
> +
> +	release_firmware(fw_entry);
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (!res) {
> +		ret = -ENODEV;
> +		goto egetres;
> +	}
> +
> +	region = request_mem_region(res->start, resource_size(res),
> +				    pdev->name);
> +	if (!region) {
> +		dev_err(&pdev->dev, "SIU region already claimed\n");
> +		ret = -EBUSY;
> +		goto ereqmemreg;
> +	}
> +
> +	ret = -ENOMEM;
> +	info->pram = ioremap(res->start, PRAM_SIZE);
> +	if (!info->pram)
> +		goto emappram;
> +	info->xram = ioremap(res->start + XRAM_OFFSET, XRAM_SIZE);
> +	if (!info->xram)
> +		goto emapxram;
> +	info->yram = ioremap(res->start + YRAM_OFFSET, YRAM_SIZE);
> +	if (!info->yram)
> +		goto emapyram;
> +	info->reg = ioremap(res->start + REG_OFFSET, resource_size(res) -
> +			    REG_OFFSET);
> +	if (!info->reg)
> +		goto emapreg;
> +
> +	siu_i2s_dai.dev = &pdev->dev;
> +	siu_i2s_dai.private_data = info;
> +
> +	ret = snd_soc_register_dais(&siu_i2s_dai, 1);
> +	if (ret < 0)
> +		goto edaiinit;
> +
> +	ret = snd_soc_register_platform(&siu_platform);
> +	if (ret < 0)
> +		goto esocregp;
> +
> +	pm_runtime_enable(&pdev->dev);
> +
> +	return ret;
> +
> +esocregp:
> +	snd_soc_unregister_dais(&siu_i2s_dai, 1);
> +edaiinit:
> +	iounmap(info->reg);
> +emapreg:
> +	iounmap(info->yram);
> +emapyram:
> +	iounmap(info->xram);
> +emapxram:
> +	iounmap(info->pram);
> +emappram:
> +	release_mem_region(res->start, resource_size(res));
> +ereqmemreg:
> +egetres:
> +ereqfw:
> +	kfree(info);
> +
> +	return ret;
> +}
> +
> +static int __devexit siu_remove(struct platform_device *pdev)
> +{
> +	struct siu_info *info = siu_i2s_dai.private_data;
> +	struct resource *res;
> +
> +	pm_runtime_disable(&pdev->dev);
> +
> +	snd_soc_unregister_platform(&siu_platform);
> +	snd_soc_unregister_dais(&siu_i2s_dai, 1);
> +
> +	iounmap(info->reg);
> +	iounmap(info->yram);
> +	iounmap(info->xram);
> +	iounmap(info->pram);
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (res)
> +		release_mem_region(res->start, resource_size(res));
> +	kfree(info);
> +
> +	return 0;
> +}
> +
> +static struct platform_driver siu_driver = {
> +	.driver 	= {
> +		.name	= "sh_siu",
> +	},
> +	.probe		= siu_probe,
> +	.remove		= __devexit_p(siu_remove),
> +};
> +
> +static int __init siu_init(void)
> +{
> +	return platform_driver_register(&siu_driver);
> +}
> +
> +static void __exit siu_exit(void)
> +{
> +	platform_driver_unregister(&siu_driver);
> +}
> +
> +module_init(siu_init)
> +module_exit(siu_exit)
> +
> +MODULE_AUTHOR("Carlos Munoz <carlos@kenati.com>");
> +MODULE_DESCRIPTION("ALSA SoC SH7722 SIU driver");
> +MODULE_LICENSE("GPL");
> diff --git a/sound/soc/sh/siu_pcm.c b/sound/soc/sh/siu_pcm.c
> new file mode 100644
> index 0000000..afe2e6e
> --- /dev/null
> +++ b/sound/soc/sh/siu_pcm.c
> @@ -0,0 +1,716 @@
> +/*
> + * siu_pcm.c - ALSA driver for Renesas SH7343, SH7722 SIU peripheral.
> + *
> + * Copyright (C) 2009-2010 Guennadi Liakhovetski <g.liakhovetski@gmx.de>
> + * Copyright (C) 2006 Carlos Munoz <carlos@kenati.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.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
> + */
> +#include <linux/delay.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/dmaengine.h>
> +#include <linux/interrupt.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +
> +#include <sound/control.h>
> +#include <sound/core.h>
> +#include <sound/pcm.h>
> +#include <sound/pcm_params.h>
> +#include <sound/soc-dai.h>
> +
> +#include <asm/dma-sh.h>
> +#include <asm/siu.h>
> +
> +#include "siu.h"
> +
> +struct siu_port *siu_ports[MAX_SIU_PORTS];
> +
> +static void copy_playback_period(struct siu_stream *siu_stream)
> +{
> +	struct snd_pcm_runtime *rt = siu_stream->substream->runtime;
> +	u16 *src;
> +	u32 *dst;
> +	int cp_cnt;
> +	int i;
> +
> +	src = (u16 *)PERIOD_OFFSET(rt->dma_area,
> +				   siu_stream->cur_period,
> +				   siu_stream->period_bytes);
> +	dst = siu_stream->mono_buf;
> +	cp_cnt = siu_stream->xfer_cnt;
> +
> +	for (i = 0; i < cp_cnt; i++)
> +		*dst++ = *src++;
> +}
> +
> +static void copy_capture_period(struct siu_stream *siu_stream)
> +{
> +	struct snd_pcm_runtime *rt = siu_stream->substream->runtime;
> +	u16 *src;
> +	u16 *dst;
> +	int cp_cnt;
> +	int i;
> +
> +	dst = (u16 *)PERIOD_OFFSET(rt->dma_area,
> +				   siu_stream->cur_period,
> +				   siu_stream->period_bytes);
> +	src = (u16 *)siu_stream->mono_buf;
> +	cp_cnt = siu_stream->xfer_cnt;
> +
> +	for (i = 0; i < cp_cnt; i++) {
> +		*dst++ = *src;
> +		src += 2;
> +	}
> +}
> +
> +/* transfersize is number of u32 dma transfers per period */
> +static int siu_pcm_stmwrite_stop(struct siu_port *port_info)
> +{
> +	struct siu_info *info = siu_i2s_dai.private_data;
> +	u32 __iomem *base = info->reg;
> +	struct siu_stream *siu_stream = &port_info->playback;
> +	u32 stfifo;
> +
> +	if (!siu_stream->rw_flg)
> +		return -EPERM;
> +
> +	/* output FIFO disable */
> +	stfifo = siu_read32(base + STFIFO);
> +	siu_write32(base + STFIFO, stfifo & ~0x0c180c18);
> +	pr_debug("%s: STFIFO %x -> %x\n", __func__,
> +		 stfifo, stfifo & ~0x0c180c18);
> +
> +	/* during stmwrite clear */
> +	siu_stream->rw_flg = 0;
> +
> +	return 0;
> +}
> +
> +static int siu_pcm_stmwrite_start(struct siu_port *port_info)
> +{
> +	struct siu_stream *siu_stream = &port_info->playback;
> +
> +	if (siu_stream->rw_flg)
> +		return -EPERM;
> +
> +	/* Current period in buffer */
> +	port_info->playback.cur_period = 0;
> +
> +	/* during stmwrite flag set */
> +	siu_stream->rw_flg = RWF_STM_WT;
> +
> +	/* DMA transfer start */
> +	tasklet_schedule(&siu_stream->tasklet);
> +
> +	return 0;
> +}
> +
> +static void siu_dma_tx_complete(void *arg)
> +{
> +	struct siu_stream *siu_stream = arg;
> +	struct snd_pcm_substream *substream = siu_stream->substream;
> +
> +	if (!siu_stream->rw_flg)
> +		return;
> +
> +	if (substream->runtime->channels == 1 &&
> +	    substream->stream == SNDRV_PCM_STREAM_CAPTURE)
> +		copy_capture_period(siu_stream);
> +
> +	/* Update completed period count */
> +	if (++siu_stream->cur_period >=
> +	    GET_MAX_PERIODS(siu_stream->buf_bytes,
> +			    siu_stream->period_bytes))
> +		siu_stream->cur_period = 0;
> +
> +	pr_debug("%s: done period #%d (%u/%u bytes), cookie %d\n",
> +		__func__, siu_stream->cur_period,
> +		siu_stream->cur_period * siu_stream->period_bytes,
> +		siu_stream->buf_bytes, siu_stream->cookie);
> +
> +	tasklet_schedule(&siu_stream->tasklet);
> +
> +	/* Notify alsa: a period is done */
> +	snd_pcm_period_elapsed(siu_stream->substream);
> +}
> +
> +static int siu_pcm_wr_set(struct siu_port *port_info,
> +			  dma_addr_t buff, u32 size)
> +{
> +	struct siu_info *info = siu_i2s_dai.private_data;
> +	u32 __iomem *base = info->reg;
> +	struct siu_stream *siu_stream = &port_info->playback;
> +	struct snd_pcm_substream *substream = siu_stream->substream;
> +	struct device *dev = substream->pcm->card->dev;
> +	struct dma_async_tx_descriptor *desc;
> +	dma_cookie_t cookie;
> +	struct scatterlist sg;
> +	u32 stfifo;
> +
> +	sg_init_table(&sg, 1);
> +	sg_set_page(&sg, pfn_to_page(PFN_DOWN(buff)),
> +		    size, offset_in_page(buff));
> +	sg_dma_address(&sg) = buff;
> +
> +	desc = siu_stream->chan->device->device_prep_slave_sg(siu_stream->chan,
> +		&sg, 1, DMA_TO_DEVICE, DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
> +	if (!desc) {
> +		dev_err(dev, "Failed to allocate a dma descriptor\n");
> +		return -ENOMEM;
> +	}
> +
> +	desc->callback = siu_dma_tx_complete;
> +	desc->callback_param = siu_stream;
> +	cookie = desc->tx_submit(desc);
> +	if (cookie < 0) {
> +		dev_err(dev, "Failed to submit a dma transfer\n");
> +		return cookie;
> +	}
> +
> +	siu_stream->tx_desc = desc;
> +	siu_stream->cookie = cookie;
> +
> +	dma_async_issue_pending(siu_stream->chan);
> +
> +	/* only output FIFO enable */
> +	stfifo = siu_read32(base + STFIFO);
> +	siu_write32(base + STFIFO, stfifo | (port_info->stfifo & 0x0c180c18));
> +	dev_dbg(dev, "%s: STFIFO %x -> %x\n", __func__,
> +		stfifo, stfifo | (port_info->stfifo & 0x0c180c18));
> +
> +	return 0;
> +}
> +
> +static int siu_pcm_rd_set(struct siu_port *port_info,
> +			  dma_addr_t buff, size_t size)
> +{
> +	struct siu_info *info = siu_i2s_dai.private_data;
> +	u32 __iomem *base = info->reg;
> +	struct siu_stream *siu_stream = &port_info->capture;
> +	struct snd_pcm_substream *substream = siu_stream->substream;
> +	struct device *dev = substream->pcm->card->dev;
> +	struct dma_async_tx_descriptor *desc;
> +	dma_cookie_t cookie;
> +	struct scatterlist sg;
> +	u32 stfifo;
> +
> +	dev_dbg(dev, "%s: %u@%llx\n", __func__, size, (unsigned long long)buff);
> +
> +	sg_init_table(&sg, 1);
> +	sg_set_page(&sg, pfn_to_page(PFN_DOWN(buff)),
> +		    size, offset_in_page(buff));
> +	sg_dma_address(&sg) = buff;
> +
> +	desc = siu_stream->chan->device->device_prep_slave_sg(siu_stream->chan,
> +		&sg, 1, DMA_FROM_DEVICE, DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
> +	if (!desc) {
> +		dev_err(dev, "Failed to allocate dma descriptor\n");
> +		return -ENOMEM;
> +	}
> +
> +	desc->callback = siu_dma_tx_complete;
> +	desc->callback_param = siu_stream;
> +	cookie = desc->tx_submit(desc);
> +	if (cookie < 0) {
> +		dev_err(dev, "Failed to submit dma descriptor\n");
> +		return cookie;
> +	}
> +
> +	siu_stream->tx_desc = desc;
> +	siu_stream->cookie = cookie;
> +
> +	dma_async_issue_pending(siu_stream->chan);
> +
> +	/* only input FIFO enable */
> +	stfifo = siu_read32(base + STFIFO);
> +	siu_write32(base + STFIFO, siu_read32(base + STFIFO) |
> +		    (port_info->stfifo & 0x13071307));
> +	dev_dbg(dev, "%s: STFIFO %x -> %x\n", __func__,
> +		stfifo, stfifo | (port_info->stfifo & 0x13071307));
> +
> +	return 0;
> +}
> +
> +static void siu_io_tasklet(unsigned long data)
> +{
> +	struct siu_stream *siu_stream = (struct siu_stream *)data;
> +	struct snd_pcm_substream *substream = siu_stream->substream;
> +	struct device *dev = substream->pcm->card->dev;
> +	struct snd_pcm_runtime *rt = substream->runtime;
> +	struct siu_port *port_info = siu_port_info(substream);
> +
> +	dev_dbg(dev, "%s: flags %x\n", __func__, siu_stream->rw_flg);
> +
> +	if (!siu_stream->rw_flg) {
> +		dev_dbg(dev, "%s: stream inactive\n", __func__);
> +		return;
> +	}
> +
> +	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
> +		dma_addr_t buff;
> +		size_t count;
> +		u8 *virt;
> +
> +		if (rt->channels == 1) {
> +			buff = siu_stream->mono_dma;
> +			virt = siu_stream->mono_buf;
> +			count = siu_stream->mono_size;
> +		} else {
> +			buff = (dma_addr_t)PERIOD_OFFSET(rt->dma_addr,
> +					siu_stream->cur_period,
> +					siu_stream->period_bytes);
> +			virt = PERIOD_OFFSET(rt->dma_area,
> +					siu_stream->cur_period,
> +					siu_stream->period_bytes);
> +			count = siu_stream->period_bytes;
> +		}
> +
> +		/* DMA transfer start */
> +		siu_pcm_rd_set(port_info, buff, count);
> +	} else {
> +		/* For mono streams we need to use the mono buffer */
> +		if (rt->channels == 1) {
> +			copy_playback_period(siu_stream);
> +			siu_pcm_wr_set(port_info,
> +				siu_stream->mono_dma, siu_stream->mono_size);
> +		} else {
> +			siu_pcm_wr_set(port_info,
> +				(dma_addr_t)PERIOD_OFFSET(rt->dma_addr,
> +					siu_stream->cur_period,
> +					siu_stream->period_bytes),
> +				siu_stream->period_bytes);
> +		}
> +	}
> +}
> +
> +/* Capture */
> +static int siu_pcm_stmread_start(struct siu_port *port_info)
> +{
> +	struct siu_stream *siu_stream = &port_info->capture;
> +
> +	if (siu_stream->xfer_cnt > 0x1000000)
> +		return -EINVAL;
> +	if (siu_stream->rw_flg)
> +		return -EPERM;
> +
> +	/* Current period in buffer */
> +	siu_stream->cur_period = 0;
> +
> +	/* during stmread flag set */
> +	siu_stream->rw_flg = RWF_STM_RD;
> +
> +	tasklet_schedule(&siu_stream->tasklet);
> +
> +	return 0;
> +}
> +
> +static int siu_pcm_stmread_stop(struct siu_port *port_info)
> +{
> +	struct siu_info *info = siu_i2s_dai.private_data;
> +	u32 __iomem *base = info->reg;
> +	struct siu_stream *siu_stream = &port_info->capture;
> +	struct device *dev = siu_stream->substream->pcm->card->dev;
> +	u32 stfifo;
> +
> +	if (!siu_stream->rw_flg)
> +		return -EPERM;
> +
> +	/* input FIFO disable */
> +	stfifo = siu_read32(base + STFIFO);
> +	siu_write32(base + STFIFO, stfifo & ~0x13071307);
> +	dev_dbg(dev, "%s: STFIFO %x -> %x\n", __func__,
> +		stfifo, stfifo & ~0x13071307);
> +
> +	/* during stmread flag clear */
> +	siu_stream->rw_flg = 0;
> +
> +	return 0;
> +}
> +
> +static int siu_pcm_hw_params(struct snd_pcm_substream *ss,
> +			     struct snd_pcm_hw_params *hw_params)
> +{
> +	struct siu_info *info = siu_i2s_dai.private_data;
> +	struct device *dev = ss->pcm->card->dev;
> +	int ret;
> +
> +	dev_dbg(dev, "%s: port=%d\n", __func__, info->port_id);
> +
> +	ret = snd_pcm_lib_malloc_pages(ss, params_buffer_bytes(hw_params));
> +	if (ret < 0)
> +		dev_err(dev, "snd_pcm_lib_malloc_pages() failed\n");
> +
> +	return ret;
> +}
> +
> +static void siu_pcm_mono_free(struct device *dev, struct siu_stream *stream)
> +{
> +	dma_free_coherent(dev, stream->mono_size,
> +			  stream->mono_buf, stream->mono_dma);
> +	stream->mono_buf = NULL;
> +	stream->mono_size = 0;
> +}
> +
> +static int siu_pcm_hw_free(struct snd_pcm_substream *ss)
> +{
> +	struct siu_info *info = siu_i2s_dai.private_data;
> +	struct siu_port	*port_info = siu_port_info(ss);
> +	struct device *dev = ss->pcm->card->dev;
> +	struct siu_stream *siu_stream;
> +
> +	if (ss->stream == SNDRV_PCM_STREAM_PLAYBACK)
> +		siu_stream = &port_info->playback;
> +	else
> +		siu_stream = &port_info->capture;
> +
> +	dev_dbg(dev, "%s: port=%d, mono %p\n", __func__,
> +		info->port_id, siu_stream->mono_buf);
> +
> +	if (siu_stream->mono_buf && ss->runtime->channels == 1)
> +		siu_pcm_mono_free(ss->pcm->card->dev, siu_stream);
> +
> +	return snd_pcm_lib_free_pages(ss);
> +}
> +
> +static bool filter(struct dma_chan *chan, void *slave)
> +{
> +	struct sh_dmae_slave *param = slave;
> +
> +	pr_debug("%s: slave ID %d\n", __func__, param->slave_id);
> +
> +	if (unlikely(param->dma_dev != chan->device->dev))
> +		return false;
> +
> +	chan->private = param;
> +	return true;
> +}
> +
> +static int siu_pcm_open(struct snd_pcm_substream *ss)
> +{
> +	/* Playback / Capture */
> +	struct siu_info *info = siu_i2s_dai.private_data;
> +	struct siu_port *port_info = siu_port_info(ss);
> +	struct siu_stream *siu_stream;
> +	u32 port = info->port_id;
> +	struct siu_platform *pdata = siu_i2s_dai.dev->platform_data;
> +	struct device *dev = ss->pcm->card->dev;
> +	dma_cap_mask_t mask;
> +	struct sh_dmae_slave *param;
> +
> +	dma_cap_zero(mask);
> +	dma_cap_set(DMA_SLAVE, mask);
> +
> +	dev_dbg(dev, "%s, port=%d@%p\n", __func__, port, port_info);
> +
> +	if (ss->stream == SNDRV_PCM_STREAM_PLAYBACK) {
> +		siu_stream = &port_info->playback;
> +		param = &siu_stream->param;
> +		param->slave_id = port ? SHDMA_SLAVE_SIUB_TX :
> +			SHDMA_SLAVE_SIUA_TX;
> +	} else {
> +		siu_stream = &port_info->capture;
> +		param = &siu_stream->param;
> +		param->slave_id = port ? SHDMA_SLAVE_SIUB_RX :
> +			SHDMA_SLAVE_SIUA_RX;
> +	}
> +
> +	param->dma_dev = pdata->dma_dev;
> +	/* Get DMA channel */
> +	siu_stream->chan = dma_request_channel(mask, filter, param);
> +	if (!siu_stream->chan) {
> +		dev_err(dev, "DMA channel allocation failed!\n");
> +		return -EBUSY;
> +	}
> +
> +	siu_stream->substream = ss;
> +
> +	return 0;
> +}
> +
> +static int siu_pcm_close(struct snd_pcm_substream *ss)
> +{
> +	struct siu_info *info = siu_i2s_dai.private_data;
> +	struct device *dev = ss->pcm->card->dev;
> +	struct siu_port *port_info = siu_port_info(ss);
> +	struct siu_stream *siu_stream;
> +
> +	dev_dbg(dev, "%s: port=%d\n", __func__, info->port_id);
> +
> +	if (ss->stream == SNDRV_PCM_STREAM_PLAYBACK)
> +		siu_stream = &port_info->playback;
> +	else
> +		siu_stream = &port_info->capture;
> +
> +	dma_release_channel(siu_stream->chan);
> +	siu_stream->chan = NULL;
> +
> +	siu_stream->substream = NULL;
> +
> +	return 0;
> +}
> +
> +static int siu_pcm_mono_alloc(struct device *dev, struct siu_stream *siu_stream)
> +{
> +	/*
> +	 * The hardware only supports stereo (2 channels) streams. We must
> +	 * convert mono streams (1 channel) to stereo streams. To do that we
> +	 * just copy the mono data to one of the stereo channels and instruct
> +	 * the siu to play the data on both channels. However, the idle
> +	 * channel must also be present in the buffer, so we use an extra
> +	 * buffer twice as big as one mono period. Also since this function
> +	 * can be called multiple times, we must adjust the buffer size.
> +	 */

Shouldn't this be done by userspace. i.e. alsa plugin or pulseaudio ?

> +	if (siu_stream->mono_buf && siu_stream->mono_size !=
> +	    siu_stream->period_bytes * 2) {
> +		dma_free_coherent(dev, siu_stream->mono_size,
> +				  siu_stream->mono_buf, siu_stream->mono_dma);
> +		siu_stream->mono_buf = NULL;
> +		siu_stream->mono_size = 0;
> +	}
> +
> +	if (!siu_stream->mono_buf) {
> +		siu_stream->mono_buf = dma_alloc_coherent(dev,
> +						siu_stream->period_bytes * 2,
> +						&siu_stream->mono_dma,
> +						GFP_KERNEL);
> +		if (!siu_stream->mono_buf)
> +			return -ENOMEM;
> +
> +		siu_stream->mono_size = siu_stream->period_bytes * 2;
> +	}
> +
> +	dev_dbg(dev, "%s: mono buffer @ %p\n", __func__, siu_stream->mono_buf);
> +
> +	return 0;
> +}
> +
> +static int siu_pcm_prepare(struct snd_pcm_substream *ss)
> +{
> +	struct siu_info *info = siu_i2s_dai.private_data;
> +	struct siu_port *port_info = siu_port_info(ss);
> +	struct device *dev = ss->pcm->card->dev;
> +	struct snd_pcm_runtime 	*rt = ss->runtime;
> +	struct siu_stream *siu_stream;
> +	snd_pcm_sframes_t xfer_cnt;
> +
> +	if (ss->stream == SNDRV_PCM_STREAM_PLAYBACK)
> +		siu_stream = &port_info->playback;
> +	else
> +		siu_stream = &port_info->capture;
> +
> +	rt = siu_stream->substream->runtime;
> +
> +	siu_stream->buf_bytes = snd_pcm_lib_buffer_bytes(ss);
> +	siu_stream->period_bytes = snd_pcm_lib_period_bytes(ss);
> +
> +	dev_dbg(dev, "%s: port=%d, %d channels, period=%u bytes\n", __func__,
> +		info->port_id, rt->channels, siu_stream->period_bytes);
> +
> +	/* We only support buffers that are multiples of the period */
> +	if (siu_stream->buf_bytes % siu_stream->period_bytes) {
> +		dev_err(dev, "%s() - buffer=%d not multiple of period=%d\n",
> +		       __func__, siu_stream->buf_bytes,
> +		       siu_stream->period_bytes);
> +		return -EINVAL;
> +	}
> +
> +	xfer_cnt = bytes_to_frames(rt, siu_stream->period_bytes);
> +	if (!xfer_cnt || xfer_cnt > 0x1000000)
> +		return -EINVAL;
> +
> +	if (rt->channels == 1) {
> +		int ret = siu_pcm_mono_alloc(ss->pcm->card->dev,
> +					     siu_stream);
> +		if (ret < 0)
> +			return ret;
> +	}
> +
> +	siu_stream->format = rt->format;
> +	siu_stream->xfer_cnt = xfer_cnt;
> +
> +	dev_dbg(dev, "port=%d buf=%lx buf_bytes=%d period_bytes=%d "
> +		"format=%d channels=%d xfer_cnt=%d\n", info->port_id,
> +		(unsigned long)rt->dma_addr, siu_stream->buf_bytes,
> +		siu_stream->period_bytes,
> +		siu_stream->format, rt->channels, (int)xfer_cnt);
> +
> +	return 0;
> +}
> +
> +static int siu_pcm_trigger(struct snd_pcm_substream *ss, int cmd)
> +{
> +	struct siu_info *info = siu_i2s_dai.private_data;
> +	struct device *dev = ss->pcm->card->dev;
> +	struct siu_port *port_info = siu_port_info(ss);
> +	int ret;
> +
> +	dev_dbg(dev, "%s: port=%d@%p, cmd=%d\n", __func__,
> +		info->port_id, port_info, cmd);
> +
> +	switch (cmd) {
> +	case SNDRV_PCM_TRIGGER_START:
> +		if (ss->stream == SNDRV_PCM_STREAM_PLAYBACK)
> +			ret = siu_pcm_stmwrite_start(port_info);
> +		else
> +			ret = siu_pcm_stmread_start(port_info);
> +
> +		if (ret < 0)
> +			dev_warn(dev, "%s: start failed on port=%d\n",
> +				 __func__, info->port_id);
> +
> +		break;
> +	case SNDRV_PCM_TRIGGER_STOP:
> +		if (ss->stream == SNDRV_PCM_STREAM_PLAYBACK)
> +			siu_pcm_stmwrite_stop(port_info);
> +		else
> +			siu_pcm_stmread_stop(port_info);
> +		ret = 0;
> +
> +		break;
> +	default:
> +		dev_err(dev, "%s() unsupported cmd=%d\n", __func__, cmd);
> +		ret = -EINVAL;
> +	}
> +
> +	return ret;
> +}
> +
> +/*
> + * So far only resolution of one period is supported, subject to extending the
> + * dmangine API
> + */
> +static snd_pcm_uframes_t siu_pcm_pointer_dma(struct snd_pcm_substream *ss)
> +{
> +	struct device *dev = ss->pcm->card->dev;
> +	struct siu_info *info = siu_i2s_dai.private_data;
> +	u32 __iomem *base = info->reg;
> +	struct siu_port *port_info = siu_port_info(ss);
> +	struct snd_pcm_runtime *rt = ss->runtime;
> +	size_t ptr;
> +	struct siu_stream *siu_stream;
> +
> +	if (ss->stream == SNDRV_PCM_STREAM_PLAYBACK)
> +		siu_stream = &port_info->playback;
> +	else
> +		siu_stream = &port_info->capture;
> +
> +	/*
> +	 * ptr is the offset into the buffer where the dma is currently at. We
> +	 * check if the dma buffer has just wrapped.
> +	 */
> +	ptr = PERIOD_OFFSET(rt->dma_addr,
> +			    siu_stream->cur_period,
> +			    siu_stream->period_bytes) - rt->dma_addr;
> +
> +	dev_dbg(dev,
> +		"%s: port=%d, events %x, FSTS %x, xferred %u/%u, cookie %d\n",
> +		__func__, info->port_id, siu_read32(base + EVNTC),
> +		siu_read32(base + SBFSTS), ptr, siu_stream->buf_bytes,
> +		siu_stream->cookie);
> +
> +	if (ptr >= siu_stream->buf_bytes)
> +		ptr = 0;
> +
> +	return bytes_to_frames(ss->runtime, ptr);
> +}
> +
> +static int siu_pcm_new(struct snd_card *card, struct snd_soc_dai *dai,
> +		       struct snd_pcm *pcm)
> +{
> +	/* card->dev == socdev->dev, see snd_soc_new_pcms() */
> +	struct siu_info *info = siu_i2s_dai.private_data;
> +	struct platform_device *pdev = to_platform_device(card->dev);
> +	int ret;
> +	int i;
> +
> +	/* pdev->id selects between SIUA and SIUB */
> +	if (pdev->id < 0 || pdev->id >= MAX_SIU_PORTS)
> +		return -EINVAL;
> +
> +	info->port_id = pdev->id;
> +
> +	/*
> +	 * While the siu has 2 ports, only one port can be on at a time (only 1
> +	 * SPB). So far all the boards using the siu had only one of the ports
> +	 * wired to a codec. To simplify things, we only register one port with
> +	 * alsa. In case both ports are needed, it should be changed here
> +	 */
> +	for (i = pdev->id; i < pdev->id + 1; i++) {
> +		struct siu_port **port_info = &siu_ports[i];
> +
> +		ret = siu_init_port(i, port_info, card);
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = snd_pcm_lib_preallocate_pages_for_all(pcm,
> +					SNDRV_DMA_TYPE_DEV, NULL,
> +					BUFFER_BYTES_MAX, BUFFER_BYTES_MAX);
> +		if (ret < 0) {
> +			dev_err(card->dev,
> +			       "snd_pcm_lib_preallocate_pages_for_all() err=%d",
> +				ret);
> +			goto fail;
> +		}
> +
> +		/* IO tasklets */
> +		tasklet_init(&(*port_info)->playback.tasklet, siu_io_tasklet,
> +			     (unsigned long)&(*port_info)->playback);
> +		tasklet_init(&(*port_info)->capture.tasklet, siu_io_tasklet,
> +			     (unsigned long)&(*port_info)->capture);
> +	}
> +
> +	dev_info(card->dev, "SuperH SIU driver initialized.\n");
> +	return 0;
> +
> +fail:
> +	siu_free_port(siu_ports[pdev->id]);
> +	dev_err(card->dev, "SIU: failed to initialize.\n");
> +	return ret;
> +}
> +
> +static void siu_pcm_free(struct snd_pcm *pcm)
> +{
> +	struct platform_device *pdev = to_platform_device(pcm->card->dev);
> +	struct siu_port *port_info = siu_ports[pdev->id];
> +
> +	tasklet_kill(&port_info->capture.tasklet);
> +	tasklet_kill(&port_info->playback.tasklet);
> +
> +	siu_free_port(port_info);
> +	snd_pcm_lib_preallocate_free_for_all(pcm);
> +
> +	dev_dbg(pcm->card->dev, "%s\n", __func__);
> +}
> +
> +static struct snd_pcm_ops siu_pcm_ops = {
> +	.open		= siu_pcm_open,
> +	.close		= siu_pcm_close,
> +	.ioctl		= snd_pcm_lib_ioctl,
> +	.hw_params	= siu_pcm_hw_params,
> +	.hw_free	= siu_pcm_hw_free,
> +	.prepare	= siu_pcm_prepare,
> +	.trigger	= siu_pcm_trigger,
> +	.pointer	= siu_pcm_pointer_dma,
> +};
> +
> +struct snd_soc_platform siu_platform = {
> +	.name		= "siu-audio",
> +	.pcm_ops 	= &siu_pcm_ops,
> +	.pcm_new	= siu_pcm_new,
> +	.pcm_free	= siu_pcm_free,
> +};
> +EXPORT_SYMBOL_GPL(siu_platform);



WARNING: multiple messages have this Message-ID (diff)
From: Liam Girdwood <lrg@slimlogic.co.uk>
To: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
Cc: alsa-devel@alsa-project.org,
	Kuninori Morimoto <morimoto.kuninori@renesas.com>,
	Magnus Damm <damm@opensource.se>,
	Mark Brown <broonie@opensource.wolfsonmicro.com>,
	linux-sh@vger.kernel.org
Subject: Re: [alsa-devel] [PATCH 2/4] ASoC: add DAI and platform drivers
Date: Tue, 19 Jan 2010 11:13:16 +0000	[thread overview]
Message-ID: <1263899596.3089.81.camel@odin> (raw)
In-Reply-To: <Pine.LNX.4.64.1001190902000.4607@axis700.grange>

On Tue, 2010-01-19 at 09:09 +0100, Guennadi Liakhovetski wrote:
> Several SuperH platforms, including sh7722, sh7343, sh7354, sh7367 include a
> Sound Interface Unit (SIU). This patch adds drivers for this interface and
> support for the sh7722 Migo-R board.
> 

Had a quick look wrt the ALSA parts. Comments below.

> Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
> ---
> 
> As mentioned in the introduction mail, this driver requires firmware to be 
> loaded from user-space using the standard hotplug functionality.
> 
>  arch/sh/include/asm/siu.h |   26 ++
>  sound/soc/sh/Kconfig      |   16 +
>  sound/soc/sh/Makefile     |    4 +
>  sound/soc/sh/migor.c      |  261 ++++++++++++++
>  sound/soc/sh/siu.h        |  217 ++++++++++++
>  sound/soc/sh/siu_dai.c    |  833 +++++++++++++++++++++++++++++++++++++++++++++
>  sound/soc/sh/siu_pcm.c    |  716 ++++++++++++++++++++++++++++++++++++++
>  7 files changed, 2073 insertions(+), 0 deletions(-)
>  create mode 100644 arch/sh/include/asm/siu.h
>  create mode 100644 sound/soc/sh/migor.c
>  create mode 100644 sound/soc/sh/siu.h
>  create mode 100644 sound/soc/sh/siu_dai.c
>  create mode 100644 sound/soc/sh/siu_pcm.c
> 
> diff --git a/arch/sh/include/asm/siu.h b/arch/sh/include/asm/siu.h
> new file mode 100644
> index 0000000..57565a3
> --- /dev/null
> +++ b/arch/sh/include/asm/siu.h
> @@ -0,0 +1,26 @@
> +/*
> + * platform header for the SIU ASoC driver
> + *
> + * Copyright (C) 2009-2010 Guennadi Liakhovetski <g.liakhovetski@gmx.de>
> + *
> + * 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 ASM_SIU_H
> +#define ASM_SIU_H
> +
> +#include <asm/dma-sh.h>
> +
> +struct device;
> +
> +struct siu_platform {
> +	struct device *dma_dev;
> +	enum sh_dmae_slave_chan_id dma_slave_tx_a;
> +	enum sh_dmae_slave_chan_id dma_slave_rx_a;
> +	enum sh_dmae_slave_chan_id dma_slave_tx_b;
> +	enum sh_dmae_slave_chan_id dma_slave_rx_b;
> +};
> +
> +#endif /* ASM_SIU_H */
> diff --git a/sound/soc/sh/Kconfig b/sound/soc/sh/Kconfig
> index 8072a6d..eec6fe5 100644
> --- a/sound/soc/sh/Kconfig
> +++ b/sound/soc/sh/Kconfig
> @@ -26,6 +26,14 @@ config SND_SOC_SH4_FSI
>  	help
>  	  This option enables FSI sound support
>  
> +config SND_SOC_SH4_SIU
> +	tristate "SH4 SIU support"
> +	depends on CPU_SUBTYPE_SH7722
> +	select DMADEVICES
> +	select SH_DMAE
> +	help
> +	  This option enables SIU sound support
> +
>  ##
>  ## Boards
>  ##
> @@ -55,4 +63,12 @@ config SND_FSI_DA7210
>  	  This option enables generic sound support for the
>  	  FSI - DA7210 unit
>  
> +config SND_SIU_MIGOR
> +	tristate "SIU sound support on Migo-R"
> +	depends on SND_SOC_SH4_SIU && SH_MIGOR
> +	select SND_SOC_WM8978
> +	help
> +	  This option enables generic sound support for the
> +	  SH7722 Migo-R board
> +
>  endmenu
> diff --git a/sound/soc/sh/Makefile b/sound/soc/sh/Makefile
> index 1d0ec0a..8a5a192 100644
> --- a/sound/soc/sh/Makefile
> +++ b/sound/soc/sh/Makefile
> @@ -6,15 +6,19 @@ obj-$(CONFIG_SND_SOC_PCM_SH7760)	+= snd-soc-dma-sh7760.o
>  snd-soc-hac-objs	:= hac.o
>  snd-soc-ssi-objs	:= ssi.o
>  snd-soc-fsi-objs	:= fsi.o
> +snd-soc-siu-objs	:= siu_pcm.o siu_dai.o
>  obj-$(CONFIG_SND_SOC_SH4_HAC)	+= snd-soc-hac.o
>  obj-$(CONFIG_SND_SOC_SH4_SSI)	+= snd-soc-ssi.o
>  obj-$(CONFIG_SND_SOC_SH4_FSI)	+= snd-soc-fsi.o
> +obj-$(CONFIG_SND_SOC_SH4_SIU)	+= snd-soc-siu.o
>  
>  ## boards
>  snd-soc-sh7760-ac97-objs	:= sh7760-ac97.o
>  snd-soc-fsi-ak4642-objs		:= fsi-ak4642.o
>  snd-soc-fsi-da7210-objs		:= fsi-da7210.o
> +snd-soc-migor-objs		:= migor.o
>  
>  obj-$(CONFIG_SND_SH7760_AC97)	+= snd-soc-sh7760-ac97.o
>  obj-$(CONFIG_SND_FSI_AK4642)	+= snd-soc-fsi-ak4642.o
>  obj-$(CONFIG_SND_FSI_DA7210)	+= snd-soc-fsi-da7210.o
> +obj-$(CONFIG_SND_SIU_MIGOR)	+= snd-soc-migor.o
> diff --git a/sound/soc/sh/migor.c b/sound/soc/sh/migor.c
> new file mode 100644
> index 0000000..507e59e
> --- /dev/null
> +++ b/sound/soc/sh/migor.c
> @@ -0,0 +1,261 @@
> +/*
> + * ALSA SoC driver for Migo-R
> + *
> + * Copyright (C) 2009-2010 Guennadi Liakhovetski <g.liakhovetski@gmx.de>
> + *
> + * 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/device.h>
> +#include <linux/firmware.h>
> +#include <linux/module.h>
> +
> +#include <asm/clock.h>
> +
> +#include <cpu/sh7722.h>
> +
> +#include <sound/core.h>
> +#include <sound/pcm.h>
> +#include <sound/soc.h>
> +#include <sound/soc-dapm.h>
> +
> +#include "../codecs/wm8978.h"
> +#include "siu.h"
> +
> +/* Default 8000Hz sampling frequency */
> +static unsigned long codec_freq = 49152350 / 12;
> +
> +static const int mclk_numerator[]	= {1, 3, 2, 3, 4, 6, 8, 12};
> +static const int mclk_denominator[]	= {1, 2, 1, 1, 1, 1, 1, 1};
> +
> +/* External clock, sourced from the codec at the SIUMCKB pin */
> +static unsigned long siumckb_recalc(struct clk *clk)
> +{
> +	return codec_freq;
> +}
> +
> +static struct clk_ops siumckb_clk_ops = {
> +	.recalc = siumckb_recalc,
> +};
> +
> +static struct clk siumckb_clk = {
> +	.name		= "siumckb_clk",
> +	.id		= -1,
> +	.ops		= &siumckb_clk_ops,
> +	.rate		= 0, /* initialised at run-time */
> +};
> +
> +static int migor_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;
> +	unsigned int mclk_div, opclk_div, f2;
> +	int ret, mclk_idx;
> +	unsigned int rate = params_rate(params);
> +
> +	switch (rate) {
> +	case 48000:
> +		mclk_div = 0x40;
> +		opclk_div = 0;
> +		/* f2 = 98304000, was 98304050 */

What does the "was" value represent here ?

> +		break;
> +	case 44100:
> +		mclk_div = 0x40;
> +		opclk_div = 0;
> +		/* f2 = 90316800, was 90317500 */
> +		break;
> +	case 32000:
> +		mclk_div = 0x80;
> +		opclk_div = 0x010;
> +		/* f2 = 131072000, was 131072500 */
> +		break;
> +	case 24000:
> +		mclk_div = 0x80;
> +		opclk_div = 0x010;
> +		/* f2 = 98304000, was 98304700 */
> +		break;
> +	case 22050:
> +		mclk_div = 0x80;
> +		opclk_div = 0x010;
> +		/* f2 = 90316800, was 90317500 */
> +		break;
> +	case 16000:
> +		mclk_div = 0xa0;
> +		opclk_div = 0x020;
> +		/* f2 = 98304000, was 98304700 */
> +		break;
> +	case 11025:
> +		mclk_div = 0x80;
> +		opclk_div = 0x010;
> +		/* f2 = 45158400, was 45158752 */
> +		break;
> +	default:
> +	case 8000:
> +		mclk_div = 0xa0;
> +		opclk_div = 0x020;
> +		/* f2 = 49152000, was 49152350 */
> +		break;
> +	}
> +
> +	mclk_idx = mclk_div >> 5;
> +	/*
> +	 * Calculate f2, according to Figure 40 "PLL and Clock Select Circuit"
> +	 * in WM8978 datasheet
> +	 */
> +	f2 = rate * 256 * 4 * mclk_numerator[mclk_idx] /
> +		mclk_denominator[mclk_idx];
> +
> +	ret = snd_soc_dai_set_clkdiv(codec_dai, WM8978_MCLKDIV,
> +				     mclk_div & 0xe0);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = snd_soc_dai_set_clkdiv(codec_dai, WM8978_OPCLKDIV, opclk_div);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = snd_soc_dai_set_clkdiv(codec_dai, WM8978_DACCLK, 8);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = snd_soc_dai_set_pll(codec_dai, 0, 0, 13000000, f2);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_NB_IF |
> +				  SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = snd_soc_dai_set_fmt(rtd->dai->cpu_dai, SND_SOC_DAIFMT_NB_IF |
> +				  SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS);
> +	if (ret < 0)
> +		return ret;
> +
> +	/* See Figure 40 */
> +	codec_freq = f2 / ((opclk_div >> 4) + 1) >> 2;
> +	/*
> +	 * This propagates the parent frequency change to children and
> +	 * recalculates the frequency table
> +	 */
> +	clk_set_rate(&siumckb_clk, codec_freq);
> +	dev_dbg(codec_dai->dev, "%s: configure %luHz\n", __func__, codec_freq);
> +
> +	snd_soc_dai_set_sysclk(rtd->dai->cpu_dai, CLKB_EXT, codec_freq / 2,
> +			       SND_SOC_CLOCK_IN);
> +
> +	return ret;
> +}
> +
> +static int migor_hw_free(struct snd_pcm_substream *substream)
> +{
> +	struct snd_soc_pcm_runtime *rtd = substream->private_data;
> +	struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
> +
> +	/* disable the PLL */
> +	return snd_soc_dai_set_pll(codec_dai, 0, 0, 0, 0);
> +}
> +
> +static int migor_startup(struct snd_pcm_substream *substream)
> +{
> +	struct snd_soc_pcm_runtime *rtd = substream->private_data;
> +	struct snd_soc_device *socdev = rtd->socdev;
> +	struct snd_soc_codec *codec = socdev->card->codec;
> +	int ret;
> +
> +	/* Activate DAC output routes */
> +	ret = snd_soc_dapm_enable_pin(codec, "Left Speaker Out");
> +	if (ret < 0) {
> +		dev_warn(socdev->dev, "Left err %d\n", ret);
> +		return ret;
> +	}
> +
> +	ret = snd_soc_dapm_enable_pin(codec, "Right Speaker Out");
> +	if (ret < 0) {
> +		dev_warn(socdev->dev, "Right err %d\n", ret);
> +		return ret;
> +	}
> +
> +	snd_soc_dapm_sync(codec);
> +
> +	return 0;
> +}
> +
> +static struct snd_soc_ops migor_dai_ops = {
> +	.hw_params = migor_hw_params,
> +	.hw_free = migor_hw_free,
> +	.startup = migor_startup,
> +};
> +
> +/* migor digital audio interface glue - connects codec <--> CPU */
> +static struct snd_soc_dai_link migor_dai = {
> +	.name = "wm8978",
> +	.stream_name = "WM8978",
> +	.cpu_dai = &siu_i2s_dai,
> +	.codec_dai = &wm8978_dai,
> +	.ops = &migor_dai_ops,
> +};
> +
> +/* migor audio machine driver */
> +static struct snd_soc_card snd_soc_migor = {
> +	.name = "Migo-R",
> +	.platform = &siu_platform,
> +	.dai_link = &migor_dai,
> +	.num_links = 1,
> +};
> +
> +/* migor audio subsystem */
> +static struct snd_soc_device migor_snd_devdata = {
> +	.card = &snd_soc_migor,
> +	.codec_dev = &soc_codec_dev_wm8978,
> +};
> +
> +static struct platform_device *migor_snd_device;
> +
> +static int __init migor_init(void)
> +{
> +	int ret;
> +
> +	ret = clk_register(&siumckb_clk);
> +	if (ret < 0)
> +		return ret;
> +
> +	/* Port number used on this machine: port B */
> +	migor_snd_device = platform_device_alloc("soc-audio", 1);
> +	if (!migor_snd_device) {
> +		ret = -ENOMEM;
> +		goto epdevalloc;
> +	}
> +
> +	platform_set_drvdata(migor_snd_device, &migor_snd_devdata);
> +
> +	migor_snd_devdata.dev = &migor_snd_device->dev;
> +
> +	ret = platform_device_add(migor_snd_device);
> +	if (ret)
> +		goto epdevadd;
> +
> +	return 0;
> +
> +epdevadd:
> +	platform_device_put(migor_snd_device);
> +epdevalloc:
> +	clk_unregister(&siumckb_clk);
> +	return ret;
> +}
> +
> +static void __exit migor_exit(void)
> +{
> +	clk_unregister(&siumckb_clk);
> +	platform_device_unregister(migor_snd_device);
> +}
> +
> +module_init(migor_init);
> +module_exit(migor_exit);
> +
> +MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>");
> +MODULE_DESCRIPTION("ALSA SoC Migor");
> +MODULE_LICENSE("GPL v2");
> diff --git a/sound/soc/sh/siu.h b/sound/soc/sh/siu.h
> new file mode 100644
> index 0000000..e7cba83
> --- /dev/null
> +++ b/sound/soc/sh/siu.h
> @@ -0,0 +1,217 @@
> +/*
> + * siu.h - ALSA SoC driver for Renesas SH7343, SH7722 SIU peripheral.
> + *
> + * Copyright (C) 2009-2010 Guennadi Liakhovetski <g.liakhovetski@gmx.de>
> + * Copyright (C) 2006 Carlos Munoz <carlos@kenati.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.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
> + */
> +
> +#ifndef SIU_H
> +#define SIU_H
> +
> +/* Common kernel and user-space firmware-building defines and types */
> +
> +#define YRAM0_SIZE		(0x0040 / 4)		/* 16 */
> +#define YRAM1_SIZE		(0x0080 / 4)		/* 32 */
> +#define YRAM2_SIZE		(0x0040 / 4)		/* 16 */
> +#define YRAM3_SIZE		(0x0080 / 4)		/* 32 */
> +#define YRAM4_SIZE		(0x0080 / 4)		/* 32 */
> +#define YRAM_DEF_SIZE		(YRAM0_SIZE + YRAM1_SIZE + YRAM2_SIZE + \
> +				 YRAM3_SIZE + YRAM4_SIZE)
> +#define YRAM_FIR_SIZE		(0x0400 / 4)		/* 256 */
> +#define YRAM_IIR_SIZE		(0x0200 / 4)		/* 128 */
> +
> +#define XRAM0_SIZE		(0x0400 / 4)		/* 256 */
> +#define XRAM1_SIZE		(0x0200 / 4)		/* 128 */
> +#define XRAM2_SIZE		(0x0200 / 4)		/* 128 */
> +
> +/* PRAM program array size */
> +#define PRAM0_SIZE		(0x0100 / 4)		/* 64 */
> +#define PRAM1_SIZE		((0x2000 - 0x0100) / 4)	/* 1984 */
> +
> +#include <linux/types.h>
> +
> +struct siu_spb_param {
> +	__u32	ab1a;	/* input FIFO address */
> +	__u32	ab0a;	/* output FIFO address */
> +	__u32	dir;	/* 0=the ather except CPUOUTPUT, 1=CPUINPUT */
> +	__u32	event;	/* SPB program starting conditions */
> +	__u32	stfifo;	/* STFIFO register setting value */
> +	__u32	trdat;	/* TRDAT register setting value */
> +};
> +
> +struct siu_firmware {
> +	__u32			yram_fir_coeff[YRAM_FIR_SIZE];
> +	__u32			pram0[PRAM0_SIZE];
> +	__u32			pram1[PRAM1_SIZE];
> +	__u32			yram0[YRAM0_SIZE];
> +	__u32			yram1[YRAM1_SIZE];
> +	__u32			yram2[YRAM2_SIZE];
> +	__u32			yram3[YRAM3_SIZE];
> +	__u32			yram4[YRAM4_SIZE];
> +	__u32			spbpar_num;
> +	struct siu_spb_param	spbpar[32];
> +};
> +
> +#ifdef __KERNEL__
> +
> +#include <linux/dmaengine.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +
> +#include <asm/dma-sh.h>
> +
> +#include <sound/core.h>
> +#include <sound/pcm.h>
> +#include <sound/soc-dai.h>
> +
> +#define SIU_PORTA		0		/* port A */
> +#define SIU_PORTB		1		/* port B */
> +#define MAX_SIU_PORTS		2
> +
> +/* SIU clock configuration */
> +enum {CLKA_PLL, CLKA_EXT, CLKB_PLL, CLKB_EXT};
> +
> +/* Board specifics */
> +#if defined(CONFIG_CPU_SUBTYPE_SH7722)
> +# define MAX_VOLUME		0x1000
> +#else
> +# define MAX_VOLUME		0x7fff
> +#endif
> +
> +struct siu_info {
> +	int			port_id;
> +	u32 __iomem		*pram;
> +	u32 __iomem		*xram;
> +	u32 __iomem		*yram;
> +	u32 __iomem		*reg;
> +	struct siu_firmware	fw;
> +};
> +
> +#define PRAM_SIZE	0x2000
> +#define XRAM_SIZE	0x800
> +#define YRAM_SIZE	0x800
> +
> +#define XRAM_OFFSET	0x4000
> +#define YRAM_OFFSET	0x6000
> +#define REG_OFFSET	0xc000
> +
> +struct siu_stream {
> +	struct tasklet_struct		tasklet;
> +	struct snd_pcm_substream	*substream;
> +	snd_pcm_format_t		format;
> +	size_t				buf_bytes;
> +	size_t				period_bytes;
> +	int				cur_period;	/* Period currently in dma */
> +	u32				volume;
> +	void				*mono_buf;	/* Mono buffer */
> +	size_t				mono_size;	/* and its size in bytes */
> +	snd_pcm_sframes_t		xfer_cnt;	/* Number of frames */
> +	u8				rw_flg;		/* transfer status */
> +	/* DMA status */
> +	dma_addr_t			mono_dma;
> +	struct dma_chan			*chan;		/* DMA channel */
> +	struct dma_async_tx_descriptor	*tx_desc;
> +	dma_cookie_t			cookie;
> +	struct sh_dmae_slave		param;
> +};
> +
> +struct siu_port {
> +	unsigned long		play_cap;	/* Used to track full duplex */
> +	struct snd_pcm		*pcm;
> +	struct siu_stream	playback;
> +	struct siu_stream	capture;
> +	u32			stfifo;		/* STFIFO value from firmware */
> +	u32			trdat;		/* TRDAT value from firmware */
> +};
> +
> +extern struct siu_port *siu_ports[MAX_SIU_PORTS];
> +
> +static inline struct siu_port *siu_port_info(struct snd_pcm_substream *substream)
> +{
> +	struct platform_device *pdev > +		to_platform_device(substream->pcm->card->dev);
> +	return siu_ports[pdev->id];
> +}
> +
> +#define PLAYBACK_ENABLED	1
> +#define CAPTURE_ENABLED		2
> +
> +#define VOLUME_CAPTURE		0
> +#define VOLUME_PLAYBACK		1
> +#define DFLT_VOLUME_LEVEL	0x08000800
> +
> +#define PERIOD_BYTES_MAX	8192		/* DMA transfer/period size */
> +#define PERIOD_BYTES_MIN	256		/* DMA transfer/period size */
> +#define PERIODS_MAX		64		/* Max periods in buffer */
> +#define PERIODS_MIN		4		/* Min periods in buffer */
> +#define BUFFER_BYTES_MAX	(PERIOD_BYTES_MAX * PERIODS_MAX)
> +#define GET_MAX_PERIODS(buf_bytes, period_bytes) \
> +				((buf_bytes) / (period_bytes))
> +#define PERIOD_OFFSET(buf_addr, period_num, period_bytes) \
> +				((buf_addr) + ((period_num) * (period_bytes)))
> +
> +#define RWF_STM_RD		0x01		/* Read in progress */
> +#define RWF_STM_WT		0x02		/* Write in progress */
> +
> +/* Register access */
> +static inline void siu_write32(u32 __iomem *addr, u32 val)
> +{
> +	__raw_writel(val, addr);
> +}
> +
> +static inline u32 siu_read32(u32 __iomem *addr)
> +{
> +	return __raw_readl(addr);
> +}
> +
> +/* SIU registers */
> +#define IFCTL		(0x000 / sizeof(u32))
> +#define SRCTL		(0x004 / sizeof(u32))
> +#define SFORM		(0x008 / sizeof(u32))
> +#define CKCTL		(0x00c / sizeof(u32))
> +#define TRDAT		(0x010 / sizeof(u32))
> +#define STFIFO		(0x014 / sizeof(u32))
> +#define DPAK		(0x01c / sizeof(u32))
> +#define CKREV		(0x020 / sizeof(u32))
> +#define EVNTC		(0x028 / sizeof(u32))
> +#define SBCTL		(0x040 / sizeof(u32))
> +#define SBPSET		(0x044 / sizeof(u32))
> +#define SBFSTS		(0x068 / sizeof(u32))
> +#define SBDVCA		(0x06c / sizeof(u32))
> +#define SBDVCB		(0x070 / sizeof(u32))
> +#define SBACTIV		(0x074 / sizeof(u32))
> +#define DMAIA		(0x090 / sizeof(u32))
> +#define DMAIB		(0x094 / sizeof(u32))
> +#define DMAOA		(0x098 / sizeof(u32))
> +#define DMAOB		(0x09c / sizeof(u32))
> +#define DMAML		(0x0a0 / sizeof(u32))
> +#define SPSTS		(0x0cc / sizeof(u32))
> +#define SPCTL		(0x0d0 / sizeof(u32))
> +#define BRGASEL		(0x100 / sizeof(u32))
> +#define BRRA		(0x104 / sizeof(u32))
> +#define BRGBSEL		(0x108 / sizeof(u32))
> +#define BRRB		(0x10c / sizeof(u32))
> +
> +extern struct snd_soc_platform siu_platform;
> +extern struct snd_soc_dai siu_i2s_dai;
> +
> +int siu_init_port(int port, struct siu_port **port_info, struct snd_card *card);
> +void siu_free_port(struct siu_port *port_info);
> +
> +#endif
> +
> +#endif /* SIU_H */
> diff --git a/sound/soc/sh/siu_dai.c b/sound/soc/sh/siu_dai.c
> new file mode 100644
> index 0000000..e5dbedb
> --- /dev/null
> +++ b/sound/soc/sh/siu_dai.c
> @@ -0,0 +1,833 @@
> +/*
> + * siu_dai.c - ALSA SoC driver for Renesas SH7343, SH7722 SIU peripheral.
> + *
> + * Copyright (C) 2009-2010 Guennadi Liakhovetski <g.liakhovetski@gmx.de>
> + * Copyright (C) 2006 Carlos Munoz <carlos@kenati.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.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/firmware.h>
> +#include <linux/pm_runtime.h>
> +
> +#include <asm/clock.h>
> +#include <asm/siu.h>
> +
> +#include <sound/control.h>
> +#include <sound/soc-dai.h>
> +
> +#include "siu.h"
> +
> +/*
> + * SPDIF is only available on port A and on some SIU implementations it is only
> + * available for input. Due to the lack of hardware to test it, SPDIF is left
> + * disabled in this driver version
> + */
> +struct format_flag {
> +	u32	i2s;
> +	u32	pcm;
> +	u32	spdif;
> +	u32	mask;
> +};
> +
> +struct port_flag {
> +	struct format_flag	playback;
> +	struct format_flag	capture;
> +};
> +
> +static struct port_flag siu_flags[MAX_SIU_PORTS] = {
> +	[SIU_PORTA] = {
> +		.playback = {
> +			.i2s	= 0x50000000,
> +			.pcm	= 0x40000000,
> +			.spdif	= 0x80000000,	/* not on all SIU versions */
> +			.mask	= 0xd0000000,
> +		},
> +		.capture = {
> +			.i2s	= 0x05000000,
> +			.pcm	= 0x04000000,
> +			.spdif	= 0x08000000,
> +			.mask	= 0x0d000000,
> +		},
> +	},
> +	[SIU_PORTB] = {
> +		.playback = {
> +			.i2s	= 0x00500000,
> +			.pcm	= 0x00400000,
> +			.spdif	= 0,		/* impossible - turn off */
> +			.mask	= 0x00500000,
> +		},
> +		.capture = {
> +			.i2s	= 0x00050000,
> +			.pcm	= 0x00040000,
> +			.spdif	= 0,		/* impossible - turn off */
> +			.mask	= 0x00050000,
> +		},
> +	},
> +};
> +
> +static void siu_dai_start(struct siu_port *port_info)
> +{
> +	struct siu_info *info = siu_i2s_dai.private_data;
> +	u32 __iomem *base = info->reg;
> +
> +	dev_dbg(port_info->pcm->card->dev, "%s\n", __func__);
> +
> +	/* Turn on SIU clock */
> +	pm_runtime_get_sync(siu_i2s_dai.dev);
> +
> +	/* Issue software reset to siu */
> +	siu_write32(base + SRCTL, 0);
> +
> +	/* Wait for the reset to take effect */
> +	udelay(1);
> +
> +	port_info->stfifo = 0;
> +	port_info->trdat = 0;
> +
> +	/* portA, portB, SIU operate */
> +	siu_write32(base + SRCTL, 0x301);
> +
> +	/* portA%6fs, portB%6fs */
> +	siu_write32(base + CKCTL, 0x40400000);
> +
> +	/* portA's BRG does not divide SIUCKA */
> +	siu_write32(base + BRGASEL, 0);
> +	siu_write32(base + BRRA, 0);
> +
> +	/* portB's BRG divides SIUCKB by half */
> +	siu_write32(base + BRGBSEL, 1);
> +	siu_write32(base + BRRB, 0);
> +
> +	siu_write32(base + IFCTL, 0x44440000);
> +
> +	/* portA: 32 bit/fs, master; portB: 32 bit/fs, master */
> +	siu_write32(base + SFORM, 0x0c0c0000);
> +
> +	/*
> +	 * Volume levels: looks like the DSP firmware implements volume controls
> +	 * differently from what's described in the datasheet
> +	 */
> +	siu_write32(base + SBDVCA, port_info->playback.volume);
> +	siu_write32(base + SBDVCB, port_info->capture.volume);
> +}
> +
> +static void siu_dai_stop(void)
> +{
> +	struct siu_info *info = siu_i2s_dai.private_data;
> +	u32 __iomem *base = info->reg;
> +
> +	/* SIU software reset */
> +	siu_write32(base + SRCTL, 0);
> +
> +	/* Turn off SIU clock */
> +	pm_runtime_put_sync(siu_i2s_dai.dev);
> +}
> +
> +static void siu_dai_spbAselect(struct siu_port *port_info)
> +{
> +	struct siu_info *info = siu_i2s_dai.private_data;
> +	struct siu_firmware *fw = &info->fw;
> +	u32 *ydef = fw->yram0;
> +	u32 idx;
> +
> +	/* path A use */
> +	if (!info->port_id)
> +		idx = 1;		/* portA */
> +	else
> +		idx = 2;		/* portB */
> +
> +	ydef[0] = (fw->spbpar[idx].ab1a << 16) |
> +		(fw->spbpar[idx].ab0a << 8) |
> +		(fw->spbpar[idx].dir << 7) | 3;
> +	ydef[1] = fw->yram0[1];	/* 0x03000300 */
> +	ydef[2] = (16 / 2) << 24;
> +	ydef[3] = fw->yram0[3];	/* 0 */
> +	ydef[4] = fw->yram0[4];	/* 0 */
> +	ydef[7] = fw->spbpar[idx].event;
> +	port_info->stfifo |= fw->spbpar[idx].stfifo;
> +	port_info->trdat |= fw->spbpar[idx].trdat;
> +}
> +
> +static void siu_dai_spbBselect(struct siu_port *port_info)
> +{
> +	struct siu_info *info = siu_i2s_dai.private_data;
> +	struct siu_firmware *fw = &info->fw;
> +	u32 *ydef = fw->yram0;
> +	u32 idx;
> +
> +	/* path B use */
> +	if (!info->port_id)
> +		idx = 7;		/* portA */
> +	else
> +		idx = 8;		/* portB */
> +
> +	ydef[5] = (fw->spbpar[idx].ab1a << 16) |
> +		(fw->spbpar[idx].ab0a << 8) | 1;
> +	ydef[6] = fw->spbpar[idx].event;
> +	port_info->stfifo |= fw->spbpar[idx].stfifo;
> +	port_info->trdat |= fw->spbpar[idx].trdat;
> +}
> +
> +static void siu_dai_open(struct siu_stream *siu_stream)
> +{
> +	struct siu_info *info = siu_i2s_dai.private_data;
> +	u32 __iomem *base = info->reg;
> +	struct snd_pcm_substream *substream = siu_stream->substream;
> +	struct snd_pcm_runtime *rt = substream->runtime;
> +	u32 srctl, ifctl;
> +
> +	srctl = siu_read32(base + SRCTL);
> +	ifctl = siu_read32(base + IFCTL);
> +
> +	switch (info->port_id) {
> +	case SIU_PORTA:
> +		/* portA operates */
> +		srctl |= 0x200;
> +		ifctl &= ~0xc2;
> +		/* Mono mode is not used, instead, stereo is simulated */
> +		if (rt->channels = 1)
> +			ifctl |= 0x80;
> +		break;
> +	case SIU_PORTB:
> +		/* portB operates */
> +		srctl |= 0x100;
> +		ifctl &= ~0x31;
> +		/* Mono mode is not used, instead, stereo is simulated */
> +		if (rt->channels = 1)
> +			ifctl |= 0x20;
> +		break;
> +	}
> +
> +	siu_write32(base + SRCTL, srctl);
> +	/* Unmute and configure portA */
> +	siu_write32(base + IFCTL, ifctl);
> +}
> +
> +/*
> + * At the moment only fixed Left-upper, Left-lower, Right-upper, Right-lower
> + * packing is supported
> + */
> +static void siu_dai_pcmdatapack(struct siu_stream *siu_stream)
> +{
> +	struct siu_info *info = siu_i2s_dai.private_data;
> +	u32 __iomem *base = info->reg;
> +	u32 dpak;
> +
> +	dpak = siu_read32(base + DPAK);
> +
> +	switch (info->port_id) {
> +	case SIU_PORTA:
> +		dpak &= ~0xc0000000;
> +		break;
> +	case SIU_PORTB:
> +		dpak &= ~0x00c00000;
> +		break;
> +	}
> +
> +	siu_write32(base + DPAK, dpak);
> +}
> +
> +static int siu_dai_spbstart(struct siu_port *port_info)
> +{
> +	struct siu_info *info = siu_i2s_dai.private_data;
> +	u32 __iomem *base = info->reg;
> +	struct siu_firmware *fw = &info->fw;
> +	u32 *ydef = fw->yram0;
> +	int cnt;
> +	u32 __iomem *add;
> +	u32 *ptr;
> +
> +	/* Load SPB Program in PRAM */
> +	ptr = fw->pram0;
> +	add = info->pram;
> +	for (cnt = 0; cnt < PRAM0_SIZE; cnt++, add++, ptr++)
> +		siu_write32(add, *ptr);
> +
> +	ptr = fw->pram1;
> +	add = info->pram + (0x0100 / sizeof(u32));
> +	for (cnt = 0; cnt < PRAM1_SIZE; cnt++, add++, ptr++)
> +		siu_write32(add, *ptr);
> +
> +	/* XRAM initialization */
> +	add = info->xram;
> +	for (cnt = 0; cnt < XRAM0_SIZE + XRAM1_SIZE + XRAM2_SIZE; cnt++, add++)
> +		siu_write32(add, 0);
> +
> +	/* YRAM variable area initialization */
> +	add = info->yram;
> +	for (cnt = 0; cnt < YRAM_DEF_SIZE; cnt++, add++)
> +		siu_write32(add, ydef[cnt]);
> +
> +	/* YRAM FIR coefficient area initialization */
> +	add = info->yram + (0x0200 / sizeof(u32));
> +	for (cnt = 0; cnt < YRAM_FIR_SIZE; cnt++, add++)
> +		siu_write32(add, fw->yram_fir_coeff[cnt]);
> +
> +	/* YRAM IIR coefficient area initialization */
> +	add = info->yram + (0x0600 / sizeof(u32));
> +	for (cnt = 0; cnt < YRAM_IIR_SIZE; cnt++, add++)
> +		siu_write32(add, 0);
> +
> +	siu_write32(base + TRDAT, port_info->trdat);
> +	port_info->trdat = 0x0;
> +
> +
> +	/* SPB start condition: software */
> +	siu_write32(base + SBACTIV, 0);
> +	/* Start SPB */
> +	siu_write32(base + SBCTL, 0xc0000000);
> +	/* Wait for program to halt */
> +	cnt = 0x10000;
> +	while (--cnt && siu_read32(base + SBCTL) != 0x80000000)
> +		cpu_relax();
> +
> +	if (!cnt)
> +		return -EBUSY;
> +
> +	/* SPB program start address setting */
> +	siu_write32(base + SBPSET, 0x00400000);
> +	/* SPB hardware start(FIFOCTL source) */
> +	siu_write32(base + SBACTIV, 0xc0000000);
> +
> +	return 0;
> +}
> +
> +static void siu_dai_spbstop(struct siu_port *port_info)
> +{
> +	struct siu_info *info = siu_i2s_dai.private_data;
> +	u32 __iomem *base = info->reg;
> +
> +	siu_write32(base + SBACTIV, 0);
> +	/* SPB stop */
> +	siu_write32(base + SBCTL, 0);
> +
> +	port_info->stfifo = 0;
> +}
> +
> +/*		API functions		*/
> +
> +/* Playback and capture hardware properties are identical */
> +static struct snd_pcm_hardware siu_dai_pcm_hw = {
> +	.info			= SNDRV_PCM_INFO_INTERLEAVED,
> +	.formats		= SNDRV_PCM_FMTBIT_S16,
> +	.rates			= SNDRV_PCM_RATE_8000_48000,
> +	.rate_min		= 8000,
> +	.rate_max		= 48000,
> +	.channels_min		= 1,

Shouldn't this be 2 as it's stated in siu_dai_open() that mono is not
used.

> +	.channels_max		= 2,
> +	.buffer_bytes_max	= BUFFER_BYTES_MAX,
> +	.period_bytes_min	= PERIOD_BYTES_MIN,
> +	.period_bytes_max	= PERIOD_BYTES_MAX,
> +	.periods_min		= PERIODS_MIN,
> +	.periods_max		= PERIODS_MAX,
> +};
> +
> +static int siu_dai_info_volume(struct snd_kcontrol *kctrl,
> +			       struct snd_ctl_elem_info *uinfo)
> +{
> +	struct siu_port *port_info = snd_kcontrol_chip(kctrl);
> +
> +	dev_dbg(port_info->pcm->card->dev, "%s\n", __func__);
> +
> +	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
> +	uinfo->count = 2;
> +	uinfo->value.integer.min = 0;
> +	uinfo->value.integer.max = MAX_VOLUME;
> +
> +	return 0;
> +}
> +
> +static int siu_dai_get_volume(struct snd_kcontrol *kctrl,
> +			      struct snd_ctl_elem_value *ucontrol)
> +{
> +	struct siu_port *port_info = snd_kcontrol_chip(kctrl);
> +	struct device *dev = port_info->pcm->card->dev;
> +	u32 vol;
> +
> +	dev_dbg(dev, "%s\n", __func__);
> +
> +	switch (kctrl->private_value) {
> +	case VOLUME_PLAYBACK:
> +		/* Playback is always on port 0 */
> +		vol = port_info->playback.volume;
> +		ucontrol->value.integer.value[0] = vol & 0xffff;
> +		ucontrol->value.integer.value[1] = vol >> 16 & 0xffff;
> +		break;
> +	case VOLUME_CAPTURE:
> +		/* Capture is always on port 1 */
> +		vol = port_info->capture.volume;
> +		ucontrol->value.integer.value[0] = vol & 0xffff;
> +		ucontrol->value.integer.value[1] = vol >> 16 & 0xffff;
> +		break;
> +	default:
> +		dev_err(dev, "%s() invalid private_value=%ld\n",
> +			__func__, kctrl->private_value);
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static int siu_dai_put_volume(struct snd_kcontrol *kctrl,
> +			      struct snd_ctl_elem_value *ucontrol)
> +{
> +	struct siu_port *port_info = snd_kcontrol_chip(kctrl);
> +	struct device *dev = port_info->pcm->card->dev;
> +	struct siu_info *info = siu_i2s_dai.private_data;
> +	u32 __iomem *base = info->reg;
> +	u32 new_vol;
> +	u32 cur_vol;
> +
> +	dev_dbg(dev, "%s\n", __func__);
> +
> +	if (ucontrol->value.integer.value[0] < 0 ||
> +	    ucontrol->value.integer.value[0] > MAX_VOLUME ||
> +	    ucontrol->value.integer.value[1] < 0 ||
> +	    ucontrol->value.integer.value[1] > MAX_VOLUME)
> +		return -EINVAL;
> +
> +	new_vol = ucontrol->value.integer.value[0] |
> +		ucontrol->value.integer.value[1] << 16;
> +
> +	/* See comment above - DSP firmware implementation */
> +	switch (kctrl->private_value) {
> +	case VOLUME_PLAYBACK:
> +		/* Playback is always on port 0 */
> +		cur_vol = port_info->playback.volume;
> +		siu_write32(base + SBDVCA, new_vol);
> +		port_info->playback.volume = new_vol;
> +		break;
> +	case VOLUME_CAPTURE:
> +		/* Capture is always on port 1 */
> +		cur_vol = port_info->capture.volume;
> +		siu_write32(base + SBDVCB, new_vol);
> +		port_info->capture.volume = new_vol;
> +		break;
> +	default:
> +		dev_err(dev, "%s() invalid private_value=%ld\n",
> +			__func__, kctrl->private_value);
> +		return -EINVAL;
> +	}
> +
> +	if (cur_vol != new_vol)
> +		return 1;
> +
> +	return 0;
> +}
> +
> +static struct snd_kcontrol_new playback_controls = {
> +	.iface		= SNDRV_CTL_ELEM_IFACE_MIXER,
> +	.name		= "PCM Playback Volume",
> +	.index		= 0,
> +	.info		= siu_dai_info_volume,
> +	.get		= siu_dai_get_volume,
> +	.put		= siu_dai_put_volume,
> +	.private_value	= VOLUME_PLAYBACK,
> +};
> +
> +static struct snd_kcontrol_new capture_controls = {
> +	.iface		= SNDRV_CTL_ELEM_IFACE_MIXER,
> +	.name		= "PCM Capture Volume",
> +	.index		= 0,
> +	.info		= siu_dai_info_volume,
> +	.get		= siu_dai_get_volume,
> +	.put		= siu_dai_put_volume,
> +	.private_value	= VOLUME_CAPTURE,
> +};
> +
> +int siu_init_port(int port, struct siu_port **port_info, struct snd_card *card)
> +{
> +	struct device *dev = card->dev;
> +	struct snd_kcontrol *kctrl;
> +	int ret;
> +
> +	*port_info = kzalloc(sizeof(**port_info), GFP_KERNEL);
> +	if (!*port_info)
> +		return -ENOMEM;
> +
> +	dev_dbg(dev, "%s: port #%d@%p\n", __func__, port, *port_info);
> +
> +	(*port_info)->playback.volume = DFLT_VOLUME_LEVEL;
> +	(*port_info)->capture.volume = DFLT_VOLUME_LEVEL;
> +
> +	/*
> +	 * Add mixer support. The SPB is used to change the volume. Both
> +	 * ports use the same SPB. Therefore, we only register one
> +	 * control instance since it will be used by both channels.
> +	 * In error case we continue without controls.
> +	 */
> +	kctrl = snd_ctl_new1(&playback_controls, *port_info);
> +	ret = snd_ctl_add(card, kctrl);
> +	if (ret < 0)
> +		dev_err(dev,
> +			"failed to add playback controls %p port=%d err=%d\n",
> +			kctrl, port, ret);
> +
> +	kctrl = snd_ctl_new1(&capture_controls, *port_info);
> +	ret = snd_ctl_add(card, kctrl);
> +	if (ret < 0)
> +		dev_err(dev,
> +			"failed to add capture controls %p port=%d err=%d\n",
> +			kctrl, port, ret);
> +
> +	return 0;
> +}
> +
> +void siu_free_port(struct siu_port *port_info)
> +{
> +	kfree(port_info);
> +}
> +
> +static int siu_dai_startup(struct snd_pcm_substream *substream,
> +			   struct snd_soc_dai *dai)
> +{
> +	struct siu_info *info = siu_i2s_dai.private_data;
> +	struct snd_pcm_runtime *rt = substream->runtime;
> +	struct siu_port	*port_info = siu_port_info(substream);
> +	int ret;
> +
> +	dev_dbg(substream->pcm->card->dev, "%s: port=%d@%p\n", __func__,
> +		info->port_id, port_info);
> +
> +	snd_soc_set_runtime_hwparams(substream, &siu_dai_pcm_hw);
> +
> +	ret = snd_pcm_hw_constraint_integer(rt, SNDRV_PCM_HW_PARAM_PERIODS);
> +	if (unlikely(ret < 0))
> +		return ret;
> +
> +	siu_dai_start(port_info);
> +
> +	return 0;
> +}
> +
> +static void siu_dai_shutdown(struct snd_pcm_substream *substream,
> +			     struct snd_soc_dai *dai)
> +{
> +	struct siu_info *info = siu_i2s_dai.private_data;
> +	struct siu_port	*port_info = siu_port_info(substream);
> +
> +	dev_dbg(substream->pcm->card->dev, "%s: port=%d@%p\n", __func__,
> +		info->port_id, port_info);
> +
> +	if (substream->stream = SNDRV_PCM_STREAM_PLAYBACK)
> +		port_info->play_cap &= ~PLAYBACK_ENABLED;
> +	else
> +		port_info->play_cap &= ~CAPTURE_ENABLED;
> +
> +	/* Stop the siu if the other stream is not using it */
> +	if (!port_info->play_cap) {
> +		/* during stmread or stmwrite ? */
> +		BUG_ON(port_info->playback.rw_flg || port_info->capture.rw_flg);
> +		siu_dai_spbstop(port_info);
> +		siu_dai_stop();
> +	}
> +}
> +
> +/* PCM part of siu_dai_playback_prepare() / siu_dai_capture_prepare() */
> +static int siu_dai_prepare(struct snd_pcm_substream *substream,
> +			   struct snd_soc_dai *dai)
> +{
> +	struct siu_info *info = siu_i2s_dai.private_data;
> +	struct snd_pcm_runtime *rt = substream->runtime;
> +	struct siu_port *port_info = siu_port_info(substream);
> +	struct siu_stream *siu_stream;
> +	int self, ret;
> +
> +	dev_dbg(substream->pcm->card->dev,
> +		"%s: port %d, active streams %lx, %d channels\n",
> +		__func__, info->port_id, port_info->play_cap, rt->channels);
> +
> +	if (substream->stream = SNDRV_PCM_STREAM_PLAYBACK) {
> +		self = PLAYBACK_ENABLED;
> +		siu_stream = &port_info->playback;
> +	} else {
> +		self = CAPTURE_ENABLED;
> +		siu_stream = &port_info->capture;
> +	}
> +
> +	/* Set up the siu if not already done */
> +	if (!port_info->play_cap) {
> +		siu_stream->rw_flg = 0;	/* stream-data transfer flag */
> +
> +		siu_dai_spbAselect(port_info);
> +		siu_dai_spbBselect(port_info);
> +
> +		siu_dai_open(siu_stream);
> +
> +		siu_dai_pcmdatapack(siu_stream);
> +
> +		ret = siu_dai_spbstart(port_info);
> +		if (ret < 0)
> +			goto fail;
> +	}
> +
> +	port_info->play_cap |= self;
> +
> +fail:
> +	return ret;
> +}
> +
> +/*
> + * SIU can set bus format to I2S / PCM / SPDIF independently for playback and
> + * capture, however, the current API sets the bus format globally for a DAI.
> + */
> +static int siu_dai_set_fmt(struct snd_soc_dai *dai,
> +			   unsigned int fmt)
> +{
> +	struct siu_info *info = siu_i2s_dai.private_data;
> +	u32 __iomem *base = info->reg;
> +	u32 ifctl;
> +
> +	dev_dbg(dai->dev, "%s: fmt 0x%x on port %d\n",
> +		__func__, fmt, info->port_id);
> +
> +	if (info->port_id < 0)
> +		return -ENODEV;
> +
> +	/* Here select between I2S / PCM / SPDIF */
> +	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
> +	case SND_SOC_DAIFMT_I2S:
> +		ifctl = siu_flags[info->port_id].playback.i2s |
> +			siu_flags[info->port_id].capture.i2s;
> +		break;
> +	case SND_SOC_DAIFMT_LEFT_J:
> +		ifctl = siu_flags[info->port_id].playback.pcm |
> +			siu_flags[info->port_id].capture.pcm;
> +		break;
> +	/* SPDIF disabled - see comment at the top */
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	ifctl |= ~(siu_flags[info->port_id].playback.mask |
> +		   siu_flags[info->port_id].capture.mask) &
> +		siu_read32(base + IFCTL);
> +	siu_write32(base + IFCTL, ifctl);
> +
> +	return 0;
> +}
> +
> +static int siu_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id,
> +			      unsigned int freq, int dir)
> +{
> +	struct clk *siu_clk, *parent_clk;
> +	char *siu_name, *parent_name;
> +	int ret;
> +
> +	if (dir != SND_SOC_CLOCK_IN)
> +		return -EINVAL;
> +
> +	dev_dbg(dai->dev, "%s: using clock %d\n", __func__, clk_id);
> +
> +	switch (clk_id) {
> +	case CLKA_PLL:
> +		siu_name = "siua_clk";
> +		parent_name = "pll_clk";
> +		break;
> +	case CLKA_EXT:
> +		siu_name = "siua_clk";
> +		parent_name = "siumcka_clk";
> +		break;
> +	case CLKB_PLL:
> +		siu_name = "siub_clk";
> +		parent_name = "pll_clk";
> +		break;
> +	case CLKB_EXT:
> +		siu_name = "siub_clk";
> +		parent_name = "siumckb_clk";
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	siu_clk = clk_get(siu_i2s_dai.dev, siu_name);
> +	if (IS_ERR(siu_clk))
> +		return PTR_ERR(siu_clk);
> +
> +	parent_clk = clk_get(siu_i2s_dai.dev, parent_name);
> +	if (!IS_ERR(parent_clk)) {
> +		ret = clk_set_parent(siu_clk, parent_clk);
> +		if (!ret)
> +			clk_set_rate(siu_clk, freq);
> +	}
> +
> +	clk_put(parent_clk);
> +	clk_put(siu_clk);
> +
> +	return 0;
> +}
> +
> +static struct snd_soc_dai_ops siu_dai_ops = {
> +	.startup	= siu_dai_startup,
> +	.shutdown	= siu_dai_shutdown,
> +	.prepare	= siu_dai_prepare,
> +	.set_sysclk	= siu_dai_set_sysclk,
> +	.set_fmt	= siu_dai_set_fmt,
> +};
> +
> +struct snd_soc_dai siu_i2s_dai = {
> +	.name = "sh-siu",
> +	.id = 0,
> +	.playback = {
> +		.channels_min = 1,

Shouldn't this also be 2 due to mono not used statement in
siu_dai_open()

> +		.channels_max = 2,
> +		.formats = SNDRV_PCM_FMTBIT_S16,
> +		.rates = SNDRV_PCM_RATE_8000_48000,
> +	},
> +	.capture = {
> +		.channels_min = 1,
> +		.channels_max = 2,
> +		.formats = SNDRV_PCM_FMTBIT_S16,
> +		.rates = SNDRV_PCM_RATE_8000_48000,
> +	 },
> +	.ops = &siu_dai_ops,
> +};
> +EXPORT_SYMBOL_GPL(siu_i2s_dai);
> +
> +static int __devinit siu_probe(struct platform_device *pdev)
> +{
> +	const struct firmware *fw_entry;
> +	struct resource *res, *region;
> +	struct siu_info *info;
> +	int ret;
> +
> +	info = kmalloc(sizeof(*info), GFP_KERNEL);
> +	if (!info)
> +		return -ENOMEM;
> +
> +	ret = request_firmware(&fw_entry, "siu_spb.bin", &pdev->dev);
> +	if (ret)
> +		goto ereqfw;
> +
> +	/*
> +	 * Loaded firmware is "const" - read only, but we have to modify it in
> +	 * snd_siu_sh7343_spbAselect() and snd_siu_sh7343_spbBselect()
> +	 */
> +	memcpy(&info->fw, fw_entry->data, fw_entry->size);
> +
> +	release_firmware(fw_entry);
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (!res) {
> +		ret = -ENODEV;
> +		goto egetres;
> +	}
> +
> +	region = request_mem_region(res->start, resource_size(res),
> +				    pdev->name);
> +	if (!region) {
> +		dev_err(&pdev->dev, "SIU region already claimed\n");
> +		ret = -EBUSY;
> +		goto ereqmemreg;
> +	}
> +
> +	ret = -ENOMEM;
> +	info->pram = ioremap(res->start, PRAM_SIZE);
> +	if (!info->pram)
> +		goto emappram;
> +	info->xram = ioremap(res->start + XRAM_OFFSET, XRAM_SIZE);
> +	if (!info->xram)
> +		goto emapxram;
> +	info->yram = ioremap(res->start + YRAM_OFFSET, YRAM_SIZE);
> +	if (!info->yram)
> +		goto emapyram;
> +	info->reg = ioremap(res->start + REG_OFFSET, resource_size(res) -
> +			    REG_OFFSET);
> +	if (!info->reg)
> +		goto emapreg;
> +
> +	siu_i2s_dai.dev = &pdev->dev;
> +	siu_i2s_dai.private_data = info;
> +
> +	ret = snd_soc_register_dais(&siu_i2s_dai, 1);
> +	if (ret < 0)
> +		goto edaiinit;
> +
> +	ret = snd_soc_register_platform(&siu_platform);
> +	if (ret < 0)
> +		goto esocregp;
> +
> +	pm_runtime_enable(&pdev->dev);
> +
> +	return ret;
> +
> +esocregp:
> +	snd_soc_unregister_dais(&siu_i2s_dai, 1);
> +edaiinit:
> +	iounmap(info->reg);
> +emapreg:
> +	iounmap(info->yram);
> +emapyram:
> +	iounmap(info->xram);
> +emapxram:
> +	iounmap(info->pram);
> +emappram:
> +	release_mem_region(res->start, resource_size(res));
> +ereqmemreg:
> +egetres:
> +ereqfw:
> +	kfree(info);
> +
> +	return ret;
> +}
> +
> +static int __devexit siu_remove(struct platform_device *pdev)
> +{
> +	struct siu_info *info = siu_i2s_dai.private_data;
> +	struct resource *res;
> +
> +	pm_runtime_disable(&pdev->dev);
> +
> +	snd_soc_unregister_platform(&siu_platform);
> +	snd_soc_unregister_dais(&siu_i2s_dai, 1);
> +
> +	iounmap(info->reg);
> +	iounmap(info->yram);
> +	iounmap(info->xram);
> +	iounmap(info->pram);
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (res)
> +		release_mem_region(res->start, resource_size(res));
> +	kfree(info);
> +
> +	return 0;
> +}
> +
> +static struct platform_driver siu_driver = {
> +	.driver 	= {
> +		.name	= "sh_siu",
> +	},
> +	.probe		= siu_probe,
> +	.remove		= __devexit_p(siu_remove),
> +};
> +
> +static int __init siu_init(void)
> +{
> +	return platform_driver_register(&siu_driver);
> +}
> +
> +static void __exit siu_exit(void)
> +{
> +	platform_driver_unregister(&siu_driver);
> +}
> +
> +module_init(siu_init)
> +module_exit(siu_exit)
> +
> +MODULE_AUTHOR("Carlos Munoz <carlos@kenati.com>");
> +MODULE_DESCRIPTION("ALSA SoC SH7722 SIU driver");
> +MODULE_LICENSE("GPL");
> diff --git a/sound/soc/sh/siu_pcm.c b/sound/soc/sh/siu_pcm.c
> new file mode 100644
> index 0000000..afe2e6e
> --- /dev/null
> +++ b/sound/soc/sh/siu_pcm.c
> @@ -0,0 +1,716 @@
> +/*
> + * siu_pcm.c - ALSA driver for Renesas SH7343, SH7722 SIU peripheral.
> + *
> + * Copyright (C) 2009-2010 Guennadi Liakhovetski <g.liakhovetski@gmx.de>
> + * Copyright (C) 2006 Carlos Munoz <carlos@kenati.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.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
> + */
> +#include <linux/delay.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/dmaengine.h>
> +#include <linux/interrupt.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +
> +#include <sound/control.h>
> +#include <sound/core.h>
> +#include <sound/pcm.h>
> +#include <sound/pcm_params.h>
> +#include <sound/soc-dai.h>
> +
> +#include <asm/dma-sh.h>
> +#include <asm/siu.h>
> +
> +#include "siu.h"
> +
> +struct siu_port *siu_ports[MAX_SIU_PORTS];
> +
> +static void copy_playback_period(struct siu_stream *siu_stream)
> +{
> +	struct snd_pcm_runtime *rt = siu_stream->substream->runtime;
> +	u16 *src;
> +	u32 *dst;
> +	int cp_cnt;
> +	int i;
> +
> +	src = (u16 *)PERIOD_OFFSET(rt->dma_area,
> +				   siu_stream->cur_period,
> +				   siu_stream->period_bytes);
> +	dst = siu_stream->mono_buf;
> +	cp_cnt = siu_stream->xfer_cnt;
> +
> +	for (i = 0; i < cp_cnt; i++)
> +		*dst++ = *src++;
> +}
> +
> +static void copy_capture_period(struct siu_stream *siu_stream)
> +{
> +	struct snd_pcm_runtime *rt = siu_stream->substream->runtime;
> +	u16 *src;
> +	u16 *dst;
> +	int cp_cnt;
> +	int i;
> +
> +	dst = (u16 *)PERIOD_OFFSET(rt->dma_area,
> +				   siu_stream->cur_period,
> +				   siu_stream->period_bytes);
> +	src = (u16 *)siu_stream->mono_buf;
> +	cp_cnt = siu_stream->xfer_cnt;
> +
> +	for (i = 0; i < cp_cnt; i++) {
> +		*dst++ = *src;
> +		src += 2;
> +	}
> +}
> +
> +/* transfersize is number of u32 dma transfers per period */
> +static int siu_pcm_stmwrite_stop(struct siu_port *port_info)
> +{
> +	struct siu_info *info = siu_i2s_dai.private_data;
> +	u32 __iomem *base = info->reg;
> +	struct siu_stream *siu_stream = &port_info->playback;
> +	u32 stfifo;
> +
> +	if (!siu_stream->rw_flg)
> +		return -EPERM;
> +
> +	/* output FIFO disable */
> +	stfifo = siu_read32(base + STFIFO);
> +	siu_write32(base + STFIFO, stfifo & ~0x0c180c18);
> +	pr_debug("%s: STFIFO %x -> %x\n", __func__,
> +		 stfifo, stfifo & ~0x0c180c18);
> +
> +	/* during stmwrite clear */
> +	siu_stream->rw_flg = 0;
> +
> +	return 0;
> +}
> +
> +static int siu_pcm_stmwrite_start(struct siu_port *port_info)
> +{
> +	struct siu_stream *siu_stream = &port_info->playback;
> +
> +	if (siu_stream->rw_flg)
> +		return -EPERM;
> +
> +	/* Current period in buffer */
> +	port_info->playback.cur_period = 0;
> +
> +	/* during stmwrite flag set */
> +	siu_stream->rw_flg = RWF_STM_WT;
> +
> +	/* DMA transfer start */
> +	tasklet_schedule(&siu_stream->tasklet);
> +
> +	return 0;
> +}
> +
> +static void siu_dma_tx_complete(void *arg)
> +{
> +	struct siu_stream *siu_stream = arg;
> +	struct snd_pcm_substream *substream = siu_stream->substream;
> +
> +	if (!siu_stream->rw_flg)
> +		return;
> +
> +	if (substream->runtime->channels = 1 &&
> +	    substream->stream = SNDRV_PCM_STREAM_CAPTURE)
> +		copy_capture_period(siu_stream);
> +
> +	/* Update completed period count */
> +	if (++siu_stream->cur_period >> +	    GET_MAX_PERIODS(siu_stream->buf_bytes,
> +			    siu_stream->period_bytes))
> +		siu_stream->cur_period = 0;
> +
> +	pr_debug("%s: done period #%d (%u/%u bytes), cookie %d\n",
> +		__func__, siu_stream->cur_period,
> +		siu_stream->cur_period * siu_stream->period_bytes,
> +		siu_stream->buf_bytes, siu_stream->cookie);
> +
> +	tasklet_schedule(&siu_stream->tasklet);
> +
> +	/* Notify alsa: a period is done */
> +	snd_pcm_period_elapsed(siu_stream->substream);
> +}
> +
> +static int siu_pcm_wr_set(struct siu_port *port_info,
> +			  dma_addr_t buff, u32 size)
> +{
> +	struct siu_info *info = siu_i2s_dai.private_data;
> +	u32 __iomem *base = info->reg;
> +	struct siu_stream *siu_stream = &port_info->playback;
> +	struct snd_pcm_substream *substream = siu_stream->substream;
> +	struct device *dev = substream->pcm->card->dev;
> +	struct dma_async_tx_descriptor *desc;
> +	dma_cookie_t cookie;
> +	struct scatterlist sg;
> +	u32 stfifo;
> +
> +	sg_init_table(&sg, 1);
> +	sg_set_page(&sg, pfn_to_page(PFN_DOWN(buff)),
> +		    size, offset_in_page(buff));
> +	sg_dma_address(&sg) = buff;
> +
> +	desc = siu_stream->chan->device->device_prep_slave_sg(siu_stream->chan,
> +		&sg, 1, DMA_TO_DEVICE, DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
> +	if (!desc) {
> +		dev_err(dev, "Failed to allocate a dma descriptor\n");
> +		return -ENOMEM;
> +	}
> +
> +	desc->callback = siu_dma_tx_complete;
> +	desc->callback_param = siu_stream;
> +	cookie = desc->tx_submit(desc);
> +	if (cookie < 0) {
> +		dev_err(dev, "Failed to submit a dma transfer\n");
> +		return cookie;
> +	}
> +
> +	siu_stream->tx_desc = desc;
> +	siu_stream->cookie = cookie;
> +
> +	dma_async_issue_pending(siu_stream->chan);
> +
> +	/* only output FIFO enable */
> +	stfifo = siu_read32(base + STFIFO);
> +	siu_write32(base + STFIFO, stfifo | (port_info->stfifo & 0x0c180c18));
> +	dev_dbg(dev, "%s: STFIFO %x -> %x\n", __func__,
> +		stfifo, stfifo | (port_info->stfifo & 0x0c180c18));
> +
> +	return 0;
> +}
> +
> +static int siu_pcm_rd_set(struct siu_port *port_info,
> +			  dma_addr_t buff, size_t size)
> +{
> +	struct siu_info *info = siu_i2s_dai.private_data;
> +	u32 __iomem *base = info->reg;
> +	struct siu_stream *siu_stream = &port_info->capture;
> +	struct snd_pcm_substream *substream = siu_stream->substream;
> +	struct device *dev = substream->pcm->card->dev;
> +	struct dma_async_tx_descriptor *desc;
> +	dma_cookie_t cookie;
> +	struct scatterlist sg;
> +	u32 stfifo;
> +
> +	dev_dbg(dev, "%s: %u@%llx\n", __func__, size, (unsigned long long)buff);
> +
> +	sg_init_table(&sg, 1);
> +	sg_set_page(&sg, pfn_to_page(PFN_DOWN(buff)),
> +		    size, offset_in_page(buff));
> +	sg_dma_address(&sg) = buff;
> +
> +	desc = siu_stream->chan->device->device_prep_slave_sg(siu_stream->chan,
> +		&sg, 1, DMA_FROM_DEVICE, DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
> +	if (!desc) {
> +		dev_err(dev, "Failed to allocate dma descriptor\n");
> +		return -ENOMEM;
> +	}
> +
> +	desc->callback = siu_dma_tx_complete;
> +	desc->callback_param = siu_stream;
> +	cookie = desc->tx_submit(desc);
> +	if (cookie < 0) {
> +		dev_err(dev, "Failed to submit dma descriptor\n");
> +		return cookie;
> +	}
> +
> +	siu_stream->tx_desc = desc;
> +	siu_stream->cookie = cookie;
> +
> +	dma_async_issue_pending(siu_stream->chan);
> +
> +	/* only input FIFO enable */
> +	stfifo = siu_read32(base + STFIFO);
> +	siu_write32(base + STFIFO, siu_read32(base + STFIFO) |
> +		    (port_info->stfifo & 0x13071307));
> +	dev_dbg(dev, "%s: STFIFO %x -> %x\n", __func__,
> +		stfifo, stfifo | (port_info->stfifo & 0x13071307));
> +
> +	return 0;
> +}
> +
> +static void siu_io_tasklet(unsigned long data)
> +{
> +	struct siu_stream *siu_stream = (struct siu_stream *)data;
> +	struct snd_pcm_substream *substream = siu_stream->substream;
> +	struct device *dev = substream->pcm->card->dev;
> +	struct snd_pcm_runtime *rt = substream->runtime;
> +	struct siu_port *port_info = siu_port_info(substream);
> +
> +	dev_dbg(dev, "%s: flags %x\n", __func__, siu_stream->rw_flg);
> +
> +	if (!siu_stream->rw_flg) {
> +		dev_dbg(dev, "%s: stream inactive\n", __func__);
> +		return;
> +	}
> +
> +	if (substream->stream = SNDRV_PCM_STREAM_CAPTURE) {
> +		dma_addr_t buff;
> +		size_t count;
> +		u8 *virt;
> +
> +		if (rt->channels = 1) {
> +			buff = siu_stream->mono_dma;
> +			virt = siu_stream->mono_buf;
> +			count = siu_stream->mono_size;
> +		} else {
> +			buff = (dma_addr_t)PERIOD_OFFSET(rt->dma_addr,
> +					siu_stream->cur_period,
> +					siu_stream->period_bytes);
> +			virt = PERIOD_OFFSET(rt->dma_area,
> +					siu_stream->cur_period,
> +					siu_stream->period_bytes);
> +			count = siu_stream->period_bytes;
> +		}
> +
> +		/* DMA transfer start */
> +		siu_pcm_rd_set(port_info, buff, count);
> +	} else {
> +		/* For mono streams we need to use the mono buffer */
> +		if (rt->channels = 1) {
> +			copy_playback_period(siu_stream);
> +			siu_pcm_wr_set(port_info,
> +				siu_stream->mono_dma, siu_stream->mono_size);
> +		} else {
> +			siu_pcm_wr_set(port_info,
> +				(dma_addr_t)PERIOD_OFFSET(rt->dma_addr,
> +					siu_stream->cur_period,
> +					siu_stream->period_bytes),
> +				siu_stream->period_bytes);
> +		}
> +	}
> +}
> +
> +/* Capture */
> +static int siu_pcm_stmread_start(struct siu_port *port_info)
> +{
> +	struct siu_stream *siu_stream = &port_info->capture;
> +
> +	if (siu_stream->xfer_cnt > 0x1000000)
> +		return -EINVAL;
> +	if (siu_stream->rw_flg)
> +		return -EPERM;
> +
> +	/* Current period in buffer */
> +	siu_stream->cur_period = 0;
> +
> +	/* during stmread flag set */
> +	siu_stream->rw_flg = RWF_STM_RD;
> +
> +	tasklet_schedule(&siu_stream->tasklet);
> +
> +	return 0;
> +}
> +
> +static int siu_pcm_stmread_stop(struct siu_port *port_info)
> +{
> +	struct siu_info *info = siu_i2s_dai.private_data;
> +	u32 __iomem *base = info->reg;
> +	struct siu_stream *siu_stream = &port_info->capture;
> +	struct device *dev = siu_stream->substream->pcm->card->dev;
> +	u32 stfifo;
> +
> +	if (!siu_stream->rw_flg)
> +		return -EPERM;
> +
> +	/* input FIFO disable */
> +	stfifo = siu_read32(base + STFIFO);
> +	siu_write32(base + STFIFO, stfifo & ~0x13071307);
> +	dev_dbg(dev, "%s: STFIFO %x -> %x\n", __func__,
> +		stfifo, stfifo & ~0x13071307);
> +
> +	/* during stmread flag clear */
> +	siu_stream->rw_flg = 0;
> +
> +	return 0;
> +}
> +
> +static int siu_pcm_hw_params(struct snd_pcm_substream *ss,
> +			     struct snd_pcm_hw_params *hw_params)
> +{
> +	struct siu_info *info = siu_i2s_dai.private_data;
> +	struct device *dev = ss->pcm->card->dev;
> +	int ret;
> +
> +	dev_dbg(dev, "%s: port=%d\n", __func__, info->port_id);
> +
> +	ret = snd_pcm_lib_malloc_pages(ss, params_buffer_bytes(hw_params));
> +	if (ret < 0)
> +		dev_err(dev, "snd_pcm_lib_malloc_pages() failed\n");
> +
> +	return ret;
> +}
> +
> +static void siu_pcm_mono_free(struct device *dev, struct siu_stream *stream)
> +{
> +	dma_free_coherent(dev, stream->mono_size,
> +			  stream->mono_buf, stream->mono_dma);
> +	stream->mono_buf = NULL;
> +	stream->mono_size = 0;
> +}
> +
> +static int siu_pcm_hw_free(struct snd_pcm_substream *ss)
> +{
> +	struct siu_info *info = siu_i2s_dai.private_data;
> +	struct siu_port	*port_info = siu_port_info(ss);
> +	struct device *dev = ss->pcm->card->dev;
> +	struct siu_stream *siu_stream;
> +
> +	if (ss->stream = SNDRV_PCM_STREAM_PLAYBACK)
> +		siu_stream = &port_info->playback;
> +	else
> +		siu_stream = &port_info->capture;
> +
> +	dev_dbg(dev, "%s: port=%d, mono %p\n", __func__,
> +		info->port_id, siu_stream->mono_buf);
> +
> +	if (siu_stream->mono_buf && ss->runtime->channels = 1)
> +		siu_pcm_mono_free(ss->pcm->card->dev, siu_stream);
> +
> +	return snd_pcm_lib_free_pages(ss);
> +}
> +
> +static bool filter(struct dma_chan *chan, void *slave)
> +{
> +	struct sh_dmae_slave *param = slave;
> +
> +	pr_debug("%s: slave ID %d\n", __func__, param->slave_id);
> +
> +	if (unlikely(param->dma_dev != chan->device->dev))
> +		return false;
> +
> +	chan->private = param;
> +	return true;
> +}
> +
> +static int siu_pcm_open(struct snd_pcm_substream *ss)
> +{
> +	/* Playback / Capture */
> +	struct siu_info *info = siu_i2s_dai.private_data;
> +	struct siu_port *port_info = siu_port_info(ss);
> +	struct siu_stream *siu_stream;
> +	u32 port = info->port_id;
> +	struct siu_platform *pdata = siu_i2s_dai.dev->platform_data;
> +	struct device *dev = ss->pcm->card->dev;
> +	dma_cap_mask_t mask;
> +	struct sh_dmae_slave *param;
> +
> +	dma_cap_zero(mask);
> +	dma_cap_set(DMA_SLAVE, mask);
> +
> +	dev_dbg(dev, "%s, port=%d@%p\n", __func__, port, port_info);
> +
> +	if (ss->stream = SNDRV_PCM_STREAM_PLAYBACK) {
> +		siu_stream = &port_info->playback;
> +		param = &siu_stream->param;
> +		param->slave_id = port ? SHDMA_SLAVE_SIUB_TX :
> +			SHDMA_SLAVE_SIUA_TX;
> +	} else {
> +		siu_stream = &port_info->capture;
> +		param = &siu_stream->param;
> +		param->slave_id = port ? SHDMA_SLAVE_SIUB_RX :
> +			SHDMA_SLAVE_SIUA_RX;
> +	}
> +
> +	param->dma_dev = pdata->dma_dev;
> +	/* Get DMA channel */
> +	siu_stream->chan = dma_request_channel(mask, filter, param);
> +	if (!siu_stream->chan) {
> +		dev_err(dev, "DMA channel allocation failed!\n");
> +		return -EBUSY;
> +	}
> +
> +	siu_stream->substream = ss;
> +
> +	return 0;
> +}
> +
> +static int siu_pcm_close(struct snd_pcm_substream *ss)
> +{
> +	struct siu_info *info = siu_i2s_dai.private_data;
> +	struct device *dev = ss->pcm->card->dev;
> +	struct siu_port *port_info = siu_port_info(ss);
> +	struct siu_stream *siu_stream;
> +
> +	dev_dbg(dev, "%s: port=%d\n", __func__, info->port_id);
> +
> +	if (ss->stream = SNDRV_PCM_STREAM_PLAYBACK)
> +		siu_stream = &port_info->playback;
> +	else
> +		siu_stream = &port_info->capture;
> +
> +	dma_release_channel(siu_stream->chan);
> +	siu_stream->chan = NULL;
> +
> +	siu_stream->substream = NULL;
> +
> +	return 0;
> +}
> +
> +static int siu_pcm_mono_alloc(struct device *dev, struct siu_stream *siu_stream)
> +{
> +	/*
> +	 * The hardware only supports stereo (2 channels) streams. We must
> +	 * convert mono streams (1 channel) to stereo streams. To do that we
> +	 * just copy the mono data to one of the stereo channels and instruct
> +	 * the siu to play the data on both channels. However, the idle
> +	 * channel must also be present in the buffer, so we use an extra
> +	 * buffer twice as big as one mono period. Also since this function
> +	 * can be called multiple times, we must adjust the buffer size.
> +	 */

Shouldn't this be done by userspace. i.e. alsa plugin or pulseaudio ?

> +	if (siu_stream->mono_buf && siu_stream->mono_size !> +	    siu_stream->period_bytes * 2) {
> +		dma_free_coherent(dev, siu_stream->mono_size,
> +				  siu_stream->mono_buf, siu_stream->mono_dma);
> +		siu_stream->mono_buf = NULL;
> +		siu_stream->mono_size = 0;
> +	}
> +
> +	if (!siu_stream->mono_buf) {
> +		siu_stream->mono_buf = dma_alloc_coherent(dev,
> +						siu_stream->period_bytes * 2,
> +						&siu_stream->mono_dma,
> +						GFP_KERNEL);
> +		if (!siu_stream->mono_buf)
> +			return -ENOMEM;
> +
> +		siu_stream->mono_size = siu_stream->period_bytes * 2;
> +	}
> +
> +	dev_dbg(dev, "%s: mono buffer @ %p\n", __func__, siu_stream->mono_buf);
> +
> +	return 0;
> +}
> +
> +static int siu_pcm_prepare(struct snd_pcm_substream *ss)
> +{
> +	struct siu_info *info = siu_i2s_dai.private_data;
> +	struct siu_port *port_info = siu_port_info(ss);
> +	struct device *dev = ss->pcm->card->dev;
> +	struct snd_pcm_runtime 	*rt = ss->runtime;
> +	struct siu_stream *siu_stream;
> +	snd_pcm_sframes_t xfer_cnt;
> +
> +	if (ss->stream = SNDRV_PCM_STREAM_PLAYBACK)
> +		siu_stream = &port_info->playback;
> +	else
> +		siu_stream = &port_info->capture;
> +
> +	rt = siu_stream->substream->runtime;
> +
> +	siu_stream->buf_bytes = snd_pcm_lib_buffer_bytes(ss);
> +	siu_stream->period_bytes = snd_pcm_lib_period_bytes(ss);
> +
> +	dev_dbg(dev, "%s: port=%d, %d channels, period=%u bytes\n", __func__,
> +		info->port_id, rt->channels, siu_stream->period_bytes);
> +
> +	/* We only support buffers that are multiples of the period */
> +	if (siu_stream->buf_bytes % siu_stream->period_bytes) {
> +		dev_err(dev, "%s() - buffer=%d not multiple of period=%d\n",
> +		       __func__, siu_stream->buf_bytes,
> +		       siu_stream->period_bytes);
> +		return -EINVAL;
> +	}
> +
> +	xfer_cnt = bytes_to_frames(rt, siu_stream->period_bytes);
> +	if (!xfer_cnt || xfer_cnt > 0x1000000)
> +		return -EINVAL;
> +
> +	if (rt->channels = 1) {
> +		int ret = siu_pcm_mono_alloc(ss->pcm->card->dev,
> +					     siu_stream);
> +		if (ret < 0)
> +			return ret;
> +	}
> +
> +	siu_stream->format = rt->format;
> +	siu_stream->xfer_cnt = xfer_cnt;
> +
> +	dev_dbg(dev, "port=%d buf=%lx buf_bytes=%d period_bytes=%d "
> +		"format=%d channels=%d xfer_cnt=%d\n", info->port_id,
> +		(unsigned long)rt->dma_addr, siu_stream->buf_bytes,
> +		siu_stream->period_bytes,
> +		siu_stream->format, rt->channels, (int)xfer_cnt);
> +
> +	return 0;
> +}
> +
> +static int siu_pcm_trigger(struct snd_pcm_substream *ss, int cmd)
> +{
> +	struct siu_info *info = siu_i2s_dai.private_data;
> +	struct device *dev = ss->pcm->card->dev;
> +	struct siu_port *port_info = siu_port_info(ss);
> +	int ret;
> +
> +	dev_dbg(dev, "%s: port=%d@%p, cmd=%d\n", __func__,
> +		info->port_id, port_info, cmd);
> +
> +	switch (cmd) {
> +	case SNDRV_PCM_TRIGGER_START:
> +		if (ss->stream = SNDRV_PCM_STREAM_PLAYBACK)
> +			ret = siu_pcm_stmwrite_start(port_info);
> +		else
> +			ret = siu_pcm_stmread_start(port_info);
> +
> +		if (ret < 0)
> +			dev_warn(dev, "%s: start failed on port=%d\n",
> +				 __func__, info->port_id);
> +
> +		break;
> +	case SNDRV_PCM_TRIGGER_STOP:
> +		if (ss->stream = SNDRV_PCM_STREAM_PLAYBACK)
> +			siu_pcm_stmwrite_stop(port_info);
> +		else
> +			siu_pcm_stmread_stop(port_info);
> +		ret = 0;
> +
> +		break;
> +	default:
> +		dev_err(dev, "%s() unsupported cmd=%d\n", __func__, cmd);
> +		ret = -EINVAL;
> +	}
> +
> +	return ret;
> +}
> +
> +/*
> + * So far only resolution of one period is supported, subject to extending the
> + * dmangine API
> + */
> +static snd_pcm_uframes_t siu_pcm_pointer_dma(struct snd_pcm_substream *ss)
> +{
> +	struct device *dev = ss->pcm->card->dev;
> +	struct siu_info *info = siu_i2s_dai.private_data;
> +	u32 __iomem *base = info->reg;
> +	struct siu_port *port_info = siu_port_info(ss);
> +	struct snd_pcm_runtime *rt = ss->runtime;
> +	size_t ptr;
> +	struct siu_stream *siu_stream;
> +
> +	if (ss->stream = SNDRV_PCM_STREAM_PLAYBACK)
> +		siu_stream = &port_info->playback;
> +	else
> +		siu_stream = &port_info->capture;
> +
> +	/*
> +	 * ptr is the offset into the buffer where the dma is currently at. We
> +	 * check if the dma buffer has just wrapped.
> +	 */
> +	ptr = PERIOD_OFFSET(rt->dma_addr,
> +			    siu_stream->cur_period,
> +			    siu_stream->period_bytes) - rt->dma_addr;
> +
> +	dev_dbg(dev,
> +		"%s: port=%d, events %x, FSTS %x, xferred %u/%u, cookie %d\n",
> +		__func__, info->port_id, siu_read32(base + EVNTC),
> +		siu_read32(base + SBFSTS), ptr, siu_stream->buf_bytes,
> +		siu_stream->cookie);
> +
> +	if (ptr >= siu_stream->buf_bytes)
> +		ptr = 0;
> +
> +	return bytes_to_frames(ss->runtime, ptr);
> +}
> +
> +static int siu_pcm_new(struct snd_card *card, struct snd_soc_dai *dai,
> +		       struct snd_pcm *pcm)
> +{
> +	/* card->dev = socdev->dev, see snd_soc_new_pcms() */
> +	struct siu_info *info = siu_i2s_dai.private_data;
> +	struct platform_device *pdev = to_platform_device(card->dev);
> +	int ret;
> +	int i;
> +
> +	/* pdev->id selects between SIUA and SIUB */
> +	if (pdev->id < 0 || pdev->id >= MAX_SIU_PORTS)
> +		return -EINVAL;
> +
> +	info->port_id = pdev->id;
> +
> +	/*
> +	 * While the siu has 2 ports, only one port can be on at a time (only 1
> +	 * SPB). So far all the boards using the siu had only one of the ports
> +	 * wired to a codec. To simplify things, we only register one port with
> +	 * alsa. In case both ports are needed, it should be changed here
> +	 */
> +	for (i = pdev->id; i < pdev->id + 1; i++) {
> +		struct siu_port **port_info = &siu_ports[i];
> +
> +		ret = siu_init_port(i, port_info, card);
> +		if (ret < 0)
> +			return ret;
> +
> +		ret = snd_pcm_lib_preallocate_pages_for_all(pcm,
> +					SNDRV_DMA_TYPE_DEV, NULL,
> +					BUFFER_BYTES_MAX, BUFFER_BYTES_MAX);
> +		if (ret < 0) {
> +			dev_err(card->dev,
> +			       "snd_pcm_lib_preallocate_pages_for_all() err=%d",
> +				ret);
> +			goto fail;
> +		}
> +
> +		/* IO tasklets */
> +		tasklet_init(&(*port_info)->playback.tasklet, siu_io_tasklet,
> +			     (unsigned long)&(*port_info)->playback);
> +		tasklet_init(&(*port_info)->capture.tasklet, siu_io_tasklet,
> +			     (unsigned long)&(*port_info)->capture);
> +	}
> +
> +	dev_info(card->dev, "SuperH SIU driver initialized.\n");
> +	return 0;
> +
> +fail:
> +	siu_free_port(siu_ports[pdev->id]);
> +	dev_err(card->dev, "SIU: failed to initialize.\n");
> +	return ret;
> +}
> +
> +static void siu_pcm_free(struct snd_pcm *pcm)
> +{
> +	struct platform_device *pdev = to_platform_device(pcm->card->dev);
> +	struct siu_port *port_info = siu_ports[pdev->id];
> +
> +	tasklet_kill(&port_info->capture.tasklet);
> +	tasklet_kill(&port_info->playback.tasklet);
> +
> +	siu_free_port(port_info);
> +	snd_pcm_lib_preallocate_free_for_all(pcm);
> +
> +	dev_dbg(pcm->card->dev, "%s\n", __func__);
> +}
> +
> +static struct snd_pcm_ops siu_pcm_ops = {
> +	.open		= siu_pcm_open,
> +	.close		= siu_pcm_close,
> +	.ioctl		= snd_pcm_lib_ioctl,
> +	.hw_params	= siu_pcm_hw_params,
> +	.hw_free	= siu_pcm_hw_free,
> +	.prepare	= siu_pcm_prepare,
> +	.trigger	= siu_pcm_trigger,
> +	.pointer	= siu_pcm_pointer_dma,
> +};
> +
> +struct snd_soc_platform siu_platform = {
> +	.name		= "siu-audio",
> +	.pcm_ops 	= &siu_pcm_ops,
> +	.pcm_new	= siu_pcm_new,
> +	.pcm_free	= siu_pcm_free,
> +};
> +EXPORT_SYMBOL_GPL(siu_platform);



  reply	other threads:[~2010-01-19 11:13 UTC|newest]

Thread overview: 57+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2010-01-19  8:08 [PATCH 0/4] ALSA: SH: add ASoC driver for SIU audio engine, an audio codec and platform support Guennadi Liakhovetski
2010-01-19  8:08 ` [PATCH 0/4] ALSA: SH: add ASoC driver for SIU audio engine, an audio Guennadi Liakhovetski
2010-01-19  8:08 ` [PATCH 1/4] ASoC: add a WM8978 codec driver Guennadi Liakhovetski
2010-01-19  8:08   ` Guennadi Liakhovetski
2010-01-19 10:46   ` [alsa-devel] " Liam Girdwood
2010-01-19 10:46     ` Liam Girdwood
2010-01-20 20:01     ` Guennadi Liakhovetski
2010-01-20 20:01       ` [alsa-devel] " Guennadi Liakhovetski
2010-01-20 20:21       ` Mark Brown
2010-01-20 20:21         ` [alsa-devel] " Mark Brown
2010-01-20 20:25       ` Liam Girdwood
2010-01-19 10:57   ` Mark Brown
     [not found]   ` <20100119105729.GA32559@opensource.wolfsonmicro.com::587>
2010-01-20 19:50     ` Guennadi Liakhovetski
2010-01-20 19:50       ` Guennadi Liakhovetski
2010-01-20 20:17       ` Mark Brown
2010-01-20 20:17         ` [alsa-devel] " Mark Brown
2010-01-22  8:35         ` Guennadi Liakhovetski
2010-01-22  8:35           ` Guennadi Liakhovetski
2010-01-22 10:35           ` Mark Brown
2010-01-22 10:35             ` [alsa-devel] " Mark Brown
2010-01-22 16:27   ` [PATCH 1/4 v2] " Guennadi Liakhovetski
2010-01-22 16:27     ` Guennadi Liakhovetski
2010-01-22 17:39     ` Liam Girdwood
2010-01-23 20:47     ` [alsa-devel] " Mark Brown
2010-01-23 20:47       ` Mark Brown
2010-01-26 13:04       ` Guennadi Liakhovetski
2010-01-26 13:04         ` Guennadi Liakhovetski
2010-01-26 13:26         ` Mark Brown
2010-01-26 13:26           ` [alsa-devel] " Mark Brown
2010-01-26 14:08           ` Guennadi Liakhovetski
2010-01-26 14:08             ` [alsa-devel] " Guennadi Liakhovetski
2010-01-26 15:22             ` Mark Brown
2010-01-19  8:09 ` [PATCH 2/4] ASoC: add DAI and platform drivers for SH SIU and support for the Migo-R board Guennadi Liakhovetski
2010-01-19  8:09   ` [PATCH 2/4] ASoC: add DAI and platform drivers for SH SIU and support Guennadi Liakhovetski
2010-01-19 11:13   ` Liam Girdwood [this message]
2010-01-19 11:13     ` [alsa-devel] [PATCH 2/4] ASoC: add DAI and platform drivers Liam Girdwood
2010-01-19 12:34   ` [PATCH 2/4] ASoC: add DAI and platform drivers for SH SIU and support for the Migo-R board Mark Brown
2010-01-19 12:34     ` [PATCH 2/4] ASoC: add DAI and platform drivers for SH SIU and Mark Brown
2010-01-22 18:09   ` [PATCH 2a/4 v2] ASoC: add DAI and platform / DMA drivers for SH SIU Guennadi Liakhovetski
2010-01-22 18:09     ` Guennadi Liakhovetski
2010-01-25 13:58     ` Liam Girdwood
2010-01-25 13:58       ` [PATCH 2a/4 v2] ASoC: add DAI and platform / DMA drivers for Liam Girdwood
2010-01-25 15:06     ` [PATCH 2a/4 v2] ASoC: add DAI and platform / DMA drivers for SH SIU Mark Brown
2010-01-25 15:06       ` [PATCH 2a/4 v2] ASoC: add DAI and platform / DMA drivers for Mark Brown
2010-01-22 18:17   ` [PATCH 2b/4 v2] ASoC: add support for the sh7722 Migo-R board Guennadi Liakhovetski
2010-01-22 18:17     ` Guennadi Liakhovetski
2010-01-25 13:21     ` Mark Brown
2010-01-25 13:47       ` [alsa-devel] " Liam Girdwood
2010-01-25 13:47         ` [alsa-devel] [PATCH 2b/4 v2] ASoC: add support for the sh7722 Liam Girdwood
2010-01-19  8:09 ` [PATCH 3/4] sh: add DMA slave definitions and SIU platform data to sh7722 setup Guennadi Liakhovetski
2010-01-19  8:09   ` [PATCH 3/4] sh: add DMA slave definitions and SIU platform data to Guennadi Liakhovetski
2010-01-19  8:09 ` [PATCH 4/4] sh: audio support for the sh7722 Migo-R board Guennadi Liakhovetski
2010-01-19  8:09   ` Guennadi Liakhovetski
  -- strict thread matches above, loose matches on Subject: below --
2010-01-27 11:15 [PATCH 2b/4 v2] ASoC: add " Guennadi Liakhovetski
2010-01-27 11:15 ` Guennadi Liakhovetski
2010-01-29 14:13 ` [alsa-devel] " Mark Brown
2010-01-29 14:13   ` [alsa-devel] [PATCH 2b/4 v2] ASoC: add support for the sh7722 Mark Brown

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1263899596.3089.81.camel@odin \
    --to=lrg@slimlogic.co.uk \
    --cc=alsa-devel@alsa-project.org \
    --cc=broonie@opensource.wolfsonmicro.com \
    --cc=damm@opensource.se \
    --cc=g.liakhovetski@gmx.de \
    --cc=linux-sh@vger.kernel.org \
    --cc=morimoto.kuninori@renesas.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.