lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date:	Thu, 6 Jan 2011 16:05:19 +0800
From:	"Guan Xuetao" <guanxuetao@...c.pku.edu.cn>
To:	<linux-arch@...r.kernel.org>, <linux-kernel@...r.kernel.org>
Subject: RE: [PATCHv1 0/8] unicore32 machine related files: summary

[PATCHv1 6/8] unicore32 machine related files: ac97
From: Guan Xuetao <guanxuetao@...c.pku.edu.cn>

Patch 6 implements ac97 driver.

Signed-off-by: Guan Xuetao <guanxuetao@...c.pku.edu.cn>
---
 drivers/staging/puv3/puv3_ac97.c |  383 ++++++++++++++++++++++++++++++++
 drivers/staging/puv3/puv3_pcm.c  |  449 ++++++++++++++++++++++++++++++++++++++
 drivers/staging/puv3/puv3_pcm.h  |   33 +++
 3 files changed, 865 insertions(+), 0 deletions(-)

diff --git a/drivers/staging/puv3/puv3_ac97.c b/drivers/staging/puv3/puv3_ac97.c
new file mode 100644
index 0000000..f02707e
--- /dev/null
+++ b/drivers/staging/puv3/puv3_ac97.c
@@ -0,0 +1,383 @@
+/*
+ * linux/drivers/staging/puv3/puv3_ac97.c
+ *
+ * Code specific to PKUnity SoC and UniCore ISA
+ *
+ *	Maintained by GUAN Xue-tao <gxt@...c.pku.edu.cn>
+ *	Copyright (C) 2001-2010 Guan Xuetao
+ *
+ * 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.
+ *
+ * Based on: sound/arm/pxa2xx-ac97.c
+ *
+ * Contributors & Additions/Fixes:
+ *     2009-07-30: enhance the PM support, resume/suspend can operate
+ *                 while ac97 is working by ZHONG Qi
+ *     2009-07-23: cause the ac97's flaw, only support fix sample rate
+ *			by ZHONG Qi
+ *     2009-07-23: add ac97 PM support by ZHONG Qi
+ *     2009-03-11: First version by ZHONG Qi
+ *
+ * TODO:
+ *     1. local_ac97_read, local_ac97_write and puv3_codec_regs
+ *        are involved for fixing bug, about reseting codec automatically
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/wait.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/ac97_codec.h>
+#include <sound/initval.h>
+
+#include <asm/irq.h>
+#include <linux/mutex.h>
+#include <asm/mach/hardware.h>
+
+#include "puv3_pcm.h"
+
+static DEFINE_MUTEX(car_mutex);
+
+typedef struct {
+	int (*startup)(struct snd_pcm_substream *, void *);
+	void (*shutdown)(struct snd_pcm_substream *, void *);
+	void (*suspend)(void *);
+	void (*resume)(void *);
+	void *priv;
+} puv3_audio_ops_t;
+
+unsigned short puv3_codec_regs[64] = {
+	0x5990, 0x8000, 0x8000, 0x8000, 0x0000, 0x8000, 0x8008, 0x8008,
+	0x8808, 0x8808, 0x8808, 0x8808, 0x8808, 0x0000, 0x8000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x000f, 0x0607, 0x0410, 0xbb80, 0x0000,
+	0x0000, 0xbb80, 0x0000, 0x0000, 0x0000, 0x2000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x2000, 0x2000, 0x000d,
+	0x044c, 0x044c, 0x0800, 0x0800, 0x0003, 0x0000, 0x0000, 0X4740,
+};
+
+/* TODO This is unnecessary, except for fixing bug */
+unsigned short local_ac97_read(unsigned short reg)
+{
+	return puv3_codec_regs[reg/2];
+}
+
+/* TODO This is unnecessary, except for fixing bug */
+void local_ac97_write(unsigned short reg, unsigned short val)
+{
+	int i = 1000;
+
+	puv3_codec_regs[reg/2] = val;
+
+	PKUNITY_AC97_CRAC = AC97_CODEC_REG(reg) | AC97_CODEC_VAL(val);
+
+	while (!(PKUNITY_AC97_CONR & AC97_CODEC_WRITECOMPLETE)) {
+		if (!(i--)) {
+			printk(KERN_WARNING "AC97: %s timeout\n", __func__);
+			break;
+		}
+		udelay(1);
+	}
+}
+
+static unsigned short puv3_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
+{
+	return puv3_codec_regs[reg/2];
+}
+
+static void puv3_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
+		unsigned short val)
+{
+	int i = 1000;
+
+	mutex_lock(&car_mutex);
+
+	if (reg == 0x2 || reg == 0x18)
+		puv3_codec_regs[reg/2] = val & 0x9F1F;
+	else
+		puv3_codec_regs[reg/2] = val;
+
+	PKUNITY_AC97_CRAC = AC97_CODEC_REG(reg) | AC97_CODEC_VAL(val);
+
+	while (!(PKUNITY_AC97_CONR & AC97_CODEC_WRITECOMPLETE)) {
+		if (!(i--)) {
+			printk(KERN_WARNING "AC97: %s timeout\n", __func__);
+			break;
+		}
+		udelay(1);
+	}
+
+	mutex_unlock(&car_mutex);
+}
+
+static void puv3_ac97_reset(struct snd_ac97 *ac97)
+{
+/*	int i = 3;
+
+	PKUNITY_AC97_CONR = AC97_CMD_RESET;
+reset:
+	msleep(1);
+
+	if ((PKUNITY_AC97_CONR & 0x2) == 2) {
+		msleep(1);
+		PKUNITY_AC97_CONR = 0x1;
+		if (i--)
+			goto reset;
+		else {
+			ac97_reset_err = -1;
+			printk("AC97 reset error!\n");
+		}
+	}
+*/
+}
+
+static struct snd_ac97_bus_ops puv3_ac97_ops = {
+	.read	= puv3_ac97_read,
+	.write	= puv3_ac97_write,
+	.reset	= puv3_ac97_reset,
+};
+
+static struct puv3_pcm_dma_params puv3_ac97_pcm_out = {
+	.name			= "AC97 PCM out",
+	.dev_addr		= PKUNITY_AC97_BASE + 0x20, /* OUT FIFO */
+};
+
+static struct puv3_pcm_dma_params puv3_ac97_pcm_in = {
+	.name			= "AC97 PCM in",
+	.dev_addr		= PKUNITY_AC97_BASE + 0x30, /* IN FIFO */
+};
+
+static struct snd_pcm *puv3_ac97_pcm;
+static struct snd_ac97 *puv3_ac97_ac97;
+
+static int puv3_ac97_pcm_startup(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	puv3_audio_ops_t *platform_ops;
+	int r;
+
+	runtime->hw.channels_min = 2;
+	runtime->hw.channels_max = 2;
+
+	r = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
+		AC97_RATES_FRONT_DAC : AC97_RATES_ADC;
+
+	platform_ops = substream->pcm->card->dev->platform_data;
+	if (platform_ops && platform_ops->startup)
+		return platform_ops->startup(substream, platform_ops->priv);
+	else
+		return 0;
+}
+
+static void puv3_ac97_pcm_shutdown(struct snd_pcm_substream *substream)
+{
+	puv3_audio_ops_t *platform_ops;
+
+	platform_ops = substream->pcm->card->dev->platform_data;
+	if (platform_ops && platform_ops->shutdown)
+		platform_ops->shutdown(substream, platform_ops->priv);
+}
+
+static int puv3_ac97_pcm_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	int reg = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
+		  AC97_PCM_FRONT_DAC_RATE : AC97_PCM_LR_ADC_RATE;
+	return snd_ac97_set_rate(puv3_ac97_ac97, reg, runtime->rate);
+}
+
+static struct puv3_pcm_client puv3_ac97_pcm_client = {
+	.playback_params	= &puv3_ac97_pcm_out,
+	.capture_params		= &puv3_ac97_pcm_in,
+	.startup		= puv3_ac97_pcm_startup,
+	.shutdown		= puv3_ac97_pcm_shutdown,
+	.prepare		= puv3_ac97_pcm_prepare,
+};
+
+#ifdef CONFIG_PM
+
+static unsigned long ac97_regs[2];
+static unsigned long ac97_dma_regs[4];
+int ac97_dma_ch = 0;
+
+static int puv3_ac97_do_suspend(struct snd_card *card, pm_message_t state)
+{
+	puv3_audio_ops_t *platform_ops = card->dev->platform_data;
+
+	snd_power_change_state(card, SNDRV_CTL_POWER_D3cold);
+	snd_pcm_suspend_all(puv3_ac97_pcm);
+	snd_ac97_suspend(puv3_ac97_ac97);
+	if (platform_ops && platform_ops->suspend)
+		platform_ops->suspend(platform_ops->priv);
+
+	/* store DMA & ac97 */
+	ac97_dma_regs[0] = DMAC_SRCADDR(ac97_dma_ch);
+	ac97_dma_regs[1] = DMAC_DESTADDR(ac97_dma_ch);
+	ac97_dma_regs[2] = DMAC_CONTROL(ac97_dma_ch);
+	ac97_dma_regs[3] = DMAC_CONFIG(ac97_dma_ch);
+
+	ac97_regs[0] = PKUNITY_AC97_OUT_FIFO;
+	ac97_regs[1] = PKUNITY_AC97_IN_FIFO;
+
+	return 0;
+}
+
+static int puv3_ac97_do_resume(struct snd_card *card)
+{
+	puv3_audio_ops_t *platform_ops = card->dev->platform_data;
+	int i;
+
+	if (platform_ops && platform_ops->resume)
+		platform_ops->resume(platform_ops->priv);
+	snd_ac97_resume(puv3_ac97_ac97);
+	snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+
+	/* restore DMA & codec & AC97 */
+
+	DMAC_SRCADDR(ac97_dma_ch)  = ac97_dma_regs[0];
+	DMAC_DESTADDR(ac97_dma_ch) = ac97_dma_regs[1];
+	DMAC_CONTROL(ac97_dma_ch)  = ac97_dma_regs[2];
+	DMAC_CONFIG(ac97_dma_ch)   = ac97_dma_regs[3];
+
+	for (i = 0; i < 64; i++)
+		local_ac97_write(i*2, puv3_codec_regs[i]);
+
+	PKUNITY_AC97_OCR = AC97_CMD_VPSAMPLE;	/* phone @ var sample rate */
+	PKUNITY_AC97_ICR = AC97_CMD_FCSAMPLE;	/* mic @ fix sample rate   */
+	PKUNITY_AC97_ENABLE = AC97_CMD_ENABLE;
+
+	local_ac97_write(0x2, 0x0000);
+
+	PKUNITY_AC97_OUT_FIFO  = ac97_regs[0];
+	PKUNITY_AC97_IN_FIFO   = ac97_regs[1];
+
+	return 0;
+}
+
+static int puv3_ac97_suspend(struct platform_device *dev, pm_message_t state)
+{
+	struct snd_card *card = platform_get_drvdata(dev);
+	int ret = 0;
+
+	if (card)
+		ret = puv3_ac97_do_suspend(card, PMSG_SUSPEND);
+
+	return ret;
+}
+
+static int puv3_ac97_resume(struct platform_device *dev)
+{
+	struct snd_card *card = platform_get_drvdata(dev);
+	int ret = 0;
+
+	if (card)
+		ret = puv3_ac97_do_resume(card);
+
+	return ret;
+}
+
+#else
+#define puv3_ac97_suspend	NULL
+#define puv3_ac97_resume	NULL
+#endif
+
+static int __devinit puv3_ac97_probe(struct platform_device *dev)
+{
+	struct snd_card *card;
+	struct snd_ac97_bus *ac97_bus;
+	struct snd_ac97_template ac97_template;
+	int ret;
+
+	ret = -ENOMEM;
+	if (snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
+			    THIS_MODULE, 0, &card) < 0)
+		goto err;
+
+	card->dev = &dev->dev;
+	strncpy(card->driver, dev->dev.driver->name, sizeof(card->driver));
+
+	ret = puv3_pcm_new(card, &puv3_ac97_pcm_client, &puv3_ac97_pcm);
+	if (ret)
+		goto err;
+
+	ret = snd_ac97_bus(card, 0, &puv3_ac97_ops, NULL, &ac97_bus);
+	if (ret)
+		goto err;
+	memset(&ac97_template, 0, sizeof(ac97_template));
+	ret = snd_ac97_mixer(ac97_bus, &ac97_template, &puv3_ac97_ac97);
+	if (ret)
+		goto err;
+
+	snprintf(card->shortname, sizeof(card->shortname),
+			"%s", snd_ac97_get_short_name(puv3_ac97_ac97));
+	snprintf(card->longname, sizeof(card->longname),
+			"%s (%s)", dev->dev.driver->name, card->mixername);
+
+	snd_card_set_dev(card, &dev->dev);
+	ret = snd_card_register(card);
+	if (ret == 0) {
+		platform_set_drvdata(dev, card);
+		/* phone @ var sample rate */
+		PKUNITY_AC97_OCR = AC97_CMD_VPSAMPLE;
+		/* mic @ fix sample rate   */
+		PKUNITY_AC97_ICR = AC97_CMD_FCSAMPLE;
+		PKUNITY_AC97_ENABLE = AC97_CMD_ENABLE;
+		return 0;
+	}
+
+err:
+	if (card)
+		snd_card_free(card);
+	return ret;
+}
+
+static int __devexit puv3_ac97_remove(struct platform_device *dev)
+{
+	struct snd_card *card = platform_get_drvdata(dev);
+
+	PKUNITY_AC97_ENABLE = AC97_CMD_DISABLE;
+
+	if (card) {
+		snd_card_free(card);
+		platform_set_drvdata(dev, NULL);
+	}
+
+	return 0;
+}
+
+static struct platform_driver puv3_ac97_driver = {
+	.probe		= puv3_ac97_probe,
+	.remove		= __devexit_p(puv3_ac97_remove),
+	.suspend	= puv3_ac97_suspend,
+	.resume		= puv3_ac97_resume,
+	.driver		= {
+		.name		= "PKUnity-v3-AC97",
+		.owner		= THIS_MODULE,
+	},
+};
+
+static int __init puv3_ac97_init(void)
+{
+	return platform_driver_register(&puv3_ac97_driver);
+}
+
+static void __exit puv3_ac97_exit(void)
+{
+	platform_driver_unregister(&puv3_ac97_driver);
+}
+
+module_init(puv3_ac97_init);
+module_exit(puv3_ac97_exit);
+MODULE_AUTHOR("ZHONG Qi");
+MODULE_DESCRIPTION("AC97 driver for the PKUNITY chip");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/puv3/puv3_pcm.c b/drivers/staging/puv3/puv3_pcm.c
new file mode 100644
index 0000000..53ce7dc
--- /dev/null
+++ b/drivers/staging/puv3/puv3_pcm.c
@@ -0,0 +1,449 @@
+/*
+ * linux/drivers/staging/puv3/puv3_pcm.c
+ *
+ * Code specific to PKUnity SoC and UniCore ISA
+ *
+ *	Maintained by GUAN Xue-tao <gxt@...c.pku.edu.cn>
+ *	Copyright (C) 2001-2010 Guan Xuetao
+ *
+ * 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.
+ *
+ * Based on: sound/arm/pxa2xx-pcm.c
+ *
+ * Contributors & Additions/Fixes:
+ *     2009-07-30: 1. increase the size of dma_buffer for vphone's performance
+ *                 2. fix the XRUN bug by setting the dma buffer counter to zero
+ *			by ZHONG Qi
+ *     2009-07-23: cause the ac97's flaw, only support fix sample rate
+ *			by ZHONG Qi
+ *     2009-03-11: First version by ZHONG Qi
+ *
+ * TODO:
+ *     1. local_ac97_read, local_ac97_write and puv3_codec_regs
+ *        are involved for fixing bug, about reseting codec automatically
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <linux/irqreturn.h>
+#include <linux/interrupt.h>
+#include <linux/timer.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+
+#include <asm/irq.h>
+#include <asm/mach/dma.h>
+#include <asm/mach/hardware.h>
+
+#include "puv3_pcm.h"
+
+#define COUNTER_SIZE	16
+#define PERIOD_SIZE	(4 * 1024)
+
+static void kick_dma(int, struct snd_pcm_substream *);
+static unsigned long next_counter(unsigned long);
+
+static const struct snd_pcm_hardware puv3_pcm_hardware = {
+	.info			= SNDRV_PCM_INFO_MMAP |
+				  SNDRV_PCM_INFO_MMAP_VALID |
+				  SNDRV_PCM_INFO_INTERLEAVED |
+				  SNDRV_PCM_INFO_PAUSE,
+	.formats		= SNDRV_PCM_FMTBIT_S16_LE,
+	.rates                  = SNDRV_PCM_RATE_48000,
+	.rate_min               = 48000,
+	.rate_max               = 48000,
+	.channels_min           = 1,
+	.channels_max           = 2,
+	.period_bytes_min	= PERIOD_SIZE,
+	.period_bytes_max	= PERIOD_SIZE,
+	.periods_min		= 1,
+	.periods_max		= COUNTER_SIZE, /* 2 */
+	.buffer_bytes_max	= COUNTER_SIZE * PERIOD_SIZE, /* 8 * 1024 */
+	.fifo_size		= 32,
+};
+
+struct puv3_runtime_data {
+	struct puv3_pcm_dma_params *params;
+	unsigned long buffer_counter;
+	size_t period;
+	int dma_lock;
+	int dma_ch;
+	/* TODO add other filed */
+};
+
+static unsigned long next_counter(unsigned long buffer_counter)
+{
+	return ++buffer_counter % COUNTER_SIZE;
+}
+
+static void reload_dma(int dma_ch, struct snd_pcm_substream *substream,
+		       struct snd_pcm_runtime *runtime,
+		       struct puv3_runtime_data *rtd)
+{
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		local_ac97_write(0x2,  puv3_codec_regs[0x2/2]);
+		local_ac97_write(0x18, puv3_codec_regs[0x18/2]);
+		local_ac97_write(0x2a, puv3_codec_regs[0x2a/2]);
+		local_ac97_write(0x2c, puv3_codec_regs[0x2c/2]);
+
+		DMAC_SRCADDR(dma_ch)  = runtime->dma_addr +
+					rtd->buffer_counter * rtd->period;
+		DMAC_DESTADDR(dma_ch) = rtd->params->dev_addr;
+		DMAC_CONTROL(dma_ch)  = DMAC_CONTROL_SIZE_WORD(rtd->period) |
+					DMAC_CONTROL_SI |
+					DMAC_CONTROL_BURST_8BYTE;
+		DMAC_CONFIG(dma_ch)   = DMAC_CONFIG_AC97WR;
+	} else {
+		local_ac97_write(0x1C, puv3_codec_regs[0x1C/2]);
+
+		DMAC_DESTADDR(rtd->dma_ch) = runtime->dma_addr +
+					rtd->buffer_counter * rtd->period;
+		DMAC_SRCADDR(rtd->dma_ch) = rtd->params->dev_addr;
+		DMAC_CONTROL(rtd->dma_ch) = DMAC_CONTROL_SIZE_WORD(rtd->period)
+					| DMAC_CONTROL_DI
+					| DMAC_CONTROL_BURST_8BYTE;
+		DMAC_CONFIG(rtd->dma_ch) = DMAC_CONFIG_AC97RD;
+	}
+}
+
+static void kick_dma(int dma_ch, struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime;
+	struct puv3_runtime_data *rtd;
+
+	runtime = substream->runtime;
+	rtd = runtime->private_data;
+
+	rtd->buffer_counter = next_counter(rtd->buffer_counter);
+
+	reload_dma(dma_ch, substream, runtime, rtd);
+
+	puv3_resume_dma(dma_ch);
+
+	return;
+}
+
+static void reset_dma(int dma_ch, struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime;
+	struct puv3_runtime_data *rtd;
+
+	puv3_stop_dma(dma_ch);
+
+	runtime = substream->runtime;
+	rtd = runtime->private_data;
+
+	rtd->buffer_counter = 0;
+
+	reload_dma(dma_ch, substream, runtime, rtd);
+
+	return;
+}
+
+static int puv3_pcm_hw_params(struct snd_pcm_substream *substream,
+			      struct snd_pcm_hw_params *params)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct puv3_runtime_data *rtd = runtime->private_data;
+	size_t period = params_period_bytes(params);
+	size_t totsize = params_buffer_bytes(params);
+
+	snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+	runtime->dma_bytes = totsize;
+
+	rtd->period = period;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		DMAC_SRCADDR(rtd->dma_ch)  = runtime->dma_addr +
+					rtd->buffer_counter * rtd->period;
+		DMAC_DESTADDR(rtd->dma_ch) = rtd->params->dev_addr;
+		DMAC_CONTROL(rtd->dma_ch) = DMAC_CONTROL_SIZE_WORD(rtd->period)
+					| DMAC_CONTROL_SI
+					| DMAC_CONTROL_BURST_8BYTE;
+		DMAC_CONFIG(rtd->dma_ch)   = DMAC_CONFIG_AC97WR;
+	} else {
+		DMAC_DESTADDR(rtd->dma_ch) = runtime->dma_addr +
+					rtd->buffer_counter * rtd->period;
+		DMAC_SRCADDR(rtd->dma_ch)  = rtd->params->dev_addr;
+		DMAC_CONTROL(rtd->dma_ch) = DMAC_CONTROL_SIZE_WORD(rtd->period)
+					| DMAC_CONTROL_DI
+					| DMAC_CONTROL_BURST_8BYTE;
+		DMAC_CONFIG(rtd->dma_ch)   = DMAC_CONFIG_AC97RD;
+	}
+
+	return 0;
+}
+
+static int puv3_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct puv3_runtime_data *rtd = runtime->private_data;
+
+	puv3_stop_dma(rtd->dma_ch);
+	snd_pcm_set_runtime_buffer(substream, NULL);
+
+	return 0;
+}
+
+static int puv3_pcm_prepare(struct snd_pcm_substream *substream)
+{
+	struct puv3_pcm_client *client = substream->private_data;
+	return client->prepare(substream);
+}
+
+static int puv3_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct puv3_runtime_data *rtd = runtime->private_data;
+	int ret = 0;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		rtd->dma_lock = 0;
+		puv3_resume_dma(rtd->dma_ch);
+		break;
+
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		rtd->dma_lock = 1;
+		ac97_dma_ch = rtd->dma_ch;
+		reset_dma(rtd->dma_ch, substream);
+		break;
+
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		puv3_resume_dma(rtd->dma_ch);
+		break;
+
+	default:
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static void puv3_pcm_dma_irq(int dma_ch, void *dev_id)
+{
+	struct snd_pcm_substream *substream = dev_id;
+	struct puv3_runtime_data *rtd = substream->runtime->private_data;
+
+	if (!rtd->dma_lock)
+		kick_dma(dma_ch, substream);
+
+	snd_pcm_period_elapsed(substream);
+}
+
+static snd_pcm_uframes_t puv3_pcm_pointer(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct puv3_runtime_data *rtd = substream->runtime->private_data;
+	dma_addr_t ptr = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
+			 DMAC_SRCADDR(rtd->dma_ch) : DMAC_DESTADDR(rtd->dma_ch);
+	snd_pcm_uframes_t x = bytes_to_frames(runtime, ptr - runtime->dma_addr);
+	if (x == runtime->buffer_size)
+		x = 0;
+	return x;
+}
+
+static int puv3_pcm_hw_rule_mult32(struct snd_pcm_hw_params *params,
+		struct snd_pcm_hw_rule *rule)
+{
+	struct snd_interval *i = hw_param_interval(params, rule->var);
+	int changed = 0;
+
+	if (i->min & 31) {
+		i->min = (i->min & ~31) + 32;
+		i->openmin = 0;
+		changed = 1;
+	}
+
+	if (i->max & 31) {
+		i->max &= ~31;
+		i->openmax = 0;
+		changed = 1;
+	}
+
+	return changed;
+}
+
+static int puv3_pcm_open(struct snd_pcm_substream *substream)
+{
+	struct puv3_pcm_client *client = substream->private_data;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct puv3_runtime_data *rtd;
+	int ret = 0;
+
+	runtime->hw = puv3_pcm_hardware;
+
+	/*
+	 * For mysterious reasons (and despite what the manual says)
+	 * playback samples are lost if the DMA count is not a multiple
+	 * of the DMA burst size.  Let's add a rule to enforce that.
+	 */
+	ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
+				  puv3_pcm_hw_rule_mult32, NULL,
+				  SNDRV_PCM_HW_PARAM_PERIOD_BYTES, -1);
+	if (ret)
+		goto out;
+	ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
+				  puv3_pcm_hw_rule_mult32, NULL,
+				  SNDRV_PCM_HW_PARAM_BUFFER_BYTES, -1);
+	if (ret)
+		goto out;
+
+	ret = -ENOMEM;
+	rtd = kmalloc(sizeof(*rtd), GFP_KERNEL);
+	if (!rtd)
+		goto out;
+
+	rtd->buffer_counter = 0;
+	rtd->dma_lock = 0;
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		/* TODO unnecessary */
+		local_ac97_write(0x2, 0x0000);
+
+	} else {
+		local_ac97_write(0x1A, 0);
+		local_ac97_write(0x1C, 0x0f0f);
+	}
+
+	rtd->params = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
+		client->playback_params : client->capture_params;
+	ret = puv3_request_dma(rtd->params->name, DMA_PRIO_HIGH,
+			     puv3_pcm_dma_irq, NULL, substream);
+	if (ret < 0)
+		goto err;
+	rtd->dma_ch = ret;
+
+	runtime->private_data = rtd;
+	ret = client->startup(substream);
+	if (!ret)
+		goto out;
+
+	puv3_free_dma(rtd->dma_ch);
+ err:
+	kfree(rtd);
+ out:
+	return ret;
+}
+
+static int puv3_pcm_close(struct snd_pcm_substream *substream)
+{
+	struct puv3_pcm_client *client = substream->private_data;
+	struct puv3_runtime_data *rtd = substream->runtime->private_data;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		local_ac97_write(0x1A, 0);
+		local_ac97_write(0x1C, 0x8000);
+	}
+
+	puv3_free_dma(rtd->dma_ch);
+	client->shutdown(substream);
+	kfree(rtd);
+
+	return 0;
+}
+
+static struct snd_pcm_ops puv3_pcm_ops = {
+	.open		= puv3_pcm_open,
+	.close		= puv3_pcm_close,
+	.ioctl		= snd_pcm_lib_ioctl,
+	.hw_params	= puv3_pcm_hw_params,
+	.hw_free	= puv3_pcm_hw_free,
+	.prepare	= puv3_pcm_prepare,
+	.trigger	= puv3_pcm_trigger,
+	.pointer	= puv3_pcm_pointer,
+};
+
+static int puv3_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
+{
+	struct snd_pcm_substream *substream = pcm->streams[stream].substream;
+	struct snd_dma_buffer *buf = &substream->dma_buffer;
+	size_t size = puv3_pcm_hardware.buffer_bytes_max;
+	buf->dev.type = SNDRV_DMA_TYPE_DEV;
+	buf->dev.dev = pcm->card->dev;
+	buf->private_data = NULL;
+	buf->area = dma_alloc_coherent(pcm->card->dev, size,
+					&buf->addr, GFP_KERNEL);
+	dma_cache_sync(pcm->card->dev, &buf->addr, size, DMA_BIDIRECTIONAL);
+	if (!buf->area)
+		return -ENOMEM;
+	buf->bytes = size;
+	return 0;
+}
+
+static void puv3_pcm_free_dma_buffers(struct snd_pcm *pcm)
+{
+	struct snd_pcm_substream *substream;
+	struct snd_dma_buffer *buf;
+	int stream;
+
+	for (stream = 0; stream < 2; stream++) {
+		substream = pcm->streams[stream].substream;
+		if (!substream)
+			continue;
+		buf = &substream->dma_buffer;
+		if (!buf->area)
+			continue;
+		dma_free_coherent(pcm->card->dev, buf->bytes,
+				buf->area, buf->addr);
+		buf->area = NULL;
+	}
+}
+
+static u64 puv3_pcm_dmamask = 0xffffffff;
+
+int puv3_pcm_new(struct snd_card *card, struct puv3_pcm_client *client,
+		struct snd_pcm **rpcm)
+{
+	struct snd_pcm *pcm;
+	int play = client->playback_params ? 1 : 0;
+	int capt = client->capture_params ? 1 : 0;
+	int ret;
+
+	ret = snd_pcm_new(card, "PKUNITY-PCM", 0, play, capt, &pcm);
+	if (ret)
+		goto out;
+
+	pcm->private_data = client;
+	pcm->private_free = puv3_pcm_free_dma_buffers;
+
+	if (!card->dev->dma_mask)
+		card->dev->dma_mask = &puv3_pcm_dmamask;
+	if (!card->dev->coherent_dma_mask)
+		card->dev->coherent_dma_mask = 0xffffffff;
+
+	if (play) {
+		int stream = SNDRV_PCM_STREAM_PLAYBACK;
+		snd_pcm_set_ops(pcm, stream, &puv3_pcm_ops);
+		ret = puv3_pcm_preallocate_dma_buffer(pcm, stream);
+		if (ret)
+			goto out;
+	}
+	if (capt) {
+		int stream = SNDRV_PCM_STREAM_CAPTURE;
+		snd_pcm_set_ops(pcm, stream, &puv3_pcm_ops);
+		ret = puv3_pcm_preallocate_dma_buffer(pcm, stream);
+		if (ret)
+			goto out;
+	}
+
+	if (rpcm)
+		*rpcm = pcm;
+	ret = 0;
+
+ out:
+	return ret;
+}
+EXPORT_SYMBOL(puv3_pcm_new);
+
+MODULE_AUTHOR("ZHONG Qi");
+MODULE_DESCRIPTION("PKUNITY PCM DMA module");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/puv3/puv3_pcm.h b/drivers/staging/puv3/puv3_pcm.h
new file mode 100644
index 0000000..aa9be9b
--- /dev/null
+++ b/drivers/staging/puv3/puv3_pcm.h
@@ -0,0 +1,33 @@
+/*
+ * linux/drivers/staging/puv3/puv3_pcm.h
+ *
+ * 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.
+ *
+ * Based on: sound/arm/pxa2xx-pcm.h
+ *
+ * Contributors & Additions/Fixes:
+ *	2009-03-11: First version by ZHONG Qi
+ */
+
+struct puv3_pcm_dma_params {
+	char *name;			/* stream identifier */
+	u32 dev_addr;			/* device physical address for DMA */
+};
+
+struct puv3_pcm_client {
+	struct puv3_pcm_dma_params *playback_params;
+	struct puv3_pcm_dma_params *capture_params;
+	int (*startup)(struct snd_pcm_substream *);
+	void (*shutdown)(struct snd_pcm_substream *);
+	int (*prepare)(struct snd_pcm_substream *);
+};
+
+extern int puv3_pcm_new(struct snd_card *, struct puv3_pcm_client *,
+		struct snd_pcm **);
+extern void local_ac97_write(unsigned short, unsigned short);
+extern unsigned short local_ac97_read(unsigned short);
+extern unsigned short puv3_codec_regs[];
+extern int ac97_dma_ch;
+

> -----Original Message-----
> From: linux-arch-owner@...r.kernel.org [mailto:linux-arch-owner@...r.kernel.org] On Behalf Of Guan Xuetao
> Sent: Thursday, January 06, 2011 3:57 PM
> To: linux-arch@...r.kernel.org; linux-kernel@...r.kernel.org
> Subject: [PATCHv1 0/8] unicore32 machine related files: summary
> 
> From: Guan Xuetao <guanxuetao@...c.pku.edu.cn>
> 
> The whole patch could be fetched from:
>   git://git.kernel.org/pub/scm/linux/kernel/git/epip/unicore32.git
>   with branch name: unicore32.
> And it is divided into  three patch sets: core architecture files,
>   additional architecture files, and machine related files.
> 
> This patch set adds the machine related files for UniCore32 ISA and PKUnity SoC.
> 
> Patch 1 adds machine related  core files, also including build infrastructure.
> 
> Patch 2 add all hardware registers definitions, which are not split and inserted into
>  different drivers.
> 
> Patch 3 implements arch-specific pci bus driver.
> 
> Patch 4 implements arch-specific ps2 dirver.
> 
> Patch 5 implements frame buffer driver.
> 
> Patch 6 implements ac97 driver.
> 
> Patch 7 implements arch-specific i2c bus driver.
> 
> Patch 8 implements network driver.
> 
> Signed-off-by: Guan Xuetao <guanxuetao@...c.pku.edu.cn>
> ---
>  arch/unicore32/include/asm/mach/PKUnity.h     |  104 ++
>  arch/unicore32/include/asm/mach/bitfield.h    |   24 +
>  arch/unicore32/include/asm/mach/hardware.h    |   45 +
>  arch/unicore32/include/asm/mach/regs-ac97.h   |   32 +
>  arch/unicore32/include/asm/mach/regs-dmac.h   |   81 +
>  arch/unicore32/include/asm/mach/regs-gpio.h   |   70 +
>  arch/unicore32/include/asm/mach/regs-i2c.h    |   63 +
>  arch/unicore32/include/asm/mach/regs-intc.h   |   28 +
>  arch/unicore32/include/asm/mach/regs-nand.h   |   79 +
>  arch/unicore32/include/asm/mach/regs-ost.h    |   92 ++
>  arch/unicore32/include/asm/mach/regs-pci.h    |   94 ++
>  arch/unicore32/include/asm/mach/regs-pm.h     |  126 ++
>  arch/unicore32/include/asm/mach/regs-ps2.h    |   20 +
>  arch/unicore32/include/asm/mach/regs-resetc.h |   34 +
>  arch/unicore32/include/asm/mach/regs-rtc.h    |   37 +
>  arch/unicore32/include/asm/mach/regs-sdc.h    |  156 ++
>  arch/unicore32/include/asm/mach/regs-spi.h    |   98 ++
>  arch/unicore32/include/asm/mach/regs-uart.h   |    3 +
>  arch/unicore32/include/asm/mach/regs-umal.h   |  229 +++
>  arch/unicore32/include/asm/mach/regs-unigfx.h |  200 +++
>  arch/unicore32/include/asm/pci.h              |   46 +
>  arch/unicore32/kernel/pci.c                   |  404 +++++
>  arch/unicore32/kernel/puv3-core.c             |  266 ++++
>  arch/unicore32/kernel/puv3-nb0916.c           |  183 +++
>  arch/unicore32/kernel/puv3-smw0919.c          |  120 ++
>  drivers/input/keyboard/Kconfig                |   11 +
>  drivers/input/keyboard/atkbd.c                |    4 +
>  drivers/input/mouse/psmouse-base.c            |   41 +
>  drivers/input/serio/i8042.h                   |    2 +
>  drivers/pci/Makefile                          |    1 +
>  drivers/staging/puv3/Kconfig                  |  142 ++
>  drivers/staging/puv3/Makefile                 |   27 +
>  drivers/staging/puv3/TODO                     |    7 +
>  drivers/staging/puv3/i8042-ucio.h             |   96 ++
>  drivers/staging/puv3/nb0916-atkbd.h           |   43 +
>  drivers/staging/puv3/puv3_ac97.c              |  383 +++++
>  drivers/staging/puv3/puv3_i2c.c               |  325 ++++
>  drivers/staging/puv3/puv3_pcm.c               |  449 ++++++
>  drivers/staging/puv3/puv3_pcm.h               |   33 +
>  drivers/staging/puv3/puv3_umal.c              | 2082 +++++++++++++++++++++++++
>  drivers/staging/puv3/puv3_unifb.c             |  972 ++++++++++++
>  include/linux/fb.h                            |    2 +
>  42 files changed, 7254 insertions(+), 0 deletions(-)
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-arch" in
> the body of a message to majordomo@...r.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ