diff mbox

[1/2] Initial pass at a framework for greybus audio driver

Message ID 1425680973-471-1-git-send-email-john.stultz@linaro.org
State New
Headers show

Commit Message

John Stultz March 6, 2015, 10:29 p.m. UTC
So this is my initial pass at a framework for the greybus
audio driver. This is basically a fairly simple dummy driver
that "consumes" audio that is played and provides access to
the codec knobs. It currently only does playback and not do
capture yet (nothing to capture from).

The dummy timer logic needs to be replaced and to be hooked
upto the greybus I2S interfaces, and possibly utilize dma
to the bridge (I'm still having to look into that).

It does interface w/ the codec driver (rt5639 on jetson).

I also has not yet been integrated it into the greybus
git tree, and instead this is against the tegra kernel in
our repo (mostly due to convenience w/ testing).

Would love some initial feedback on this. I'm sure changes
are goign to be needed to make it a proper driver module.
As well as changes for dealing with the fact that we'll need
to instantiate a number of devices with this driver.

Cc: mark greer <mark.greer@animalcreek.com>
Cc: Rob Herring <rob.herring@linaro.org>
Cc: Pankaj Bharadiya <pankaj.bharadiya@linaro.org>
Cc: Mark Brown <broonie@linaro.org>
Change-Id: Ib40d2b473ef4e371cb55d7dc7afd19d6721662f9
Signed-off-by: John Stultz <john.stultz@linaro.org>
---
 sound/soc/Kconfig        |   1 +
 sound/soc/Makefile       |   1 +
 sound/soc/ara/Kconfig    |   8 ++
 sound/soc/ara/Makefile   |   1 +
 sound/soc/ara/gb_audio.c | 308 +++++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 319 insertions(+)
 create mode 100644 sound/soc/ara/Kconfig
 create mode 100644 sound/soc/ara/Makefile
 create mode 100644 sound/soc/ara/gb_audio.c
diff mbox

Patch

diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
index 423e06d..8b3f503 100644
--- a/sound/soc/Kconfig
+++ b/sound/soc/Kconfig
@@ -35,6 +35,7 @@  config SND_SOC_GENERIC_DMAENGINE_PCM
 
 # All the supported SoCs
 source "sound/soc/atmel/Kconfig"
+source "sound/soc/ara/Kconfig"
 source "sound/soc/au1x/Kconfig"
 source "sound/soc/blackfin/Kconfig"
 source "sound/soc/cirrus/Kconfig"
diff --git a/sound/soc/Makefile b/sound/soc/Makefile
index 3d14cb82..87b79e5 100644
--- a/sound/soc/Makefile
+++ b/sound/soc/Makefile
@@ -13,6 +13,7 @@  obj-$(CONFIG_SND_SOC)	+= snd-soc-core.o
 obj-$(CONFIG_SND_SOC)	+= codecs/
 obj-$(CONFIG_SND_SOC)	+= generic/
 obj-$(CONFIG_SND_SOC)	+= atmel/
+obj-$(CONFIG_SND_SOC)	+= ara/
 obj-$(CONFIG_SND_SOC)	+= au1x/
 obj-$(CONFIG_SND_SOC)	+= blackfin/
 obj-$(CONFIG_SND_SOC)	+= cirrus/
diff --git a/sound/soc/ara/Kconfig b/sound/soc/ara/Kconfig
new file mode 100644
index 0000000..011e6d8
--- /dev/null
+++ b/sound/soc/ara/Kconfig
@@ -0,0 +1,8 @@ 
+config SND_ARA_CARD
+	tristate "ASoC Ara sound card support"
+	select SND_SOC_RT5639
+	select SND_SOC_SPDIF
+	select SND_SOC_GENERIC_DMAENGINE_PCM
+	select SND_SIMPLE_CARD
+	help
+	  This option enables generic ara sound card support
diff --git a/sound/soc/ara/Makefile b/sound/soc/ara/Makefile
new file mode 100644
index 0000000..8282e4a
--- /dev/null
+++ b/sound/soc/ara/Makefile
@@ -0,0 +1 @@ 
+obj-$(CONFIG_SND_ARA_CARD)	+= gb_audio.o
diff --git a/sound/soc/ara/gb_audio.c b/sound/soc/ara/gb_audio.c
new file mode 100644
index 0000000..7c1760b
--- /dev/null
+++ b/sound/soc/ara/gb_audio.c
@@ -0,0 +1,308 @@ 
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/dmaengine_pcm.h>
+#include <sound/simple_card.h>
+
+
+#define GB_RATES	SNDRV_PCM_RATE_8000_48000
+#define GB_FMTS		SNDRV_PCM_FMTBIT_S16_LE
+#define PREALLOC_BUFFER         (32 * 1024)
+#define PREALLOC_BUFFER_MAX     (32 * 1024)
+
+/***************************************************************
+ * This is the gb_pcm_timer logic which fakes DMA interrupts
+ * via a timer.
+ ***************************************************************/
+
+struct gb_pcm_timer {
+	struct snd_pcm_substream *substream;
+	struct timer_list timer;
+	int hwptr_done;
+};
+
+static void dummy_timer_start(struct gb_pcm_timer *pcm)
+{
+	pcm->timer.expires = jiffies + HZ/8;
+	add_timer(&pcm->timer);
+}
+
+static void dummy_timer_stop(struct gb_pcm_timer *pcm)
+{
+	del_timer(&pcm->timer);
+	pcm->timer.expires = 0;
+}
+
+static void dummy_timer_function(unsigned long data)
+{
+	struct gb_pcm_timer *pcm = (struct gb_pcm_timer *)data;
+	struct snd_pcm_substream *substream = pcm->substream;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+
+	/* pretend to consume a chunk of data */
+	pcm->hwptr_done += 1024;
+	if (pcm->hwptr_done > frames_to_bytes(runtime, runtime->buffer_size))
+		pcm->hwptr_done = frames_to_bytes(runtime, runtime->buffer_size);
+
+	dummy_timer_start(pcm);
+	snd_pcm_period_elapsed(substream);
+}
+
+
+/******************************************
+ * dai op functions)
+ ******************************************/
+
+static int gb_dai_startup(struct snd_pcm_substream *substream,
+				struct snd_soc_dai *dai)
+{
+	return 0;
+}
+
+static void gb_dai_shutdown(struct snd_pcm_substream *substream,
+				struct snd_soc_dai *dai)
+{
+}
+
+static int gb_dai_trigger(struct snd_pcm_substream *substream, int cmd,
+				struct snd_soc_dai *dai)
+{
+	struct gb_pcm_timer *pcmt = substream->runtime->private_data;
+
+	printk("JDB: gb_dai_trigger!\n");
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		dummy_timer_start(pcmt);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+		dummy_timer_stop(pcmt);
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int gb_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+	return 0;
+}
+
+static int gb_dai_hw_params(struct snd_pcm_substream *substream,
+				struct snd_pcm_hw_params *params,
+				struct snd_soc_dai *dai)
+{
+	printk("JDB: gb_dai_hw_params!\n");
+	return 0;
+}
+
+
+static const struct snd_soc_dai_ops gb_dai_ops = {
+	.startup        = gb_dai_startup,
+	.shutdown       = gb_dai_shutdown,
+	.trigger        = gb_dai_trigger,
+	.set_fmt        = gb_dai_set_fmt,
+	.hw_params      = gb_dai_hw_params,
+};
+
+static struct snd_soc_dai_driver gb_cpu_dai = {
+	/*XXX WTF, this doesn't seem to be the name of the dai that gets registered! */
+	.name                   = "gb-cpu-dai",
+	.playback = {
+		.rates          = GB_RATES,
+		.formats        = GB_FMTS,
+		.channels_min   = 2,
+		.channels_max   = 2,
+	},
+	.ops = &gb_dai_ops,
+};
+
+/******************************************************
+ * gb pcm logic
+ *****************************************************/
+static struct snd_pcm_hardware gb_plat_pcm_hardware = {
+	.info =         SNDRV_PCM_INFO_INTERLEAVED,
+	.formats                = GB_FMTS,
+	.rates                  = GB_RATES,
+	.rate_min               = 8000,
+	.rate_max               = 48000,
+	.channels_min           = 2,
+	.channels_max           = 2,
+	/* XXX - All the values below are junk */
+	.buffer_bytes_max       = 64 * 1024,
+	.period_bytes_min       = 32,
+	.period_bytes_max       = 8192,
+	.periods_min            = 1,
+	.periods_max            = 32,
+	.fifo_size              = 256,
+};
+
+static snd_pcm_uframes_t gb_pcm_pointer(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct gb_pcm_timer *pcmt = substream->runtime->private_data;
+	int hwptr_done;
+
+	hwptr_done = pcmt->hwptr_done;
+
+	return hwptr_done;
+}
+
+static int gb_pcm_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct gb_pcm_timer *pcmt = substream->runtime->private_data;
+
+	pcmt->hwptr_done = 0;
+
+	return 0;
+}
+
+static int gb_pcm_open(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct gb_pcm_timer *pcmt;
+
+	pcmt = kzalloc(sizeof(*pcmt), GFP_KERNEL);
+	if (!pcmt)
+		return -ENOMEM;
+	pcmt->substream = substream;
+	setup_timer(&pcmt->timer, dummy_timer_function, (unsigned long)pcmt);
+	runtime->private_data = pcmt;
+
+	snd_soc_set_runtime_hwparams(substream, &gb_plat_pcm_hardware);
+	return snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+}
+
+static int gb_pcm_close(struct snd_pcm_substream *substream)
+{
+	if (substream->runtime->private_data)
+		kfree(substream->runtime->private_data);
+	substream->runtime->private_data = NULL;
+	return 0;
+}
+
+static int gb_pcm_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 int gb_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+	return snd_pcm_lib_free_pages(substream);
+}
+
+static struct snd_pcm_ops gb_pcm_ops = {
+	.open           = gb_pcm_open,
+	.close		= gb_pcm_close,
+	.ioctl          = snd_pcm_lib_ioctl,
+	.hw_params      = gb_pcm_hw_params,
+	.hw_free        = gb_pcm_hw_free,
+	.prepare	= gb_pcm_prepare,
+	.pointer        = gb_pcm_pointer,
+};
+
+static void gb_pcm_free(struct snd_pcm *pcm)
+{
+	snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+static int gb_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+	struct snd_pcm *pcm = rtd->pcm;
+
+	return snd_pcm_lib_preallocate_pages_for_all(
+			pcm,
+			SNDRV_DMA_TYPE_CONTINUOUS,
+			snd_dma_continuous_data(GFP_KERNEL),
+			PREALLOC_BUFFER, PREALLOC_BUFFER_MAX);
+}
+
+static struct snd_soc_platform_driver gb_soc_platform = {
+	.ops            = &gb_pcm_ops,
+	.pcm_new        = gb_pcm_new,
+	.pcm_free       = gb_pcm_free,
+};
+
+
+/************************************************************
+ * This is the aosc simple card junk which binds the platform
+ * codec, cpu and codec-dais etc togheter, also all the
+ * nested gross platfrom driver/device junk is here.
+ ************************************************************/
+static struct asoc_simple_card_info gb_card_info = {
+	.name           = "Greybus Audio Module",
+	.card           = "gb-card",
+	.codec          = "rt5639.0-001c", /* XXX this will need to be dynamic*/
+	.platform       = "gb-pcm-audio.0",
+	.daifmt         = GB_FMTS,
+	.cpu_dai = {
+		.name   = "gb-pcm-audio.0", /*XXX shouldn't this be gb-cpu-dai?*/
+		.fmt    = GB_FMTS,
+	},
+	.codec_dai = {
+		.name   = "rt5639-aif1",
+		.fmt    = SND_SOC_DAIFMT_CBM_CFM,
+		.sysclk = 11289600,
+	}
+};
+
+static struct platform_device gb_snd_plat_device = {
+	.name   = "asoc-simple-card",
+	.dev    = {
+		.platform_data  = &gb_card_info,
+	},
+};
+
+static struct platform_device gb_device = {
+	.name           = "gb-pcm-audio",
+};
+
+static struct platform_device *gb_devices[] = {
+	&gb_snd_plat_device,
+	&gb_device,
+};
+
+static const struct snd_soc_component_driver gb_soc_component = {
+	.name           = "gb-component",
+};
+
+static int gb_plat_probe(struct platform_device *pdev)
+{
+	int ret;
+
+	ret = snd_soc_register_platform(&pdev->dev, &gb_soc_platform);
+	ret = snd_soc_register_component(&pdev->dev, &gb_soc_component,
+							&gb_cpu_dai, 1);
+	return ret;
+}
+
+static struct platform_driver gb_plat_driver = {
+	.driver         = {
+		.name   = "gb-pcm-audio",
+	},
+	.probe          = gb_plat_probe,
+};
+
+
+/******************************************************************
+ * This is the basic hook to let me get things initialized
+ ******************************************************************/
+static int __init devices_setup(void)
+{
+	int err;
+	struct platform_device *device;
+
+	err = platform_driver_register(&gb_plat_driver);
+
+	platform_add_devices(gb_devices, ARRAY_SIZE(gb_devices));
+
+	return err;
+}
+device_initcall(devices_setup);