diff options
Diffstat (limited to 'sound/soc/sh')
| -rw-r--r-- | sound/soc/sh/Kconfig | 4 | ||||
| -rw-r--r-- | sound/soc/sh/dma-sh7760.c | 17 | ||||
| -rw-r--r-- | sound/soc/sh/fsi.c | 44 | ||||
| -rw-r--r-- | sound/soc/sh/migor.c | 19 | ||||
| -rw-r--r-- | sound/soc/sh/rcar/Makefile | 2 | ||||
| -rw-r--r-- | sound/soc/sh/rcar/adg.c | 354 | ||||
| -rw-r--r-- | sound/soc/sh/rcar/core.c | 629 | ||||
| -rw-r--r-- | sound/soc/sh/rcar/dvc.c | 289 | ||||
| -rw-r--r-- | sound/soc/sh/rcar/gen.c | 574 | ||||
| -rw-r--r-- | sound/soc/sh/rcar/rsnd.h | 287 | ||||
| -rw-r--r-- | sound/soc/sh/rcar/scu.c | 236 | ||||
| -rw-r--r-- | sound/soc/sh/rcar/src.c | 687 | ||||
| -rw-r--r-- | sound/soc/sh/rcar/ssi.c | 435 | ||||
| -rw-r--r-- | sound/soc/sh/siu_dai.c | 3 | 
14 files changed, 2522 insertions, 1058 deletions
diff --git a/sound/soc/sh/Kconfig b/sound/soc/sh/Kconfig index 56d8ff6a402..b43fdf0d08a 100644 --- a/sound/soc/sh/Kconfig +++ b/sound/soc/sh/Kconfig @@ -37,7 +37,7 @@ config SND_SOC_SH4_SIU  config SND_SOC_RCAR  	tristate "R-Car series SRU/SCU/SSIU/SSI support"  	select SND_SIMPLE_CARD -	select RCAR_CLK_ADG +	select REGMAP  	help  	  This option enables R-Car SUR/SCU/SSIU/SSI sound support @@ -56,7 +56,7 @@ config SND_SH7760_AC97  config SND_SIU_MIGOR  	tristate "SIU sound support on Migo-R" -	depends on SH_MIGOR +	depends on SH_MIGOR && I2C  	select SND_SOC_SH4_SIU  	select SND_SOC_WM8978  	help diff --git a/sound/soc/sh/dma-sh7760.c b/sound/soc/sh/dma-sh7760.c index 1a8b03e4b41..c85f8eb66c9 100644 --- a/sound/soc/sh/dma-sh7760.c +++ b/sound/soc/sh/dma-sh7760.c @@ -89,29 +89,12 @@ struct camelot_pcm {  #define DMABRG_PREALLOC_BUFFER		32 * 1024  #define DMABRG_PREALLOC_BUFFER_MAX	32 * 1024 -/* support everything the SSI supports */ -#define DMABRG_RATES	\ -	SNDRV_PCM_RATE_8000_192000 - -#define DMABRG_FMTS	\ -	(SNDRV_PCM_FMTBIT_S8      | SNDRV_PCM_FMTBIT_U8      |	\ -	 SNDRV_PCM_FMTBIT_S16_LE  | SNDRV_PCM_FMTBIT_U16_LE  |	\ -	 SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_U20_3LE |	\ -	 SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_U24_3LE |	\ -	 SNDRV_PCM_FMTBIT_S32_LE  | SNDRV_PCM_FMTBIT_U32_LE) -  static struct snd_pcm_hardware camelot_pcm_hardware = {  	.info = (SNDRV_PCM_INFO_MMAP |  		SNDRV_PCM_INFO_INTERLEAVED |  		SNDRV_PCM_INFO_BLOCK_TRANSFER |  		SNDRV_PCM_INFO_MMAP_VALID |  		SNDRV_PCM_INFO_BATCH), -	.formats =	DMABRG_FMTS, -	.rates =	DMABRG_RATES, -	.rate_min =		8000, -	.rate_max =		192000, -	.channels_min =		2, -	.channels_max =		8,		/* max of the SSI */  	.buffer_bytes_max =	DMABRG_PERIOD_MAX,  	.period_bytes_min =	DMABRG_PERIOD_MIN,  	.period_bytes_max =	DMABRG_PERIOD_MAX / 2, diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c index b33ca7cd085..710a079a737 100644 --- a/sound/soc/sh/fsi.c +++ b/sound/soc/sh/fsi.c @@ -232,9 +232,9 @@ struct fsi_stream {  	 * these are for DMAEngine  	 */  	struct dma_chan		*chan; -	struct sh_dmae_slave	slave; /* see fsi_handler_init() */  	struct work_struct	work;  	dma_addr_t		dma; +	int			dma_id;  	int			loop_cnt;  	int			additional_pos;  }; @@ -1410,15 +1410,6 @@ static void fsi_dma_do_work(struct work_struct *work)  	}  } -static bool fsi_dma_filter(struct dma_chan *chan, void *param) -{ -	struct sh_dmae_slave *slave = param; - -	chan->private = slave; - -	return true; -} -  static int fsi_dma_transfer(struct fsi_priv *fsi, struct fsi_stream *io)  {  	schedule_work(&io->work); @@ -1446,15 +1437,34 @@ static int fsi_dma_push_start_stop(struct fsi_priv *fsi, struct fsi_stream *io,  static int fsi_dma_probe(struct fsi_priv *fsi, struct fsi_stream *io, struct device *dev)  {  	dma_cap_mask_t mask; +	int is_play = fsi_stream_is_play(fsi, io);  	dma_cap_zero(mask);  	dma_cap_set(DMA_SLAVE, mask); -	io->chan = dma_request_channel(mask, fsi_dma_filter, &io->slave); +	io->chan = dma_request_slave_channel_compat(mask, +				shdma_chan_filter, (void *)io->dma_id, +				dev, is_play ? "tx" : "rx"); +	if (io->chan) { +		struct dma_slave_config cfg; +		int ret; + +		cfg.slave_id	= io->dma_id; +		cfg.dst_addr	= 0; /* use default addr */ +		cfg.src_addr	= 0; /* use default addr */ +		cfg.direction	= is_play ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM; + +		ret = dmaengine_slave_config(io->chan, &cfg); +		if (ret < 0) { +			dma_release_channel(io->chan); +			io->chan = NULL; +		} +	} +  	if (!io->chan) {  		/* switch to PIO handler */ -		if (fsi_stream_is_play(fsi, io)) +		if (is_play)  			fsi->playback.handler	= &fsi_pio_push_handler;  		else  			fsi->capture.handler	= &fsi_pio_pop_handler; @@ -1701,9 +1711,9 @@ static int fsi_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)  	/* set master/slave audio interface */  	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {  	case SND_SOC_DAIFMT_CBM_CFM: -		fsi->clk_master = 1;  		break;  	case SND_SOC_DAIFMT_CBS_CFS: +		fsi->clk_master = 1; /* codec is slave, cpu is master */  		break;  	default:  		return -EINVAL; @@ -1777,12 +1787,6 @@ static struct snd_pcm_hardware fsi_pcm_hardware = {  			SNDRV_PCM_INFO_MMAP		|  			SNDRV_PCM_INFO_MMAP_VALID	|  			SNDRV_PCM_INFO_PAUSE, -	.formats		= FSI_FMTS, -	.rates			= FSI_RATES, -	.rate_min		= 8000, -	.rate_max		= 192000, -	.channels_min		= 2, -	.channels_max		= 2,  	.buffer_bytes_max	= 64 * 1024,  	.period_bytes_min	= 32,  	.period_bytes_max	= 8192, @@ -1960,7 +1964,7 @@ static void fsi_handler_init(struct fsi_priv *fsi,  	fsi->capture.priv	= fsi;  	if (info->tx_id) { -		fsi->playback.slave.shdma_slave.slave_id = info->tx_id; +		fsi->playback.dma_id  = info->tx_id;  		fsi->playback.handler = &fsi_dma_push_handler;  	}  } diff --git a/sound/soc/sh/migor.c b/sound/soc/sh/migor.c index 5014a884afe..c58c2529f10 100644 --- a/sound/soc/sh/migor.c +++ b/sound/soc/sh/migor.c @@ -136,19 +136,6 @@ static const struct snd_soc_dapm_route audio_map[] = {  	{ "Mic Bias", NULL, "External Microphone" },  }; -static int migor_dai_init(struct snd_soc_pcm_runtime *rtd) -{ -	struct snd_soc_codec *codec = rtd->codec; -	struct snd_soc_dapm_context *dapm = &codec->dapm; - -	snd_soc_dapm_new_controls(dapm, migor_dapm_widgets, -				  ARRAY_SIZE(migor_dapm_widgets)); - -	snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); - -	return 0; -} -  /* migor digital audio interface glue - connects codec <--> CPU */  static struct snd_soc_dai_link migor_dai = {  	.name = "wm8978", @@ -158,7 +145,6 @@ static struct snd_soc_dai_link migor_dai = {  	.platform_name = "siu-pcm-audio",  	.codec_name = "wm8978.0-001a",  	.ops = &migor_dai_ops, -	.init = migor_dai_init,  };  /* migor audio machine driver */ @@ -167,6 +153,11 @@ static struct snd_soc_card snd_soc_migor = {  	.owner = THIS_MODULE,  	.dai_link = &migor_dai,  	.num_links = 1, + +	.dapm_widgets = migor_dapm_widgets, +	.num_dapm_widgets = ARRAY_SIZE(migor_dapm_widgets), +	.dapm_routes = audio_map, +	.num_dapm_routes = ARRAY_SIZE(audio_map),  };  static struct platform_device *migor_snd_device; diff --git a/sound/soc/sh/rcar/Makefile b/sound/soc/sh/rcar/Makefile index 0ff492df792..9ac53642980 100644 --- a/sound/soc/sh/rcar/Makefile +++ b/sound/soc/sh/rcar/Makefile @@ -1,2 +1,2 @@ -snd-soc-rcar-objs	:= core.o gen.o scu.o adg.o ssi.o +snd-soc-rcar-objs	:= core.o gen.o src.o adg.o ssi.o dvc.o  obj-$(CONFIG_SND_SOC_RCAR)	+= snd-soc-rcar.o
\ No newline at end of file diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c index d80deb7ccf1..fc41a0e8b09 100644 --- a/sound/soc/sh/rcar/adg.c +++ b/sound/soc/sh/rcar/adg.c @@ -8,7 +8,6 @@   * for more details.   */  #include <linux/sh_clk.h> -#include <mach/clock.h>  #include "rsnd.h"  #define CLKA	0 @@ -20,51 +19,281 @@  struct rsnd_adg {  	struct clk *clk[CLKMAX]; -	int rate_of_441khz_div_6; -	int rate_of_48khz_div_6; +	int rbga_rate_for_441khz_div_6;	/* RBGA */ +	int rbgb_rate_for_48khz_div_6;	/* RBGB */ +	u32 ckr;  };  #define for_each_rsnd_clk(pos, adg, i)		\ -	for (i = 0, (pos) = adg->clk[i];	\ -	     i < CLKMAX;			\ -	     i++, (pos) = adg->clk[i]) +	for (i = 0;				\ +	     (i < CLKMAX) &&			\ +	     ((pos) = adg->clk[i]);		\ +	     i++)  #define rsnd_priv_to_adg(priv) ((struct rsnd_adg *)(priv)->adg) -static enum rsnd_reg rsnd_adg_ssi_reg_get(int id) + +static u32 rsnd_adg_ssi_ws_timing_gen2(struct rsnd_dai_stream *io) +{ +	struct rsnd_mod *mod = rsnd_io_to_mod_ssi(io); +	struct rsnd_priv *priv = rsnd_mod_to_priv(mod); +	int id = rsnd_mod_id(mod); +	int ws = id; + +	if (rsnd_ssi_is_pin_sharing(rsnd_ssi_mod_get(priv, id))) { +		switch (id) { +		case 1: +		case 2: +			ws = 0; +			break; +		case 4: +			ws = 3; +			break; +		case 8: +			ws = 7; +			break; +		} +	} + +	return (0x6 + ws) << 8; +} + +int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_dai *rdai, +				 struct rsnd_mod *mod, +				 struct rsnd_dai_stream *io) +{ +	int id = rsnd_mod_id(mod); +	int shift = (id % 2) ? 16 : 0; +	u32 mask, val; + +	val = rsnd_adg_ssi_ws_timing_gen2(io); + +	val  = val	<< shift; +	mask = 0xffff	<< shift; + +	rsnd_mod_bset(mod, CMDOUT_TIMSEL, mask, val); + +	return 0; +} + +static int rsnd_adg_set_src_timsel_gen2(struct rsnd_dai *rdai, +					struct rsnd_mod *mod, +					struct rsnd_dai_stream *io, +					u32 timsel) +{ +	int is_play = rsnd_dai_is_play(rdai, io); +	int id = rsnd_mod_id(mod); +	int shift = (id % 2) ? 16 : 0; +	u32 mask, ws; +	u32 in, out; + +	ws = rsnd_adg_ssi_ws_timing_gen2(io); + +	in  = (is_play) ? timsel : ws; +	out = (is_play) ? ws     : timsel; + +	in   = in	<< shift; +	out  = out	<< shift; +	mask = 0xffff	<< shift; + +	switch (id / 2) { +	case 0: +		rsnd_mod_bset(mod, SRCIN_TIMSEL0,  mask, in); +		rsnd_mod_bset(mod, SRCOUT_TIMSEL0, mask, out); +		break; +	case 1: +		rsnd_mod_bset(mod, SRCIN_TIMSEL1,  mask, in); +		rsnd_mod_bset(mod, SRCOUT_TIMSEL1, mask, out); +		break; +	case 2: +		rsnd_mod_bset(mod, SRCIN_TIMSEL2,  mask, in); +		rsnd_mod_bset(mod, SRCOUT_TIMSEL2, mask, out); +		break; +	case 3: +		rsnd_mod_bset(mod, SRCIN_TIMSEL3,  mask, in); +		rsnd_mod_bset(mod, SRCOUT_TIMSEL3, mask, out); +		break; +	case 4: +		rsnd_mod_bset(mod, SRCIN_TIMSEL4,  mask, in); +		rsnd_mod_bset(mod, SRCOUT_TIMSEL4, mask, out); +		break; +	} + +	return 0; +} + +int rsnd_adg_set_convert_clk_gen2(struct rsnd_mod *mod, +				  struct rsnd_dai *rdai, +				  struct rsnd_dai_stream *io, +				  unsigned int src_rate, +				  unsigned int dst_rate) +{ +	struct rsnd_priv *priv = rsnd_mod_to_priv(mod); +	struct rsnd_adg *adg = rsnd_priv_to_adg(priv); +	struct device *dev = rsnd_priv_to_dev(priv); +	int idx, sel, div, step, ret; +	u32 val, en; +	unsigned int min, diff; +	unsigned int sel_rate [] = { +		clk_get_rate(adg->clk[CLKA]),	/* 0000: CLKA */ +		clk_get_rate(adg->clk[CLKB]),	/* 0001: CLKB */ +		clk_get_rate(adg->clk[CLKC]),	/* 0010: CLKC */ +		adg->rbga_rate_for_441khz_div_6,/* 0011: RBGA */ +		adg->rbgb_rate_for_48khz_div_6,	/* 0100: RBGB */ +	}; + +	min = ~0; +	val = 0; +	en = 0; +	for (sel = 0; sel < ARRAY_SIZE(sel_rate); sel++) { +		idx = 0; +		step = 2; + +		if (!sel_rate[sel]) +			continue; + +		for (div = 2; div <= 98304; div += step) { +			diff = abs(src_rate - sel_rate[sel] / div); +			if (min > diff) { +				val = (sel << 8) | idx; +				min = diff; +				en = 1 << (sel + 1); /* fixme */ +			} + +			/* +			 * step of 0_0000 / 0_0001 / 0_1101 +			 * are out of order +			 */ +			if ((idx > 2) && (idx % 2)) +				step *= 2; +			if (idx == 0x1c) { +				div += step; +				step *= 2; +			} +			idx++; +		} +	} + +	if (min == ~0) { +		dev_err(dev, "no Input clock\n"); +		return -EIO; +	} + +	ret = rsnd_adg_set_src_timsel_gen2(rdai, mod, io, val); +	if (ret < 0) { +		dev_err(dev, "timsel error\n"); +		return ret; +	} + +	rsnd_mod_bset(mod, DIV_EN, en, en); + +	return 0; +} + +int rsnd_adg_set_convert_timing_gen2(struct rsnd_mod *mod, +				     struct rsnd_dai *rdai, +				     struct rsnd_dai_stream *io)  { -	enum rsnd_reg reg; +	u32 val = rsnd_adg_ssi_ws_timing_gen2(io); + +	return rsnd_adg_set_src_timsel_gen2(rdai, mod, io, val); +} + +int rsnd_adg_set_convert_clk_gen1(struct rsnd_priv *priv, +				  struct rsnd_mod *mod, +				  unsigned int src_rate, +				  unsigned int dst_rate) +{ +	struct rsnd_adg *adg = rsnd_priv_to_adg(priv); +	struct device *dev = rsnd_priv_to_dev(priv); +	int idx, sel, div, shift; +	u32 mask, val; +	int id = rsnd_mod_id(mod); +	unsigned int sel_rate [] = { +		clk_get_rate(adg->clk[CLKA]),	/* 000: CLKA */ +		clk_get_rate(adg->clk[CLKB]),	/* 001: CLKB */ +		clk_get_rate(adg->clk[CLKC]),	/* 010: CLKC */ +		0,				/* 011: MLBCLK (not used) */ +		adg->rbga_rate_for_441khz_div_6,/* 100: RBGA */ +		adg->rbgb_rate_for_48khz_div_6,	/* 101: RBGB */ +	}; + +	/* find div (= 1/128, 1/256, 1/512, 1/1024, 1/2048 */ +	for (sel = 0; sel < ARRAY_SIZE(sel_rate); sel++) { +		for (div  = 128,	idx = 0; +		     div <= 2048; +		     div *= 2,		idx++) { +			if (src_rate == sel_rate[sel] / div) { +				val = (idx << 4) | sel; +				goto find_rate; +			} +		} +	} +	dev_err(dev, "can't find convert src clk\n"); +	return -EINVAL; + +find_rate: +	shift	= (id % 4) * 8; +	mask	= 0xFF << shift; +	val	= val << shift; + +	dev_dbg(dev, "adg convert src clk = %02x\n", val); + +	switch (id / 4) { +	case 0: +		rsnd_mod_bset(mod, AUDIO_CLK_SEL3, mask, val); +		break; +	case 1: +		rsnd_mod_bset(mod, AUDIO_CLK_SEL4, mask, val); +		break; +	case 2: +		rsnd_mod_bset(mod, AUDIO_CLK_SEL5, mask, val); +		break; +	} + +	/* +	 * Gen1 doesn't need dst_rate settings, +	 * since it uses SSI WS pin. +	 * see also rsnd_src_set_route_if_gen1() +	 */ + +	return 0; +} + +static void rsnd_adg_set_ssi_clk(struct rsnd_mod *mod, u32 val) +{ +	int id = rsnd_mod_id(mod); +	int shift = (id % 4) * 8; +	u32 mask = 0xFF << shift; + +	val = val << shift;  	/*  	 * SSI 8 is not connected to ADG.  	 * it works with SSI 7  	 */  	if (id == 8) -		return RSND_REG_MAX; - -	if (0 <= id && id <= 3) -		reg = RSND_REG_AUDIO_CLK_SEL0; -	else if (4 <= id && id <= 7) -		reg = RSND_REG_AUDIO_CLK_SEL1; -	else -		reg = RSND_REG_AUDIO_CLK_SEL2; - -	return reg; +		return; + +	switch (id / 4) { +	case 0: +		rsnd_mod_bset(mod, AUDIO_CLK_SEL0, mask, val); +		break; +	case 1: +		rsnd_mod_bset(mod, AUDIO_CLK_SEL1, mask, val); +		break; +	case 2: +		rsnd_mod_bset(mod, AUDIO_CLK_SEL2, mask, val); +		break; +	}  }  int rsnd_adg_ssi_clk_stop(struct rsnd_mod *mod)  { -	struct rsnd_priv *priv = rsnd_mod_to_priv(mod); -	enum rsnd_reg reg; -	int id; -  	/*  	 * "mod" = "ssi" here.  	 * we can get "ssi id" from mod  	 */ -	id  = rsnd_mod_id(mod); -	reg = rsnd_adg_ssi_reg_get(id); - -	rsnd_write(priv, mod, reg, 0); +	rsnd_adg_set_ssi_clk(mod, 0);  	return 0;  } @@ -75,8 +304,7 @@ int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *mod, unsigned int rate)  	struct rsnd_adg *adg = rsnd_priv_to_adg(priv);  	struct device *dev = rsnd_priv_to_dev(priv);  	struct clk *clk; -	enum rsnd_reg reg; -	int id, shift, i; +	int i;  	u32 data;  	int sel_table[] = {  		[CLKA] = 0x1, @@ -102,12 +330,12 @@ int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *mod, unsigned int rate)  	/*  	 * find 1/6 clock from BRGA/BRGB  	 */ -	if (rate == adg->rate_of_441khz_div_6) { +	if (rate == adg->rbga_rate_for_441khz_div_6) {  		data = 0x10;  		goto found_clock;  	} -	if (rate == adg->rate_of_48khz_div_6) { +	if (rate == adg->rbgb_rate_for_48khz_div_6) {  		data = 0x20;  		goto found_clock;  	} @@ -116,23 +344,19 @@ int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *mod, unsigned int rate)  found_clock: +	/* see rsnd_adg_ssi_clk_init() */ +	rsnd_mod_bset(mod, SSICKR, 0x00FF0000, adg->ckr); +	rsnd_mod_write(mod, BRRA,  0x00000002); /* 1/6 */ +	rsnd_mod_write(mod, BRRB,  0x00000002); /* 1/6 */ +  	/*  	 * This "mod" = "ssi" here.  	 * we can get "ssi id" from mod  	 */ -	id  = rsnd_mod_id(mod); -	reg = rsnd_adg_ssi_reg_get(id); +	rsnd_adg_set_ssi_clk(mod, data); -	dev_dbg(dev, "ADG: ssi%d selects clk%d = %d", id, i, rate); - -	/* -	 * Enable SSIx clock -	 */ -	shift = (id % 4) * 8; - -	rsnd_bset(priv, mod, reg, -		   0xFF << shift, -		   data << shift); +	dev_dbg(dev, "ADG: ssi%d selects clk%d = %d", +		rsnd_mod_id(mod), i, rate);  	return 0;  } @@ -161,8 +385,8 @@ static void rsnd_adg_ssi_clk_init(struct rsnd_priv *priv, struct rsnd_adg *adg)  	 *	rsnd_adg_ssi_clk_try_start()  	 */  	ckr = 0; -	adg->rate_of_441khz_div_6 = 0; -	adg->rate_of_48khz_div_6  = 0; +	adg->rbga_rate_for_441khz_div_6 = 0; +	adg->rbgb_rate_for_48khz_div_6  = 0;  	for_each_rsnd_clk(clk, adg, i) {  		rate = clk_get_rate(clk); @@ -170,25 +394,23 @@ static void rsnd_adg_ssi_clk_init(struct rsnd_priv *priv, struct rsnd_adg *adg)  			continue;  		/* RBGA */ -		if (!adg->rate_of_441khz_div_6 && (0 == rate % 44100)) { -			adg->rate_of_441khz_div_6 = rate / 6; +		if (!adg->rbga_rate_for_441khz_div_6 && (0 == rate % 44100)) { +			adg->rbga_rate_for_441khz_div_6 = rate / 6;  			ckr |= brg_table[i] << 20;  		}  		/* RBGB */ -		if (!adg->rate_of_48khz_div_6 && (0 == rate % 48000)) { -			adg->rate_of_48khz_div_6 = rate / 6; +		if (!adg->rbgb_rate_for_48khz_div_6 && (0 == rate % 48000)) { +			adg->rbgb_rate_for_48khz_div_6 = rate / 6;  			ckr |= brg_table[i] << 16;  		}  	} -	rsnd_priv_bset(priv, SSICKR, 0x00FF0000, ckr); -	rsnd_priv_write(priv, BRRA,  0x00000002); /* 1/6 */ -	rsnd_priv_write(priv, BRRB,  0x00000002); /* 1/6 */ +	adg->ckr = ckr;  }  int rsnd_adg_probe(struct platform_device *pdev, -		   struct rcar_snd_info *info, +		   const struct rsnd_of_data *of_data,  		   struct rsnd_priv *priv)  {  	struct rsnd_adg *adg; @@ -202,16 +424,13 @@ int rsnd_adg_probe(struct platform_device *pdev,  		return -ENOMEM;  	} -	adg->clk[CLKA] = clk_get(NULL, "audio_clk_a"); -	adg->clk[CLKB] = clk_get(NULL, "audio_clk_b"); -	adg->clk[CLKC] = clk_get(NULL, "audio_clk_c"); -	adg->clk[CLKI] = clk_get(NULL, "audio_clk_internal"); -	for_each_rsnd_clk(clk, adg, i) { -		if (IS_ERR(clk)) { -			dev_err(dev, "Audio clock failed\n"); -			return -EIO; -		} -	} +	adg->clk[CLKA]	= devm_clk_get(dev, "clk_a"); +	adg->clk[CLKB]	= devm_clk_get(dev, "clk_b"); +	adg->clk[CLKC]	= devm_clk_get(dev, "clk_c"); +	adg->clk[CLKI]	= devm_clk_get(dev, "clk_i"); + +	for_each_rsnd_clk(clk, adg, i) +		dev_dbg(dev, "clk %d : %p\n", i, clk);  	rsnd_adg_ssi_clk_init(priv, adg); @@ -221,14 +440,3 @@ int rsnd_adg_probe(struct platform_device *pdev,  	return 0;  } - -void rsnd_adg_remove(struct platform_device *pdev, -		     struct rsnd_priv *priv) -{ -	struct rsnd_adg *adg = priv->adg; -	struct clk *clk; -	int i; - -	for_each_rsnd_clk(clk, adg, i) -		clk_put(clk); -} diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index a3570602851..4e86265f625 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -73,13 +73,13 @@   *   |  +- ssi[2]   *   |  ...   *   | - *   | ** these control scu + *   | ** these control src   *   | - *   +- scu + *   +- src   *      | - *      +- scu[0] - *      +- scu[1] - *      +- scu[2] + *      +- src[0] + *      +- src[1] + *      +- src[2]   *      ...   *   * @@ -94,62 +94,38 @@   *   */  #include <linux/pm_runtime.h> +#include <linux/shdma-base.h>  #include "rsnd.h"  #define RSND_RATES SNDRV_PCM_RATE_8000_96000  #define RSND_FMTS (SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE) +static struct rsnd_of_data rsnd_of_data_gen1 = { +	.flags = RSND_GEN1, +}; + +static struct rsnd_of_data rsnd_of_data_gen2 = { +	.flags = RSND_GEN2, +}; + +static struct of_device_id rsnd_of_match[] = { +	{ .compatible = "renesas,rcar_sound-gen1", .data = &rsnd_of_data_gen1 }, +	{ .compatible = "renesas,rcar_sound-gen2", .data = &rsnd_of_data_gen2 }, +	{}, +}; +MODULE_DEVICE_TABLE(of, rsnd_of_match); +  /*   *	rsnd_platform functions   */  #define rsnd_platform_call(priv, dai, func, param...)	\ -	(!(priv->info->func) ? -ENODEV :		\ +	(!(priv->info->func) ? 0 :		\  	 priv->info->func(param)) - -/* - *	basic function - */ -u32 rsnd_read(struct rsnd_priv *priv, -	      struct rsnd_mod *mod, enum rsnd_reg reg) -{ -	void __iomem *base = rsnd_gen_reg_get(priv, mod, reg); - -	BUG_ON(!base); - -	return ioread32(base); -} - -void rsnd_write(struct rsnd_priv *priv, -		struct rsnd_mod *mod, -		enum rsnd_reg reg, u32 data) -{ -	void __iomem *base = rsnd_gen_reg_get(priv, mod, reg); -	struct device *dev = rsnd_priv_to_dev(priv); - -	BUG_ON(!base); - -	dev_dbg(dev, "w %p : %08x\n", base, data); - -	iowrite32(data, base); -} - -void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod, -	       enum rsnd_reg reg, u32 mask, u32 data) -{ -	void __iomem *base = rsnd_gen_reg_get(priv, mod, reg); -	struct device *dev = rsnd_priv_to_dev(priv); -	u32 val; - -	BUG_ON(!base); - -	val = ioread32(base); -	val &= ~mask; -	val |= data & mask; -	iowrite32(val, base); - -	dev_dbg(dev, "s %p : %08x\n", base, val); -} +#define rsnd_is_enable_path(io, name) \ +	((io)->info ? (io)->info->name : NULL) +#define rsnd_info_id(priv, io, name) \ +	((io)->info->name - priv->info->name##_info)  /*   *	rsnd_mod functions @@ -165,17 +141,19 @@ char *rsnd_mod_name(struct rsnd_mod *mod)  void rsnd_mod_init(struct rsnd_priv *priv,  		   struct rsnd_mod *mod,  		   struct rsnd_mod_ops *ops, +		   enum rsnd_mod_type type,  		   int id)  {  	mod->priv	= priv;  	mod->id		= id;  	mod->ops	= ops; -	INIT_LIST_HEAD(&mod->list); +	mod->type	= type;  }  /*   *	rsnd_dma functions   */ +static void __rsnd_dma_start(struct rsnd_dma *dma);  static void rsnd_dma_continue(struct rsnd_dma *dma)  {  	/* push next A or B plane */ @@ -186,8 +164,9 @@ static void rsnd_dma_continue(struct rsnd_dma *dma)  void rsnd_dma_start(struct rsnd_dma *dma)  {  	/* push both A and B plane*/ +	dma->offset = 0;  	dma->submit_loop = 2; -	schedule_work(&dma->work); +	__rsnd_dma_start(dma);  }  void rsnd_dma_stop(struct rsnd_dma *dma) @@ -200,33 +179,49 @@ void rsnd_dma_stop(struct rsnd_dma *dma)  static void rsnd_dma_complete(void *data)  {  	struct rsnd_dma *dma = (struct rsnd_dma *)data; -	struct rsnd_priv *priv = dma->priv; +	struct rsnd_mod *mod = rsnd_dma_to_mod(dma); +	struct rsnd_priv *priv = rsnd_mod_to_priv(rsnd_dma_to_mod(dma)); +	struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);  	unsigned long flags;  	rsnd_lock(priv, flags); -	dma->complete(dma); - +	/* +	 * Renesas sound Gen1 needs 1 DMAC, +	 * Gen2 needs 2 DMAC. +	 * In Gen2 case, it are Audio-DMAC, and Audio-DMAC-peri-peri. +	 * But, Audio-DMAC-peri-peri doesn't have interrupt, +	 * and this driver is assuming that here. +	 * +	 * If Audio-DMAC-peri-peri has interrpt, +	 * rsnd_dai_pointer_update() will be called twice, +	 * ant it will breaks io->byte_pos +	 */  	if (dma->submit_loop)  		rsnd_dma_continue(dma);  	rsnd_unlock(priv, flags); + +	rsnd_dai_pointer_update(io, io->byte_per_period);  } -static void rsnd_dma_do_work(struct work_struct *work) +static void __rsnd_dma_start(struct rsnd_dma *dma)  { -	struct rsnd_dma *dma = container_of(work, struct rsnd_dma, work); -	struct rsnd_priv *priv = dma->priv; +	struct rsnd_mod *mod = rsnd_dma_to_mod(dma); +	struct rsnd_priv *priv = rsnd_mod_to_priv(mod); +	struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); +	struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);  	struct device *dev = rsnd_priv_to_dev(priv);  	struct dma_async_tx_descriptor *desc;  	dma_addr_t buf; -	size_t len; +	size_t len = io->byte_per_period;  	int i;  	for (i = 0; i < dma->submit_loop; i++) { -		if (dma->inquiry(dma, &buf, &len) < 0) -			return; +		buf = runtime->dma_addr + +			rsnd_dai_pointer_offset(io, dma->offset + len); +		dma->offset = len;  		desc = dmaengine_prep_slave_single(  			dma->chan, buf, len, dma->dir, @@ -244,9 +239,15 @@ static void rsnd_dma_do_work(struct work_struct *work)  			return;  		} +		dma_async_issue_pending(dma->chan);  	} +} + +static void rsnd_dma_do_work(struct work_struct *work) +{ +	struct rsnd_dma *dma = container_of(work, struct rsnd_dma, work); -	dma_async_issue_pending(dma->chan); +	__rsnd_dma_start(dma);  }  int rsnd_dma_available(struct rsnd_dma *dma) @@ -254,21 +255,83 @@ int rsnd_dma_available(struct rsnd_dma *dma)  	return !!dma->chan;  } -static bool rsnd_dma_filter(struct dma_chan *chan, void *param) +#define DMA_NAME_SIZE 16 +#define MOD_MAX 4 /* MEM/SSI/SRC/DVC */ +static int _rsnd_dma_of_name(char *dma_name, struct rsnd_mod *mod) +{ +	if (mod) +		return snprintf(dma_name, DMA_NAME_SIZE / 2, "%s%d", +			 rsnd_mod_name(mod), rsnd_mod_id(mod)); +	else +		return snprintf(dma_name, DMA_NAME_SIZE / 2, "mem"); + +} + +static void rsnd_dma_of_name(struct rsnd_dma *dma, +			     int is_play, char *dma_name)  { -	chan->private = param; +	struct rsnd_mod *this = rsnd_dma_to_mod(dma); +	struct rsnd_dai_stream *io = rsnd_mod_to_io(this); +	struct rsnd_mod *ssi = rsnd_io_to_mod_ssi(io); +	struct rsnd_mod *src = rsnd_io_to_mod_src(io); +	struct rsnd_mod *dvc = rsnd_io_to_mod_dvc(io); +	struct rsnd_mod *mod[MOD_MAX]; +	struct rsnd_mod *src_mod, *dst_mod; +	int i, index; -	return true; + +	for (i = 0; i < MOD_MAX; i++) +		mod[i] = NULL; + +	/* +	 * in play case... +	 * +	 * src -> dst +	 * +	 * mem -> SSI +	 * mem -> SRC -> SSI +	 * mem -> SRC -> DVC -> SSI +	 */ +	mod[0] = NULL; /* for "mem" */ +	index = 1; +	for (i = 1; i < MOD_MAX; i++) { +		if (!src) { +			mod[i] = ssi; +			break; +		} else if (!dvc) { +			mod[i] = src; +			src = NULL; +		} else { +			mod[i] = dvc; +			dvc = NULL; +		} + +		if (mod[i] == this) +			index = i; +	} + +	if (is_play) { +		src_mod = mod[index - 1]; +		dst_mod = mod[index]; +	} else { +		src_mod = mod[index]; +		dst_mod = mod[index - 1]; +	} + +	index = 0; +	index = _rsnd_dma_of_name(dma_name + index, src_mod); +	*(dma_name + index++) = '_'; +	index = _rsnd_dma_of_name(dma_name + index, dst_mod);  }  int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma, -		  int is_play, int id, -		  int (*inquiry)(struct rsnd_dma *dma, -				  dma_addr_t *buf, int *len), -		  int (*complete)(struct rsnd_dma *dma)) +		  int is_play, int id)  {  	struct device *dev = rsnd_priv_to_dev(priv); +	struct dma_slave_config cfg; +	char dma_name[DMA_NAME_SIZE];  	dma_cap_mask_t mask; +	int ret;  	if (dma->chan) {  		dev_err(dev, "it already has dma channel\n"); @@ -278,22 +341,37 @@ int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma,  	dma_cap_zero(mask);  	dma_cap_set(DMA_SLAVE, mask); -	dma->slave.shdma_slave.slave_id = id; +	if (dev->of_node) +		rsnd_dma_of_name(dma, is_play, dma_name); +	else +		snprintf(dma_name, DMA_NAME_SIZE, +			 is_play ? "tx" : "rx"); + +	dev_dbg(dev, "dma name : %s\n", dma_name); -	dma->chan = dma_request_channel(mask, rsnd_dma_filter, -					&dma->slave.shdma_slave); +	dma->chan = dma_request_slave_channel_compat(mask, shdma_chan_filter, +						     (void *)id, dev, +						     dma_name);  	if (!dma->chan) {  		dev_err(dev, "can't get dma channel\n");  		return -EIO;  	} +	rsnd_gen_dma_addr(priv, dma, &cfg, is_play, id); + +	ret = dmaengine_slave_config(dma->chan, &cfg); +	if (ret < 0) +		goto rsnd_dma_init_err; +  	dma->dir = is_play ? DMA_TO_DEVICE : DMA_FROM_DEVICE; -	dma->priv = priv; -	dma->inquiry = inquiry; -	dma->complete = complete;  	INIT_WORK(&dma->work, rsnd_dma_do_work);  	return 0; + +rsnd_dma_init_err: +	rsnd_dma_quit(priv, dma); + +	return ret;  }  void  rsnd_dma_quit(struct rsnd_priv *priv, @@ -306,47 +384,81 @@ void  rsnd_dma_quit(struct rsnd_priv *priv,  }  /* - *	rsnd_dai functions + *	settting function   */ -#define rsnd_dai_call(rdai, io, fn)			\ -({							\ -	struct rsnd_mod *mod, *n;			\ -	int ret = 0;					\ -	for_each_rsnd_mod(mod, n, io) {			\ -		ret = rsnd_mod_call(mod, fn, rdai, io);	\ -		if (ret < 0)				\ -			break;				\ -	}						\ -	ret;						\ -}) - -int rsnd_dai_connect(struct rsnd_dai *rdai, -		     struct rsnd_mod *mod, -		     struct rsnd_dai_stream *io) +u32 rsnd_get_adinr(struct rsnd_mod *mod)  {  	struct rsnd_priv *priv = rsnd_mod_to_priv(mod); +	struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); +	struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);  	struct device *dev = rsnd_priv_to_dev(priv); +	u32 adinr = runtime->channels; -	if (!mod) { -		dev_err(dev, "NULL mod\n"); -		return -EIO; +	switch (runtime->sample_bits) { +	case 16: +		adinr |= (8 << 16); +		break; +	case 32: +		adinr |= (0 << 16); +		break; +	default: +		dev_warn(dev, "not supported sample bits\n"); +		return 0;  	} -	if (!list_empty(&mod->list)) { +	return adinr; +} + +/* + *	rsnd_dai functions + */ +#define __rsnd_mod_call(mod, func, rdai...)			\ +({								\ +	struct rsnd_priv *priv = rsnd_mod_to_priv(mod);		\ +	struct device *dev = rsnd_priv_to_dev(priv);		\ +	dev_dbg(dev, "%s [%d] %s\n",				\ +		rsnd_mod_name(mod), rsnd_mod_id(mod), #func);	\ +	(mod)->ops->func(mod, rdai);				\ +}) + +#define rsnd_mod_call(mod, func, rdai...)	\ +	(!(mod) ? -ENODEV :			\ +	 !((mod)->ops->func) ? 0 :		\ +	 __rsnd_mod_call(mod, func, rdai)) + +#define rsnd_dai_call(fn, io, rdai...)				\ +({								\ +	struct rsnd_mod *mod;					\ +	int ret = 0, i;						\ +	for (i = 0; i < RSND_MOD_MAX; i++) {			\ +		mod = (io)->mod[i];				\ +		if (!mod)					\ +			continue;				\ +		ret = rsnd_mod_call(mod, fn, rdai);		\ +		if (ret < 0)					\ +			break;					\ +	}							\ +	ret;							\ +}) + +static int rsnd_dai_connect(struct rsnd_mod *mod, +			    struct rsnd_dai_stream *io) +{ +	if (!mod) +		return -EIO; + +	if (io->mod[mod->type]) { +		struct rsnd_priv *priv = rsnd_mod_to_priv(mod); +		struct device *dev = rsnd_priv_to_dev(priv); +  		dev_err(dev, "%s%d is not empty\n",  			rsnd_mod_name(mod),  			rsnd_mod_id(mod));  		return -EIO;  	} -	list_add_tail(&mod->list, &io->head); - -	return 0; -} - -int rsnd_dai_disconnect(struct rsnd_mod *mod) -{ -	list_del_init(&mod->list); +	io->mod[mod->type] = mod; +	mod->io = io;  	return 0;  } @@ -355,7 +467,7 @@ int rsnd_dai_id(struct rsnd_priv *priv, struct rsnd_dai *rdai)  {  	int id = rdai - priv->rdai; -	if ((id < 0) || (id >= rsnd_dai_nr(priv))) +	if ((id < 0) || (id >= rsnd_rdai_nr(priv)))  		return -EINVAL;  	return id; @@ -363,6 +475,9 @@ int rsnd_dai_id(struct rsnd_priv *priv, struct rsnd_dai *rdai)  struct rsnd_dai *rsnd_dai_get(struct rsnd_priv *priv, int id)  { +	if ((id < 0) || (id >= rsnd_rdai_nr(priv))) +		return NULL; +  	return priv->rdai + id;  } @@ -418,10 +533,6 @@ static int rsnd_dai_stream_init(struct rsnd_dai_stream *io,  {  	struct snd_pcm_runtime *runtime = substream->runtime; -	if (!list_empty(&io->head)) -		return -EIO; - -	INIT_LIST_HEAD(&io->head);  	io->substream		= substream;  	io->byte_pos		= 0;  	io->period_pos		= 0; @@ -457,10 +568,7 @@ static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd,  	struct rsnd_priv *priv = snd_soc_dai_get_drvdata(dai);  	struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai);  	struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream); -	struct rsnd_mod *mod = rsnd_ssi_mod_get_frm_dai(priv, -						rsnd_dai_id(priv, rdai), -						rsnd_dai_is_play(rdai, io)); -	int ssi_id = rsnd_mod_id(mod); +	int ssi_id = rsnd_mod_id(rsnd_io_to_mod_ssi(io));  	int ret;  	unsigned long flags; @@ -476,28 +584,20 @@ static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd,  		if (ret < 0)  			goto dai_trigger_end; -		ret = rsnd_gen_path_init(priv, rdai, io); -		if (ret < 0) -			goto dai_trigger_end; - -		ret = rsnd_dai_call(rdai, io, init); +		ret = rsnd_dai_call(init, io, rdai);  		if (ret < 0)  			goto dai_trigger_end; -		ret = rsnd_dai_call(rdai, io, start); +		ret = rsnd_dai_call(start, io, rdai);  		if (ret < 0)  			goto dai_trigger_end;  		break;  	case SNDRV_PCM_TRIGGER_STOP: -		ret = rsnd_dai_call(rdai, io, stop); -		if (ret < 0) -			goto dai_trigger_end; - -		ret = rsnd_dai_call(rdai, io, quit); +		ret = rsnd_dai_call(stop, io, rdai);  		if (ret < 0)  			goto dai_trigger_end; -		ret = rsnd_gen_path_exit(priv, rdai, io); +		ret = rsnd_dai_call(quit, io, rdai);  		if (ret < 0)  			goto dai_trigger_end; @@ -522,10 +622,10 @@ static int rsnd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)  	/* set master/slave audio interface */  	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {  	case SND_SOC_DAIFMT_CBM_CFM: -		rdai->clk_master = 1; +		rdai->clk_master = 0;  		break;  	case SND_SOC_DAIFMT_CBS_CFS: -		rdai->clk_master = 0; +		rdai->clk_master = 1; /* codec is slave, cpu is master */  		break;  	default:  		return -EINVAL; @@ -576,26 +676,156 @@ static const struct snd_soc_dai_ops rsnd_soc_dai_ops = {  	.set_fmt	= rsnd_soc_dai_set_fmt,  }; +#define rsnd_path_parse(priv, io, type)				\ +({								\ +	struct rsnd_mod *mod;					\ +	int ret = 0;						\ +	int id = -1;						\ +								\ +	if (rsnd_is_enable_path(io, type)) {			\ +		id = rsnd_info_id(priv, io, type);		\ +		if (id >= 0) {					\ +			mod = rsnd_##type##_mod_get(priv, id);	\ +			ret = rsnd_dai_connect(mod, io);	\ +		}						\ +	}							\ +	ret;							\ +}) + +static int rsnd_path_init(struct rsnd_priv *priv, +			  struct rsnd_dai *rdai, +			  struct rsnd_dai_stream *io) +{ +	int ret; + +	/* +	 * Gen1 is created by SRU/SSI, and this SRU is base module of +	 * Gen2's SCU/SSIU/SSI. (Gen2 SCU/SSIU came from SRU) +	 * +	 * Easy image is.. +	 *	Gen1 SRU = Gen2 SCU + SSIU + etc +	 * +	 * Gen2 SCU path is very flexible, but, Gen1 SRU (SCU parts) is +	 * using fixed path. +	 */ + +	/* SRC */ +	ret = rsnd_path_parse(priv, io, src); +	if (ret < 0) +		return ret; + +	/* SSI */ +	ret = rsnd_path_parse(priv, io, ssi); +	if (ret < 0) +		return ret; + +	/* DVC */ +	ret = rsnd_path_parse(priv, io, dvc); +	if (ret < 0) +		return ret; + +	return ret; +} + +static void rsnd_of_parse_dai(struct platform_device *pdev, +			      const struct rsnd_of_data *of_data, +			      struct rsnd_priv *priv) +{ +	struct device_node *dai_node,	*dai_np; +	struct device_node *ssi_node,	*ssi_np; +	struct device_node *src_node,	*src_np; +	struct device_node *playback, *capture; +	struct rsnd_dai_platform_info *dai_info; +	struct rcar_snd_info *info = rsnd_priv_to_info(priv); +	struct device *dev = &pdev->dev; +	int nr, i; +	int dai_i, ssi_i, src_i; + +	if (!of_data) +		return; + +	dai_node = of_get_child_by_name(dev->of_node, "rcar_sound,dai"); +	if (!dai_node) +		return; + +	nr = of_get_child_count(dai_node); +	if (!nr) +		return; + +	dai_info = devm_kzalloc(dev, +				sizeof(struct rsnd_dai_platform_info) * nr, +				GFP_KERNEL); +	if (!dai_info) { +		dev_err(dev, "dai info allocation error\n"); +		return; +	} + +	info->dai_info_nr	= nr; +	info->dai_info		= dai_info; + +	ssi_node = of_get_child_by_name(dev->of_node, "rcar_sound,ssi"); +	src_node = of_get_child_by_name(dev->of_node, "rcar_sound,src"); + +#define mod_parse(name)							\ +if (name##_node) {							\ +	struct rsnd_##name##_platform_info *name##_info;		\ +									\ +	name##_i = 0;							\ +	for_each_child_of_node(name##_node, name##_np) {		\ +		name##_info = info->name##_info + name##_i;		\ +									\ +		if (name##_np == playback)				\ +			dai_info->playback.name = name##_info;		\ +		if (name##_np == capture)				\ +			dai_info->capture.name = name##_info;		\ +									\ +		name##_i++;						\ +	}								\ +} + +	/* +	 * parse all dai +	 */ +	dai_i = 0; +	for_each_child_of_node(dai_node, dai_np) { +		dai_info = info->dai_info + dai_i; + +		for (i = 0;; i++) { + +			playback = of_parse_phandle(dai_np, "playback", i); +			capture  = of_parse_phandle(dai_np, "capture", i); + +			if (!playback && !capture) +				break; + +			mod_parse(ssi); +			mod_parse(src); + +			if (playback) +				of_node_put(playback); +			if (capture) +				of_node_put(capture); +		} + +		dai_i++; +	} +} +  static int rsnd_dai_probe(struct platform_device *pdev, -			  struct rcar_snd_info *info, +			  const struct rsnd_of_data *of_data,  			  struct rsnd_priv *priv)  {  	struct snd_soc_dai_driver *drv; +	struct rcar_snd_info *info = rsnd_priv_to_info(priv);  	struct rsnd_dai *rdai; -	struct rsnd_mod *pmod, *cmod; +	struct rsnd_ssi_platform_info *pmod, *cmod;  	struct device *dev = rsnd_priv_to_dev(priv);  	int dai_nr;  	int i; -	/* get max dai nr */ -	for (dai_nr = 0; dai_nr < 32; dai_nr++) { -		pmod = rsnd_ssi_mod_get_frm_dai(priv, dai_nr, 1); -		cmod = rsnd_ssi_mod_get_frm_dai(priv, dai_nr, 0); - -		if (!pmod && !cmod) -			break; -	} +	rsnd_of_parse_dai(pdev, of_data, priv); +	dai_nr = info->dai_info_nr;  	if (!dai_nr) {  		dev_err(dev, "no dai\n");  		return -EIO; @@ -608,17 +838,19 @@ static int rsnd_dai_probe(struct platform_device *pdev,  		return -ENOMEM;  	} +	priv->rdai_nr	= dai_nr; +	priv->daidrv	= drv; +	priv->rdai	= rdai; +  	for (i = 0; i < dai_nr; i++) { +		rdai[i].info = &info->dai_info[i]; -		pmod = rsnd_ssi_mod_get_frm_dai(priv, i, 1); -		cmod = rsnd_ssi_mod_get_frm_dai(priv, i, 0); +		pmod = rdai[i].info->playback.ssi; +		cmod = rdai[i].info->capture.ssi;  		/*  		 *	init rsnd_dai  		 */ -		INIT_LIST_HEAD(&rdai[i].playback.head); -		INIT_LIST_HEAD(&rdai[i].capture.head); -  		snprintf(rdai[i].name, RSND_DAI_NAME_SIZE, "rsnd-dai.%d", i);  		/* @@ -631,12 +863,18 @@ static int rsnd_dai_probe(struct platform_device *pdev,  			drv[i].playback.formats		= RSND_FMTS;  			drv[i].playback.channels_min	= 2;  			drv[i].playback.channels_max	= 2; + +			rdai[i].playback.info = &info->dai_info[i].playback; +			rsnd_path_init(priv, &rdai[i], &rdai[i].playback);  		}  		if (cmod) {  			drv[i].capture.rates		= RSND_RATES;  			drv[i].capture.formats		= RSND_FMTS;  			drv[i].capture.channels_min	= 2;  			drv[i].capture.channels_max	= 2; + +			rdai[i].capture.info = &info->dai_info[i].capture; +			rsnd_path_init(priv, &rdai[i], &rdai[i].capture);  		}  		dev_dbg(dev, "%s (%s/%s)\n", rdai[i].name, @@ -644,18 +882,9 @@ static int rsnd_dai_probe(struct platform_device *pdev,  			cmod ? "capture" : "  --   ");  	} -	priv->dai_nr	= dai_nr; -	priv->daidrv	= drv; -	priv->rdai	= rdai; -  	return 0;  } -static void rsnd_dai_remove(struct platform_device *pdev, -			  struct rsnd_priv *priv) -{ -} -  /*   *		pcm ops   */ @@ -664,12 +893,6 @@ static struct snd_pcm_hardware rsnd_pcm_hardware = {  			SNDRV_PCM_INFO_MMAP		|  			SNDRV_PCM_INFO_MMAP_VALID	|  			SNDRV_PCM_INFO_PAUSE, -	.formats		= RSND_FMTS, -	.rates			= RSND_RATES, -	.rate_min		= 8000, -	.rate_max		= 192000, -	.channels_min		= 2, -	.channels_max		= 2,  	.buffer_bytes_max	= 64 * 1024,  	.period_bytes_min	= 32,  	.period_bytes_max	= 8192, @@ -725,6 +948,20 @@ static struct snd_pcm_ops rsnd_pcm_ops = {  static int rsnd_pcm_new(struct snd_soc_pcm_runtime *rtd)  { +	struct rsnd_priv *priv = snd_soc_dai_get_drvdata(rtd->cpu_dai); +	struct rsnd_dai *rdai; +	int i, ret; + +	for_each_rsnd_dai(rdai, priv, i) { +		ret = rsnd_dai_call(pcm_new, &rdai->playback, rdai, rtd); +		if (ret) +			return ret; + +		ret = rsnd_dai_call(pcm_new, &rdai->capture, rdai, rtd); +		if (ret) +			return ret; +	} +  	return snd_pcm_lib_preallocate_pages_for_all(  		rtd->pcm,  		SNDRV_DMA_TYPE_DEV, @@ -755,9 +992,31 @@ static int rsnd_probe(struct platform_device *pdev)  	struct rcar_snd_info *info;  	struct rsnd_priv *priv;  	struct device *dev = &pdev->dev; -	int ret; +	struct rsnd_dai *rdai; +	const struct of_device_id *of_id = of_match_device(rsnd_of_match, dev); +	const struct rsnd_of_data *of_data; +	int (*probe_func[])(struct platform_device *pdev, +			    const struct rsnd_of_data *of_data, +			    struct rsnd_priv *priv) = { +		rsnd_gen_probe, +		rsnd_ssi_probe, +		rsnd_src_probe, +		rsnd_dvc_probe, +		rsnd_adg_probe, +		rsnd_dai_probe, +	}; +	int ret, i; + +	info = NULL; +	of_data = NULL; +	if (of_id) { +		info = devm_kzalloc(&pdev->dev, +				    sizeof(struct rcar_snd_info), GFP_KERNEL); +		of_data = of_id->data; +	} else { +		info = pdev->dev.platform_data; +	} -	info = pdev->dev.platform_data;  	if (!info) {  		dev_err(dev, "driver needs R-Car sound information\n");  		return -ENODEV; @@ -772,32 +1031,28 @@ static int rsnd_probe(struct platform_device *pdev)  		return -ENODEV;  	} -	priv->dev	= dev; +	priv->pdev	= pdev;  	priv->info	= info;  	spin_lock_init(&priv->lock);  	/*  	 *	init each module  	 */ -	ret = rsnd_gen_probe(pdev, info, priv); -	if (ret < 0) -		return ret; - -	ret = rsnd_scu_probe(pdev, info, priv); -	if (ret < 0) -		return ret; - -	ret = rsnd_adg_probe(pdev, info, priv); -	if (ret < 0) -		return ret; +	for (i = 0; i < ARRAY_SIZE(probe_func); i++) { +		ret = probe_func[i](pdev, of_data, priv); +		if (ret) +			return ret; +	} -	ret = rsnd_ssi_probe(pdev, info, priv); -	if (ret < 0) -		return ret; +	for_each_rsnd_dai(rdai, priv, i) { +		ret = rsnd_dai_call(probe, &rdai->playback, rdai); +		if (ret) +			return ret; -	ret = rsnd_dai_probe(pdev, info, priv); -	if (ret < 0) -		return ret; +		ret = rsnd_dai_call(probe, &rdai->capture, rdai); +		if (ret) +			return ret; +	}  	/*  	 *	asoc register @@ -809,7 +1064,7 @@ static int rsnd_probe(struct platform_device *pdev)  	}  	ret = snd_soc_register_component(dev, &rsnd_soc_component, -					 priv->daidrv, rsnd_dai_nr(priv)); +					 priv->daidrv, rsnd_rdai_nr(priv));  	if (ret < 0) {  		dev_err(dev, "cannot snd dai register\n");  		goto exit_snd_soc; @@ -831,17 +1086,20 @@ exit_snd_soc:  static int rsnd_remove(struct platform_device *pdev)  {  	struct rsnd_priv *priv = dev_get_drvdata(&pdev->dev); +	struct rsnd_dai *rdai; +	int ret, i;  	pm_runtime_disable(&pdev->dev); -	/* -	 *	remove each module -	 */ -	rsnd_ssi_remove(pdev, priv); -	rsnd_adg_remove(pdev, priv); -	rsnd_scu_remove(pdev, priv); -	rsnd_dai_remove(pdev, priv); -	rsnd_gen_remove(pdev, priv); +	for_each_rsnd_dai(rdai, priv, i) { +		ret = rsnd_dai_call(remove, &rdai->playback, rdai); +		if (ret) +			return ret; + +		ret = rsnd_dai_call(remove, &rdai->capture, rdai); +		if (ret) +			return ret; +	}  	return 0;  } @@ -849,6 +1107,7 @@ static int rsnd_remove(struct platform_device *pdev)  static struct platform_driver rsnd_driver = {  	.driver	= {  		.name	= "rcar_sound", +		.of_match_table = rsnd_of_match,  	},  	.probe		= rsnd_probe,  	.remove		= rsnd_remove, diff --git a/sound/soc/sh/rcar/dvc.c b/sound/soc/sh/rcar/dvc.c new file mode 100644 index 00000000000..ed000700689 --- /dev/null +++ b/sound/soc/sh/rcar/dvc.c @@ -0,0 +1,289 @@ +/* + * Renesas R-Car DVC support + * + * Copyright (C) 2014 Renesas Solutions Corp. + * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> + * + * 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 "rsnd.h" + +#define RSND_DVC_NAME_SIZE	16 +#define RSND_DVC_VOLUME_MAX	100 +#define RSND_DVC_VOLUME_NUM	2 + +#define DVC_NAME "dvc" + +struct rsnd_dvc { +	struct rsnd_dvc_platform_info *info; /* rcar_snd.h */ +	struct rsnd_mod mod; +	struct clk *clk; +	long volume[RSND_DVC_VOLUME_NUM]; +}; + +#define rsnd_mod_to_dvc(_mod)	\ +	container_of((_mod), struct rsnd_dvc, mod) + +#define for_each_rsnd_dvc(pos, priv, i)				\ +	for ((i) = 0;						\ +	     ((i) < rsnd_dvc_nr(priv)) &&			\ +	     ((pos) = (struct rsnd_dvc *)(priv)->dvc + i);	\ +	     i++) + +static void rsnd_dvc_volume_update(struct rsnd_mod *mod) +{ +	struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod); +	u32 max = (0x00800000 - 1); +	u32 vol[RSND_DVC_VOLUME_NUM]; +	int i; + +	for (i = 0; i < RSND_DVC_VOLUME_NUM; i++) +		vol[i] = max / RSND_DVC_VOLUME_MAX * dvc->volume[i]; + +	rsnd_mod_write(mod, DVC_VOL0R, vol[0]); +	rsnd_mod_write(mod, DVC_VOL1R, vol[1]); +} + +static int rsnd_dvc_probe_gen2(struct rsnd_mod *mod, +			       struct rsnd_dai *rdai) +{ +	struct rsnd_priv *priv = rsnd_mod_to_priv(mod); +	struct device *dev = rsnd_priv_to_dev(priv); + +	dev_dbg(dev, "%s (Gen2) is probed\n", rsnd_mod_name(mod)); + +	return 0; +} + +static int rsnd_dvc_init(struct rsnd_mod *dvc_mod, +			 struct rsnd_dai *rdai) +{ +	struct rsnd_dvc *dvc = rsnd_mod_to_dvc(dvc_mod); +	struct rsnd_dai_stream *io = rsnd_mod_to_io(dvc_mod); +	struct rsnd_priv *priv = rsnd_mod_to_priv(dvc_mod); +	struct rsnd_mod *src_mod = rsnd_io_to_mod_src(io); +	struct device *dev = rsnd_priv_to_dev(priv); +	int dvc_id = rsnd_mod_id(dvc_mod); +	int src_id = rsnd_mod_id(src_mod); +	u32 route[] = { +		[0] = 0x30000, +		[1] = 0x30001, +		[2] = 0x40000, +		[3] = 0x10000, +		[4] = 0x20000, +		[5] = 0x40100 +	}; + +	if (src_id >= ARRAY_SIZE(route)) { +		dev_err(dev, "DVC%d isn't connected to SRC%d\n", dvc_id, src_id); +		return -EINVAL; +	} + +	clk_prepare_enable(dvc->clk); + +	/* +	 * fixme +	 * it doesn't support CTU/MIX +	 */ +	rsnd_mod_write(dvc_mod, CMD_ROUTE_SLCT, route[src_id]); + +	rsnd_mod_write(dvc_mod, DVC_SWRSR, 0); +	rsnd_mod_write(dvc_mod, DVC_SWRSR, 1); + +	rsnd_mod_write(dvc_mod, DVC_DVUIR, 1); + +	rsnd_mod_write(dvc_mod, DVC_ADINR, rsnd_get_adinr(dvc_mod)); + +	/*  enable Volume  */ +	rsnd_mod_write(dvc_mod, DVC_DVUCR, 0x100); + +	/* ch0/ch1 Volume */ +	rsnd_dvc_volume_update(dvc_mod); + +	rsnd_mod_write(dvc_mod, DVC_DVUIR, 0); + +	rsnd_mod_write(dvc_mod, DVC_DVUER, 1); + +	rsnd_adg_set_cmd_timsel_gen2(rdai, dvc_mod, io); + +	return 0; +} + +static int rsnd_dvc_quit(struct rsnd_mod *mod, +			 struct rsnd_dai *rdai) +{ +	struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod); + +	clk_disable_unprepare(dvc->clk); + +	return 0; +} + +static int rsnd_dvc_start(struct rsnd_mod *mod, +			  struct rsnd_dai *rdai) +{ +	rsnd_mod_write(mod, CMD_CTRL, 0x10); + +	return 0; +} + +static int rsnd_dvc_stop(struct rsnd_mod *mod, +			 struct rsnd_dai *rdai) +{ +	rsnd_mod_write(mod, CMD_CTRL, 0); + +	return 0; +} + +static int rsnd_dvc_volume_info(struct snd_kcontrol *kctrl, +			       struct snd_ctl_elem_info *uinfo) +{ +	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; +	uinfo->count = RSND_DVC_VOLUME_NUM; +	uinfo->value.integer.min = 0; +	uinfo->value.integer.max = RSND_DVC_VOLUME_MAX; + +	return 0; +} + +static int rsnd_dvc_volume_get(struct snd_kcontrol *kctrl, +			      struct snd_ctl_elem_value *ucontrol) +{ +	struct rsnd_mod *mod = snd_kcontrol_chip(kctrl); +	struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod); +	int i; + +	for (i = 0; i < RSND_DVC_VOLUME_NUM; i++) +		ucontrol->value.integer.value[i] = dvc->volume[i]; + +	return 0; +} + +static int rsnd_dvc_volume_put(struct snd_kcontrol *kctrl, +			      struct snd_ctl_elem_value *ucontrol) +{ +	struct rsnd_mod *mod = snd_kcontrol_chip(kctrl); +	struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod); +	int i, change = 0; + +	for (i = 0; i < RSND_DVC_VOLUME_NUM; i++) { +		if (ucontrol->value.integer.value[i] < 0 || +		    ucontrol->value.integer.value[i] > RSND_DVC_VOLUME_MAX) +			return -EINVAL; + +		change |= (ucontrol->value.integer.value[i] != dvc->volume[i]); +	} + +	if (change) { +		for (i = 0; i < RSND_DVC_VOLUME_NUM; i++) +			dvc->volume[i] = ucontrol->value.integer.value[i]; + +		rsnd_dvc_volume_update(mod); +	} + +	return change; +} + +static int rsnd_dvc_pcm_new(struct rsnd_mod *mod, +			    struct rsnd_dai *rdai, +			    struct snd_soc_pcm_runtime *rtd) +{ +	struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); +	struct rsnd_priv *priv = rsnd_mod_to_priv(mod); +	struct device *dev = rsnd_priv_to_dev(priv); +	struct snd_card *card = rtd->card->snd_card; +	struct snd_kcontrol *kctrl; +	static struct snd_kcontrol_new knew = { +		.iface		= SNDRV_CTL_ELEM_IFACE_MIXER, +		.name		= "Playback Volume", +		.info		= rsnd_dvc_volume_info, +		.get		= rsnd_dvc_volume_get, +		.put		= rsnd_dvc_volume_put, +	}; +	int ret; + +	if (!rsnd_dai_is_play(rdai, io)) { +		dev_err(dev, "DVC%d is connected to Capture DAI\n", +			rsnd_mod_id(mod)); +		return -EINVAL; +	} + +	kctrl = snd_ctl_new1(&knew, mod); +	if (!kctrl) +		return -ENOMEM; + +	ret = snd_ctl_add(card, kctrl); +	if (ret < 0) +		return ret; + +	return 0; +} + +static struct rsnd_mod_ops rsnd_dvc_ops = { +	.name		= DVC_NAME, +	.probe		= rsnd_dvc_probe_gen2, +	.init		= rsnd_dvc_init, +	.quit		= rsnd_dvc_quit, +	.start		= rsnd_dvc_start, +	.stop		= rsnd_dvc_stop, +	.pcm_new	= rsnd_dvc_pcm_new, +}; + +struct rsnd_mod *rsnd_dvc_mod_get(struct rsnd_priv *priv, int id) +{ +	if (WARN_ON(id < 0 || id >= rsnd_dvc_nr(priv))) +		id = 0; + +	return &((struct rsnd_dvc *)(priv->dvc) + id)->mod; +} + +int rsnd_dvc_probe(struct platform_device *pdev, +		   const struct rsnd_of_data *of_data, +		   struct rsnd_priv *priv) +{ +	struct rcar_snd_info *info = rsnd_priv_to_info(priv); +	struct device *dev = rsnd_priv_to_dev(priv); +	struct rsnd_dvc *dvc; +	struct clk *clk; +	char name[RSND_DVC_NAME_SIZE]; +	int i, nr; + +	nr = info->dvc_info_nr; +	if (!nr) +		return 0; + +	/* This driver doesn't support Gen1 at this point */ +	if (rsnd_is_gen1(priv)) { +		dev_warn(dev, "CMD is not supported on Gen1\n"); +		return -EINVAL; +	} + +	dvc	= devm_kzalloc(dev, sizeof(*dvc) * nr, GFP_KERNEL); +	if (!dvc) { +		dev_err(dev, "CMD allocate failed\n"); +		return -ENOMEM; +	} + +	priv->dvc_nr	= nr; +	priv->dvc	= dvc; + +	for_each_rsnd_dvc(dvc, priv, i) { +		snprintf(name, RSND_DVC_NAME_SIZE, "%s.%d", +			 DVC_NAME, i); + +		clk = devm_clk_get(dev, name); +		if (IS_ERR(clk)) +			return PTR_ERR(clk); + +		dvc->info = &info->dvc_info[i]; +		dvc->clk  = clk; + +		rsnd_mod_init(priv, &dvc->mod, &rsnd_dvc_ops, RSND_MOD_DVC, i); + +		dev_dbg(dev, "CMD%d probed\n", i); +	} + +	return 0; +} diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c index babb203b43b..1dd2b7d38c2 100644 --- a/sound/soc/sh/rcar/gen.c +++ b/sound/soc/sh/rcar/gen.c @@ -10,136 +10,418 @@   */  #include "rsnd.h" -struct rsnd_gen_ops { -	int (*path_init)(struct rsnd_priv *priv, -			 struct rsnd_dai *rdai, -			 struct rsnd_dai_stream *io); -	int (*path_exit)(struct rsnd_priv *priv, -			 struct rsnd_dai *rdai, -			 struct rsnd_dai_stream *io); -}; - -struct rsnd_gen_reg_map { -	int index;	/* -1 : not supported */ -	u32 offset_id;	/* offset of ssi0, ssi1, ssi2... */ -	u32 offset_adr;	/* offset of SSICR, SSISR, ... */ -}; -  struct rsnd_gen {  	void __iomem *base[RSND_BASE_MAX]; -	struct rsnd_gen_reg_map reg_map[RSND_REG_MAX];  	struct rsnd_gen_ops *ops; + +	struct regmap *regmap; +	struct regmap_field *regs[RSND_REG_MAX];  };  #define rsnd_priv_to_gen(p)	((struct rsnd_gen *)(p)->gen) +#define RSND_REG_SET(gen, id, reg_id, offset, _id_offset, _id_size)	\ +	[id] = {							\ +		.reg = (unsigned int)gen->base[reg_id] + offset,	\ +		.lsb = 0,						\ +		.msb = 31,						\ +		.id_size = _id_size,					\ +		.id_offset = _id_offset,				\ +	} +  /* - *		Gen2 - *		will be filled in the future + *		basic function   */ +static int rsnd_regmap_write32(void *context, const void *_data, size_t count) +{ +	struct rsnd_priv *priv = context; +	struct device *dev = rsnd_priv_to_dev(priv); +	u32 *data = (u32 *)_data; +	u32 val = data[1]; +	void __iomem *reg = (void *)data[0]; + +	iowrite32(val, reg); + +	dev_dbg(dev, "w %p : %08x\n", reg, val); + +	return 0; +} + +static int rsnd_regmap_read32(void *context, +			      const void *_data, size_t reg_size, +			      void *_val, size_t val_size) +{ +	struct rsnd_priv *priv = context; +	struct device *dev = rsnd_priv_to_dev(priv); +	u32 *data = (u32 *)_data; +	u32 *val = (u32 *)_val; +	void __iomem *reg = (void *)data[0]; + +	*val = ioread32(reg); + +	dev_dbg(dev, "r %p : %08x\n", reg, *val); + +	return 0; +} + +static struct regmap_bus rsnd_regmap_bus = { +	.write				= rsnd_regmap_write32, +	.read				= rsnd_regmap_read32, +	.reg_format_endian_default	= REGMAP_ENDIAN_NATIVE, +	.val_format_endian_default	= REGMAP_ENDIAN_NATIVE, +}; + +static int rsnd_is_accessible_reg(struct rsnd_priv *priv, +				  struct rsnd_gen *gen, enum rsnd_reg reg) +{ +	if (!gen->regs[reg]) { +		struct device *dev = rsnd_priv_to_dev(priv); + +		dev_err(dev, "unsupported register access %x\n", reg); +		return 0; +	} + +	return 1; +} + +u32 rsnd_read(struct rsnd_priv *priv, +	      struct rsnd_mod *mod, enum rsnd_reg reg) +{ +	struct rsnd_gen *gen = rsnd_priv_to_gen(priv); +	u32 val; + +	if (!rsnd_is_accessible_reg(priv, gen, reg)) +		return 0; + +	regmap_fields_read(gen->regs[reg], rsnd_mod_id(mod), &val); + +	return val; +} + +void rsnd_write(struct rsnd_priv *priv, +		struct rsnd_mod *mod, +		enum rsnd_reg reg, u32 data) +{ +	struct rsnd_gen *gen = rsnd_priv_to_gen(priv); + +	if (!rsnd_is_accessible_reg(priv, gen, reg)) +		return; + +	regmap_fields_write(gen->regs[reg], rsnd_mod_id(mod), data); +} + +void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod, +	       enum rsnd_reg reg, u32 mask, u32 data) +{ +	struct rsnd_gen *gen = rsnd_priv_to_gen(priv); + +	if (!rsnd_is_accessible_reg(priv, gen, reg)) +		return; + +	regmap_fields_update_bits(gen->regs[reg], rsnd_mod_id(mod), +				  mask, data); +} + +static int rsnd_gen_regmap_init(struct rsnd_priv *priv, +				struct rsnd_gen  *gen, +				struct reg_field *regf) +{ +	int i; +	struct device *dev = rsnd_priv_to_dev(priv); +	struct regmap_config regc; + +	memset(®c, 0, sizeof(regc)); +	regc.reg_bits = 32; +	regc.val_bits = 32; + +	gen->regmap = devm_regmap_init(dev, &rsnd_regmap_bus, priv, ®c); +	if (IS_ERR(gen->regmap)) { +		dev_err(dev, "regmap error %ld\n", PTR_ERR(gen->regmap)); +		return PTR_ERR(gen->regmap); +	} + +	for (i = 0; i < RSND_REG_MAX; i++) { +		gen->regs[i] = NULL; +		if (!regf[i].reg) +			continue; + +		gen->regs[i] = devm_regmap_field_alloc(dev, gen->regmap, regf[i]); +		if (IS_ERR(gen->regs[i])) +			return PTR_ERR(gen->regs[i]); + +	} + +	return 0; +}  /* - *		Gen1 + *	DMA read/write register offset + * + *	RSND_xxx_I_N	for Audio DMAC input + *	RSND_xxx_O_N	for Audio DMAC output + *	RSND_xxx_I_P	for Audio DMAC peri peri input + *	RSND_xxx_O_P	for Audio DMAC peri peri output + * + *	ex) R-Car H2 case + *	      mod        / DMAC in    / DMAC out   / DMAC PP in / DMAC pp out + *	SSI : 0xec541000 / 0xec241008 / 0xec24100c / 0xec400000 / 0xec400000 + *	SCU : 0xec500000 / 0xec000000 / 0xec004000 / 0xec300000 / 0xec304000 + *	CMD : 0xec500000 / 0xec008000                             0xec308000   */ -static int rsnd_gen1_path_init(struct rsnd_priv *priv, -			       struct rsnd_dai *rdai, -			       struct rsnd_dai_stream *io) +#define RDMA_SSI_I_N(addr, i)	(addr ##_reg - 0x00300000 + (0x40 * i) + 0x8) +#define RDMA_SSI_O_N(addr, i)	(addr ##_reg - 0x00300000 + (0x40 * i) + 0xc) + +#define RDMA_SSI_I_P(addr, i)	(addr ##_reg - 0x00141000 + (0x1000 * i)) +#define RDMA_SSI_O_P(addr, i)	(addr ##_reg - 0x00141000 + (0x1000 * i)) + +#define RDMA_SRC_I_N(addr, i)	(addr ##_reg - 0x00500000 + (0x400 * i)) +#define RDMA_SRC_O_N(addr, i)	(addr ##_reg - 0x004fc000 + (0x400 * i)) + +#define RDMA_SRC_I_P(addr, i)	(addr ##_reg - 0x00200000 + (0x400 * i)) +#define RDMA_SRC_O_P(addr, i)	(addr ##_reg - 0x001fc000 + (0x400 * i)) + +#define RDMA_CMD_O_N(addr, i)	(addr ##_reg - 0x004f8000 + (0x400 * i)) +#define RDMA_CMD_O_P(addr, i)	(addr ##_reg - 0x001f8000 + (0x400 * i)) + +void rsnd_gen_dma_addr(struct rsnd_priv *priv, +		       struct rsnd_dma *dma, +		       struct dma_slave_config *cfg, +		       int is_play, int slave_id)  { -	struct rsnd_mod *mod; -	int ret; -	int id; +	struct platform_device *pdev = rsnd_priv_to_pdev(priv); +	struct device *dev = rsnd_priv_to_dev(priv); +	struct rsnd_mod *mod = rsnd_dma_to_mod(dma); +	struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); +	dma_addr_t ssi_reg = platform_get_resource(pdev, +				IORESOURCE_MEM, RSND_GEN2_SSI)->start; +	dma_addr_t src_reg = platform_get_resource(pdev, +				IORESOURCE_MEM, RSND_GEN2_SCU)->start; +	int is_ssi = !!(rsnd_io_to_mod_ssi(io) == mod); +	int use_src = !!rsnd_io_to_mod_src(io); +	int use_dvc = !!rsnd_io_to_mod_dvc(io); +	int id = rsnd_mod_id(mod); +	struct dma_addr { +		dma_addr_t src_addr; +		dma_addr_t dst_addr; +	} dma_addrs[2][2][3] = { +		{ /* SRC */ +			/* Capture */ +			{{ 0,				0 }, +			 { RDMA_SRC_O_N(src, id),	0 }, +			 { RDMA_CMD_O_N(src, id),	0 }}, +			/* Playback */ +			{{ 0,				0, }, +			 { 0,				RDMA_SRC_I_N(src, id) }, +			 { 0,				RDMA_SRC_I_N(src, id) }} +		}, { /* SSI */ +			/* Capture */ +			{{ RDMA_SSI_O_N(ssi, id),	0 }, +			 { RDMA_SSI_O_P(ssi, id),	RDMA_SRC_I_P(src, id) }, +			 { RDMA_SSI_O_P(ssi, id),	RDMA_SRC_I_P(src, id) }}, +			/* Playback */ +			{{ 0,				RDMA_SSI_I_N(ssi, id) }, +			 { RDMA_SRC_O_P(src, id),	RDMA_SSI_I_P(ssi, id) }, +			 { RDMA_CMD_O_P(src, id),	RDMA_SSI_I_P(ssi, id) }} +		} +	}; + +	cfg->slave_id	= slave_id; +	cfg->src_addr	= 0; +	cfg->dst_addr	= 0; +	cfg->direction	= is_play ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM;  	/* -	 * Gen1 is created by SRU/SSI, and this SRU is base module of -	 * Gen2's SCU/SSIU/SSI. (Gen2 SCU/SSIU came from SRU) -	 * -	 * Easy image is.. -	 *	Gen1 SRU = Gen2 SCU + SSIU + etc -	 * -	 * Gen2 SCU path is very flexible, but, Gen1 SRU (SCU parts) is -	 * using fixed path. -	 * -	 * Then, SSI id = SCU id here +	 * gen1 uses default DMA addr  	 */ +	if (rsnd_is_gen1(priv)) +		return; -	/* get SSI's ID */ -	mod = rsnd_ssi_mod_get_frm_dai(priv, -				       rsnd_dai_id(priv, rdai), -				       rsnd_dai_is_play(rdai, io)); -	id = rsnd_mod_id(mod); +	/* it shouldn't happen */ +	if (use_dvc & !use_src) { +		dev_err(dev, "DVC is selected without SRC\n"); +		return; +	} -	/* SSI */ -	mod = rsnd_ssi_mod_get(priv, id); -	ret = rsnd_dai_connect(rdai, mod, io); -	if (ret < 0) -		return ret; +	cfg->src_addr = dma_addrs[is_ssi][is_play][use_src + use_dvc].src_addr; +	cfg->dst_addr = dma_addrs[is_ssi][is_play][use_src + use_dvc].dst_addr; -	/* SCU */ -	mod = rsnd_scu_mod_get(priv, id); -	ret = rsnd_dai_connect(rdai, mod, io); +	dev_dbg(dev, "dma%d addr - src : %x / dst : %x\n", +		id, cfg->src_addr, cfg->dst_addr); +} -	return ret; +/* + *		Gen2 + */ + +/* single address mapping */ +#define RSND_GEN2_S_REG(gen, reg, id, offset)				\ +	RSND_REG_SET(gen, RSND_REG_##id, RSND_GEN2_##reg, offset, 0, 10) + +/* multi address mapping */ +#define RSND_GEN2_M_REG(gen, reg, id, offset, _id_offset)		\ +	RSND_REG_SET(gen, RSND_REG_##id, RSND_GEN2_##reg, offset, _id_offset, 10) + +static int rsnd_gen2_regmap_init(struct rsnd_priv *priv, struct rsnd_gen *gen) +{ +	struct reg_field regf[RSND_REG_MAX] = { +		RSND_GEN2_S_REG(gen, SSIU,	SSI_MODE0,	0x800), +		RSND_GEN2_S_REG(gen, SSIU,	SSI_MODE1,	0x804), +		/* FIXME: it needs SSI_MODE2/3 in the future */ +		RSND_GEN2_M_REG(gen, SSIU,	SSI_BUSIF_MODE,	0x0,	0x80), +		RSND_GEN2_M_REG(gen, SSIU,	SSI_BUSIF_ADINR,0x4,	0x80), +		RSND_GEN2_M_REG(gen, SSIU,	SSI_CTRL,	0x10,	0x80), +		RSND_GEN2_M_REG(gen, SSIU,	INT_ENABLE,	0x18,	0x80), + +		RSND_GEN2_M_REG(gen, SCU,	SRC_BUSIF_MODE,	0x0,	0x20), +		RSND_GEN2_M_REG(gen, SCU,	SRC_ROUTE_MODE0,0xc,	0x20), +		RSND_GEN2_M_REG(gen, SCU,	SRC_CTRL,	0x10,	0x20), +		RSND_GEN2_M_REG(gen, SCU,	CMD_ROUTE_SLCT,	0x18c,	0x20), +		RSND_GEN2_M_REG(gen, SCU,	CMD_CTRL,	0x190,	0x20), +		RSND_GEN2_M_REG(gen, SCU,	SRC_SWRSR,	0x200,	0x40), +		RSND_GEN2_M_REG(gen, SCU,	SRC_SRCIR,	0x204,	0x40), +		RSND_GEN2_M_REG(gen, SCU,	SRC_ADINR,	0x214,	0x40), +		RSND_GEN2_M_REG(gen, SCU,	SRC_IFSCR,	0x21c,	0x40), +		RSND_GEN2_M_REG(gen, SCU,	SRC_IFSVR,	0x220,	0x40), +		RSND_GEN2_M_REG(gen, SCU,	SRC_SRCCR,	0x224,	0x40), +		RSND_GEN2_M_REG(gen, SCU,	SRC_BSDSR,	0x22c,	0x40), +		RSND_GEN2_M_REG(gen, SCU,	SRC_BSISR,	0x238,	0x40), +		RSND_GEN2_M_REG(gen, SCU,	DVC_SWRSR,	0xe00,	0x100), +		RSND_GEN2_M_REG(gen, SCU,	DVC_DVUIR,	0xe04,	0x100), +		RSND_GEN2_M_REG(gen, SCU,	DVC_ADINR,	0xe08,	0x100), +		RSND_GEN2_M_REG(gen, SCU,	DVC_DVUCR,	0xe10,	0x100), +		RSND_GEN2_M_REG(gen, SCU,	DVC_ZCMCR,	0xe14,	0x100), +		RSND_GEN2_M_REG(gen, SCU,	DVC_VOL0R,	0xe28,	0x100), +		RSND_GEN2_M_REG(gen, SCU,	DVC_VOL1R,	0xe2c,	0x100), +		RSND_GEN2_M_REG(gen, SCU,	DVC_DVUER,	0xe48,	0x100), + +		RSND_GEN2_S_REG(gen, ADG,	BRRA,		0x00), +		RSND_GEN2_S_REG(gen, ADG,	BRRB,		0x04), +		RSND_GEN2_S_REG(gen, ADG,	SSICKR,		0x08), +		RSND_GEN2_S_REG(gen, ADG,	AUDIO_CLK_SEL0,	0x0c), +		RSND_GEN2_S_REG(gen, ADG,	AUDIO_CLK_SEL1,	0x10), +		RSND_GEN2_S_REG(gen, ADG,	AUDIO_CLK_SEL2,	0x14), +		RSND_GEN2_S_REG(gen, ADG,	DIV_EN,		0x30), +		RSND_GEN2_S_REG(gen, ADG,	SRCIN_TIMSEL0,	0x34), +		RSND_GEN2_S_REG(gen, ADG,	SRCIN_TIMSEL1,	0x38), +		RSND_GEN2_S_REG(gen, ADG,	SRCIN_TIMSEL2,	0x3c), +		RSND_GEN2_S_REG(gen, ADG,	SRCIN_TIMSEL3,	0x40), +		RSND_GEN2_S_REG(gen, ADG,	SRCIN_TIMSEL4,	0x44), +		RSND_GEN2_S_REG(gen, ADG,	SRCOUT_TIMSEL0,	0x48), +		RSND_GEN2_S_REG(gen, ADG,	SRCOUT_TIMSEL1,	0x4c), +		RSND_GEN2_S_REG(gen, ADG,	SRCOUT_TIMSEL2,	0x50), +		RSND_GEN2_S_REG(gen, ADG,	SRCOUT_TIMSEL3,	0x54), +		RSND_GEN2_S_REG(gen, ADG,	SRCOUT_TIMSEL4,	0x58), +		RSND_GEN2_S_REG(gen, ADG,	CMDOUT_TIMSEL,	0x5c), + +		RSND_GEN2_M_REG(gen, SSI,	SSICR,		0x00,	0x40), +		RSND_GEN2_M_REG(gen, SSI,	SSISR,		0x04,	0x40), +		RSND_GEN2_M_REG(gen, SSI,	SSITDR,		0x08,	0x40), +		RSND_GEN2_M_REG(gen, SSI,	SSIRDR,		0x0c,	0x40), +		RSND_GEN2_M_REG(gen, SSI,	SSIWSR,		0x20,	0x40), +	}; + +	return rsnd_gen_regmap_init(priv, gen, regf);  } -static int rsnd_gen1_path_exit(struct rsnd_priv *priv, -			       struct rsnd_dai *rdai, -			       struct rsnd_dai_stream *io) +static int rsnd_gen2_probe(struct platform_device *pdev, +			   struct rsnd_priv *priv)  { -	struct rsnd_mod *mod, *n; -	int ret = 0; +	struct device *dev = rsnd_priv_to_dev(priv); +	struct rsnd_gen *gen = rsnd_priv_to_gen(priv); +	struct resource *scu_res; +	struct resource *adg_res; +	struct resource *ssiu_res; +	struct resource *ssi_res; +	int ret;  	/* -	 * remove all mod from rdai +	 * map address  	 */ -	for_each_rsnd_mod(mod, n, io) -		ret |= rsnd_dai_disconnect(mod); +	scu_res  = platform_get_resource(pdev, IORESOURCE_MEM, RSND_GEN2_SCU); +	adg_res  = platform_get_resource(pdev, IORESOURCE_MEM, RSND_GEN2_ADG); +	ssiu_res = platform_get_resource(pdev, IORESOURCE_MEM, RSND_GEN2_SSIU); +	ssi_res  = platform_get_resource(pdev, IORESOURCE_MEM, RSND_GEN2_SSI); + +	gen->base[RSND_GEN2_SCU]  = devm_ioremap_resource(dev, scu_res); +	gen->base[RSND_GEN2_ADG]  = devm_ioremap_resource(dev, adg_res); +	gen->base[RSND_GEN2_SSIU] = devm_ioremap_resource(dev, ssiu_res); +	gen->base[RSND_GEN2_SSI]  = devm_ioremap_resource(dev, ssi_res); +	if (IS_ERR(gen->base[RSND_GEN2_SCU])  || +	    IS_ERR(gen->base[RSND_GEN2_ADG])  || +	    IS_ERR(gen->base[RSND_GEN2_SSIU]) || +	    IS_ERR(gen->base[RSND_GEN2_SSI])) +		return -ENODEV; -	return ret; +	ret = rsnd_gen2_regmap_init(priv, gen); +	if (ret < 0) +		return ret; + +	dev_dbg(dev, "Gen2 device probed\n"); +	dev_dbg(dev, "SCU  : %pap => %p\n", &scu_res->start, +		gen->base[RSND_GEN2_SCU]); +	dev_dbg(dev, "ADG  : %pap => %p\n", &adg_res->start, +		gen->base[RSND_GEN2_ADG]); +	dev_dbg(dev, "SSIU : %pap => %p\n", &ssiu_res->start, +		gen->base[RSND_GEN2_SSIU]); +	dev_dbg(dev, "SSI  : %pap => %p\n", &ssi_res->start, +		gen->base[RSND_GEN2_SSI]); + +	return 0;  } -static struct rsnd_gen_ops rsnd_gen1_ops = { -	.path_init	= rsnd_gen1_path_init, -	.path_exit	= rsnd_gen1_path_exit, -}; +/* + *		Gen1 + */ + +/* single address mapping */ +#define RSND_GEN1_S_REG(gen, reg, id, offset)	\ +	RSND_REG_SET(gen, RSND_REG_##id, RSND_GEN1_##reg, offset, 0, 9) -#define RSND_GEN1_REG_MAP(g, s, i, oi, oa)				\ -	do {								\ -		(g)->reg_map[RSND_REG_##i].index  = RSND_GEN1_##s;	\ -		(g)->reg_map[RSND_REG_##i].offset_id = oi;		\ -		(g)->reg_map[RSND_REG_##i].offset_adr = oa;		\ -	} while (0) +/* multi address mapping */ +#define RSND_GEN1_M_REG(gen, reg, id, offset, _id_offset)	\ +	RSND_REG_SET(gen, RSND_REG_##id, RSND_GEN1_##reg, offset, _id_offset, 9) -static void rsnd_gen1_reg_map_init(struct rsnd_gen *gen) +static int rsnd_gen1_regmap_init(struct rsnd_priv *priv, struct rsnd_gen *gen)  { -	RSND_GEN1_REG_MAP(gen, SRU,	SRC_ROUTE_SEL,	0x0,	0x00); -	RSND_GEN1_REG_MAP(gen, SRU,	SRC_TMG_SEL0,	0x0,	0x08); -	RSND_GEN1_REG_MAP(gen, SRU,	SRC_TMG_SEL1,	0x0,	0x0c); -	RSND_GEN1_REG_MAP(gen, SRU,	SRC_TMG_SEL2,	0x0,	0x10); -	RSND_GEN1_REG_MAP(gen, SRU,	SRC_CTRL,	0x0,	0xc0); -	RSND_GEN1_REG_MAP(gen, SRU,	SSI_MODE0,	0x0,	0xD0); -	RSND_GEN1_REG_MAP(gen, SRU,	SSI_MODE1,	0x0,	0xD4); -	RSND_GEN1_REG_MAP(gen, SRU,	BUSIF_MODE,	0x4,	0x20); -	RSND_GEN1_REG_MAP(gen, SRU,	BUSIF_ADINR,	0x40,	0x214); - -	RSND_GEN1_REG_MAP(gen, ADG,	BRRA,		0x0,	0x00); -	RSND_GEN1_REG_MAP(gen, ADG,	BRRB,		0x0,	0x04); -	RSND_GEN1_REG_MAP(gen, ADG,	SSICKR,		0x0,	0x08); -	RSND_GEN1_REG_MAP(gen, ADG,	AUDIO_CLK_SEL0,	0x0,	0x0c); -	RSND_GEN1_REG_MAP(gen, ADG,	AUDIO_CLK_SEL1,	0x0,	0x10); -	RSND_GEN1_REG_MAP(gen, ADG,	AUDIO_CLK_SEL3,	0x0,	0x18); -	RSND_GEN1_REG_MAP(gen, ADG,	AUDIO_CLK_SEL4,	0x0,	0x1c); -	RSND_GEN1_REG_MAP(gen, ADG,	AUDIO_CLK_SEL5,	0x0,	0x20); - -	RSND_GEN1_REG_MAP(gen, SSI,	SSICR,		0x40,	0x00); -	RSND_GEN1_REG_MAP(gen, SSI,	SSISR,		0x40,	0x04); -	RSND_GEN1_REG_MAP(gen, SSI,	SSITDR,		0x40,	0x08); -	RSND_GEN1_REG_MAP(gen, SSI,	SSIRDR,		0x40,	0x0c); -	RSND_GEN1_REG_MAP(gen, SSI,	SSIWSR,		0x40,	0x20); +	struct reg_field regf[RSND_REG_MAX] = { +		RSND_GEN1_S_REG(gen, SRU,	SRC_ROUTE_SEL,	0x00), +		RSND_GEN1_S_REG(gen, SRU,	SRC_TMG_SEL0,	0x08), +		RSND_GEN1_S_REG(gen, SRU,	SRC_TMG_SEL1,	0x0c), +		RSND_GEN1_S_REG(gen, SRU,	SRC_TMG_SEL2,	0x10), +		RSND_GEN1_S_REG(gen, SRU,	SRC_ROUTE_CTRL,	0xc0), +		RSND_GEN1_S_REG(gen, SRU,	SSI_MODE0,	0xD0), +		RSND_GEN1_S_REG(gen, SRU,	SSI_MODE1,	0xD4), +		RSND_GEN1_M_REG(gen, SRU,	SRC_BUSIF_MODE,	0x20,	0x4), +		RSND_GEN1_M_REG(gen, SRU,	SRC_ROUTE_MODE0,0x50,	0x8), +		RSND_GEN1_M_REG(gen, SRU,	SRC_SWRSR,	0x200,	0x40), +		RSND_GEN1_M_REG(gen, SRU,	SRC_SRCIR,	0x204,	0x40), +		RSND_GEN1_M_REG(gen, SRU,	SRC_ADINR,	0x214,	0x40), +		RSND_GEN1_M_REG(gen, SRU,	SRC_IFSCR,	0x21c,	0x40), +		RSND_GEN1_M_REG(gen, SRU,	SRC_IFSVR,	0x220,	0x40), +		RSND_GEN1_M_REG(gen, SRU,	SRC_SRCCR,	0x224,	0x40), +		RSND_GEN1_M_REG(gen, SRU,	SRC_MNFSR,	0x228,	0x40), + +		RSND_GEN1_S_REG(gen, ADG,	BRRA,		0x00), +		RSND_GEN1_S_REG(gen, ADG,	BRRB,		0x04), +		RSND_GEN1_S_REG(gen, ADG,	SSICKR,		0x08), +		RSND_GEN1_S_REG(gen, ADG,	AUDIO_CLK_SEL0,	0x0c), +		RSND_GEN1_S_REG(gen, ADG,	AUDIO_CLK_SEL1,	0x10), +		RSND_GEN1_S_REG(gen, ADG,	AUDIO_CLK_SEL3,	0x18), +		RSND_GEN1_S_REG(gen, ADG,	AUDIO_CLK_SEL4,	0x1c), +		RSND_GEN1_S_REG(gen, ADG,	AUDIO_CLK_SEL5,	0x20), + +		RSND_GEN1_M_REG(gen, SSI,	SSICR,		0x00,	0x40), +		RSND_GEN1_M_REG(gen, SSI,	SSISR,		0x04,	0x40), +		RSND_GEN1_M_REG(gen, SSI,	SSITDR,		0x08,	0x40), +		RSND_GEN1_M_REG(gen, SSI,	SSIRDR,		0x0c,	0x40), +		RSND_GEN1_M_REG(gen, SSI,	SSIWSR,		0x20,	0x40), +	}; + +	return rsnd_gen_regmap_init(priv, gen, regf);  }  static int rsnd_gen1_probe(struct platform_device *pdev, -			   struct rcar_snd_info *info,  			   struct rsnd_priv *priv)  {  	struct device *dev = rsnd_priv_to_dev(priv); @@ -147,6 +429,7 @@ static int rsnd_gen1_probe(struct platform_device *pdev,  	struct resource *sru_res;  	struct resource *adg_res;  	struct resource *ssi_res; +	int ret;  	/*  	 * map address @@ -163,87 +446,46 @@ static int rsnd_gen1_probe(struct platform_device *pdev,  	    IS_ERR(gen->base[RSND_GEN1_SSI]))  		return -ENODEV; -	gen->ops = &rsnd_gen1_ops; -	rsnd_gen1_reg_map_init(gen); +	ret = rsnd_gen1_regmap_init(priv, gen); +	if (ret < 0) +		return ret;  	dev_dbg(dev, "Gen1 device probed\n"); -	dev_dbg(dev, "SRU : %08x => %p\n",	sru_res->start, +	dev_dbg(dev, "SRU : %pap => %p\n",	&sru_res->start,  						gen->base[RSND_GEN1_SRU]); -	dev_dbg(dev, "ADG : %08x => %p\n",	adg_res->start, +	dev_dbg(dev, "ADG : %pap => %p\n",	&adg_res->start,  						gen->base[RSND_GEN1_ADG]); -	dev_dbg(dev, "SSI : %08x => %p\n",	ssi_res->start, +	dev_dbg(dev, "SSI : %pap => %p\n",	&ssi_res->start,  						gen->base[RSND_GEN1_SSI]);  	return 0;  } -static void rsnd_gen1_remove(struct platform_device *pdev, -			     struct rsnd_priv *priv) -{ -} -  /*   *		Gen   */ -int rsnd_gen_path_init(struct rsnd_priv *priv, -		       struct rsnd_dai *rdai, -		       struct rsnd_dai_stream *io) -{ -	struct rsnd_gen *gen = rsnd_priv_to_gen(priv); - -	return gen->ops->path_init(priv, rdai, io); -} - -int rsnd_gen_path_exit(struct rsnd_priv *priv, -		       struct rsnd_dai *rdai, -		       struct rsnd_dai_stream *io) -{ -	struct rsnd_gen *gen = rsnd_priv_to_gen(priv); - -	return gen->ops->path_exit(priv, rdai, io); -} - -void __iomem *rsnd_gen_reg_get(struct rsnd_priv *priv, -			       struct rsnd_mod *mod, -			       enum rsnd_reg reg) +static void rsnd_of_parse_gen(struct platform_device *pdev, +			      const struct rsnd_of_data *of_data, +			      struct rsnd_priv *priv)  { -	struct rsnd_gen *gen = rsnd_priv_to_gen(priv); -	struct device *dev = rsnd_priv_to_dev(priv); -	int index; -	u32 offset_id, offset_adr; - -	if (reg >= RSND_REG_MAX) { -		dev_err(dev, "rsnd_reg reg error\n"); -		return NULL; -	} - -	index		= gen->reg_map[reg].index; -	offset_id	= gen->reg_map[reg].offset_id; -	offset_adr	= gen->reg_map[reg].offset_adr; - -	if (index < 0) { -		dev_err(dev, "unsupported reg access %d\n", reg); -		return NULL; -	} +	struct rcar_snd_info *info = priv->info; -	if (offset_id && mod) -		offset_id *= rsnd_mod_id(mod); +	if (!of_data) +		return; -	/* -	 * index/offset were set on gen1/gen2 -	 */ - -	return gen->base[index] + offset_id + offset_adr; +	info->flags = of_data->flags;  }  int rsnd_gen_probe(struct platform_device *pdev, -		   struct rcar_snd_info *info, +		   const struct rsnd_of_data *of_data,  		   struct rsnd_priv *priv)  {  	struct device *dev = rsnd_priv_to_dev(priv);  	struct rsnd_gen *gen; -	int i; +	int ret; + +	rsnd_of_parse_gen(pdev, of_data, priv);  	gen = devm_kzalloc(dev, sizeof(*gen), GFP_KERNEL);  	if (!gen) { @@ -253,28 +495,14 @@ int rsnd_gen_probe(struct platform_device *pdev,  	priv->gen = gen; -	/* -	 * see -	 *	rsnd_reg_get() -	 *	rsnd_gen_probe() -	 */ -	for (i = 0; i < RSND_REG_MAX; i++) -		gen->reg_map[i].index = -1; - -	/* -	 *	init each module -	 */ +	ret = -ENODEV;  	if (rsnd_is_gen1(priv)) -		return rsnd_gen1_probe(pdev, info, priv); +		ret = rsnd_gen1_probe(pdev, priv); +	else if (rsnd_is_gen2(priv)) +		ret = rsnd_gen2_probe(pdev, priv); -	dev_err(dev, "unknown generation R-Car sound device\n"); - -	return -ENODEV; -} +	if (ret < 0) +		dev_err(dev, "unknown generation R-Car sound device\n"); -void rsnd_gen_remove(struct platform_device *pdev, -		     struct rsnd_priv *priv) -{ -	if (rsnd_is_gen1(priv)) -		rsnd_gen1_remove(pdev, priv); +	return ret;  } diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 9cc6986a8cf..39d98af5ee0 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -17,6 +17,8 @@  #include <linux/io.h>  #include <linux/list.h>  #include <linux/module.h> +#include <linux/of_device.h> +#include <linux/of_irq.h>  #include <linux/sh_dma.h>  #include <linux/workqueue.h>  #include <sound/rcar_snd.h> @@ -31,16 +33,26 @@   * see gen1/gen2 for detail   */  enum rsnd_reg { -	/* SRU/SCU */ -	RSND_REG_SRC_ROUTE_SEL, -	RSND_REG_SRC_TMG_SEL0, -	RSND_REG_SRC_TMG_SEL1, -	RSND_REG_SRC_TMG_SEL2, -	RSND_REG_SRC_CTRL, +	/* SRU/SCU/SSIU */  	RSND_REG_SSI_MODE0,  	RSND_REG_SSI_MODE1, -	RSND_REG_BUSIF_MODE, -	RSND_REG_BUSIF_ADINR, +	RSND_REG_SRC_BUSIF_MODE, +	RSND_REG_SRC_ROUTE_MODE0, +	RSND_REG_SRC_SWRSR, +	RSND_REG_SRC_SRCIR, +	RSND_REG_SRC_ADINR, +	RSND_REG_SRC_IFSCR, +	RSND_REG_SRC_IFSVR, +	RSND_REG_SRC_SRCCR, +	RSND_REG_CMD_ROUTE_SLCT, +	RSND_REG_DVC_SWRSR, +	RSND_REG_DVC_DVUIR, +	RSND_REG_DVC_ADINR, +	RSND_REG_DVC_DVUCR, +	RSND_REG_DVC_ZCMCR, +	RSND_REG_DVC_VOL0R, +	RSND_REG_DVC_VOL1R, +	RSND_REG_DVC_DVUER,  	/* ADG */  	RSND_REG_BRRA, @@ -48,10 +60,6 @@ enum rsnd_reg {  	RSND_REG_SSICKR,  	RSND_REG_AUDIO_CLK_SEL0,  	RSND_REG_AUDIO_CLK_SEL1, -	RSND_REG_AUDIO_CLK_SEL2, -	RSND_REG_AUDIO_CLK_SEL3, -	RSND_REG_AUDIO_CLK_SEL4, -	RSND_REG_AUDIO_CLK_SEL5,  	/* SSI */  	RSND_REG_SSICR, @@ -60,9 +68,67 @@ enum rsnd_reg {  	RSND_REG_SSIRDR,  	RSND_REG_SSIWSR, +	/* SHARE see below */ +	RSND_REG_SHARE01, +	RSND_REG_SHARE02, +	RSND_REG_SHARE03, +	RSND_REG_SHARE04, +	RSND_REG_SHARE05, +	RSND_REG_SHARE06, +	RSND_REG_SHARE07, +	RSND_REG_SHARE08, +	RSND_REG_SHARE09, +	RSND_REG_SHARE10, +	RSND_REG_SHARE11, +	RSND_REG_SHARE12, +	RSND_REG_SHARE13, +	RSND_REG_SHARE14, +	RSND_REG_SHARE15, +	RSND_REG_SHARE16, +	RSND_REG_SHARE17, +	RSND_REG_SHARE18, +	RSND_REG_SHARE19, +	RSND_REG_SHARE20, +	RSND_REG_SHARE21, +  	RSND_REG_MAX,  }; +/* Gen1 only */ +#define RSND_REG_SRC_ROUTE_SEL		RSND_REG_SHARE01 +#define RSND_REG_SRC_TMG_SEL0		RSND_REG_SHARE02 +#define RSND_REG_SRC_TMG_SEL1		RSND_REG_SHARE03 +#define RSND_REG_SRC_TMG_SEL2		RSND_REG_SHARE04 +#define RSND_REG_SRC_ROUTE_CTRL		RSND_REG_SHARE05 +#define RSND_REG_SRC_MNFSR		RSND_REG_SHARE06 +#define RSND_REG_AUDIO_CLK_SEL3		RSND_REG_SHARE07 +#define RSND_REG_AUDIO_CLK_SEL4		RSND_REG_SHARE08 +#define RSND_REG_AUDIO_CLK_SEL5		RSND_REG_SHARE09 + +/* Gen2 only */ +#define RSND_REG_SRC_CTRL		RSND_REG_SHARE01 +#define RSND_REG_SSI_CTRL		RSND_REG_SHARE02 +#define RSND_REG_SSI_BUSIF_MODE		RSND_REG_SHARE03 +#define RSND_REG_SSI_BUSIF_ADINR	RSND_REG_SHARE04 +#define RSND_REG_INT_ENABLE		RSND_REG_SHARE05 +#define RSND_REG_SRC_BSDSR		RSND_REG_SHARE06 +#define RSND_REG_SRC_BSISR		RSND_REG_SHARE07 +#define RSND_REG_DIV_EN			RSND_REG_SHARE08 +#define RSND_REG_SRCIN_TIMSEL0		RSND_REG_SHARE09 +#define RSND_REG_SRCIN_TIMSEL1		RSND_REG_SHARE10 +#define RSND_REG_SRCIN_TIMSEL2		RSND_REG_SHARE11 +#define RSND_REG_SRCIN_TIMSEL3		RSND_REG_SHARE12 +#define RSND_REG_SRCIN_TIMSEL4		RSND_REG_SHARE13 +#define RSND_REG_SRCOUT_TIMSEL0		RSND_REG_SHARE14 +#define RSND_REG_SRCOUT_TIMSEL1		RSND_REG_SHARE15 +#define RSND_REG_SRCOUT_TIMSEL2		RSND_REG_SHARE16 +#define RSND_REG_SRCOUT_TIMSEL3		RSND_REG_SHARE17 +#define RSND_REG_SRCOUT_TIMSEL4		RSND_REG_SHARE18 +#define RSND_REG_AUDIO_CLK_SEL2		RSND_REG_SHARE19 +#define RSND_REG_CMD_CTRL		RSND_REG_SHARE20 +#define RSND_REG_CMDOUT_TIMSEL		RSND_REG_SHARE21 + +struct rsnd_of_data;  struct rsnd_priv;  struct rsnd_mod;  struct rsnd_dai; @@ -78,38 +144,31 @@ struct rsnd_dai_stream;  #define rsnd_mod_bset(m, r, s, d) \  	rsnd_bset(rsnd_mod_to_priv(m), m, RSND_REG_##r, s, d) -#define rsnd_priv_read(p, r)		rsnd_read(p, NULL, RSND_REG_##r) -#define rsnd_priv_write(p, r, d)	rsnd_write(p, NULL, RSND_REG_##r, d) -#define rsnd_priv_bset(p, r, s, d)	rsnd_bset(p, NULL, RSND_REG_##r, s, d) -  u32 rsnd_read(struct rsnd_priv *priv, struct rsnd_mod *mod, enum rsnd_reg reg);  void rsnd_write(struct rsnd_priv *priv, struct rsnd_mod *mod,  		enum rsnd_reg reg, u32 data);  void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod, enum rsnd_reg reg,  		    u32 mask, u32 data); +u32 rsnd_get_adinr(struct rsnd_mod *mod);  /*   *	R-Car DMA   */  struct rsnd_dma { -	struct rsnd_priv	*priv;  	struct sh_dmae_slave	slave;  	struct work_struct	work;  	struct dma_chan		*chan;  	enum dma_data_direction dir; -	int (*inquiry)(struct rsnd_dma *dma, dma_addr_t *buf, int *len); -	int (*complete)(struct rsnd_dma *dma);  	int submit_loop; +	int offset; /* it cares A/B plane */  };  void rsnd_dma_start(struct rsnd_dma *dma);  void rsnd_dma_stop(struct rsnd_dma *dma);  int rsnd_dma_available(struct rsnd_dma *dma);  int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma, -	int is_play, int id, -	int (*inquiry)(struct rsnd_dma *dma, dma_addr_t *buf, int *len), -	int (*complete)(struct rsnd_dma *dma)); +	int is_play, int id);  void  rsnd_dma_quit(struct rsnd_priv *priv,  		    struct rsnd_dma *dma); @@ -117,45 +176,52 @@ void  rsnd_dma_quit(struct rsnd_priv *priv,  /*   *	R-Car sound mod   */ +enum rsnd_mod_type { +	RSND_MOD_SRC = 0, +	RSND_MOD_SSI, +	RSND_MOD_DVC, +	RSND_MOD_MAX, +};  struct rsnd_mod_ops {  	char *name; +	int (*probe)(struct rsnd_mod *mod, +		     struct rsnd_dai *rdai); +	int (*remove)(struct rsnd_mod *mod, +		      struct rsnd_dai *rdai);  	int (*init)(struct rsnd_mod *mod, -		    struct rsnd_dai *rdai, -		    struct rsnd_dai_stream *io); +		    struct rsnd_dai *rdai);  	int (*quit)(struct rsnd_mod *mod, -		    struct rsnd_dai *rdai, -		    struct rsnd_dai_stream *io); +		    struct rsnd_dai *rdai);  	int (*start)(struct rsnd_mod *mod, -		     struct rsnd_dai *rdai, -		     struct rsnd_dai_stream *io); +		     struct rsnd_dai *rdai);  	int (*stop)(struct rsnd_mod *mod, -		    struct rsnd_dai *rdai, -		    struct rsnd_dai_stream *io); +		    struct rsnd_dai *rdai); +	int (*pcm_new)(struct rsnd_mod *mod, +		       struct rsnd_dai *rdai, +		       struct snd_soc_pcm_runtime *rtd);  }; +struct rsnd_dai_stream;  struct rsnd_mod {  	int id; +	enum rsnd_mod_type type;  	struct rsnd_priv *priv;  	struct rsnd_mod_ops *ops; -	struct list_head list; /* connect to rsnd_dai playback/capture */  	struct rsnd_dma dma; +	struct rsnd_dai_stream *io;  };  #define rsnd_mod_to_priv(mod) ((mod)->priv)  #define rsnd_mod_to_dma(mod) (&(mod)->dma)  #define rsnd_dma_to_mod(_dma) container_of((_dma), struct rsnd_mod, dma) +#define rsnd_mod_to_io(mod) ((mod)->io)  #define rsnd_mod_id(mod) ((mod)->id) -#define for_each_rsnd_mod(pos, n, io)	\ -	list_for_each_entry_safe(pos, n, &(io)->head, list) -#define rsnd_mod_call(mod, func, rdai, io)	\ -	(!(mod) ? -ENODEV :			\ -	 !((mod)->ops->func) ? 0 :		\ -	 (mod)->ops->func(mod, rdai, io))  void rsnd_mod_init(struct rsnd_priv *priv,  		   struct rsnd_mod *mod,  		   struct rsnd_mod_ops *ops, +		   enum rsnd_mod_type type,  		   int id);  char *rsnd_mod_name(struct rsnd_mod *mod); @@ -164,13 +230,17 @@ char *rsnd_mod_name(struct rsnd_mod *mod);   */  #define RSND_DAI_NAME_SIZE	16  struct rsnd_dai_stream { -	struct list_head head; /* head of rsnd_mod list */  	struct snd_pcm_substream *substream; +	struct rsnd_mod *mod[RSND_MOD_MAX]; +	struct rsnd_dai_path_info *info; /* rcar_snd.h */  	int byte_pos;  	int period_pos;  	int byte_per_period;  	int next_period_byte;  }; +#define rsnd_io_to_mod_ssi(io)	((io)->mod[RSND_MOD_SSI]) +#define rsnd_io_to_mod_src(io)	((io)->mod[RSND_MOD_SRC]) +#define rsnd_io_to_mod_dvc(io)	((io)->mod[RSND_MOD_DVC])  struct rsnd_dai {  	char name[RSND_DAI_NAME_SIZE]; @@ -178,23 +248,21 @@ struct rsnd_dai {  	struct rsnd_dai_stream playback;  	struct rsnd_dai_stream capture; -	int clk_master:1; -	int bit_clk_inv:1; -	int frm_clk_inv:1; -	int sys_delay:1; -	int data_alignment:1; +	unsigned int clk_master:1; +	unsigned int bit_clk_inv:1; +	unsigned int frm_clk_inv:1; +	unsigned int sys_delay:1; +	unsigned int data_alignment:1;  }; -#define rsnd_dai_nr(priv) ((priv)->dai_nr) +#define rsnd_rdai_nr(priv) ((priv)->rdai_nr)  #define for_each_rsnd_dai(rdai, priv, i)		\ -	for (i = 0, (rdai) = rsnd_dai_get(priv, i);	\ -	     i < rsnd_dai_nr(priv);			\ -	     i++, (rdai) = rsnd_dai_get(priv, i)) +	for (i = 0;					\ +	     (i < rsnd_rdai_nr(priv)) &&		\ +	     ((rdai) = rsnd_dai_get(priv, i));		\ +	     i++)  struct rsnd_dai *rsnd_dai_get(struct rsnd_priv *priv, int id); -int rsnd_dai_disconnect(struct rsnd_mod *mod); -int rsnd_dai_connect(struct rsnd_dai *rdai, struct rsnd_mod *mod, -		     struct rsnd_dai_stream *io);  int rsnd_dai_is_play(struct rsnd_dai *rdai, struct rsnd_dai_stream *io);  int rsnd_dai_id(struct rsnd_priv *priv, struct rsnd_dai *rdai);  #define rsnd_dai_get_platform_info(rdai) ((rdai)->info) @@ -202,26 +270,24 @@ int rsnd_dai_id(struct rsnd_priv *priv, struct rsnd_dai *rdai);  void rsnd_dai_pointer_update(struct rsnd_dai_stream *io, int cnt);  int rsnd_dai_pointer_offset(struct rsnd_dai_stream *io, int additional); +#define rsnd_dai_is_clk_master(rdai) ((rdai)->clk_master)  /*   *	R-Car Gen1/Gen2   */  int rsnd_gen_probe(struct platform_device *pdev, -		   struct rcar_snd_info *info, +		   const struct rsnd_of_data *of_data,  		   struct rsnd_priv *priv); -void rsnd_gen_remove(struct platform_device *pdev, -		     struct rsnd_priv *priv); -int rsnd_gen_path_init(struct rsnd_priv *priv, -		       struct rsnd_dai *rdai, -		       struct rsnd_dai_stream *io); -int rsnd_gen_path_exit(struct rsnd_priv *priv, -		       struct rsnd_dai *rdai, -		       struct rsnd_dai_stream *io);  void __iomem *rsnd_gen_reg_get(struct rsnd_priv *priv,  			       struct rsnd_mod *mod,  			       enum rsnd_reg reg); -#define rsnd_is_gen1(s)		((s)->info->flags & RSND_GEN1) -#define rsnd_is_gen2(s)		((s)->info->flags & RSND_GEN2) +void rsnd_gen_dma_addr(struct rsnd_priv *priv, +		       struct rsnd_dma *dma, +		       struct dma_slave_config *cfg, +		       int is_play,  int slave_id); + +#define rsnd_is_gen1(s)		(((s)->info->flags & RSND_GEN_MASK) == RSND_GEN1) +#define rsnd_is_gen2(s)		(((s)->info->flags & RSND_GEN_MASK) == RSND_GEN2)  /*   *	R-Car ADG @@ -229,17 +295,34 @@ void __iomem *rsnd_gen_reg_get(struct rsnd_priv *priv,  int rsnd_adg_ssi_clk_stop(struct rsnd_mod *mod);  int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *mod, unsigned int rate);  int rsnd_adg_probe(struct platform_device *pdev, -		   struct rcar_snd_info *info, -		   struct rsnd_priv *priv); -void rsnd_adg_remove(struct platform_device *pdev, +		   const struct rsnd_of_data *of_data,  		   struct rsnd_priv *priv); +int rsnd_adg_set_convert_clk_gen1(struct rsnd_priv *priv, +				  struct rsnd_mod *mod, +				  unsigned int src_rate, +				  unsigned int dst_rate); +int rsnd_adg_set_convert_clk_gen2(struct rsnd_mod *mod, +				  struct rsnd_dai *rdai, +				  struct rsnd_dai_stream *io, +				  unsigned int src_rate, +				  unsigned int dst_rate); +int rsnd_adg_set_convert_timing_gen2(struct rsnd_mod *mod, +				     struct rsnd_dai *rdai, +				     struct rsnd_dai_stream *io); +int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_dai *rdai, +				 struct rsnd_mod *mod, +				 struct rsnd_dai_stream *io);  /*   *	R-Car sound priv   */ +struct rsnd_of_data { +	u32 flags; +}; +  struct rsnd_priv { -	struct device *dev; +	struct platform_device *pdev;  	struct rcar_snd_info *info;  	spinlock_t lock; @@ -249,10 +332,10 @@ struct rsnd_priv {  	void *gen;  	/* -	 * below value will be filled on rsnd_scu_probe() +	 * below value will be filled on rsnd_src_probe()  	 */ -	void *scu; -	int scu_nr; +	void *src; +	int src_nr;  	/*  	 * below value will be filled on rsnd_adg_probe() @@ -262,41 +345,79 @@ struct rsnd_priv {  	/*  	 * below value will be filled on rsnd_ssi_probe()  	 */ -	void *ssiu; +	void *ssi; +	int ssi_nr; + +	/* +	 * below value will be filled on rsnd_dvc_probe() +	 */ +	void *dvc; +	int dvc_nr;  	/*  	 * below value will be filled on rsnd_dai_probe()  	 */  	struct snd_soc_dai_driver *daidrv;  	struct rsnd_dai *rdai; -	int dai_nr; +	int rdai_nr;  }; -#define rsnd_priv_to_dev(priv)	((priv)->dev) +#define rsnd_priv_to_pdev(priv)	((priv)->pdev) +#define rsnd_priv_to_dev(priv)	(&(rsnd_priv_to_pdev(priv)->dev)) +#define rsnd_priv_to_info(priv)	((priv)->info)  #define rsnd_lock(priv, flags) spin_lock_irqsave(&priv->lock, flags)  #define rsnd_unlock(priv, flags) spin_unlock_irqrestore(&priv->lock, flags) +#define rsnd_info_is_playback(priv, type)				\ +({									\ +	struct rcar_snd_info *info = rsnd_priv_to_info(priv);		\ +	int i, is_play = 0;						\ +	for (i = 0; i < info->dai_info_nr; i++) {			\ +		if (info->dai_info[i].playback.type == (type)->info) {	\ +			is_play = 1;					\ +			break;						\ +		}							\ +	}								\ +	is_play;							\ +}) +  /* - *	R-Car SCU + *	R-Car SRC   */ -int rsnd_scu_probe(struct platform_device *pdev, -		   struct rcar_snd_info *info, +int rsnd_src_probe(struct platform_device *pdev, +		   const struct rsnd_of_data *of_data,  		   struct rsnd_priv *priv); -void rsnd_scu_remove(struct platform_device *pdev, -		     struct rsnd_priv *priv); -struct rsnd_mod *rsnd_scu_mod_get(struct rsnd_priv *priv, int id); -#define rsnd_scu_nr(priv) ((priv)->scu_nr) +struct rsnd_mod *rsnd_src_mod_get(struct rsnd_priv *priv, int id); +unsigned int rsnd_src_get_ssi_rate(struct rsnd_priv *priv, +				   struct rsnd_dai_stream *io, +				   struct snd_pcm_runtime *runtime); +int rsnd_src_ssi_mode_init(struct rsnd_mod *ssi_mod, +			   struct rsnd_dai *rdai); +int rsnd_src_enable_ssi_irq(struct rsnd_mod *ssi_mod, +			    struct rsnd_dai *rdai); + +#define rsnd_src_nr(priv) ((priv)->src_nr)  /*   *	R-Car SSI   */  int rsnd_ssi_probe(struct platform_device *pdev, -		   struct rcar_snd_info *info, -		   struct rsnd_priv *priv); -void rsnd_ssi_remove(struct platform_device *pdev, +		   const struct rsnd_of_data *of_data,  		   struct rsnd_priv *priv);  struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id); -struct rsnd_mod *rsnd_ssi_mod_get_frm_dai(struct rsnd_priv *priv, -					  int dai_id, int is_play); +int rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod); + +/* + *	R-Car DVC + */ +int rsnd_dvc_probe(struct platform_device *pdev, +		   const struct rsnd_of_data *of_data, +		   struct rsnd_priv *priv); +void rsnd_dvc_remove(struct platform_device *pdev, +		     struct rsnd_priv *priv); +struct rsnd_mod *rsnd_dvc_mod_get(struct rsnd_priv *priv, int id); + +#define rsnd_dvc_nr(priv) ((priv)->dvc_nr) +  #endif diff --git a/sound/soc/sh/rcar/scu.c b/sound/soc/sh/rcar/scu.c deleted file mode 100644 index 2df2e9150b8..00000000000 --- a/sound/soc/sh/rcar/scu.c +++ /dev/null @@ -1,236 +0,0 @@ -/* - * Renesas R-Car SCU support - * - * Copyright (C) 2013 Renesas Solutions Corp. - * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> - * - * 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 "rsnd.h" - -struct rsnd_scu { -	struct rsnd_scu_platform_info *info; /* rcar_snd.h */ -	struct rsnd_mod mod; -}; - -#define rsnd_scu_mode_flags(p) ((p)->info->flags) - -/* - * ADINR - */ -#define OTBL_24		(0 << 16) -#define OTBL_22		(2 << 16) -#define OTBL_20		(4 << 16) -#define OTBL_18		(6 << 16) -#define OTBL_16		(8 << 16) - - -#define rsnd_mod_to_scu(_mod)	\ -	container_of((_mod), struct rsnd_scu, mod) - -#define for_each_rsnd_scu(pos, priv, i)					\ -	for ((i) = 0;							\ -	     ((i) < rsnd_scu_nr(priv)) &&				\ -		     ((pos) = (struct rsnd_scu *)(priv)->scu + i);	\ -	     i++) - -static int rsnd_scu_set_route(struct rsnd_priv *priv, -			      struct rsnd_mod *mod, -			      struct rsnd_dai *rdai, -			      struct rsnd_dai_stream *io) -{ -	struct scu_route_config { -		u32 mask; -		int shift; -	} routes[] = { -		{ 0xF,  0, }, /* 0 */ -		{ 0xF,  4, }, /* 1 */ -		{ 0xF,  8, }, /* 2 */ -		{ 0x7, 12, }, /* 3 */ -		{ 0x7, 16, }, /* 4 */ -		{ 0x7, 20, }, /* 5 */ -		{ 0x7, 24, }, /* 6 */ -		{ 0x3, 28, }, /* 7 */ -		{ 0x3, 30, }, /* 8 */ -	}; - -	u32 mask; -	u32 val; -	int shift; -	int id; - -	/* -	 * Gen1 only -	 */ -	if (!rsnd_is_gen1(priv)) -		return 0; - -	id = rsnd_mod_id(mod); -	if (id < 0 || id > ARRAY_SIZE(routes)) -		return -EIO; - -	/* -	 * SRC_ROUTE_SELECT -	 */ -	val = rsnd_dai_is_play(rdai, io) ? 0x1 : 0x2; -	val = val		<< routes[id].shift; -	mask = routes[id].mask	<< routes[id].shift; - -	rsnd_mod_bset(mod, SRC_ROUTE_SEL, mask, val); - -	/* -	 * SRC_TIMING_SELECT -	 */ -	shift	= (id % 4) * 8; -	mask	= 0x1F << shift; -	if (8 == id) /* SRU8 is very special */ -		val = id << shift; -	else -		val = (id + 1) << shift; - -	switch (id / 4) { -	case 0: -		rsnd_mod_bset(mod, SRC_TMG_SEL0, mask, val); -		break; -	case 1: -		rsnd_mod_bset(mod, SRC_TMG_SEL1, mask, val); -		break; -	case 2: -		rsnd_mod_bset(mod, SRC_TMG_SEL2, mask, val); -		break; -	} - -	return 0; -} - -static int rsnd_scu_set_mode(struct rsnd_priv *priv, -			     struct rsnd_mod *mod, -			     struct rsnd_dai *rdai, -			     struct rsnd_dai_stream *io) -{ -	int id = rsnd_mod_id(mod); -	u32 val; - -	if (rsnd_is_gen1(priv)) { -		val = (1 << id); -		rsnd_mod_bset(mod, SRC_CTRL, val, val); -	} - -	return 0; -} - -static int rsnd_scu_set_hpbif(struct rsnd_priv *priv, -			      struct rsnd_mod *mod, -			      struct rsnd_dai *rdai, -			      struct rsnd_dai_stream *io) -{ -	struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); -	u32 adinr = runtime->channels; - -	switch (runtime->sample_bits) { -	case 16: -		adinr |= OTBL_16; -		break; -	case 32: -		adinr |= OTBL_24; -		break; -	default: -		return -EIO; -	} - -	rsnd_mod_write(mod, BUSIF_MODE, 1); -	rsnd_mod_write(mod, BUSIF_ADINR, adinr); - -	return 0; -} - -static int rsnd_scu_start(struct rsnd_mod *mod, -			  struct rsnd_dai *rdai, -			  struct rsnd_dai_stream *io) -{ -	struct rsnd_priv *priv = rsnd_mod_to_priv(mod); -	struct rsnd_scu *scu = rsnd_mod_to_scu(mod); -	struct device *dev = rsnd_priv_to_dev(priv); -	u32 flags = rsnd_scu_mode_flags(scu); -	int ret; - -	/* -	 * SCU will be used if it has RSND_SCU_USE_HPBIF flags -	 */ -	if (!(flags & RSND_SCU_USE_HPBIF)) { -		/* it use PIO transter */ -		dev_dbg(dev, "%s%d is not used\n", -			rsnd_mod_name(mod), rsnd_mod_id(mod)); - -		return 0; -	} - -	/* it use DMA transter */ -	ret = rsnd_scu_set_route(priv, mod, rdai, io); -	if (ret < 0) -		return ret; - -	ret = rsnd_scu_set_mode(priv, mod, rdai, io); -	if (ret < 0) -		return ret; - -	ret = rsnd_scu_set_hpbif(priv, mod, rdai, io); -	if (ret < 0) -		return ret; - -	dev_dbg(dev, "%s%d start\n", rsnd_mod_name(mod), rsnd_mod_id(mod)); - -	return 0; -} - -static struct rsnd_mod_ops rsnd_scu_ops = { -	.name	= "scu", -	.start	= rsnd_scu_start, -}; - -struct rsnd_mod *rsnd_scu_mod_get(struct rsnd_priv *priv, int id) -{ -	BUG_ON(id < 0 || id >= rsnd_scu_nr(priv)); - -	return &((struct rsnd_scu *)(priv->scu) + id)->mod; -} - -int rsnd_scu_probe(struct platform_device *pdev, -		   struct rcar_snd_info *info, -		   struct rsnd_priv *priv) -{ -	struct device *dev = rsnd_priv_to_dev(priv); -	struct rsnd_scu *scu; -	int i, nr; - -	/* -	 * init SCU -	 */ -	nr	= info->scu_info_nr; -	scu	= devm_kzalloc(dev, sizeof(*scu) * nr, GFP_KERNEL); -	if (!scu) { -		dev_err(dev, "SCU allocate failed\n"); -		return -ENOMEM; -	} - -	priv->scu_nr	= nr; -	priv->scu	= scu; - -	for_each_rsnd_scu(scu, priv, i) { -		rsnd_mod_init(priv, &scu->mod, -			      &rsnd_scu_ops, i); -		scu->info = &info->scu_info[i]; - -		dev_dbg(dev, "SCU%d probed\n", i); -	} -	dev_dbg(dev, "scu probed\n"); - -	return 0; -} - -void rsnd_scu_remove(struct platform_device *pdev, -		     struct rsnd_priv *priv) -{ -} diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c new file mode 100644 index 00000000000..200eda019bc --- /dev/null +++ b/sound/soc/sh/rcar/src.c @@ -0,0 +1,687 @@ +/* + * Renesas R-Car SRC support + * + * Copyright (C) 2013 Renesas Solutions Corp. + * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> + * + * 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 "rsnd.h" + +#define SRC_NAME "src" + +struct rsnd_src { +	struct rsnd_src_platform_info *info; /* rcar_snd.h */ +	struct rsnd_mod mod; +	struct clk *clk; +}; + +#define RSND_SRC_NAME_SIZE 16 + +#define rsnd_src_convert_rate(p) ((p)->info->convert_rate) +#define rsnd_mod_to_src(_mod)				\ +	container_of((_mod), struct rsnd_src, mod) +#define rsnd_src_dma_available(src) \ +	rsnd_dma_available(rsnd_mod_to_dma(&(src)->mod)) + +#define for_each_rsnd_src(pos, priv, i)				\ +	for ((i) = 0;						\ +	     ((i) < rsnd_src_nr(priv)) &&			\ +	     ((pos) = (struct rsnd_src *)(priv)->src + i);	\ +	     i++) + + +/* + *		image of SRC (Sampling Rate Converter) + * + * 96kHz   <-> +-----+	48kHz	+-----+	 48kHz	+-------+ + * 48kHz   <-> | SRC | <------>	| SSI |	<----->	| codec | + * 44.1kHz <-> +-----+		+-----+		+-------+ + * ... + * + */ + +/* + * src.c is caring... + * + * Gen1 + * + * [mem] -> [SRU] -> [SSI] + *        |--------| + * + * Gen2 + * + * [mem] -> [SRC] -> [SSIU] -> [SSI] + *        |-----------------| + */ + +/* + *	How to use SRC bypass mode for debugging + * + * SRC has bypass mode, and it is useful for debugging. + * In Gen2 case, + * SRCm_MODE controls whether SRC is used or not + * SSI_MODE0 controls whether SSIU which receives SRC data + * is used or not. + * Both SRCm_MODE/SSI_MODE0 settings are needed if you use SRC, + * but SRC bypass mode needs SSI_MODE0 only. + * + * This driver request + * struct rsnd_src_platform_info { + *	u32 convert_rate; + *	int dma_id; + * } + * + * rsnd_src_convert_rate() indicates + * above convert_rate, and it controls + * whether SRC is used or not. + * + * ex) doesn't use SRC + * static struct rsnd_dai_platform_info rsnd_dai = { + *	.playback = { .ssi = &rsnd_ssi[0], }, + * }; + * + * ex) uses SRC + * static struct rsnd_src_platform_info rsnd_src[] = { + *	RSND_SCU(48000, 0), + *	... + * }; + * static struct rsnd_dai_platform_info rsnd_dai = { + *	.playback = { .ssi = &rsnd_ssi[0], .src = &rsnd_src[0] }, + * }; + * + * ex) uses SRC bypass mode + * static struct rsnd_src_platform_info rsnd_src[] = { + *	RSND_SCU(0, 0), + *	... + * }; + * static struct rsnd_dai_platform_info rsnd_dai = { + *	.playback = { .ssi = &rsnd_ssi[0], .src = &rsnd_src[0] }, + * }; + * + */ + +/* + *		Gen1/Gen2 common functions + */ +int rsnd_src_ssi_mode_init(struct rsnd_mod *ssi_mod, +			   struct rsnd_dai *rdai) +{ +	struct rsnd_dai_stream *io = rsnd_mod_to_io(ssi_mod); +	struct rsnd_mod *src_mod = rsnd_io_to_mod_src(io); +	int ssi_id = rsnd_mod_id(ssi_mod); + +	/* +	 * SSI_MODE0 +	 */ +	rsnd_mod_bset(ssi_mod, SSI_MODE0, (1 << ssi_id), +		      src_mod ? 0 : (1 << ssi_id)); + +	/* +	 * SSI_MODE1 +	 */ +	if (rsnd_ssi_is_pin_sharing(ssi_mod)) { +		int shift = -1; +		switch (ssi_id) { +		case 1: +			shift = 0; +			break; +		case 2: +			shift = 2; +			break; +		case 4: +			shift = 16; +			break; +		} + +		if (shift >= 0) +			rsnd_mod_bset(ssi_mod, SSI_MODE1, +				      0x3 << shift, +				      rsnd_dai_is_clk_master(rdai) ? +				      0x2 << shift : 0x1 << shift); +	} + +	return 0; +} + +int rsnd_src_enable_ssi_irq(struct rsnd_mod *ssi_mod, +			    struct rsnd_dai *rdai) +{ +	struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod); + +	/* enable PIO interrupt if Gen2 */ +	if (rsnd_is_gen2(priv)) +		rsnd_mod_write(ssi_mod, INT_ENABLE, 0x0f000000); + +	return 0; +} + +unsigned int rsnd_src_get_ssi_rate(struct rsnd_priv *priv, +				   struct rsnd_dai_stream *io, +				   struct snd_pcm_runtime *runtime) +{ +	struct rsnd_mod *src_mod = rsnd_io_to_mod_src(io); +	struct rsnd_src *src; +	unsigned int rate = 0; + +	if (src_mod) { +		src = rsnd_mod_to_src(src_mod); + +		/* +		 * return convert rate if SRC is used, +		 * otherwise, return runtime->rate as usual +		 */ +		rate = rsnd_src_convert_rate(src); +	} + +	if (!rate) +		rate = runtime->rate; + +	return rate; +} + +static int rsnd_src_set_convert_rate(struct rsnd_mod *mod, +				     struct rsnd_dai *rdai) +{ +	struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); +	struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); +	struct rsnd_src *src = rsnd_mod_to_src(mod); +	u32 convert_rate = rsnd_src_convert_rate(src); +	u32 fsrate = 0; + +	if (convert_rate) +		fsrate = 0x0400000 / convert_rate * runtime->rate; + +	/* set/clear soft reset */ +	rsnd_mod_write(mod, SRC_SWRSR, 0); +	rsnd_mod_write(mod, SRC_SWRSR, 1); + +	/* +	 * Initialize the operation of the SRC internal circuits +	 * see rsnd_src_start() +	 */ +	rsnd_mod_write(mod, SRC_SRCIR, 1); + +	/* Set channel number and output bit length */ +	rsnd_mod_write(mod, SRC_ADINR, rsnd_get_adinr(mod)); + +	/* Enable the initial value of IFS */ +	if (fsrate) { +		rsnd_mod_write(mod, SRC_IFSCR, 1); + +		/* Set initial value of IFS */ +		rsnd_mod_write(mod, SRC_IFSVR, fsrate); +	} + +	/* use DMA transfer */ +	rsnd_mod_write(mod, SRC_BUSIF_MODE, 1); + +	return 0; +} + +static int rsnd_src_init(struct rsnd_mod *mod, +			 struct rsnd_dai *rdai) +{ +	struct rsnd_src *src = rsnd_mod_to_src(mod); + +	clk_prepare_enable(src->clk); + +	return 0; +} + +static int rsnd_src_quit(struct rsnd_mod *mod, +			 struct rsnd_dai *rdai) +{ +	struct rsnd_src *src = rsnd_mod_to_src(mod); + +	clk_disable_unprepare(src->clk); + +	return 0; +} + +static int rsnd_src_start(struct rsnd_mod *mod, +			  struct rsnd_dai *rdai) +{ +	struct rsnd_src *src = rsnd_mod_to_src(mod); + +	/* +	 * Cancel the initialization and operate the SRC function +	 * see rsnd_src_set_convert_rate() +	 */ +	rsnd_mod_write(mod, SRC_SRCIR, 0); + +	if (rsnd_src_convert_rate(src)) +		rsnd_mod_write(mod, SRC_ROUTE_MODE0, 1); + +	return 0; +} + + +static int rsnd_src_stop(struct rsnd_mod *mod, +			 struct rsnd_dai *rdai) +{ +	struct rsnd_src *src = rsnd_mod_to_src(mod); + +	if (rsnd_src_convert_rate(src)) +		rsnd_mod_write(mod, SRC_ROUTE_MODE0, 0); + +	return 0; +} + +/* + *		Gen1 functions + */ +static int rsnd_src_set_route_gen1(struct rsnd_mod *mod, +				   struct rsnd_dai *rdai) +{ +	struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); +	struct src_route_config { +		u32 mask; +		int shift; +	} routes[] = { +		{ 0xF,  0, }, /* 0 */ +		{ 0xF,  4, }, /* 1 */ +		{ 0xF,  8, }, /* 2 */ +		{ 0x7, 12, }, /* 3 */ +		{ 0x7, 16, }, /* 4 */ +		{ 0x7, 20, }, /* 5 */ +		{ 0x7, 24, }, /* 6 */ +		{ 0x3, 28, }, /* 7 */ +		{ 0x3, 30, }, /* 8 */ +	}; +	u32 mask; +	u32 val; +	int id; + +	id = rsnd_mod_id(mod); +	if (id < 0 || id >= ARRAY_SIZE(routes)) +		return -EIO; + +	/* +	 * SRC_ROUTE_SELECT +	 */ +	val = rsnd_dai_is_play(rdai, io) ? 0x1 : 0x2; +	val = val		<< routes[id].shift; +	mask = routes[id].mask	<< routes[id].shift; + +	rsnd_mod_bset(mod, SRC_ROUTE_SEL, mask, val); + +	return 0; +} + +static int rsnd_src_set_convert_timing_gen1(struct rsnd_mod *mod, +					    struct rsnd_dai *rdai) +{ +	struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); +	struct rsnd_priv *priv = rsnd_mod_to_priv(mod); +	struct rsnd_src *src = rsnd_mod_to_src(mod); +	struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); +	u32 convert_rate = rsnd_src_convert_rate(src); +	u32 mask; +	u32 val; +	int shift; +	int id = rsnd_mod_id(mod); +	int ret; + +	/* +	 * SRC_TIMING_SELECT +	 */ +	shift	= (id % 4) * 8; +	mask	= 0x1F << shift; + +	/* +	 * ADG is used as source clock if SRC was used, +	 * then, SSI WS is used as destination clock. +	 * SSI WS is used as source clock if SRC is not used +	 * (when playback, source/destination become reverse when capture) +	 */ +	ret = 0; +	if (convert_rate) { +		/* use ADG */ +		val = 0; +		ret = rsnd_adg_set_convert_clk_gen1(priv, mod, +						    runtime->rate, +						    convert_rate); +	} else if (8 == id) { +		/* use SSI WS, but SRU8 is special */ +		val = id << shift; +	} else { +		/* use SSI WS */ +		val = (id + 1) << shift; +	} + +	if (ret < 0) +		return ret; + +	switch (id / 4) { +	case 0: +		rsnd_mod_bset(mod, SRC_TMG_SEL0, mask, val); +		break; +	case 1: +		rsnd_mod_bset(mod, SRC_TMG_SEL1, mask, val); +		break; +	case 2: +		rsnd_mod_bset(mod, SRC_TMG_SEL2, mask, val); +		break; +	} + +	return 0; +} + +static int rsnd_src_set_convert_rate_gen1(struct rsnd_mod *mod, +					  struct rsnd_dai *rdai) +{ +	int ret; + +	ret = rsnd_src_set_convert_rate(mod, rdai); +	if (ret < 0) +		return ret; + +	/* Select SRC mode (fixed value) */ +	rsnd_mod_write(mod, SRC_SRCCR, 0x00010110); + +	/* Set the restriction value of the FS ratio (98%) */ +	rsnd_mod_write(mod, SRC_MNFSR, +		       rsnd_mod_read(mod, SRC_IFSVR) / 100 * 98); + +	/* no SRC_BFSSR settings, since SRC_SRCCR::BUFMD is 0 */ + +	return 0; +} + +static int rsnd_src_probe_gen1(struct rsnd_mod *mod, +			      struct rsnd_dai *rdai) +{ +	struct rsnd_priv *priv = rsnd_mod_to_priv(mod); +	struct device *dev = rsnd_priv_to_dev(priv); + +	dev_dbg(dev, "%s (Gen1) is probed\n", rsnd_mod_name(mod)); + +	return 0; +} + +static int rsnd_src_init_gen1(struct rsnd_mod *mod, +			      struct rsnd_dai *rdai) +{ +	int ret; + +	ret = rsnd_src_init(mod, rdai); +	if (ret < 0) +		return ret; + +	ret = rsnd_src_set_route_gen1(mod, rdai); +	if (ret < 0) +		return ret; + +	ret = rsnd_src_set_convert_rate_gen1(mod, rdai); +	if (ret < 0) +		return ret; + +	ret = rsnd_src_set_convert_timing_gen1(mod, rdai); +	if (ret < 0) +		return ret; + +	return 0; +} + +static int rsnd_src_start_gen1(struct rsnd_mod *mod, +			       struct rsnd_dai *rdai) +{ +	int id = rsnd_mod_id(mod); + +	rsnd_mod_bset(mod, SRC_ROUTE_CTRL, (1 << id), (1 << id)); + +	return rsnd_src_start(mod, rdai); +} + +static int rsnd_src_stop_gen1(struct rsnd_mod *mod, +			      struct rsnd_dai *rdai) +{ +	int id = rsnd_mod_id(mod); + +	rsnd_mod_bset(mod, SRC_ROUTE_CTRL, (1 << id), 0); + +	return rsnd_src_stop(mod, rdai); +} + +static struct rsnd_mod_ops rsnd_src_gen1_ops = { +	.name	= SRC_NAME, +	.probe	= rsnd_src_probe_gen1, +	.init	= rsnd_src_init_gen1, +	.quit	= rsnd_src_quit, +	.start	= rsnd_src_start_gen1, +	.stop	= rsnd_src_stop_gen1, +}; + +/* + *		Gen2 functions + */ +static int rsnd_src_set_convert_rate_gen2(struct rsnd_mod *mod, +					  struct rsnd_dai *rdai) +{ +	int ret; + +	ret = rsnd_src_set_convert_rate(mod, rdai); +	if (ret < 0) +		return ret; + +	rsnd_mod_write(mod, SSI_BUSIF_ADINR, rsnd_get_adinr(mod)); +	rsnd_mod_write(mod, SSI_BUSIF_MODE,  1); + +	rsnd_mod_write(mod, SRC_SRCCR, 0x00011110); + +	rsnd_mod_write(mod, SRC_BSDSR, 0x01800000); +	rsnd_mod_write(mod, SRC_BSISR, 0x00100060); + +	return 0; +} + +static int rsnd_src_set_convert_timing_gen2(struct rsnd_mod *mod, +					    struct rsnd_dai *rdai) +{ +	struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); +	struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); +	struct rsnd_src *src = rsnd_mod_to_src(mod); +	u32 convert_rate = rsnd_src_convert_rate(src); +	int ret; + +	if (convert_rate) +		ret = rsnd_adg_set_convert_clk_gen2(mod, rdai, io, +						    runtime->rate, +						    convert_rate); +	else +		ret = rsnd_adg_set_convert_timing_gen2(mod, rdai, io); + +	return ret; +} + +static int rsnd_src_probe_gen2(struct rsnd_mod *mod, +			       struct rsnd_dai *rdai) +{ +	struct rsnd_priv *priv = rsnd_mod_to_priv(mod); +	struct rsnd_src *src = rsnd_mod_to_src(mod); +	struct device *dev = rsnd_priv_to_dev(priv); +	int ret; + +	ret = rsnd_dma_init(priv, +			    rsnd_mod_to_dma(mod), +			    rsnd_info_is_playback(priv, src), +			    src->info->dma_id); +	if (ret < 0) +		dev_err(dev, "SRC DMA failed\n"); + +	dev_dbg(dev, "%s (Gen2) is probed\n", rsnd_mod_name(mod)); + +	return ret; +} + +static int rsnd_src_remove_gen2(struct rsnd_mod *mod, +				struct rsnd_dai *rdai) +{ +	rsnd_dma_quit(rsnd_mod_to_priv(mod), rsnd_mod_to_dma(mod)); + +	return 0; +} + +static int rsnd_src_init_gen2(struct rsnd_mod *mod, +			      struct rsnd_dai *rdai) +{ +	int ret; + +	ret = rsnd_src_init(mod, rdai); +	if (ret < 0) +		return ret; + +	ret = rsnd_src_set_convert_rate_gen2(mod, rdai); +	if (ret < 0) +		return ret; + +	ret = rsnd_src_set_convert_timing_gen2(mod, rdai); +	if (ret < 0) +		return ret; + +	return 0; +} + +static int rsnd_src_start_gen2(struct rsnd_mod *mod, +			       struct rsnd_dai *rdai) +{ +	struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); +	struct rsnd_src *src = rsnd_mod_to_src(mod); +	u32 val = rsnd_io_to_mod_dvc(io) ? 0x01 : 0x11; + +	rsnd_dma_start(rsnd_mod_to_dma(&src->mod)); + +	rsnd_mod_write(mod, SSI_CTRL, 0x1); +	rsnd_mod_write(mod, SRC_CTRL, val); + +	return rsnd_src_start(mod, rdai); +} + +static int rsnd_src_stop_gen2(struct rsnd_mod *mod, +			      struct rsnd_dai *rdai) +{ +	struct rsnd_src *src = rsnd_mod_to_src(mod); + +	rsnd_mod_write(mod, SSI_CTRL, 0); +	rsnd_mod_write(mod, SRC_CTRL, 0); + +	rsnd_dma_stop(rsnd_mod_to_dma(&src->mod)); + +	return rsnd_src_stop(mod, rdai); +} + +static struct rsnd_mod_ops rsnd_src_gen2_ops = { +	.name	= SRC_NAME, +	.probe	= rsnd_src_probe_gen2, +	.remove	= rsnd_src_remove_gen2, +	.init	= rsnd_src_init_gen2, +	.quit	= rsnd_src_quit, +	.start	= rsnd_src_start_gen2, +	.stop	= rsnd_src_stop_gen2, +}; + +struct rsnd_mod *rsnd_src_mod_get(struct rsnd_priv *priv, int id) +{ +	if (WARN_ON(id < 0 || id >= rsnd_src_nr(priv))) +		id = 0; + +	return &((struct rsnd_src *)(priv->src) + id)->mod; +} + +static void rsnd_of_parse_src(struct platform_device *pdev, +			      const struct rsnd_of_data *of_data, +			      struct rsnd_priv *priv) +{ +	struct device_node *src_node; +	struct rcar_snd_info *info = rsnd_priv_to_info(priv); +	struct rsnd_src_platform_info *src_info; +	struct device *dev = &pdev->dev; +	int nr; + +	if (!of_data) +		return; + +	src_node = of_get_child_by_name(dev->of_node, "rcar_sound,src"); +	if (!src_node) +		return; + +	nr = of_get_child_count(src_node); +	if (!nr) +		goto rsnd_of_parse_src_end; + +	src_info = devm_kzalloc(dev, +				sizeof(struct rsnd_src_platform_info) * nr, +				GFP_KERNEL); +	if (!src_info) { +		dev_err(dev, "src info allocation error\n"); +		goto rsnd_of_parse_src_end; +	} + +	info->src_info		= src_info; +	info->src_info_nr	= nr; + +rsnd_of_parse_src_end: +	of_node_put(src_node); +} + +int rsnd_src_probe(struct platform_device *pdev, +		   const struct rsnd_of_data *of_data, +		   struct rsnd_priv *priv) +{ +	struct rcar_snd_info *info = rsnd_priv_to_info(priv); +	struct device *dev = rsnd_priv_to_dev(priv); +	struct rsnd_src *src; +	struct rsnd_mod_ops *ops; +	struct clk *clk; +	char name[RSND_SRC_NAME_SIZE]; +	int i, nr; + +	ops = NULL; +	if (rsnd_is_gen1(priv)) +		ops = &rsnd_src_gen1_ops; +	if (rsnd_is_gen2(priv)) +		ops = &rsnd_src_gen2_ops; +	if (!ops) { +		dev_err(dev, "unknown Generation\n"); +		return -EIO; +	} + +	rsnd_of_parse_src(pdev, of_data, priv); + +	/* +	 * init SRC +	 */ +	nr	= info->src_info_nr; +	if (!nr) +		return 0; + +	src	= devm_kzalloc(dev, sizeof(*src) * nr, GFP_KERNEL); +	if (!src) { +		dev_err(dev, "SRC allocate failed\n"); +		return -ENOMEM; +	} + +	priv->src_nr	= nr; +	priv->src	= src; + +	for_each_rsnd_src(src, priv, i) { +		snprintf(name, RSND_SRC_NAME_SIZE, "%s.%d", +			 SRC_NAME, i); + +		clk = devm_clk_get(dev, name); +		if (IS_ERR(clk)) +			return PTR_ERR(clk); + +		src->info = &info->src_info[i]; +		src->clk = clk; + +		rsnd_mod_init(priv, &src->mod, ops, RSND_MOD_SRC, i); + +		dev_dbg(dev, "SRC%d probed\n", i); +	} + +	return 0; +} diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index fae26d3f79d..2df723df5d1 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -57,6 +57,8 @@   */  #define CONT		(1 << 8)	/* WS Continue Function */ +#define SSI_NAME "ssi" +  struct rsnd_ssi {  	struct clk *clk;  	struct rsnd_ssi_platform_info *info; /* rcar_snd.h */ @@ -64,116 +66,29 @@ struct rsnd_ssi {  	struct rsnd_mod mod;  	struct rsnd_dai *rdai; -	struct rsnd_dai_stream *io;  	u32 cr_own;  	u32 cr_clk;  	u32 cr_etc;  	int err; -	int dma_offset;  	unsigned int usrcnt;  	unsigned int rate;  }; -struct rsnd_ssiu { -	u32 ssi_mode0; -	u32 ssi_mode1; - -	int ssi_nr; -	struct rsnd_ssi *ssi; -}; -  #define for_each_rsnd_ssi(pos, priv, i)					\  	for (i = 0;							\  	     (i < rsnd_ssi_nr(priv)) &&					\ -		((pos) = ((struct rsnd_ssiu *)((priv)->ssiu))->ssi + i); \ +		((pos) = ((struct rsnd_ssi *)(priv)->ssi + i));		\  	     i++) -#define rsnd_ssi_nr(priv) (((struct rsnd_ssiu *)((priv)->ssiu))->ssi_nr) +#define rsnd_ssi_nr(priv) ((priv)->ssi_nr)  #define rsnd_mod_to_ssi(_mod) container_of((_mod), struct rsnd_ssi, mod)  #define rsnd_dma_to_ssi(dma)  rsnd_mod_to_ssi(rsnd_dma_to_mod(dma))  #define rsnd_ssi_pio_available(ssi) ((ssi)->info->pio_irq > 0)  #define rsnd_ssi_dma_available(ssi) \  	rsnd_dma_available(rsnd_mod_to_dma(&(ssi)->mod))  #define rsnd_ssi_clk_from_parent(ssi) ((ssi)->parent) -#define rsnd_rdai_is_clk_master(rdai) ((rdai)->clk_master)  #define rsnd_ssi_mode_flags(p) ((p)->info->flags)  #define rsnd_ssi_dai_id(ssi) ((ssi)->info->dai_id) -#define rsnd_ssi_to_ssiu(ssi)\ -	(((struct rsnd_ssiu *)((ssi) - rsnd_mod_id(&(ssi)->mod))) - 1) - -static void rsnd_ssi_mode_init(struct rsnd_priv *priv, -			       struct rsnd_ssiu *ssiu) -{ -	struct device *dev = rsnd_priv_to_dev(priv); -	struct rsnd_ssi *ssi; -	u32 flags; -	u32 val; -	int i; - -	/* -	 * SSI_MODE0 -	 */ -	ssiu->ssi_mode0 = 0; -	for_each_rsnd_ssi(ssi, priv, i) { -		flags = rsnd_ssi_mode_flags(ssi); - -		/* see also BUSIF_MODE */ -		if (!(flags & RSND_SSI_DEPENDENT)) { -			ssiu->ssi_mode0 |= (1 << i); -			dev_dbg(dev, "SSI%d uses INDEPENDENT mode\n", i); -		} else { -			dev_dbg(dev, "SSI%d uses DEPENDENT mode\n", i); -		} -	} - -	/* -	 * SSI_MODE1 -	 */ -#define ssi_parent_set(p, sync, adg, ext)		\ -	do {						\ -		ssi->parent = ssiu->ssi + p;		\ -		if (flags & RSND_SSI_CLK_FROM_ADG)	\ -			val = adg;			\ -		else					\ -			val = ext;			\ -		if (flags & RSND_SSI_SYNC)		\ -			val |= sync;			\ -	} while (0) - -	ssiu->ssi_mode1 = 0; -	for_each_rsnd_ssi(ssi, priv, i) { -		flags = rsnd_ssi_mode_flags(ssi); - -		if (!(flags & RSND_SSI_CLK_PIN_SHARE)) -			continue; - -		val = 0; -		switch (i) { -		case 1: -			ssi_parent_set(0, (1 << 4), (0x2 << 0), (0x1 << 0)); -			break; -		case 2: -			ssi_parent_set(0, (1 << 4), (0x2 << 2), (0x1 << 2)); -			break; -		case 4: -			ssi_parent_set(3, (1 << 20), (0x2 << 16), (0x1 << 16)); -			break; -		case 8: -			ssi_parent_set(7, 0, 0, 0); -			break; -		} - -		ssiu->ssi_mode1 |= val; -	} -} - -static void rsnd_ssi_mode_set(struct rsnd_ssi *ssi) -{ -	struct rsnd_ssiu *ssiu = rsnd_ssi_to_ssiu(ssi); - -	rsnd_mod_write(&ssi->mod, SSI_MODE0, ssiu->ssi_mode0); -	rsnd_mod_write(&ssi->mod, SSI_MODE1, ssiu->ssi_mode1); -}  static void rsnd_ssi_status_check(struct rsnd_mod *mod,  				  u32 bit) @@ -195,9 +110,10 @@ static void rsnd_ssi_status_check(struct rsnd_mod *mod,  }  static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi, -				     unsigned int rate) +				     struct rsnd_dai_stream *io)  {  	struct rsnd_priv *priv = rsnd_mod_to_priv(&ssi->mod); +	struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);  	struct device *dev = rsnd_priv_to_dev(priv);  	int i, j, ret;  	int adg_clk_div_table[] = { @@ -207,6 +123,7 @@ static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi,  		1, 2, 4, 8, 16, 6, 12,  	};  	unsigned int main_rate; +	unsigned int rate = rsnd_src_get_ssi_rate(priv, io, runtime);  	/*  	 * Find best clock, and try to start ADG @@ -217,7 +134,7 @@ static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi,  			/*  			 * this driver is assuming that  			 * system word is 64fs (= 2 x 32bit) -			 * see rsnd_ssi_start() +			 * see rsnd_ssi_init()  			 */  			main_rate = rate / adg_clk_div_table[i]  				* 32 * 2 * ssi_clk_mul_table[j]; @@ -256,17 +173,13 @@ static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi,  	u32 cr;  	if (0 == ssi->usrcnt) { -		clk_enable(ssi->clk); - -		if (rsnd_rdai_is_clk_master(rdai)) { -			struct snd_pcm_runtime *runtime; - -			runtime = rsnd_io_to_runtime(io); +		clk_prepare_enable(ssi->clk); +		if (rsnd_dai_is_clk_master(rdai)) {  			if (rsnd_ssi_clk_from_parent(ssi))  				rsnd_ssi_hw_start(ssi->parent, rdai, io);  			else -				rsnd_ssi_master_clk_start(ssi, runtime->rate); +				rsnd_ssi_master_clk_start(ssi, io);  		}  	} @@ -312,14 +225,14 @@ static void rsnd_ssi_hw_stop(struct rsnd_ssi *ssi,  		rsnd_mod_write(&ssi->mod, SSICR, cr);	/* disabled all */  		rsnd_ssi_status_check(&ssi->mod, IIRQ); -		if (rsnd_rdai_is_clk_master(rdai)) { +		if (rsnd_dai_is_clk_master(rdai)) {  			if (rsnd_ssi_clk_from_parent(ssi))  				rsnd_ssi_hw_stop(ssi->parent, rdai);  			else  				rsnd_ssi_master_clk_stop(ssi);  		} -		clk_disable(ssi->clk); +		clk_disable_unprepare(ssi->clk);  	}  	dev_dbg(dev, "ssi%d hw stopped\n", rsnd_mod_id(&ssi->mod)); @@ -329,12 +242,10 @@ static void rsnd_ssi_hw_stop(struct rsnd_ssi *ssi,   *	SSI mod common functions   */  static int rsnd_ssi_init(struct rsnd_mod *mod, -			 struct rsnd_dai *rdai, -			 struct rsnd_dai_stream *io) +			 struct rsnd_dai *rdai)  {  	struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); -	struct rsnd_priv *priv = rsnd_mod_to_priv(mod); -	struct device *dev = rsnd_priv_to_dev(priv); +	struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);  	struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);  	u32 cr; @@ -375,32 +286,25 @@ static int rsnd_ssi_init(struct rsnd_mod *mod,  	 * set ssi parameter  	 */  	ssi->rdai	= rdai; -	ssi->io		= io;  	ssi->cr_own	= cr;  	ssi->err	= -1; /* ignore 1st error */ -	rsnd_ssi_mode_set(ssi); - -	dev_dbg(dev, "%s.%d init\n", rsnd_mod_name(mod), rsnd_mod_id(mod)); +	rsnd_src_ssi_mode_init(mod, rdai);  	return 0;  }  static int rsnd_ssi_quit(struct rsnd_mod *mod, -			 struct rsnd_dai *rdai, -			 struct rsnd_dai_stream *io) +			 struct rsnd_dai *rdai)  {  	struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);  	struct rsnd_priv *priv = rsnd_mod_to_priv(mod);  	struct device *dev = rsnd_priv_to_dev(priv); -	dev_dbg(dev, "%s.%d quit\n", rsnd_mod_name(mod), rsnd_mod_id(mod)); -  	if (ssi->err > 0)  		dev_warn(dev, "ssi under/over flow err = %d\n", ssi->err);  	ssi->rdai	= NULL; -	ssi->io		= NULL;  	ssi->cr_own	= 0;  	ssi->err	= 0; @@ -424,8 +328,9 @@ static void rsnd_ssi_record_error(struct rsnd_ssi *ssi, u32 status)  static irqreturn_t rsnd_ssi_pio_interrupt(int irq, void *data)  {  	struct rsnd_ssi *ssi = data; -	struct rsnd_dai_stream *io = ssi->io; -	u32 status = rsnd_mod_read(&ssi->mod, SSISR); +	struct rsnd_mod *mod = &ssi->mod; +	struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); +	u32 status = rsnd_mod_read(mod, SSISR);  	irqreturn_t ret = IRQ_NONE;  	if (io && (status & DIRQ)) { @@ -442,9 +347,9 @@ static irqreturn_t rsnd_ssi_pio_interrupt(int irq, void *data)  		 * see rsnd_ssi_init()  		 */  		if (rsnd_dai_is_play(rdai, io)) -			rsnd_mod_write(&ssi->mod, SSITDR, *buf); +			rsnd_mod_write(mod, SSITDR, *buf);  		else -			*buf = rsnd_mod_read(&ssi->mod, SSIRDR); +			*buf = rsnd_mod_read(mod, SSIRDR);  		rsnd_dai_pointer_update(io, sizeof(*buf)); @@ -454,34 +359,48 @@ static irqreturn_t rsnd_ssi_pio_interrupt(int irq, void *data)  	return ret;  } -static int rsnd_ssi_pio_start(struct rsnd_mod *mod, -			      struct rsnd_dai *rdai, -			      struct rsnd_dai_stream *io) +static int rsnd_ssi_pio_probe(struct rsnd_mod *mod, +			      struct rsnd_dai *rdai)  {  	struct rsnd_priv *priv = rsnd_mod_to_priv(mod); -	struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);  	struct device *dev = rsnd_priv_to_dev(priv); +	struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); +	int irq = ssi->info->pio_irq; +	int ret; + +	ret = devm_request_irq(dev, irq, +			       rsnd_ssi_pio_interrupt, +			       IRQF_SHARED, +			       dev_name(dev), ssi); +	if (ret) +		dev_err(dev, "SSI request interrupt failed\n"); + +	dev_dbg(dev, "%s (PIO) is probed\n", rsnd_mod_name(mod)); + +	return ret; +} + +static int rsnd_ssi_pio_start(struct rsnd_mod *mod, +			      struct rsnd_dai *rdai) +{ +	struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); +	struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);  	/* enable PIO IRQ */  	ssi->cr_etc = UIEN | OIEN | DIEN; -	rsnd_ssi_hw_start(ssi, rdai, io); +	rsnd_src_enable_ssi_irq(mod, rdai); -	dev_dbg(dev, "%s.%d start\n", rsnd_mod_name(mod), rsnd_mod_id(mod)); +	rsnd_ssi_hw_start(ssi, rdai, io);  	return 0;  }  static int rsnd_ssi_pio_stop(struct rsnd_mod *mod, -			     struct rsnd_dai *rdai, -			     struct rsnd_dai_stream *io) +			     struct rsnd_dai *rdai)  { -	struct rsnd_priv *priv = rsnd_mod_to_priv(mod); -	struct device *dev = rsnd_priv_to_dev(priv);  	struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); -	dev_dbg(dev, "%s.%d stop\n", rsnd_mod_name(mod), rsnd_mod_id(mod)); -  	ssi->cr_etc = 0;  	rsnd_ssi_hw_stop(ssi, rdai); @@ -490,71 +409,75 @@ static int rsnd_ssi_pio_stop(struct rsnd_mod *mod,  }  static struct rsnd_mod_ops rsnd_ssi_pio_ops = { -	.name	= "ssi (pio)", +	.name	= SSI_NAME, +	.probe	= rsnd_ssi_pio_probe,  	.init	= rsnd_ssi_init,  	.quit	= rsnd_ssi_quit,  	.start	= rsnd_ssi_pio_start,  	.stop	= rsnd_ssi_pio_stop,  }; -static int rsnd_ssi_dma_inquiry(struct rsnd_dma *dma, dma_addr_t *buf, int *len) +static int rsnd_ssi_dma_probe(struct rsnd_mod *mod, +			  struct rsnd_dai *rdai)  { -	struct rsnd_ssi *ssi = rsnd_dma_to_ssi(dma); -	struct rsnd_dai_stream *io = ssi->io; -	struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); +	struct rsnd_priv *priv = rsnd_mod_to_priv(mod); +	struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); +	struct device *dev = rsnd_priv_to_dev(priv); +	int dma_id = ssi->info->dma_id; +	int ret; -	*len = io->byte_per_period; -	*buf = runtime->dma_addr + -		rsnd_dai_pointer_offset(io, ssi->dma_offset + *len); -	ssi->dma_offset = *len; /* it cares A/B plane */ +	ret = rsnd_dma_init( +		priv, rsnd_mod_to_dma(mod), +		rsnd_info_is_playback(priv, ssi), +		dma_id); -	return 0; -} +	if (ret < 0) +		dev_err(dev, "SSI DMA failed\n"); -static int rsnd_ssi_dma_complete(struct rsnd_dma *dma) -{ -	struct rsnd_ssi *ssi = rsnd_dma_to_ssi(dma); -	struct rsnd_dai_stream *io = ssi->io; -	u32 status = rsnd_mod_read(&ssi->mod, SSISR); +	dev_dbg(dev, "%s (DMA) is probed\n", rsnd_mod_name(mod)); -	rsnd_ssi_record_error(ssi, status); +	return ret; +} -	rsnd_dai_pointer_update(ssi->io, io->byte_per_period); +static int rsnd_ssi_dma_remove(struct rsnd_mod *mod, +			       struct rsnd_dai *rdai) +{ +	rsnd_dma_quit(rsnd_mod_to_priv(mod), rsnd_mod_to_dma(mod));  	return 0;  }  static int rsnd_ssi_dma_start(struct rsnd_mod *mod, -			      struct rsnd_dai *rdai, -			      struct rsnd_dai_stream *io) +			      struct rsnd_dai *rdai)  {  	struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);  	struct rsnd_dma *dma = rsnd_mod_to_dma(&ssi->mod); +	struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);  	/* enable DMA transfer */  	ssi->cr_etc = DMEN; -	ssi->dma_offset = 0;  	rsnd_dma_start(dma);  	rsnd_ssi_hw_start(ssi, ssi->rdai, io);  	/* enable WS continue */ -	if (rsnd_rdai_is_clk_master(rdai)) +	if (rsnd_dai_is_clk_master(rdai))  		rsnd_mod_write(&ssi->mod, SSIWSR, CONT);  	return 0;  }  static int rsnd_ssi_dma_stop(struct rsnd_mod *mod, -			     struct rsnd_dai *rdai, -			     struct rsnd_dai_stream *io) +			     struct rsnd_dai *rdai)  {  	struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);  	struct rsnd_dma *dma = rsnd_mod_to_dma(&ssi->mod);  	ssi->cr_etc = 0; +	rsnd_ssi_record_error(ssi, rsnd_mod_read(mod, SSISR)); +  	rsnd_ssi_hw_stop(ssi, rdai);  	rsnd_dma_stop(dma); @@ -563,7 +486,9 @@ static int rsnd_ssi_dma_stop(struct rsnd_mod *mod,  }  static struct rsnd_mod_ops rsnd_ssi_dma_ops = { -	.name	= "ssi (dma)", +	.name	= SSI_NAME, +	.probe	= rsnd_ssi_dma_probe, +	.remove	= rsnd_ssi_dma_remove,  	.init	= rsnd_ssi_init,  	.quit	= rsnd_ssi_quit,  	.start	= rsnd_ssi_dma_start, @@ -573,91 +498,144 @@ static struct rsnd_mod_ops rsnd_ssi_dma_ops = {  /*   *		Non SSI   */ -static int rsnd_ssi_non(struct rsnd_mod *mod, -			struct rsnd_dai *rdai, -			struct rsnd_dai_stream *io) -{ -	struct rsnd_priv *priv = rsnd_mod_to_priv(mod); -	struct device *dev = rsnd_priv_to_dev(priv); - -	dev_dbg(dev, "%s\n", __func__); - -	return 0; -} -  static struct rsnd_mod_ops rsnd_ssi_non_ops = { -	.name	= "ssi (non)", -	.init	= rsnd_ssi_non, -	.quit	= rsnd_ssi_non, -	.start	= rsnd_ssi_non, -	.stop	= rsnd_ssi_non, +	.name	= SSI_NAME,  };  /*   *		ssi mod function   */ -struct rsnd_mod *rsnd_ssi_mod_get_frm_dai(struct rsnd_priv *priv, -					  int dai_id, int is_play) +struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id)  { -	struct rsnd_ssi *ssi; -	int i, has_play; +	if (WARN_ON(id < 0 || id >= rsnd_ssi_nr(priv))) +		id = 0; -	is_play = !!is_play; +	return &((struct rsnd_ssi *)(priv->ssi) + id)->mod; +} -	for_each_rsnd_ssi(ssi, priv, i) { -		if (rsnd_ssi_dai_id(ssi) != dai_id) -			continue; +int rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod) +{ +	struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); -		has_play = !!(rsnd_ssi_mode_flags(ssi) & RSND_SSI_PLAY); +	return !!(rsnd_ssi_mode_flags(ssi) & RSND_SSI_CLK_PIN_SHARE); +} -		if (is_play == has_play) -			return &ssi->mod; -	} +static void rsnd_ssi_parent_clk_setup(struct rsnd_priv *priv, struct rsnd_ssi *ssi) +{ +	if (!rsnd_ssi_is_pin_sharing(&ssi->mod)) +		return; -	return NULL; +	switch (rsnd_mod_id(&ssi->mod)) { +	case 1: +	case 2: +		ssi->parent = rsnd_mod_to_ssi(rsnd_ssi_mod_get(priv, 0)); +		break; +	case 4: +		ssi->parent = rsnd_mod_to_ssi(rsnd_ssi_mod_get(priv, 3)); +		break; +	case 8: +		ssi->parent = rsnd_mod_to_ssi(rsnd_ssi_mod_get(priv, 7)); +		break; +	}  } -struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id) + +static void rsnd_of_parse_ssi(struct platform_device *pdev, +			      const struct rsnd_of_data *of_data, +			      struct rsnd_priv *priv)  { -	BUG_ON(id < 0 || id >= rsnd_ssi_nr(priv)); +	struct device_node *node; +	struct device_node *np; +	struct rsnd_ssi_platform_info *ssi_info; +	struct rcar_snd_info *info = rsnd_priv_to_info(priv); +	struct device *dev = &pdev->dev; +	int nr, i; + +	if (!of_data) +		return; + +	node = of_get_child_by_name(dev->of_node, "rcar_sound,ssi"); +	if (!node) +		return; + +	nr = of_get_child_count(node); +	if (!nr) +		goto rsnd_of_parse_ssi_end; + +	ssi_info = devm_kzalloc(dev, +				sizeof(struct rsnd_ssi_platform_info) * nr, +				GFP_KERNEL); +	if (!ssi_info) { +		dev_err(dev, "ssi info allocation error\n"); +		goto rsnd_of_parse_ssi_end; +	} + +	info->ssi_info		= ssi_info; +	info->ssi_info_nr	= nr; + +	i = -1; +	for_each_child_of_node(node, np) { +		i++; + +		ssi_info = info->ssi_info + i; + +		/* +		 * pin settings +		 */ +		if (of_get_property(np, "shared-pin", NULL)) +			ssi_info->flags |= RSND_SSI_CLK_PIN_SHARE; + +		/* +		 * irq +		 */ +		ssi_info->pio_irq = irq_of_parse_and_map(np, 0); + +		/* +		 * DMA +		 */ +		ssi_info->dma_id = of_get_property(np, "pio-transfer", NULL) ? +			0 : 1; +	} -	return &(((struct rsnd_ssiu *)(priv->ssiu))->ssi + id)->mod; +rsnd_of_parse_ssi_end: +	of_node_put(node);  }  int rsnd_ssi_probe(struct platform_device *pdev, -		   struct rcar_snd_info *info, +		   const struct rsnd_of_data *of_data,  		   struct rsnd_priv *priv)  { +	struct rcar_snd_info *info = rsnd_priv_to_info(priv);  	struct rsnd_ssi_platform_info *pinfo;  	struct device *dev = rsnd_priv_to_dev(priv);  	struct rsnd_mod_ops *ops;  	struct clk *clk; -	struct rsnd_ssiu *ssiu;  	struct rsnd_ssi *ssi;  	char name[RSND_SSI_NAME_SIZE]; -	int i, nr, ret; +	int i, nr; + +	rsnd_of_parse_ssi(pdev, of_data, priv);  	/*  	 *	init SSI  	 */  	nr	= info->ssi_info_nr; -	ssiu	= devm_kzalloc(dev, sizeof(*ssiu) + (sizeof(*ssi) * nr), -			       GFP_KERNEL); -	if (!ssiu) { +	ssi	= devm_kzalloc(dev, sizeof(*ssi) * nr, GFP_KERNEL); +	if (!ssi) {  		dev_err(dev, "SSI allocate failed\n");  		return -ENOMEM;  	} -	priv->ssiu	= ssiu; -	ssiu->ssi	= (struct rsnd_ssi *)(ssiu + 1); -	ssiu->ssi_nr	= nr; +	priv->ssi	= ssi; +	priv->ssi_nr	= nr;  	for_each_rsnd_ssi(ssi, priv, i) {  		pinfo = &info->ssi_info[i]; -		snprintf(name, RSND_SSI_NAME_SIZE, "ssi.%d", i); +		snprintf(name, RSND_SSI_NAME_SIZE, "%s.%d", +			 SSI_NAME, i); -		clk = clk_get(dev, name); +		clk = devm_clk_get(dev, name);  		if (IS_ERR(clk))  			return PTR_ERR(clk); @@ -665,64 +643,15 @@ int rsnd_ssi_probe(struct platform_device *pdev,  		ssi->clk	= clk;  		ops = &rsnd_ssi_non_ops; +		if (pinfo->dma_id > 0) +			ops = &rsnd_ssi_dma_ops; +		else if (rsnd_ssi_pio_available(ssi)) +			ops = &rsnd_ssi_pio_ops; -		/* -		 * SSI DMA case -		 */ -		if (pinfo->dma_id > 0) { -			ret = rsnd_dma_init( -				priv, rsnd_mod_to_dma(&ssi->mod), -				(rsnd_ssi_mode_flags(ssi) & RSND_SSI_PLAY), -				pinfo->dma_id, -				rsnd_ssi_dma_inquiry, -				rsnd_ssi_dma_complete); -			if (ret < 0) -				dev_info(dev, "SSI DMA failed. try PIO transter\n"); -			else -				ops	= &rsnd_ssi_dma_ops; - -			dev_dbg(dev, "SSI%d use DMA transfer\n", i); -		} - -		/* -		 * SSI PIO case -		 */ -		if (!rsnd_ssi_dma_available(ssi) && -		     rsnd_ssi_pio_available(ssi)) { -			ret = devm_request_irq(dev, pinfo->pio_irq, -					       &rsnd_ssi_pio_interrupt, -					       IRQF_SHARED, -					       dev_name(dev), ssi); -			if (ret) { -				dev_err(dev, "SSI request interrupt failed\n"); -				return ret; -			} - -			ops	= &rsnd_ssi_pio_ops; - -			dev_dbg(dev, "SSI%d use PIO transfer\n", i); -		} +		rsnd_mod_init(priv, &ssi->mod, ops, RSND_MOD_SSI, i); -		rsnd_mod_init(priv, &ssi->mod, ops, i); +		rsnd_ssi_parent_clk_setup(priv, ssi);  	} -	rsnd_ssi_mode_init(priv, ssiu); - -	dev_dbg(dev, "ssi probed\n"); -  	return 0;  } - -void rsnd_ssi_remove(struct platform_device *pdev, -		   struct rsnd_priv *priv) -{ -	struct rsnd_ssi *ssi; -	int i; - -	for_each_rsnd_ssi(ssi, priv, i) { -		clk_put(ssi->clk); -		if (rsnd_ssi_dma_available(ssi)) -			rsnd_dma_quit(priv, rsnd_mod_to_dma(&ssi->mod)); -	} - -} diff --git a/sound/soc/sh/siu_dai.c b/sound/soc/sh/siu_dai.c index 9dc24ffa892..d55babee14f 100644 --- a/sound/soc/sh/siu_dai.c +++ b/sound/soc/sh/siu_dai.c @@ -543,7 +543,8 @@ static void siu_dai_shutdown(struct snd_pcm_substream *substream,  	/* 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); +		if (WARN_ON(port_info->playback.rw_flg || port_info->capture.rw_flg)) +			return;  		siu_dai_spbstop(port_info);  		siu_dai_stop(port_info);  	}  | 
