| From 469ca87d334fb915b265861b31c52e83fb7f3846 Mon Sep 17 00:00:00 2001 |
| From: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> |
| Date: Sun, 21 Jul 2013 21:35:52 -0700 |
| Subject: ASoC: add Renesas R-Car core feature |
| |
| Renesas R-Car series sound circuit consists of SSI and its peripheral. |
| But this peripheral circuits are different between |
| R-Car Generation1 (E1/M1/H1) and Generation2 (E2/M2/H2). |
| (Actually, there are many difference in Generation1 chips) |
| |
| Basically, for the future, Renesas R-Car series will use |
| Gen2 style sound circuit, but driver should care Gen1 also. |
| The main differences between Gen1 and Gen2 peripheral |
| are 1) register offset, 2) data path. |
| |
| This patch adds basic (core) feature for R-Car |
| series sound driver as prototype |
| |
| Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> |
| Signed-off-by: Mark Brown <broonie@linaro.org> |
| (cherry picked from commit 1536a968892aa9095aada4b6d2ed326432cd71c8) |
| Signed-off-by: Simon Horman <horms+renesas@verge.net.au> |
| --- |
| include/sound/rcar_snd.h | 33 +++ |
| sound/soc/sh/Kconfig | 7 + |
| sound/soc/sh/Makefile | 3 + |
| sound/soc/sh/rcar/Makefile | 2 + |
| sound/soc/sh/rcar/core.c | 554 +++++++++++++++++++++++++++++++++++++++++++++ |
| sound/soc/sh/rcar/rsnd.h | 94 ++++++++ |
| 6 files changed, 693 insertions(+) |
| create mode 100644 include/sound/rcar_snd.h |
| create mode 100644 sound/soc/sh/rcar/Makefile |
| create mode 100644 sound/soc/sh/rcar/core.c |
| create mode 100644 sound/soc/sh/rcar/rsnd.h |
| |
| diff --git a/include/sound/rcar_snd.h b/include/sound/rcar_snd.h |
| new file mode 100644 |
| index 00000000..7272b2ea |
| --- /dev/null |
| +++ b/include/sound/rcar_snd.h |
| @@ -0,0 +1,33 @@ |
| +/* |
| + * Renesas R-Car SRU/SCU/SSIU/SSI 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. |
| + */ |
| + |
| +#ifndef RCAR_SND_H |
| +#define RCAR_SND_H |
| + |
| +#include <linux/sh_clk.h> |
| + |
| + |
| +#define RSND_BASE_MAX 0 |
| + |
| +struct rsnd_dai_platform_info { |
| + int ssi_id_playback; |
| + int ssi_id_capture; |
| +}; |
| + |
| +struct rcar_snd_info { |
| + u32 flags; |
| + struct rsnd_dai_platform_info *dai_info; |
| + int dai_info_nr; |
| + int (*start)(int id); |
| + int (*stop)(int id); |
| +}; |
| + |
| +#endif |
| diff --git a/sound/soc/sh/Kconfig b/sound/soc/sh/Kconfig |
| index 6bcb1164..56d8ff6a 100644 |
| --- a/sound/soc/sh/Kconfig |
| +++ b/sound/soc/sh/Kconfig |
| @@ -34,6 +34,13 @@ config SND_SOC_SH4_SIU |
| select SH_DMAE |
| select FW_LOADER |
| |
| +config SND_SOC_RCAR |
| + tristate "R-Car series SRU/SCU/SSIU/SSI support" |
| + select SND_SIMPLE_CARD |
| + select RCAR_CLK_ADG |
| + help |
| + This option enables R-Car SUR/SCU/SSIU/SSI sound support |
| + |
| ## |
| ## Boards |
| ## |
| diff --git a/sound/soc/sh/Makefile b/sound/soc/sh/Makefile |
| index 849b387d..aaf3dcd1 100644 |
| --- a/sound/soc/sh/Makefile |
| +++ b/sound/soc/sh/Makefile |
| @@ -12,6 +12,9 @@ obj-$(CONFIG_SND_SOC_SH4_SSI) += snd-soc-ssi.o |
| obj-$(CONFIG_SND_SOC_SH4_FSI) += snd-soc-fsi.o |
| obj-$(CONFIG_SND_SOC_SH4_SIU) += snd-soc-siu.o |
| |
| +## audio units for R-Car |
| +obj-$(CONFIG_SND_SOC_RCAR) += rcar/ |
| + |
| ## boards |
| snd-soc-sh7760-ac97-objs := sh7760-ac97.o |
| snd-soc-migor-objs := migor.o |
| diff --git a/sound/soc/sh/rcar/Makefile b/sound/soc/sh/rcar/Makefile |
| new file mode 100644 |
| index 00000000..cd8089f2 |
| --- /dev/null |
| +++ b/sound/soc/sh/rcar/Makefile |
| @@ -0,0 +1,2 @@ |
| +snd-soc-rcar-objs := core.o |
| +obj-$(CONFIG_SND_SOC_RCAR) += snd-soc-rcar.o |
| diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c |
| new file mode 100644 |
| index 00000000..13b5d50e |
| --- /dev/null |
| +++ b/sound/soc/sh/rcar/core.c |
| @@ -0,0 +1,554 @@ |
| +/* |
| + * Renesas R-Car SRU/SCU/SSIU/SSI support |
| + * |
| + * Copyright (C) 2013 Renesas Solutions Corp. |
| + * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> |
| + * |
| + * Based on fsi.c |
| + * Kuninori Morimoto <morimoto.kuninori@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. |
| + */ |
| + |
| +/* |
| + * Renesas R-Car sound device structure |
| + * |
| + * Gen1 |
| + * |
| + * SRU : Sound Routing Unit |
| + * - SRC : Sampling Rate Converter |
| + * - CMD |
| + * - CTU : Channel Count Conversion Unit |
| + * - MIX : Mixer |
| + * - DVC : Digital Volume and Mute Function |
| + * - SSI : Serial Sound Interface |
| + * |
| + * Gen2 |
| + * |
| + * SCU : Sampling Rate Converter Unit |
| + * - SRC : Sampling Rate Converter |
| + * - CMD |
| + * - CTU : Channel Count Conversion Unit |
| + * - MIX : Mixer |
| + * - DVC : Digital Volume and Mute Function |
| + * SSIU : Serial Sound Interface Unit |
| + * - SSI : Serial Sound Interface |
| + */ |
| + |
| +/* |
| + * driver data Image |
| + * |
| + * rsnd_priv |
| + * | |
| + * | ** this depends on Gen1/Gen2 |
| + * | |
| + * +- gen |
| + * | |
| + * | ** these depend on data path |
| + * | ** gen and platform data control it |
| + * | |
| + * +- rdai[0] |
| + * | | sru ssiu ssi |
| + * | +- playback -> [mod] -> [mod] -> [mod] -> ... |
| + * | | |
| + * | | sru ssiu ssi |
| + * | +- capture -> [mod] -> [mod] -> [mod] -> ... |
| + * | |
| + * +- rdai[1] |
| + * | | sru ssiu ssi |
| + * | +- playback -> [mod] -> [mod] -> [mod] -> ... |
| + * | | |
| + * | | sru ssiu ssi |
| + * | +- capture -> [mod] -> [mod] -> [mod] -> ... |
| + * ... |
| + * | |
| + * | ** these control ssi |
| + * | |
| + * +- ssi |
| + * | | |
| + * | +- ssi[0] |
| + * | +- ssi[1] |
| + * | +- ssi[2] |
| + * | ... |
| + * | |
| + * | ** these control scu |
| + * | |
| + * +- scu |
| + * | |
| + * +- scu[0] |
| + * +- scu[1] |
| + * +- scu[2] |
| + * ... |
| + * |
| + * |
| + * for_each_rsnd_dai(xx, priv, xx) |
| + * rdai[0] => rdai[1] => rdai[2] => ... |
| + * |
| + * for_each_rsnd_mod(xx, rdai, xx) |
| + * [mod] => [mod] => [mod] => ... |
| + * |
| + * rsnd_dai_call(xxx, fn ) |
| + * [mod]->fn() -> [mod]->fn() -> [mod]->fn()... |
| + * |
| + */ |
| +#include <linux/pm_runtime.h> |
| +#include "rsnd.h" |
| + |
| +#define RSND_RATES SNDRV_PCM_RATE_8000_96000 |
| +#define RSND_FMTS (SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE) |
| + |
| +/* |
| + * rsnd_platform functions |
| + */ |
| +#define rsnd_platform_call(priv, dai, func, param...) \ |
| + (!(priv->info->func) ? -ENODEV : \ |
| + priv->info->func(param)) |
| + |
| + |
| +/* |
| + * rsnd_dai functions |
| + */ |
| +struct rsnd_dai *rsnd_dai_get(struct rsnd_priv *priv, int id) |
| +{ |
| + return priv->rdai + id; |
| +} |
| + |
| +static struct rsnd_dai *rsnd_dai_to_rdai(struct snd_soc_dai *dai) |
| +{ |
| + struct rsnd_priv *priv = snd_soc_dai_get_drvdata(dai); |
| + |
| + return rsnd_dai_get(priv, dai->id); |
| +} |
| + |
| +int rsnd_dai_is_play(struct rsnd_dai *rdai, struct rsnd_dai_stream *io) |
| +{ |
| + return &rdai->playback == io; |
| +} |
| + |
| +/* |
| + * rsnd_soc_dai functions |
| + */ |
| +int rsnd_dai_pointer_offset(struct rsnd_dai_stream *io, int additional) |
| +{ |
| + struct snd_pcm_substream *substream = io->substream; |
| + struct snd_pcm_runtime *runtime = substream->runtime; |
| + int pos = io->byte_pos + additional; |
| + |
| + pos %= (runtime->periods * io->byte_per_period); |
| + |
| + return pos; |
| +} |
| + |
| +void rsnd_dai_pointer_update(struct rsnd_dai_stream *io, int byte) |
| +{ |
| + io->byte_pos += byte; |
| + |
| + if (io->byte_pos >= io->next_period_byte) { |
| + struct snd_pcm_substream *substream = io->substream; |
| + struct snd_pcm_runtime *runtime = substream->runtime; |
| + |
| + io->period_pos++; |
| + io->next_period_byte += io->byte_per_period; |
| + |
| + if (io->period_pos >= runtime->periods) { |
| + io->byte_pos = 0; |
| + io->period_pos = 0; |
| + io->next_period_byte = io->byte_per_period; |
| + } |
| + |
| + snd_pcm_period_elapsed(substream); |
| + } |
| +} |
| + |
| +static int rsnd_dai_stream_init(struct rsnd_dai_stream *io, |
| + struct snd_pcm_substream *substream) |
| +{ |
| + 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; |
| + io->byte_per_period = runtime->period_size * |
| + runtime->channels * |
| + samples_to_bytes(runtime, 1); |
| + io->next_period_byte = io->byte_per_period; |
| + |
| + return 0; |
| +} |
| + |
| +static |
| +struct snd_soc_dai *rsnd_substream_to_dai(struct snd_pcm_substream *substream) |
| +{ |
| + struct snd_soc_pcm_runtime *rtd = substream->private_data; |
| + |
| + return rtd->cpu_dai; |
| +} |
| + |
| +static |
| +struct rsnd_dai_stream *rsnd_rdai_to_io(struct rsnd_dai *rdai, |
| + struct snd_pcm_substream *substream) |
| +{ |
| + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) |
| + return &rdai->playback; |
| + else |
| + return &rdai->capture; |
| +} |
| + |
| +static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd, |
| + struct snd_soc_dai *dai) |
| +{ |
| + 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_dai_platform_info *info = rsnd_dai_get_platform_info(rdai); |
| + int ssi_id = rsnd_dai_is_play(rdai, io) ? info->ssi_id_playback : |
| + info->ssi_id_capture; |
| + int ret; |
| + unsigned long flags; |
| + |
| + rsnd_lock(priv, flags); |
| + |
| + switch (cmd) { |
| + case SNDRV_PCM_TRIGGER_START: |
| + ret = rsnd_dai_stream_init(io, substream); |
| + if (ret < 0) |
| + goto dai_trigger_end; |
| + |
| + ret = rsnd_platform_call(priv, dai, start, ssi_id); |
| + if (ret < 0) |
| + goto dai_trigger_end; |
| + |
| + break; |
| + case SNDRV_PCM_TRIGGER_STOP: |
| + ret = rsnd_platform_call(priv, dai, stop, ssi_id); |
| + if (ret < 0) |
| + goto dai_trigger_end; |
| + |
| + break; |
| + default: |
| + ret = -EINVAL; |
| + } |
| + |
| +dai_trigger_end: |
| + rsnd_unlock(priv, flags); |
| + |
| + return ret; |
| +} |
| + |
| +static int rsnd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) |
| +{ |
| + struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai); |
| + |
| + /* set master/slave audio interface */ |
| + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { |
| + case SND_SOC_DAIFMT_CBM_CFM: |
| + rdai->clk_master = 1; |
| + break; |
| + case SND_SOC_DAIFMT_CBS_CFS: |
| + rdai->clk_master = 0; |
| + break; |
| + default: |
| + return -EINVAL; |
| + } |
| + |
| + /* set clock inversion */ |
| + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { |
| + case SND_SOC_DAIFMT_NB_IF: |
| + rdai->bit_clk_inv = 0; |
| + rdai->frm_clk_inv = 1; |
| + break; |
| + case SND_SOC_DAIFMT_IB_NF: |
| + rdai->bit_clk_inv = 1; |
| + rdai->frm_clk_inv = 0; |
| + break; |
| + case SND_SOC_DAIFMT_IB_IF: |
| + rdai->bit_clk_inv = 1; |
| + rdai->frm_clk_inv = 1; |
| + break; |
| + case SND_SOC_DAIFMT_NB_NF: |
| + default: |
| + rdai->bit_clk_inv = 0; |
| + rdai->frm_clk_inv = 0; |
| + break; |
| + } |
| + |
| + /* set format */ |
| + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { |
| + case SND_SOC_DAIFMT_I2S: |
| + rdai->sys_delay = 0; |
| + rdai->data_alignment = 0; |
| + break; |
| + case SND_SOC_DAIFMT_LEFT_J: |
| + rdai->sys_delay = 1; |
| + rdai->data_alignment = 0; |
| + break; |
| + case SND_SOC_DAIFMT_RIGHT_J: |
| + rdai->sys_delay = 1; |
| + rdai->data_alignment = 1; |
| + break; |
| + } |
| + |
| + return 0; |
| +} |
| + |
| +static const struct snd_soc_dai_ops rsnd_soc_dai_ops = { |
| + .trigger = rsnd_soc_dai_trigger, |
| + .set_fmt = rsnd_soc_dai_set_fmt, |
| +}; |
| + |
| +static int rsnd_dai_probe(struct platform_device *pdev, |
| + struct rcar_snd_info *info, |
| + struct rsnd_priv *priv) |
| +{ |
| + struct snd_soc_dai_driver *drv; |
| + struct rsnd_dai *rdai; |
| + struct device *dev = rsnd_priv_to_dev(priv); |
| + struct rsnd_dai_platform_info *dai_info; |
| + int dai_nr = info->dai_info_nr; |
| + int i, pid, cid; |
| + |
| + drv = devm_kzalloc(dev, sizeof(*drv) * dai_nr, GFP_KERNEL); |
| + rdai = devm_kzalloc(dev, sizeof(*rdai) * dai_nr, GFP_KERNEL); |
| + if (!drv || !rdai) { |
| + dev_err(dev, "dai allocate failed\n"); |
| + return -ENOMEM; |
| + } |
| + |
| + for (i = 0; i < dai_nr; i++) { |
| + dai_info = &info->dai_info[i]; |
| + |
| + pid = dai_info->ssi_id_playback; |
| + cid = dai_info->ssi_id_capture; |
| + |
| + /* |
| + * init rsnd_dai |
| + */ |
| + INIT_LIST_HEAD(&rdai[i].playback.head); |
| + INIT_LIST_HEAD(&rdai[i].capture.head); |
| + |
| + rdai[i].info = dai_info; |
| + |
| + snprintf(rdai[i].name, RSND_DAI_NAME_SIZE, "rsnd-dai.%d", i); |
| + |
| + /* |
| + * init snd_soc_dai_driver |
| + */ |
| + drv[i].name = rdai[i].name; |
| + drv[i].ops = &rsnd_soc_dai_ops; |
| + if (pid >= 0) { |
| + drv[i].playback.rates = RSND_RATES; |
| + drv[i].playback.formats = RSND_FMTS; |
| + drv[i].playback.channels_min = 2; |
| + drv[i].playback.channels_max = 2; |
| + } |
| + if (cid >= 0) { |
| + drv[i].capture.rates = RSND_RATES; |
| + drv[i].capture.formats = RSND_FMTS; |
| + drv[i].capture.channels_min = 2; |
| + drv[i].capture.channels_max = 2; |
| + } |
| + |
| + dev_dbg(dev, "%s (%d, %d) probed", rdai[i].name, pid, cid); |
| + } |
| + |
| + 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 |
| + */ |
| +static struct snd_pcm_hardware rsnd_pcm_hardware = { |
| + .info = SNDRV_PCM_INFO_INTERLEAVED | |
| + 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, |
| + .periods_min = 1, |
| + .periods_max = 32, |
| + .fifo_size = 256, |
| +}; |
| + |
| +static int rsnd_pcm_open(struct snd_pcm_substream *substream) |
| +{ |
| + struct snd_pcm_runtime *runtime = substream->runtime; |
| + int ret = 0; |
| + |
| + snd_soc_set_runtime_hwparams(substream, &rsnd_pcm_hardware); |
| + |
| + ret = snd_pcm_hw_constraint_integer(runtime, |
| + SNDRV_PCM_HW_PARAM_PERIODS); |
| + |
| + return ret; |
| +} |
| + |
| +static int rsnd_hw_params(struct snd_pcm_substream *substream, |
| + struct snd_pcm_hw_params *hw_params) |
| +{ |
| + return snd_pcm_lib_malloc_pages(substream, |
| + params_buffer_bytes(hw_params)); |
| +} |
| + |
| +static snd_pcm_uframes_t rsnd_pointer(struct snd_pcm_substream *substream) |
| +{ |
| + struct snd_pcm_runtime *runtime = substream->runtime; |
| + struct snd_soc_dai *dai = rsnd_substream_to_dai(substream); |
| + struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai); |
| + struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream); |
| + |
| + return bytes_to_frames(runtime, io->byte_pos); |
| +} |
| + |
| +static struct snd_pcm_ops rsnd_pcm_ops = { |
| + .open = rsnd_pcm_open, |
| + .ioctl = snd_pcm_lib_ioctl, |
| + .hw_params = rsnd_hw_params, |
| + .hw_free = snd_pcm_lib_free_pages, |
| + .pointer = rsnd_pointer, |
| +}; |
| + |
| +/* |
| + * snd_soc_platform |
| + */ |
| + |
| +#define PREALLOC_BUFFER (32 * 1024) |
| +#define PREALLOC_BUFFER_MAX (32 * 1024) |
| + |
| +static int rsnd_pcm_new(struct snd_soc_pcm_runtime *rtd) |
| +{ |
| + return snd_pcm_lib_preallocate_pages_for_all( |
| + rtd->pcm, |
| + SNDRV_DMA_TYPE_DEV, |
| + rtd->card->snd_card->dev, |
| + PREALLOC_BUFFER, PREALLOC_BUFFER_MAX); |
| +} |
| + |
| +static void rsnd_pcm_free(struct snd_pcm *pcm) |
| +{ |
| + snd_pcm_lib_preallocate_free_for_all(pcm); |
| +} |
| + |
| +static struct snd_soc_platform_driver rsnd_soc_platform = { |
| + .ops = &rsnd_pcm_ops, |
| + .pcm_new = rsnd_pcm_new, |
| + .pcm_free = rsnd_pcm_free, |
| +}; |
| + |
| +static const struct snd_soc_component_driver rsnd_soc_component = { |
| + .name = "rsnd", |
| +}; |
| + |
| +/* |
| + * rsnd probe |
| + */ |
| +static int rsnd_probe(struct platform_device *pdev) |
| +{ |
| + struct rcar_snd_info *info; |
| + struct rsnd_priv *priv; |
| + struct device *dev = &pdev->dev; |
| + int ret; |
| + |
| + info = pdev->dev.platform_data; |
| + if (!info) { |
| + dev_err(dev, "driver needs R-Car sound information\n"); |
| + return -ENODEV; |
| + } |
| + |
| + /* |
| + * init priv data |
| + */ |
| + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); |
| + if (!priv) { |
| + dev_err(dev, "priv allocate failed\n"); |
| + return -ENODEV; |
| + } |
| + |
| + priv->dev = dev; |
| + priv->info = info; |
| + spin_lock_init(&priv->lock); |
| + |
| + /* |
| + * init each module |
| + */ |
| + ret = rsnd_dai_probe(pdev, info, priv); |
| + if (ret < 0) |
| + return ret; |
| + |
| + /* |
| + * asoc register |
| + */ |
| + ret = snd_soc_register_platform(dev, &rsnd_soc_platform); |
| + if (ret < 0) { |
| + dev_err(dev, "cannot snd soc register\n"); |
| + return ret; |
| + } |
| + |
| + ret = snd_soc_register_component(dev, &rsnd_soc_component, |
| + priv->daidrv, rsnd_dai_nr(priv)); |
| + if (ret < 0) { |
| + dev_err(dev, "cannot snd dai register\n"); |
| + goto exit_snd_soc; |
| + } |
| + |
| + dev_set_drvdata(dev, priv); |
| + |
| + pm_runtime_enable(dev); |
| + |
| + dev_info(dev, "probed\n"); |
| + return ret; |
| + |
| +exit_snd_soc: |
| + snd_soc_unregister_platform(dev); |
| + |
| + return ret; |
| +} |
| + |
| +static int rsnd_remove(struct platform_device *pdev) |
| +{ |
| + struct rsnd_priv *priv = dev_get_drvdata(&pdev->dev); |
| + |
| + pm_runtime_disable(&pdev->dev); |
| + |
| + /* |
| + * remove each module |
| + */ |
| + rsnd_dai_remove(pdev, priv); |
| + |
| + return 0; |
| +} |
| + |
| +static struct platform_driver rsnd_driver = { |
| + .driver = { |
| + .name = "rcar_sound", |
| + }, |
| + .probe = rsnd_probe, |
| + .remove = rsnd_remove, |
| +}; |
| +module_platform_driver(rsnd_driver); |
| + |
| +MODULE_LICENSE("GPL"); |
| +MODULE_DESCRIPTION("Renesas R-Car audio driver"); |
| +MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>"); |
| +MODULE_ALIAS("platform:rcar-pcm-audio"); |
| diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h |
| new file mode 100644 |
| index 00000000..8d04fd03 |
| --- /dev/null |
| +++ b/sound/soc/sh/rcar/rsnd.h |
| @@ -0,0 +1,94 @@ |
| +/* |
| + * Renesas R-Car |
| + * |
| + * 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. |
| + */ |
| +#ifndef RSND_H |
| +#define RSND_H |
| + |
| +#include <linux/clk.h> |
| +#include <linux/device.h> |
| +#include <linux/io.h> |
| +#include <linux/list.h> |
| +#include <linux/module.h> |
| +#include <sound/rcar_snd.h> |
| +#include <sound/soc.h> |
| +#include <sound/pcm_params.h> |
| + |
| +/* |
| + * pseudo register |
| + * |
| + * The register address offsets SRU/SCU/SSIU on Gen1/Gen2 are very different. |
| + * This driver uses pseudo register in order to hide it. |
| + * see gen1/gen2 for detail |
| + */ |
| +struct rsnd_priv; |
| +struct rsnd_dai; |
| +struct rsnd_dai_stream; |
| + |
| +/* |
| + * R-Car sound DAI |
| + */ |
| +#define RSND_DAI_NAME_SIZE 16 |
| +struct rsnd_dai_stream { |
| + struct list_head head; /* head of rsnd_mod list */ |
| + struct snd_pcm_substream *substream; |
| + int byte_pos; |
| + int period_pos; |
| + int byte_per_period; |
| + int next_period_byte; |
| +}; |
| + |
| +struct rsnd_dai { |
| + char name[RSND_DAI_NAME_SIZE]; |
| + struct rsnd_dai_platform_info *info; /* rcar_snd.h */ |
| + 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; |
| +}; |
| + |
| +#define rsnd_dai_nr(priv) ((priv)->dai_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)) |
| + |
| +struct rsnd_dai *rsnd_dai_get(struct rsnd_priv *priv, int id); |
| +int rsnd_dai_is_play(struct rsnd_dai *rdai, struct rsnd_dai_stream *io); |
| +#define rsnd_dai_get_platform_info(rdai) ((rdai)->info) |
| + |
| +void rsnd_dai_pointer_update(struct rsnd_dai_stream *io, int cnt); |
| +int rsnd_dai_pointer_offset(struct rsnd_dai_stream *io, int additional); |
| + |
| +/* |
| + * R-Car sound priv |
| + */ |
| +struct rsnd_priv { |
| + |
| + struct device *dev; |
| + struct rcar_snd_info *info; |
| + spinlock_t lock; |
| + |
| + /* |
| + * below value will be filled on rsnd_dai_probe() |
| + */ |
| + struct snd_soc_dai_driver *daidrv; |
| + struct rsnd_dai *rdai; |
| + int dai_nr; |
| +}; |
| + |
| +#define rsnd_priv_to_dev(priv) ((priv)->dev) |
| +#define rsnd_lock(priv, flags) spin_lock_irqsave(&priv->lock, flags) |
| +#define rsnd_unlock(priv, flags) spin_unlock_irqrestore(&priv->lock, flags) |
| + |
| +#endif |
| -- |
| 1.8.4.3.gca3854a |
| |