diff options
Diffstat (limited to 'sound/soc/sh')
| -rw-r--r-- | sound/soc/sh/Kconfig | 3 | ||||
| -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 | 343 | ||||
| -rw-r--r-- | sound/soc/sh/rcar/core.c | 561 | ||||
| -rw-r--r-- | sound/soc/sh/rcar/dvc.c | 289 | ||||
| -rw-r--r-- | sound/soc/sh/rcar/gen.c | 413 | ||||
| -rw-r--r-- | sound/soc/sh/rcar/rsnd.h | 280 | ||||
| -rw-r--r-- | sound/soc/sh/rcar/scu.c | 243 | ||||
| -rw-r--r-- | sound/soc/sh/rcar/src.c | 687 | ||||
| -rw-r--r-- | sound/soc/sh/rcar/ssi.c | 426 |
13 files changed, 2380 insertions, 947 deletions
diff --git a/sound/soc/sh/Kconfig b/sound/soc/sh/Kconfig index 14011d90d70..b43fdf0d08a 100644 --- a/sound/soc/sh/Kconfig +++ b/sound/soc/sh/Kconfig @@ -37,6 +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 REGMAP help This option enables R-Car SUR/SCU/SSIU/SSI sound support @@ -55,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 9430097979a..fc41a0e8b09 100644 --- a/sound/soc/sh/rcar/adg.c +++ b/sound/soc/sh/rcar/adg.c @@ -19,52 +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) { - enum rsnd_reg reg; + 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) +{ + 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; } @@ -125,19 +353,10 @@ found_clock: * 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; } @@ -166,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); @@ -175,14 +394,14 @@ 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; } } @@ -191,7 +410,7 @@ static void rsnd_adg_ssi_clk_init(struct rsnd_priv *priv, struct rsnd_adg *adg) } 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; @@ -205,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); @@ -224,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 b3653d37f75..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] * ... * * @@ -100,6 +100,21 @@ #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 */ @@ -107,6 +122,11 @@ (!(priv->info->func) ? 0 : \ priv->info->func(param)) +#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 */ @@ -121,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 */ @@ -142,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) @@ -156,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, @@ -204,19 +243,93 @@ static void rsnd_dma_do_work(struct work_struct *work) } } +static void rsnd_dma_do_work(struct work_struct *work) +{ + struct rsnd_dma *dma = container_of(work, struct rsnd_dma, work); + + __rsnd_dma_start(dma); +} + int rsnd_dma_available(struct rsnd_dma *dma) { return !!dma->chan; } +#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) +{ + 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; + + + 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; @@ -228,27 +341,29 @@ int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma, dma_cap_zero(mask); dma_cap_set(DMA_SLAVE, mask); + 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_slave_channel_compat(mask, shdma_chan_filter, (void *)id, dev, - is_play ? "tx" : "rx"); + dma_name); if (!dma->chan) { dev_err(dev, "can't get dma channel\n"); return -EIO; } - cfg.slave_id = id; - cfg.dst_addr = 0; /* use default addr when playback */ - cfg.src_addr = 0; /* use default addr when capture */ - cfg.direction = is_play ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM; + 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; @@ -269,28 +384,70 @@ void rsnd_dma_quit(struct rsnd_priv *priv, } /* + * settting function + */ +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; + + 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; + } + + return adinr; +} + +/* * rsnd_dai functions */ -#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; \ +#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; \ }) -int rsnd_dai_connect(struct rsnd_dai *rdai, - struct rsnd_mod *mod, - struct rsnd_dai_stream *io) +static int rsnd_dai_connect(struct rsnd_mod *mod, + struct rsnd_dai_stream *io) { if (!mod) return -EIO; - if (!list_empty(&mod->list)) { + if (io->mod[mod->type]) { struct rsnd_priv *priv = rsnd_mod_to_priv(mod); struct device *dev = rsnd_priv_to_dev(priv); @@ -300,14 +457,8 @@ int rsnd_dai_connect(struct rsnd_dai *rdai, 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; } @@ -316,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; @@ -324,7 +475,7 @@ 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_dai_nr(priv))) + if ((id < 0) || (id >= rsnd_rdai_nr(priv))) return NULL; return priv->rdai + id; @@ -382,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; @@ -421,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; @@ -440,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); + ret = rsnd_dai_call(stop, io, rdai); if (ret < 0) goto dai_trigger_end; - ret = rsnd_dai_call(rdai, io, quit); - 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; @@ -486,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; @@ -540,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; @@ -572,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); /* @@ -595,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, @@ -608,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 */ @@ -628,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, @@ -689,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, @@ -719,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; @@ -736,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; + for (i = 0; i < ARRAY_SIZE(probe_func); i++) { + ret = probe_func[i](pdev, of_data, priv); + if (ret) + return ret; + } - ret = rsnd_adg_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_ssi_probe(pdev, info, priv); - if (ret < 0) - 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 @@ -773,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; @@ -795,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; } @@ -813,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 61212ee97c2..1dd2b7d38c2 100644 --- a/sound/soc/sh/rcar/gen.c +++ b/sound/soc/sh/rcar/gen.c @@ -10,20 +10,6 @@ */ #include "rsnd.h" -struct rsnd_gen_ops { - int (*probe)(struct platform_device *pdev, - struct rcar_snd_info *info, - struct rsnd_priv *priv); - void (*remove)(struct platform_device *pdev, - struct rsnd_priv *priv); - 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 { void __iomem *base[RSND_BASE_MAX]; @@ -86,12 +72,28 @@ static struct regmap_bus rsnd_regmap_bus = { .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; @@ -103,6 +105,9 @@ void rsnd_write(struct rsnd_priv *priv, { 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); } @@ -111,74 +116,264 @@ void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod, { 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); } -/* - * Gen2 - * will be filled in the future - */ +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; } +/* + * 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) @@ -189,19 +384,23 @@ static int rsnd_gen1_path_exit(struct rsnd_priv *priv, static int rsnd_gen1_regmap_init(struct rsnd_priv *priv, struct rsnd_gen *gen) { - int i; - struct device *dev = rsnd_priv_to_dev(priv); - struct regmap_config regc; 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_CTRL, 0xc0), + 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, BUSIF_MODE, 0x20, 0x4), - RSND_GEN1_M_REG(gen, SRU, BUSIF_ADINR, 0x214, 0x40), + 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), @@ -219,28 +418,10 @@ static int rsnd_gen1_regmap_init(struct rsnd_priv *priv, struct rsnd_gen *gen) RSND_GEN1_M_REG(gen, SSI, SSIWSR, 0x20, 0x40), }; - 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] = devm_regmap_field_alloc(dev, gen->regmap, regf[i]); - if (IS_ERR(gen->regs[i])) - return PTR_ERR(gen->regs[i]); - - } - - return 0; + 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); @@ -270,56 +451,41 @@ static int rsnd_gen1_probe(struct platform_device *pdev, 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) -{ -} - -static struct rsnd_gen_ops rsnd_gen1_ops = { - .probe = rsnd_gen1_probe, - .remove = rsnd_gen1_remove, - .path_init = rsnd_gen1_path_init, - .path_exit = rsnd_gen1_path_exit, -}; - /* * Gen */ -int rsnd_gen_path_init(struct rsnd_priv *priv, - struct rsnd_dai *rdai, - struct rsnd_dai_stream *io) +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 rcar_snd_info *info = priv->info; - return gen->ops->path_init(priv, rdai, io); -} + if (!of_data) + return; -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); + 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 ret; + + rsnd_of_parse_gen(pdev, of_data, priv); gen = devm_kzalloc(dev, sizeof(*gen), GFP_KERNEL); if (!gen) { @@ -327,23 +493,16 @@ int rsnd_gen_probe(struct platform_device *pdev, return -ENOMEM; } - if (rsnd_is_gen1(priv)) - gen->ops = &rsnd_gen1_ops; - - if (!gen->ops) { - dev_err(dev, "unknown generation R-Car sound device\n"); - return -ENODEV; - } - priv->gen = gen; - return gen->ops->probe(pdev, info, priv); -} + ret = -ENODEV; + if (rsnd_is_gen1(priv)) + ret = rsnd_gen1_probe(pdev, priv); + else if (rsnd_is_gen2(priv)) + ret = rsnd_gen2_probe(pdev, priv); -void rsnd_gen_remove(struct platform_device *pdev, - struct rsnd_priv *priv) -{ - struct rsnd_gen *gen = rsnd_priv_to_gen(priv); + if (ret < 0) + dev_err(dev, "unknown generation R-Car sound device\n"); - gen->ops->remove(pdev, priv); + return ret; } diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 9e463e50e7e..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; @@ -83,29 +149,26 @@ 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); @@ -113,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); @@ -160,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]; @@ -174,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) @@ -198,24 +270,22 @@ 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); +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) @@ -225,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; @@ -245,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() @@ -258,42 +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); -bool rsnd_scu_hpbif_is_enable(struct rsnd_mod *mod); -#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 fa8fa15860b..00000000000 --- a/sound/soc/sh/rcar/scu.c +++ /dev/null @@ -1,243 +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; -} - -bool rsnd_scu_hpbif_is_enable(struct rsnd_mod *mod) -{ - struct rsnd_scu *scu = rsnd_mod_to_scu(mod); - u32 flags = rsnd_scu_mode_flags(scu); - - return !!(flags & RSND_SCU_USE_HPBIF); -} - -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 device *dev = rsnd_priv_to_dev(priv); - int ret; - - /* - * SCU will be used if it has RSND_SCU_USE_HPBIF flags - */ - if (!rsnd_scu_hpbif_is_enable(mod)) { - /* 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) -{ - if (WARN_ON(id < 0 || id >= rsnd_scu_nr(priv))) - id = 0; - - 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 5ac20cd5e00..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,108 +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_set(struct rsnd_priv *priv, - struct rsnd_dai *rdai, - struct rsnd_ssi *ssi) -{ - struct device *dev = rsnd_priv_to_dev(priv); - struct rsnd_mod *scu; - struct rsnd_ssiu *ssiu = rsnd_ssi_to_ssiu(ssi); - int id = rsnd_mod_id(&ssi->mod); - u32 flags; - u32 val; - - scu = rsnd_scu_mod_get(priv, rsnd_mod_id(&ssi->mod)); - - /* - * SSI_MODE0 - */ - - /* see also BUSIF_MODE */ - if (rsnd_scu_hpbif_is_enable(scu)) { - ssiu->ssi_mode0 &= ~(1 << id); - dev_dbg(dev, "SSI%d uses DEPENDENT mode\n", id); - } else { - ssiu->ssi_mode0 |= (1 << id); - dev_dbg(dev, "SSI%d uses INDEPENDENT mode\n", id); - } - - /* - * SSI_MODE1 - */ -#define ssi_parent_set(p, sync, adg, ext) \ - do { \ - ssi->parent = ssiu->ssi + p; \ - if (rsnd_rdai_is_clk_master(rdai)) \ - val = adg; \ - else \ - val = ext; \ - if (flags & RSND_SSI_SYNC) \ - val |= sync; \ - } while (0) - - flags = rsnd_ssi_mode_flags(ssi); - if (flags & RSND_SSI_CLK_PIN_SHARE) { - - val = 0; - switch (id) { - 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; - } - - 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) @@ -187,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[] = { @@ -199,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 @@ -209,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]; @@ -248,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); } } @@ -304,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)); @@ -321,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; @@ -367,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(priv, rdai, 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; @@ -416,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)) { @@ -434,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)); @@ -446,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); @@ -482,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); @@ -555,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, @@ -565,92 +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) { - if (WARN_ON(id < 0 || id >= rsnd_ssi_nr(priv))) - id = 0; + 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; - return &(((struct rsnd_ssiu *)(priv->ssiu))->ssi + id)->mod; + 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; + } + +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); @@ -658,62 +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); - } + rsnd_mod_init(priv, &ssi->mod, ops, RSND_MOD_SSI, 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, i); + rsnd_ssi_parent_clk_setup(priv, ssi); } - 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)); - } - -} |
