From patchwork Wed Jul 14 14:28:10 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Raffaele Recalcati X-Patchwork-Id: 112231 Received: from bear.ext.ti.com (bear.ext.ti.com [192.94.94.41]) by demeter.kernel.org (8.14.4/8.14.3) with ESMTP id o6FD93W9008814 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK) for ; Thu, 15 Jul 2010 13:09:40 GMT Received: from dlep33.itg.ti.com ([157.170.170.112]) by bear.ext.ti.com (8.13.7/8.13.7) with ESMTP id o6FD93sY023489 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO) for ; Thu, 15 Jul 2010 08:09:03 -0500 Received: from linux.omap.com (localhost [127.0.0.1]) by dlep33.itg.ti.com (8.13.7/8.13.7) with ESMTP id o6FD91V2022762 for ; Thu, 15 Jul 2010 08:09:02 -0500 (CDT) Received: from linux.omap.com (localhost [127.0.0.1]) by linux.omap.com (Postfix) with ESMTP id 73AAC8062E for ; Thu, 15 Jul 2010 08:08:57 -0500 (CDT) X-Original-To: davinci-linux-open-source@linux.davincidsp.com Delivered-To: davinci-linux-open-source@linux.davincidsp.com Received: from dflp52.itg.ti.com (dflp52.itg.ti.com [128.247.22.96]) by linux.omap.com (Postfix) with ESMTP id ED37C80626 for ; Wed, 14 Jul 2010 09:36:21 -0500 (CDT) Received: from white.ext.ti.com (localhost [127.0.0.1]) by dflp52.itg.ti.com (8.13.7/8.13.7) with ESMTP id o6EEaL5j002202 for ; Wed, 14 Jul 2010 09:36:21 -0500 (CDT) Received: from psmtp.com (na3sys009amx164.postini.com [74.125.149.90]) by white.ext.ti.com (8.13.7/8.13.7) with SMTP id o6EEaCua026224 for ; Wed, 14 Jul 2010 09:36:12 -0500 Received: from source ([74.125.82.47]) by na3sys009amx164.postini.com ([74.125.148.10]) with SMTP; Wed, 14 Jul 2010 10:36:13 EDT Received: by wwi14 with SMTP id 14so2186608wwi.4 for ; Wed, 14 Jul 2010 07:35:36 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=gamma; h=domainkey-signature:received:received:from:to:cc:subject:date :message-id:x-mailer; bh=SvlJmpiNe5ovI3digx4QXRnORKGa8LtLdsISuXzSHL0=; b=Chpr1EUib++CDHFJlS2Aciz0o8dGphJAj8ZqSweMa+mlAiUe0GCbKHAlBaTz9/Ukvt 6W+3Y5UzqaY0+KP342Tzv0TceXQIMfi/jF9z0D57xBbnEa0p+Wj+HP6d4FcLQqWu987w rheYCx/gebsMUGNlczIRjz0lcevD3Cdwurzh4= DomainKey-Signature: a=rsa-sha1; c=nofws; d=gmail.com; s=gamma; h=from:to:cc:subject:date:message-id:x-mailer; b=pHRd9WbaKIvZPR7lPnE5cwe3zwX3V2wpfpdkF/JorgJfBWJar+tyyXY5GlyAdfvTdI 4SmfSGrYNFjgffyitK1ST5bAXpxzZ4Zh4Z2rpa4vB3XOt4IJmXLfIiaFdf8xKZwxkXFY TgpyDMa6VabSyxyrsI+/vZfbZ5zx8/CBBeSS4= Received: by 10.227.143.12 with SMTP id s12mr193072wbu.125.1279117700978; Wed, 14 Jul 2010 07:28:20 -0700 (PDT) Received: from localhost.localdomain ([81.72.90.81]) by mx.google.com with ESMTPS id b18sm8065298wbb.7.2010.07.14.07.28.17 (version=TLSv1/SSLv3 cipher=RC4-MD5); Wed, 14 Jul 2010 07:28:19 -0700 (PDT) From: Raffaele Recalcati To: davinci-linux-open-source@linux.davincidsp.com Subject: [PATCH] Asoc Davinci Voicecodec: Added support based on copy_from_user instead of DMA Date: Wed, 14 Jul 2010 16:28:10 +0200 Message-Id: <1279117691-5467-1-git-send-email-lamiaposta71@gmail.com> X-Mailer: git-send-email 1.7.0.4 X-pstn-neptune: 0/0/0.00/0 X-pstn-levels: (S:19.57056/99.90000 CV:99.9000 FC:95.5390 LC:95.5390 R:95.9108 P:95.9108 M:97.0282 C:98.6951 ) X-pstn-settings: 2 (0.5000:0.5000) s cv gt3 gt2 gt1 r p m c X-pstn-addresses: from [db-null] X-Mailman-Approved-At: Thu, 15 Jul 2010 08:08:42 -0500 Cc: alsa-devel@alsa-project.org, Peter Ujfalusi , Mark Brown , Barry Song <21cnbao@gmail.com>, Eero Nurkkala , Raffaele Recalcati , Liam Girdwood X-BeenThere: davinci-linux-open-source@linux.davincidsp.com X-Mailman-Version: 2.1.12 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: davinci-linux-open-source-bounces+patchwork-davinci=patchwork.kernel.org@linux.davincidsp.com Errors-To: davinci-linux-open-source-bounces+patchwork-davinci=patchwork.kernel.org@linux.davincidsp.com X-Greylist: Sender succeeded STARTTLS authentication, not delayed by milter-greylist-4.2.3 (demeter.kernel.org [140.211.167.41]); Thu, 15 Jul 2010 13:09:40 +0000 (UTC) diff --git a/sound/soc/davinci/Makefile b/sound/soc/davinci/Makefile index a93679d..7d6a9a1 100644 --- a/sound/soc/davinci/Makefile +++ b/sound/soc/davinci/Makefile @@ -1,5 +1,6 @@ # DAVINCI Platform Support snd-soc-davinci-objs := davinci-pcm.o +snd-soc-davinci-objs += davinci-pcm-copyfromuser.o snd-soc-davinci-i2s-objs := davinci-i2s.o snd-soc-davinci-mcasp-objs:= davinci-mcasp.o snd-soc-davinci-vcif-objs:= davinci-vcif.o diff --git a/sound/soc/davinci/davinci-pcm-copyfromuser.c b/sound/soc/davinci/davinci-pcm-copyfromuser.c new file mode 100644 index 0000000..df37873 --- /dev/null +++ b/sound/soc/davinci/davinci-pcm-copyfromuser.c @@ -0,0 +1,333 @@ +/* + * + * Copyright (C) 2010 Bticino S.p.a + * Author: Davide Bonfanti + * + * Contributors: + * Raffaele Recalcati + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include "davinci-pcm.h" + +/* Timer register offsets */ +#define PID12 0x00 +#define TIM12 0x10 +#define TIM34 0x14 +#define PRD12 0x18 +#define PRD34 0x1c +#define TCR 0x20 +#define TGCR 0x24 + +/* Timer register bitfields */ +#define TCR_ENAMODE_DISABLE 0x0 +#define TCR_ENAMODE_ONESHOT 0x1 +#define TCR_ENAMODE_PERIODIC 0x2 +#define TCR_ENAMODE_MASK 0x3 + +#define TGCR_TIMMODE_SHIFT 2 +#define TGCR_TIMMODE_64BIT_GP 0x0 +#define TGCR_TIMMODE_32BIT_UNCHAINED 0x1 +#define TGCR_TIMMODE_64BIT_WDOG 0x2 +#define TGCR_TIMMODE_32BIT_CHAINED 0x3 + +#define TGCR_TIM12RS_SHIFT 0 +#define TGCR_TIM34RS_SHIFT 1 +#define TGCR_RESET 0x0 +#define TGCR_UNRESET 0x1 +#define TGCR_RESET_MASK 0x3 + +#define TIMER1_BASE 0x01C21800 +#define TIMER4_BASE 0x01C23800 + +/* choose the timer to use */ +#define TIMER_BASE TIMER4_BASE +/* driver buffer dimension */ +#define BUF_SIZE 2048 + +#if (TIMER_BASE == TIMER1_BASE) +#define TINT IRQ_TINT1_TINT12 +#define TIMER "timer1" +#elif (TIMER_BASE == TIMER4_BASE) +#define TINT IRQ_PWMINT2 +#define TIMER "timer4" +#endif + +int pointer_sub; +u16 local_buffer[BUF_SIZE/2]; + +static struct snd_pcm_hardware pcm_hardware_playback = { + .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER), + .formats = (SNDRV_PCM_FMTBIT_S16_LE), + .rates = (SNDRV_PCM_RATE_8000), + .rate_min = 8000, + .rate_max = 8000, + .channels_min = 1, + .channels_max = 1, + .buffer_bytes_max = BUF_SIZE, + .period_bytes_min = 512, + .period_bytes_max = 512, + .periods_min = BUF_SIZE / 512, + .periods_max = BUF_SIZE / 512, + .fifo_size = 0, +}; + +/* + * How this driver works... + * + * Since DM365 have the same DMA event for I2S and Voicecodec this two + * peripheralscannot be used at the same time. + * This driver implements voicecodec without the use of a DMA but with + * a copy_from_user. + * There's a buffer of BUF_SIZE bytes in the driver that is filled with + * davinci_pcm_copy. + * When pcm is running, a TIMER interrupt is activated each 1.36ms in + * order to fill HW FIFO (dm_vc_irq). + * It happens that the peripheral stop working so there's a trap... + * Driver was proved with timer1 and timer4. if timer2 is used the device + * resets during power-up. Timer3 have a structure a bit different and, + * in fact, doesn't work. + * Measures using a GPIO and scope give a time for copy_from_user about + * 8/10us while interrupt takes 1.5us + */ + +static snd_pcm_uframes_t +davinci_pcm_pointer(struct snd_pcm_substream *substream) +{ + return pointer_sub; +} + +static int davinci_pcm_open(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct davinci_runtime_data *prtd; + struct snd_pcm_hardware *ppcm; + int ret = 0; + struct davinci_pcm_dma_params *pa; + struct davinci_pcm_dma_params *params; + struct clk *clk; + + pointer_sub = 0; + +/* + * ppcm = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? + * &pcm_hardware_playback : &pcm_hardware_capture; + */ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + ppcm = &pcm_hardware_playback; + else + return -ENODEV; + + snd_soc_set_runtime_hwparams(substream, ppcm); + /* ensure that buffer size is a multiple of period size */ + ret = snd_pcm_hw_constraint_integer(runtime, + SNDRV_PCM_HW_PARAM_PERIODS); + if (ret < 0) + return ret; + + __raw_writel(0x0, IO_ADDRESS(0x01D0C008)); + __raw_writel(0x7400, IO_ADDRESS(0x01D0C004)); + clk = clk_get(NULL, TIMER); + if (!IS_ERR(clk)) + clk_enable(clk); + return 0; +} + +static int davinci_pcm_close(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct clk *clk; + + clk = clk_get(NULL, TIMER); + if (!IS_ERR(clk)) + clk_disable(clk); + return 0; +} + +static int davinci_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 davinci_pcm_hw_free(struct snd_pcm_substream *substream) +{ + return snd_pcm_lib_free_pages(substream); +} + +static int davinci_pcm_copy(struct snd_pcm_substream *substream, int channel, + snd_pcm_uframes_t hwoff, void __user *buf, snd_pcm_uframes_t frames) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + + if (copy_from_user(local_buffer + hwoff, buf, + frames_to_bytes(runtime, frames))) { + printk(KERN_ERR "ERROR COPY_FROM_USER\n"); + return -EFAULT; + } + return 0; +} + +static struct snd_pcm_ops davinci_pcm_ops = { + .open = davinci_pcm_open, + .close = davinci_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = davinci_pcm_hw_params, + .hw_free = davinci_pcm_hw_free, + .pointer = davinci_pcm_pointer, + .copy = davinci_pcm_copy, +}; + +static u64 davinci_pcm_dmamask = 0xffffffff; + +static irqreturn_t dm_vc_irq(int irq, void *sbst) +{ + struct snd_pcm_substream *substream = + (struct snd_pcm_substream *) sbst; + int fifo, diff, per_size, buf_size; + static int last_ptr; + + if (substream->runtime && substream->runtime->status && + snd_pcm_running(substream)) { + fifo = __raw_readl(IO_ADDRESS(0x01D0C028)); + fifo = (fifo & 0x1f00) >> 8; + if (fifo == 15) { + /* peripheral blocked! restart */ + __raw_writel(0, IO_ADDRESS(0x01D0C004)); + __raw_writel(0x5400, IO_ADDRESS(0x01D0C004)); + } + buf_size = substream->runtime->buffer_size; + per_size = substream->runtime->period_size; + for (; fifo < 0x10; fifo++) { + __raw_writew(local_buffer[pointer_sub++], + IO_ADDRESS(0x01D0C024)); + pointer_sub %= buf_size; + do { + diff = __raw_readl(IO_ADDRESS(0x01D0C00C)); + } while (!(diff & 0x8)); + } + if (last_ptr >= pointer_sub) + diff = buf_size + pointer_sub - last_ptr; + else + diff = pointer_sub - last_ptr; + if (diff >= per_size) { + snd_pcm_period_elapsed(substream); + last_ptr += per_size; + if (last_ptr >= buf_size) + last_ptr -= buf_size; + } + } else + last_ptr = 0; + return IRQ_HANDLED; +} + +static int davinci_pcm_new(struct snd_card *card, + struct snd_soc_dai *dai, struct snd_pcm *pcm) +{ + int tcr, tgcr; + struct snd_dma_buffer *buf; + struct snd_pcm_substream *substream; + struct clk *clk; + + if (!card->dev->dma_mask) + card->dev->dma_mask = &davinci_pcm_dmamask; + if (!card->dev->coherent_dma_mask) + card->dev->coherent_dma_mask = 0xffffffff; + + if (dai->playback.channels_min) { + substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; + buf = &substream->dma_buffer; + buf->dev.type = SNDRV_DMA_TYPE_DEV; + buf->dev.dev = pcm->card->dev; + buf->private_data = NULL; + buf->bytes = pcm_hardware_playback.buffer_bytes_max; + } + clk = clk_get(NULL, TIMER); + if (IS_ERR(clk)) + printk(KERN_ERR "ERROR getting timer\n"); + + /* Disabled, Internal clock source */ + __raw_writel(0, IO_ADDRESS(TIMER_BASE + TCR)); + + /* reset both timers, no pre-scaler for timer34 */ + tgcr = 0; + __raw_writel(tgcr, IO_ADDRESS(TIMER_BASE + TGCR)); + + /* Set both timers to unchained 32-bit */ + tgcr = TGCR_TIMMODE_32BIT_UNCHAINED << TGCR_TIMMODE_SHIFT; + __raw_writel(tgcr, IO_ADDRESS(TIMER_BASE + TGCR)); + + /* Unreset timers */ + tgcr |= (TGCR_UNRESET << TGCR_TIM12RS_SHIFT); + __raw_writel(tgcr, IO_ADDRESS(TIMER_BASE + TGCR)); + + /* Init both counters to zero */ + __raw_writel(0, IO_ADDRESS(TIMER_BASE + TIM12)); + tcr = __raw_readl(IO_ADDRESS(TIMER_BASE + TCR)); + + /* disable timer */ + tcr &= ~(TCR_ENAMODE_MASK << 6); + __raw_writel(tcr, IO_ADDRESS(TIMER_BASE + TCR)); + + /* reset counter to zero, set new period */ + __raw_writel(0, IO_ADDRESS(TIMER_BASE + TIM12)); + __raw_writel(0x8000, IO_ADDRESS(TIMER_BASE + PRD12)); + + /* Set enable mode */ + tcr |= TCR_ENAMODE_PERIODIC << 6; + __raw_writel(tcr, IO_ADDRESS(TIMER_BASE + TCR)); + +#if (TIMER_BASE == TIMER4_BASE) + __raw_writel(__raw_readl(IO_ADDRESS(0x01c40018)) | + 0x1000, IO_ADDRESS(0x01c40018)); +#endif + if (request_irq(TINT, dm_vc_irq, IRQF_TIMER, "VOICE-TOUT", substream)) + printk(KERN_ERR "%s ERROR requesting Interrupt\n", __func__); + return 0; +} + +struct snd_soc_platform davinci_soc_platform_copy = { + .name = "davinci-audio-copy", + .pcm_ops = &davinci_pcm_ops, + .pcm_new = davinci_pcm_new, +}; EXPORT_SYMBOL_GPL(davinci_soc_platform_copy); + +static int __init davinci_soc_copy_platform_init(void) +{ + return snd_soc_register_platform(&davinci_soc_platform_copy); +} +module_init(davinci_soc_copy_platform_init); + +static void __exit davinci_soc_copy_platform_exit(void) +{ + snd_soc_unregister_platform(&davinci_soc_platform_copy); +} +module_exit(davinci_soc_copy_platform_exit); + +MODULE_AUTHOR("bticino s.p.a."); +MODULE_DESCRIPTION("TI DAVINCI PCM copy_from_user module"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/davinci/davinci-pcm.h b/sound/soc/davinci/davinci-pcm.h index 0764944..cb7c2aa 100644 --- a/sound/soc/davinci/davinci-pcm.h +++ b/sound/soc/davinci/davinci-pcm.h @@ -29,5 +29,6 @@ struct davinci_pcm_dma_params { extern struct snd_soc_platform davinci_soc_platform; +extern struct snd_soc_platform davinci_soc_platform_copy; #endif diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index ad7f952..696e8b3 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -801,7 +801,7 @@ static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd) } return 0; } - +#if 0 /* ASoC PCM operations */ static struct snd_pcm_ops soc_pcm_ops = { .open = soc_pcm_open, @@ -811,7 +811,7 @@ static struct snd_pcm_ops soc_pcm_ops = { .prepare = soc_pcm_prepare, .trigger = soc_pcm_trigger, }; - +#endif #ifdef CONFIG_PM /* powers down audio subsystem for suspend */ static int soc_suspend(struct device *dev) @@ -1306,6 +1306,7 @@ static int soc_new_pcm(struct snd_soc_device *socdev, struct snd_pcm *pcm; char new_name[64]; int ret = 0, playback = 0, capture = 0; + struct snd_pcm_ops *soc_pcm_ops; rtd = kzalloc(sizeof(struct snd_soc_pcm_runtime), GFP_KERNEL); if (rtd == NULL) @@ -1335,19 +1336,25 @@ static int soc_new_pcm(struct snd_soc_device *socdev, dai_link->pcm = pcm; pcm->private_data = rtd; - soc_pcm_ops.mmap = platform->pcm_ops->mmap; - soc_pcm_ops.pointer = platform->pcm_ops->pointer; - soc_pcm_ops.ioctl = platform->pcm_ops->ioctl; - soc_pcm_ops.copy = platform->pcm_ops->copy; - soc_pcm_ops.silence = platform->pcm_ops->silence; - soc_pcm_ops.ack = platform->pcm_ops->ack; - soc_pcm_ops.page = platform->pcm_ops->page; + soc_pcm_ops = kmalloc(sizeof(struct snd_pcm_ops), GFP_KERNEL); + soc_pcm_ops->open = soc_pcm_open; + soc_pcm_ops->close = soc_codec_close; + soc_pcm_ops->hw_params = soc_pcm_hw_params; + soc_pcm_ops->hw_free = soc_pcm_hw_free; + soc_pcm_ops->prepare = soc_pcm_prepare; + soc_pcm_ops->trigger = soc_pcm_trigger; + soc_pcm_ops->mmap = platform->pcm_ops->mmap; + soc_pcm_ops->pointer = platform->pcm_ops->pointer; + soc_pcm_ops->ioctl = platform->pcm_ops->ioctl; + soc_pcm_ops->copy = platform->pcm_ops->copy; + soc_pcm_ops->silence = platform->pcm_ops->silence; + soc_pcm_ops->ack = platform->pcm_ops->ack; + soc_pcm_ops->page = platform->pcm_ops->page; if (playback) - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &soc_pcm_ops); - + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, soc_pcm_ops); if (capture) - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &soc_pcm_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, soc_pcm_ops); ret = platform->pcm_new(codec->card, codec_dai, pcm); if (ret < 0) {