From patchwork Fri Mar 6 22:29:32 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: John Stultz X-Patchwork-Id: 45498 Return-Path: X-Original-To: linaro@patches.linaro.org Delivered-To: linaro@patches.linaro.org Received: from mail-la0-f69.google.com (mail-la0-f69.google.com [209.85.215.69]) by ip-10-151-82-157.ec2.internal (Postfix) with ESMTPS id EC0D7214A0 for ; Fri, 6 Mar 2015 22:29:45 +0000 (UTC) Received: by labge10 with SMTP id ge10sf46541728lab.3 for ; Fri, 06 Mar 2015 14:29:44 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:mime-version:delivered-to:from:to:cc:subject :date:message-id:x-original-sender:x-original-authentication-results :precedence:mailing-list:list-id:list-post:list-help:list-archive :list-unsubscribe; bh=sVfvsExgbvZ22hoxE8u/gX2hl+tYBB1ps33EtUEIQz0=; b=InBZQ3W6//4J8iE+rn2VzpNHSOA0kurc7hkALV62gweFt3xWCAdO5zgxOqklx5Pa95 amTpoj/cwZmuZcQjZD5InxS9eFYO34SgxNL5UHIeRr9WYTeB9pynV7s6xmkra3kcW/RZ Z76KZ6weovPO3JaPJ+J0q0IFtworOqWACm97jTLJkz64xGC44+uto2aJtIu9VVh/dZN7 I9Nansylor2Y/YkVFsdyo+U08NJjdEitZc8fryATaLsRbMu4awdGbJUZ6ZtQY3KwuLAo LdsfYWIzHR+bR6bMfa6n0EpBEQ5FbqZtEdDSAT1UdLAHDaFO3r6HN7j1ZF4GETweKMHI mb/g== X-Gm-Message-State: ALoCoQkKxxbrmAYUhvwqgzm4l6UM0XjIClOsYjQFdV1UjWmmyAHamL0Ds/2vIW61NA2XOT09QHku X-Received: by 10.194.63.208 with SMTP id i16mr2659290wjs.2.1425680984887; Fri, 06 Mar 2015 14:29:44 -0800 (PST) MIME-Version: 1.0 X-BeenThere: patchwork-forward@linaro.org Received: by 10.152.9.168 with SMTP id a8ls116003lab.14.gmail; Fri, 06 Mar 2015 14:29:44 -0800 (PST) X-Received: by 10.152.206.70 with SMTP id lm6mr15239793lac.35.1425680984723; Fri, 06 Mar 2015 14:29:44 -0800 (PST) Received: from mail-lb0-f176.google.com (mail-lb0-f176.google.com. [209.85.217.176]) by mx.google.com with ESMTPS id w11si8228059lby.28.2015.03.06.14.29.44 for (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Fri, 06 Mar 2015 14:29:44 -0800 (PST) Received-SPF: pass (google.com: domain of patch+caf_=patchwork-forward=linaro.org@linaro.org designates 209.85.217.176 as permitted sender) client-ip=209.85.217.176; Received: by lbiz11 with SMTP id z11so34640181lbi.13 for ; Fri, 06 Mar 2015 14:29:44 -0800 (PST) X-Received: by 10.112.37.198 with SMTP id a6mr15073369lbk.86.1425680984125; Fri, 06 Mar 2015 14:29:44 -0800 (PST) X-Forwarded-To: patchwork-forward@linaro.org X-Forwarded-For: patch@linaro.org patchwork-forward@linaro.org Delivered-To: patches@linaro.org Received: by 10.112.35.133 with SMTP id h5csp173841lbj; Fri, 6 Mar 2015 14:29:43 -0800 (PST) X-Received: by 10.68.198.99 with SMTP id jb3mr18493561pbc.153.1425680982187; Fri, 06 Mar 2015 14:29:42 -0800 (PST) Received: from mail-pa0-f51.google.com (mail-pa0-f51.google.com. [209.85.220.51]) by mx.google.com with ESMTPS id b5si12203122pdo.198.2015.03.06.14.29.41 for (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Fri, 06 Mar 2015 14:29:42 -0800 (PST) Received-SPF: pass (google.com: domain of john.stultz@linaro.org designates 209.85.220.51 as permitted sender) client-ip=209.85.220.51; Received: by padet14 with SMTP id et14so58377682pad.0 for ; Fri, 06 Mar 2015 14:29:41 -0800 (PST) X-Received: by 10.70.37.41 with SMTP id v9mr30098343pdj.6.1425680981247; Fri, 06 Mar 2015 14:29:41 -0800 (PST) Received: from localhost.localdomain (c-67-170-153-23.hsd1.or.comcast.net. [67.170.153.23]) by mx.google.com with ESMTPSA id fi8sm1326831pdb.43.2015.03.06.14.29.39 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Fri, 06 Mar 2015 14:29:40 -0800 (PST) From: John Stultz To: projectara-software-dev Cc: John Stultz , mark greer , Rob Herring , Pankaj Bharadiya , Mark Brown Subject: [PATCH 1/2] Initial pass at a framework for greybus audio driver Date: Fri, 6 Mar 2015 14:29:32 -0800 Message-Id: <1425680973-471-1-git-send-email-john.stultz@linaro.org> X-Mailer: git-send-email 1.9.1 X-Removed-Original-Auth: Dkim didn't pass. X-Original-Sender: john.stultz@linaro.org X-Original-Authentication-Results: mx.google.com; spf=pass (google.com: domain of patch+caf_=patchwork-forward=linaro.org@linaro.org designates 209.85.217.176 as permitted sender) smtp.mail=patch+caf_=patchwork-forward=linaro.org@linaro.org Precedence: list Mailing-list: list patchwork-forward@linaro.org; contact patchwork-forward+owners@linaro.org List-ID: X-Google-Group-Id: 836684582541 List-Post: , List-Help: , List-Archive: List-Unsubscribe: , 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 Cc: Rob Herring Cc: Pankaj Bharadiya Cc: Mark Brown Change-Id: Ib40d2b473ef4e371cb55d7dc7afd19d6721662f9 Signed-off-by: John Stultz --- 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 --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 +#include +#include +#include +#include +#include +#include +#include +#include + + +#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);