All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] sh: add SuperH DAC audio driver for ALSA
@ 2009-10-08  1:34 Rafael Ignacio Zurita
  2009-10-08  8:38 ` Kristoffer Ericson
                   ` (7 more replies)
  0 siblings, 8 replies; 21+ messages in thread
From: Rafael Ignacio Zurita @ 2009-10-08  1:34 UTC (permalink / raw)
  To: linux-sh

 
This is a port of the sound/oss/sh_dac_audio.c driver.
The driver uses an on-chip 8-bit D/A converter, which has a speaker connected
to one of its channels, found in several ancient HP machines.
For interrupts it uses a high-resolution timer (hrtimer).
Tested on SH7709 based hp6xx (HP Jornada 680/690 and HP Palmtop 620lx/660lx).

Signed-off-by: Rafael Ignacio Zurita <rizurita@yahoo.com>
---
 arch/sh/include/mach-common/mach/hp6xx.h |    4 +
 sound/sh/Kconfig                         |    9 +
 sound/sh/Makefile                        |    1 +
 sound/sh/snd_sh_dac_audio.c              |  521 ++++++++++++++++++++++++++++++
 4 files changed, 535 insertions(+), 0 deletions(-)

diff --git a/arch/sh/include/mach-common/mach/hp6xx.h b/arch/sh/include/mach-common/mach/hp6xx.h
index 0d4165a..bcc301a 100644
--- a/arch/sh/include/mach-common/mach/hp6xx.h
+++ b/arch/sh/include/mach-common/mach/hp6xx.h
@@ -29,6 +29,9 @@
 
 #define PKDR_LED_GREEN		0x10
 
+/* HP Palmtop 620lx/660lx speaker on/off */
+#define PKDR_SPEAKER		0x20
+
 #define SCPDR_TS_SCAN_ENABLE	0x20
 #define SCPDR_TS_SCAN_Y		0x02
 #define SCPDR_TS_SCAN_X		0x01
@@ -42,6 +45,7 @@
 #define ADC_CHANNEL_BACKUP	4
 #define ADC_CHANNEL_CHARGE	5
 
+/* HP Jornada 680/690 speaker on/off */
 #define HD64461_GPADR_SPEAKER	0x01
 #define HD64461_GPADR_PCMCIA0	(0x02|0x08)
 
diff --git a/sound/sh/Kconfig b/sound/sh/Kconfig
index aed0f90..fecb198 100644
--- a/sound/sh/Kconfig
+++ b/sound/sh/Kconfig
@@ -19,5 +19,14 @@ config SND_AICA
 	help
 	  ALSA Sound driver for the SEGA Dreamcast console.
 
+config SND_SH_DAC_AUDIO
+	tristate "SuperH DAC audio support"
+	depends on SND
+	depends on CPU_SH3 && HIGH_RES_TIMERS
+	select SND_PCM
+	help
+	  Alsa Sound driver for the HP Palmtop 620lx/660lx
+	  and HP Jornada 680/690.
+
 endif	# SND_SUPERH
 
diff --git a/sound/sh/Makefile b/sound/sh/Makefile
index 8fdcb6e..c4b81c4 100644
--- a/sound/sh/Makefile
+++ b/sound/sh/Makefile
@@ -6,3 +6,4 @@ snd-aica-objs := aica.o
 
 # Toplevel Module Dependency
 obj-$(CONFIG_SND_AICA) += snd-aica.o
+obj-$(CONFIG_SND_SH_DAC_AUDIO) += snd_sh_dac_audio.o
diff --git a/sound/sh/snd_sh_dac_audio.c b/sound/sh/snd_sh_dac_audio.c
new file mode 100644
index 0000000..074c43d
--- /dev/null
+++ b/sound/sh/snd_sh_dac_audio.c
@@ -0,0 +1,521 @@
+/*
+ * snd_sh_dac_audio.c - SuperH DAC audio driver for ALSA
+ *
+ * Copyright (c) 2009 by Rafael Ignacio Zurita <rizurita@yahoo.com>
+ *
+ *
+ * Based on sh_dac_audio.c (Copyright (C) 2004, 2005 by Andriy Skulysh)
+ *
+ *   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; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <linux/hrtimer.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <asm/clock.h>
+#include <asm/hd64461.h>
+#include <mach-common/mach/hp6xx.h>
+#include <cpu/dac.h>
+
+MODULE_AUTHOR("Rafael Ignacio Zurita <rizurita@yahoo.com>");
+MODULE_DESCRIPTION("SuperH DAC audio driver");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{SuperH DAC audio support}}");
+
+/* Module Parameters */
+static int index = SNDRV_DEFAULT_IDX1;
+static char *id = SNDRV_DEFAULT_STR1;
+module_param(index, int, 0444);
+MODULE_PARM_DESC(index, "Index value for SuperH DAC audio.");
+module_param(id, charp, 0444);
+MODULE_PARM_DESC(id, "ID string for SuperH DAC audio.");
+
+/* Simple platform device */
+static struct platform_device *pd;
+
+#define SND_SH_DAC_DRIVER "SH_DAC"
+#define BUFFER_SIZE	64000
+#define SH_DAC_AUDIO_CHANNEL 1
+
+/* main struct */
+struct snd_sh_dac {
+	struct snd_card *card;
+	struct snd_pcm_substream *substream;
+	struct hrtimer hrtimer;
+	ktime_t wakeups_per_second;
+
+	int rate;
+	int empty;
+	char *data_buffer, *buffer_begin, *buffer_end;
+	int processed; /* bytes proccesed, to compare with period_size */
+	int buffer_size;
+};
+
+
+static void dac_audio_start_timer(struct snd_sh_dac *chip)
+{
+	hrtimer_start(&chip->hrtimer, chip->wakeups_per_second,
+		      HRTIMER_MODE_REL);
+}
+
+static void dac_audio_stop_timer(struct snd_sh_dac *chip)
+{
+	hrtimer_cancel(&chip->hrtimer);
+}
+
+static void dac_audio_reset(struct snd_sh_dac *chip)
+{
+	dac_audio_stop_timer(chip);
+	chip->buffer_begin = chip->buffer_end = chip->data_buffer;
+	chip->processed = 0;
+	chip->empty = 1;
+}
+
+static void dac_audio_sync(struct snd_sh_dac *chip)
+{
+	while (!chip->empty)
+		schedule();
+}
+
+static void dac_audio_start(void)
+{
+#ifdef CONFIG_SH_HP6XX
+	u16 v;
+	u8 v8;
+
+	/* HP Jornada 680/690 speaker on */
+	v = inw(HD64461_GPADR);
+	v &= ~HD64461_GPADR_SPEAKER;
+	outw(v, HD64461_GPADR);
+
+	/* HP Palmtop 620lx/660lx speaker on */
+	v8 = inb(PKDR);
+	v8 &= ~PKDR_SPEAKER;
+	outb(v8, PKDR);
+#endif
+	sh_dac_enable(SH_DAC_AUDIO_CHANNEL);
+}
+
+static void dac_audio_stop(struct snd_sh_dac *chip)
+{
+
+#ifdef CONFIG_SH_HP6XX
+	u16 v;
+	u8 v8;
+#endif
+
+	dac_audio_stop_timer(chip);
+
+#ifdef CONFIG_SH_HP6XX
+	/* HP Jornada 680/690 speaker off */
+	v = inw(HD64461_GPADR);
+	v |= HD64461_GPADR_SPEAKER;
+	outw(v, HD64461_GPADR);
+
+	/* HP Palmtop 620lx/660lx speaker off */
+	v8 = inb(PKDR);
+	v8 |= PKDR_SPEAKER;
+	outb(v8, PKDR);
+#endif
+	sh_dac_output(0, SH_DAC_AUDIO_CHANNEL);
+	sh_dac_disable(SH_DAC_AUDIO_CHANNEL);
+}
+
+static void dac_audio_set_rate(struct snd_sh_dac *chip)
+{
+	chip->wakeups_per_second = ktime_set(0, 1000000000 / chip->rate);
+}
+
+
+/* PCM INTERFACE */
+
+static struct snd_pcm_hardware snd_sh_dac_pcm_hw = {
+	.info			= (SNDRV_PCM_INFO_MMAP |
+					SNDRV_PCM_INFO_MMAP_VALID |
+					SNDRV_PCM_INFO_INTERLEAVED |
+					SNDRV_PCM_INFO_HALF_DUPLEX),
+	.formats		= SNDRV_PCM_FMTBIT_U8,
+	.rates			= SNDRV_PCM_RATE_8000,
+	.rate_min		= 8000,
+	.rate_max		= 8000,
+	.channels_min		= 1,
+	.channels_max		= 1,
+	.buffer_bytes_max	= (48*1024),
+	.period_bytes_min	= 1,
+	.period_bytes_max	= (48*1024),
+	.periods_min		= 1,
+	.periods_max		= 1024,
+};
+
+static int snd_sh_dac_pcm_open(struct snd_pcm_substream *substream)
+{
+	struct snd_sh_dac *chip = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+
+	runtime->hw = snd_sh_dac_pcm_hw;
+
+	chip->substream = substream;
+	chip->buffer_begin = chip->buffer_end = chip->data_buffer;
+	chip->processed = 0;
+	chip->empty = 1;
+
+	dac_audio_start();
+
+	return 0;
+}
+
+static int snd_sh_dac_pcm_close(struct snd_pcm_substream *substream)
+{
+	struct snd_sh_dac *chip = snd_pcm_substream_chip(substream);
+
+	dac_audio_sync(chip);
+	dac_audio_stop(chip);
+
+	return 0;
+}
+
+static int snd_sh_dac_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 snd_sh_dac_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+	return snd_pcm_lib_free_pages(substream);
+}
+
+static int snd_sh_dac_pcm_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_sh_dac *chip = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = chip->substream->runtime;
+
+	chip->buffer_size = runtime->buffer_size;
+	memset(chip->data_buffer, 0, BUFFER_SIZE);
+
+	return 0;
+}
+
+static int snd_sh_dac_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	struct snd_sh_dac *chip = snd_pcm_substream_chip(substream);
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		dac_audio_start_timer(chip);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+		chip->buffer_begin = chip->buffer_end = chip->data_buffer;
+		chip->processed = 0;
+		chip->empty = 1;
+		dac_audio_stop_timer(chip);
+		break;
+	default:
+		 return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int snd_sh_dac_pcm_copy(struct snd_pcm_substream *substream, int channel,
+	snd_pcm_uframes_t pos, void __user *src, snd_pcm_uframes_t count)
+{
+	/* channel is not used (interleaved data) */
+	struct snd_sh_dac *chip = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	ssize_t b_count = frames_to_bytes(runtime , count);
+	ssize_t b_pos = frames_to_bytes(runtime , pos);
+
+	if (count < 0)
+		return -EINVAL;
+
+	if (!count) {
+		dac_audio_sync(chip);
+		return 0;
+	}
+
+	memcpy_toio(chip->data_buffer + b_pos, src, b_count);
+	chip->buffer_end = chip->data_buffer + b_pos + b_count;
+
+	if (chip->empty) {
+		chip->empty = 0;
+		dac_audio_start_timer(chip);
+	}
+
+	return 0;
+}
+
+static int snd_sh_dac_pcm_silence(struct snd_pcm_substream *substream,
+				  int channel, snd_pcm_uframes_t pos,
+				  snd_pcm_uframes_t count)
+{
+	/* channel is not used (interleaved data) */
+	struct snd_sh_dac *chip = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	ssize_t b_count = frames_to_bytes(runtime , count);
+	ssize_t b_pos = frames_to_bytes(runtime , pos);
+
+	if (count < 0)
+		return -EINVAL;
+
+	if (!count) {
+		dac_audio_sync(chip);
+		return 0;
+	}
+
+	memset_io(chip->data_buffer + b_pos, 0, b_count);
+	chip->buffer_end = chip->data_buffer + b_pos + b_count;
+
+	if (chip->empty) {
+		chip->empty = 0;
+		dac_audio_start_timer(chip);
+	}
+
+	return 0;
+}
+
+static
+snd_pcm_uframes_t snd_sh_dac_pcm_pointer(struct snd_pcm_substream *substream)
+{
+	struct snd_sh_dac *chip = snd_pcm_substream_chip(substream);
+	int pointer = chip->buffer_begin - chip->data_buffer;
+
+	return pointer;
+}
+
+/* pcm ops */
+static struct snd_pcm_ops snd_sh_dac_pcm_ops = {
+	.open		= snd_sh_dac_pcm_open,
+	.close		= snd_sh_dac_pcm_close,
+	.ioctl		= snd_pcm_lib_ioctl,
+	.hw_params	= snd_sh_dac_pcm_hw_params,
+	.hw_free	= snd_sh_dac_pcm_hw_free,
+	.prepare	= snd_sh_dac_pcm_prepare,
+	.trigger	= snd_sh_dac_pcm_trigger,
+	.pointer	= snd_sh_dac_pcm_pointer,
+	.copy		= snd_sh_dac_pcm_copy,
+	.silence	= snd_sh_dac_pcm_silence,
+	.mmap		= snd_pcm_lib_mmap_iomem,
+};
+
+static int __devinit snd_sh_dac_pcm(struct snd_sh_dac *chip, int device)
+{
+	int err;
+	struct snd_pcm *pcm;
+
+	/* device should be always 0 for us */
+	err = snd_pcm_new(chip->card, "SH_DAC PCM", device, 1, 0, &pcm);
+	if (err < 0)
+		return err;
+
+	pcm->private_data = chip;
+	strcpy(pcm->name, "SH_DAC PCM");
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_sh_dac_pcm_ops);
+
+	/* buffer sizeHK */
+	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
+					  snd_dma_continuous_data(GFP_KERNEL),
+							48 * 1024,
+							48 * 1024);
+
+	return 0;
+}
+/* END OF PCM INTERFACE */
+
+
+/* driver .remove  --  destructor */
+static int snd_sh_dac_remove(struct platform_device *devptr)
+{
+	snd_card_free(platform_get_drvdata(devptr));
+	platform_set_drvdata(devptr, NULL);
+
+	return 0;
+}
+
+/* free -- it has been defined by create */
+static int snd_sh_dac_free(struct snd_sh_dac *chip)
+{
+	/* release the data */
+	kfree(chip->data_buffer);
+	kfree(chip);
+
+	return 0;
+}
+
+static int snd_sh_dac_dev_free(struct snd_device *device)
+{
+	struct snd_sh_dac *chip = device->device_data;
+
+	return snd_sh_dac_free(chip);
+}
+
+static enum hrtimer_restart sh_dac_audio_timer(struct hrtimer *handle)
+{
+	struct snd_sh_dac *chip = container_of(handle, struct snd_sh_dac,
+					       hrtimer);
+	struct snd_pcm_runtime *runtime = chip->substream->runtime;
+	ssize_t b_ps = frames_to_bytes(runtime, runtime->period_size);
+
+	if (!chip->empty) {
+		sh_dac_output(*chip->buffer_begin, SH_DAC_AUDIO_CHANNEL);
+		chip->buffer_begin++;
+
+		chip->processed++;
+		if (chip->processed >= b_ps) {
+			chip->processed -= b_ps;
+			snd_pcm_period_elapsed(chip->substream);
+		}
+
+		if (chip->buffer_begin = (chip->data_buffer +
+					   chip->buffer_size - 1))
+			chip->buffer_begin = chip->data_buffer;
+
+		if (chip->buffer_begin = chip->buffer_end)
+			chip->empty = 1;
+
+	}
+
+	if (!chip->empty)
+		hrtimer_start(&chip->hrtimer, chip->wakeups_per_second,
+			      HRTIMER_MODE_REL);
+
+	return HRTIMER_NORESTART;
+}
+
+/* create  --  chip-specific constructor for the cards components */
+static int __devinit snd_sh_dac_create(struct snd_card *card,
+				       struct platform_device *devptr,
+				       struct snd_sh_dac **rchip)
+{
+	struct snd_sh_dac *chip;
+	int err;
+
+	static struct snd_device_ops ops = {
+		   .dev_free = snd_sh_dac_dev_free,
+	};
+
+	*rchip = NULL;
+
+	chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+	if (chip = NULL)
+		return -ENOMEM;
+
+	chip->card = card;
+
+	hrtimer_init(&chip->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+	chip->hrtimer.function = sh_dac_audio_timer;
+
+	dac_audio_reset(chip);
+	chip->rate = 8000;
+	dac_audio_set_rate(chip);
+
+	chip->data_buffer = kmalloc(BUFFER_SIZE, GFP_KERNEL);
+	if (chip->data_buffer = NULL)
+		return -ENOMEM;
+
+	err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
+	if (err < 0) {
+		snd_sh_dac_free(chip);
+		return err;
+	}
+
+	*rchip = chip;
+
+	return 0;
+}
+
+/* driver .probe  --  constructor */
+static int __devinit snd_sh_dac_probe(struct platform_device *devptr)
+{
+	struct snd_sh_dac *chip;
+	struct snd_card *card;
+	int err;
+
+	err = snd_card_create(index, id, THIS_MODULE, 0, &card);
+	if (err < 0) {
+			snd_printk(KERN_ERR "cannot allocate the card\n");
+			return err;
+	}
+
+	err = snd_sh_dac_create(card, devptr, &chip);
+	if (err < 0)
+		goto probe_error;
+
+	err = snd_sh_dac_pcm(chip, 0);
+	if (err < 0)
+		goto probe_error;
+
+	strcpy(card->driver, "snd_sh_dac");
+	strcpy(card->shortname, "SuperH DAC audio driver");
+	printk(KERN_INFO "%s %s", card->longname, card->shortname);
+
+	err = snd_card_register(card);
+	if (err < 0)
+		goto probe_error;
+
+	snd_printk("ALSA driver for SuperH DAC audio");
+
+	platform_set_drvdata(devptr, card);
+	return 0;
+
+probe_error:
+	snd_card_free(card);
+	return err;
+}
+
+/*
+ * "driver" definition
+ */
+static struct platform_driver driver = {
+	.probe	= snd_sh_dac_probe,
+	.remove = snd_sh_dac_remove,
+	.driver = {
+		.name = SND_SH_DAC_DRIVER,
+	},
+};
+
+/* clean up the module */
+static void __exit sh_dac_exit(void)
+{
+	platform_device_unregister(pd);
+	platform_driver_unregister(&driver);
+}
+
+
+static int __init sh_dac_init(void)
+{
+	int err;
+
+	err = platform_driver_register(&driver);
+	if (unlikely(err < 0))
+		return err;
+
+	pd = platform_device_register_simple(SND_SH_DAC_DRIVER, -1, NULL, 0);
+	if (unlikely(IS_ERR(pd))) {
+		platform_driver_unregister(&driver);
+		return PTR_ERR(pd);
+	}
+
+	return 0;
+}
+
+module_init(sh_dac_init);
+module_exit(sh_dac_exit);

^ permalink raw reply related	[flat|nested] 21+ messages in thread

* Re: [PATCH] sh: add SuperH DAC audio driver for ALSA
  2009-10-08  1:34 [PATCH] sh: add SuperH DAC audio driver for ALSA Rafael Ignacio Zurita
@ 2009-10-08  8:38 ` Kristoffer Ericson
  2009-10-09  1:22 ` Paul Mundt
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 21+ messages in thread
From: Kristoffer Ericson @ 2009-10-08  8:38 UTC (permalink / raw)
  To: linux-sh


Acked

On Wed, 7 Oct 2009 22:34:23 -0300
Rafael Ignacio Zurita <rizurita@yahoo.com> wrote:

>  
> This is a port of the sound/oss/sh_dac_audio.c driver.
> The driver uses an on-chip 8-bit D/A converter, which has a speaker connected
> to one of its channels, found in several ancient HP machines.
> For interrupts it uses a high-resolution timer (hrtimer).
> Tested on SH7709 based hp6xx (HP Jornada 680/690 and HP Palmtop 620lx/660lx).
> 
> Signed-off-by: Rafael Ignacio Zurita <rizurita@yahoo.com>
> ---
>  arch/sh/include/mach-common/mach/hp6xx.h |    4 +
>  sound/sh/Kconfig                         |    9 +
>  sound/sh/Makefile                        |    1 +
>  sound/sh/snd_sh_dac_audio.c              |  521 ++++++++++++++++++++++++++++++
>  4 files changed, 535 insertions(+), 0 deletions(-)
> 
> diff --git a/arch/sh/include/mach-common/mach/hp6xx.h b/arch/sh/include/mach-common/mach/hp6xx.h
> index 0d4165a..bcc301a 100644
> --- a/arch/sh/include/mach-common/mach/hp6xx.h
> +++ b/arch/sh/include/mach-common/mach/hp6xx.h
> @@ -29,6 +29,9 @@
>  
>  #define PKDR_LED_GREEN		0x10
>  
> +/* HP Palmtop 620lx/660lx speaker on/off */
> +#define PKDR_SPEAKER		0x20
> +
>  #define SCPDR_TS_SCAN_ENABLE	0x20
>  #define SCPDR_TS_SCAN_Y		0x02
>  #define SCPDR_TS_SCAN_X		0x01
> @@ -42,6 +45,7 @@
>  #define ADC_CHANNEL_BACKUP	4
>  #define ADC_CHANNEL_CHARGE	5
>  
> +/* HP Jornada 680/690 speaker on/off */
>  #define HD64461_GPADR_SPEAKER	0x01
>  #define HD64461_GPADR_PCMCIA0	(0x02|0x08)
>  
> diff --git a/sound/sh/Kconfig b/sound/sh/Kconfig
> index aed0f90..fecb198 100644
> --- a/sound/sh/Kconfig
> +++ b/sound/sh/Kconfig
> @@ -19,5 +19,14 @@ config SND_AICA
>  	help
>  	  ALSA Sound driver for the SEGA Dreamcast console.
>  
> +config SND_SH_DAC_AUDIO
> +	tristate "SuperH DAC audio support"
> +	depends on SND
> +	depends on CPU_SH3 && HIGH_RES_TIMERS
> +	select SND_PCM
> +	help
> +	  Alsa Sound driver for the HP Palmtop 620lx/660lx
> +	  and HP Jornada 680/690.
> +
>  endif	# SND_SUPERH
>  
> diff --git a/sound/sh/Makefile b/sound/sh/Makefile
> index 8fdcb6e..c4b81c4 100644
> --- a/sound/sh/Makefile
> +++ b/sound/sh/Makefile
> @@ -6,3 +6,4 @@ snd-aica-objs := aica.o
>  
>  # Toplevel Module Dependency
>  obj-$(CONFIG_SND_AICA) += snd-aica.o
> +obj-$(CONFIG_SND_SH_DAC_AUDIO) += snd_sh_dac_audio.o
> diff --git a/sound/sh/snd_sh_dac_audio.c b/sound/sh/snd_sh_dac_audio.c
> new file mode 100644
> index 0000000..074c43d
> --- /dev/null
> +++ b/sound/sh/snd_sh_dac_audio.c
> @@ -0,0 +1,521 @@
> +/*
> + * snd_sh_dac_audio.c - SuperH DAC audio driver for ALSA
> + *
> + * Copyright (c) 2009 by Rafael Ignacio Zurita <rizurita@yahoo.com>
> + *
> + *
> + * Based on sh_dac_audio.c (Copyright (C) 2004, 2005 by Andriy Skulysh)
> + *
> + *   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; either version 2 of the License, or
> + *   (at your option) any later version.
> + *
> + *   This program is distributed in the hope that it will be useful,
> + *   but WITHOUT ANY WARRANTY; without even the implied warranty of
> + *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + *   GNU General Public License for more details.
> + *
> + *   You should have received a copy of the GNU General Public License
> + *   along with this program; if not, write to the Free Software
> + *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
> + *
> + */
> +
> +#include <linux/hrtimer.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/platform_device.h>
> +#include <sound/core.h>
> +#include <sound/initval.h>
> +#include <sound/pcm.h>
> +#include <asm/clock.h>
> +#include <asm/hd64461.h>
> +#include <mach-common/mach/hp6xx.h>
> +#include <cpu/dac.h>
> +
> +MODULE_AUTHOR("Rafael Ignacio Zurita <rizurita@yahoo.com>");
> +MODULE_DESCRIPTION("SuperH DAC audio driver");
> +MODULE_LICENSE("GPL");
> +MODULE_SUPPORTED_DEVICE("{{SuperH DAC audio support}}");
> +
> +/* Module Parameters */
> +static int index = SNDRV_DEFAULT_IDX1;
> +static char *id = SNDRV_DEFAULT_STR1;
> +module_param(index, int, 0444);
> +MODULE_PARM_DESC(index, "Index value for SuperH DAC audio.");
> +module_param(id, charp, 0444);
> +MODULE_PARM_DESC(id, "ID string for SuperH DAC audio.");
> +
> +/* Simple platform device */
> +static struct platform_device *pd;
> +
> +#define SND_SH_DAC_DRIVER "SH_DAC"
> +#define BUFFER_SIZE	64000
> +#define SH_DAC_AUDIO_CHANNEL 1
> +
> +/* main struct */
> +struct snd_sh_dac {
> +	struct snd_card *card;
> +	struct snd_pcm_substream *substream;
> +	struct hrtimer hrtimer;
> +	ktime_t wakeups_per_second;
> +
> +	int rate;
> +	int empty;
> +	char *data_buffer, *buffer_begin, *buffer_end;
> +	int processed; /* bytes proccesed, to compare with period_size */
> +	int buffer_size;
> +};
> +
> +
> +static void dac_audio_start_timer(struct snd_sh_dac *chip)
> +{
> +	hrtimer_start(&chip->hrtimer, chip->wakeups_per_second,
> +		      HRTIMER_MODE_REL);
> +}
> +
> +static void dac_audio_stop_timer(struct snd_sh_dac *chip)
> +{
> +	hrtimer_cancel(&chip->hrtimer);
> +}
> +
> +static void dac_audio_reset(struct snd_sh_dac *chip)
> +{
> +	dac_audio_stop_timer(chip);
> +	chip->buffer_begin = chip->buffer_end = chip->data_buffer;
> +	chip->processed = 0;
> +	chip->empty = 1;
> +}
> +
> +static void dac_audio_sync(struct snd_sh_dac *chip)
> +{
> +	while (!chip->empty)
> +		schedule();
> +}
> +
> +static void dac_audio_start(void)
> +{
> +#ifdef CONFIG_SH_HP6XX
> +	u16 v;
> +	u8 v8;
> +
> +	/* HP Jornada 680/690 speaker on */
> +	v = inw(HD64461_GPADR);
> +	v &= ~HD64461_GPADR_SPEAKER;
> +	outw(v, HD64461_GPADR);
> +
> +	/* HP Palmtop 620lx/660lx speaker on */
> +	v8 = inb(PKDR);
> +	v8 &= ~PKDR_SPEAKER;
> +	outb(v8, PKDR);
> +#endif
> +	sh_dac_enable(SH_DAC_AUDIO_CHANNEL);
> +}
> +
> +static void dac_audio_stop(struct snd_sh_dac *chip)
> +{
> +
> +#ifdef CONFIG_SH_HP6XX
> +	u16 v;
> +	u8 v8;
> +#endif
> +
> +	dac_audio_stop_timer(chip);
> +
> +#ifdef CONFIG_SH_HP6XX
> +	/* HP Jornada 680/690 speaker off */
> +	v = inw(HD64461_GPADR);
> +	v |= HD64461_GPADR_SPEAKER;
> +	outw(v, HD64461_GPADR);
> +
> +	/* HP Palmtop 620lx/660lx speaker off */
> +	v8 = inb(PKDR);
> +	v8 |= PKDR_SPEAKER;
> +	outb(v8, PKDR);
> +#endif
> +	sh_dac_output(0, SH_DAC_AUDIO_CHANNEL);
> +	sh_dac_disable(SH_DAC_AUDIO_CHANNEL);
> +}
> +
> +static void dac_audio_set_rate(struct snd_sh_dac *chip)
> +{
> +	chip->wakeups_per_second = ktime_set(0, 1000000000 / chip->rate);
> +}
> +
> +
> +/* PCM INTERFACE */
> +
> +static struct snd_pcm_hardware snd_sh_dac_pcm_hw = {
> +	.info			= (SNDRV_PCM_INFO_MMAP |
> +					SNDRV_PCM_INFO_MMAP_VALID |
> +					SNDRV_PCM_INFO_INTERLEAVED |
> +					SNDRV_PCM_INFO_HALF_DUPLEX),
> +	.formats		= SNDRV_PCM_FMTBIT_U8,
> +	.rates			= SNDRV_PCM_RATE_8000,
> +	.rate_min		= 8000,
> +	.rate_max		= 8000,
> +	.channels_min		= 1,
> +	.channels_max		= 1,
> +	.buffer_bytes_max	= (48*1024),
> +	.period_bytes_min	= 1,
> +	.period_bytes_max	= (48*1024),
> +	.periods_min		= 1,
> +	.periods_max		= 1024,
> +};
> +
> +static int snd_sh_dac_pcm_open(struct snd_pcm_substream *substream)
> +{
> +	struct snd_sh_dac *chip = snd_pcm_substream_chip(substream);
> +	struct snd_pcm_runtime *runtime = substream->runtime;
> +
> +	runtime->hw = snd_sh_dac_pcm_hw;
> +
> +	chip->substream = substream;
> +	chip->buffer_begin = chip->buffer_end = chip->data_buffer;
> +	chip->processed = 0;
> +	chip->empty = 1;
> +
> +	dac_audio_start();
> +
> +	return 0;
> +}
> +
> +static int snd_sh_dac_pcm_close(struct snd_pcm_substream *substream)
> +{
> +	struct snd_sh_dac *chip = snd_pcm_substream_chip(substream);
> +
> +	dac_audio_sync(chip);
> +	dac_audio_stop(chip);
> +
> +	return 0;
> +}
> +
> +static int snd_sh_dac_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 snd_sh_dac_pcm_hw_free(struct snd_pcm_substream *substream)
> +{
> +	return snd_pcm_lib_free_pages(substream);
> +}
> +
> +static int snd_sh_dac_pcm_prepare(struct snd_pcm_substream *substream)
> +{
> +	struct snd_sh_dac *chip = snd_pcm_substream_chip(substream);
> +	struct snd_pcm_runtime *runtime = chip->substream->runtime;
> +
> +	chip->buffer_size = runtime->buffer_size;
> +	memset(chip->data_buffer, 0, BUFFER_SIZE);
> +
> +	return 0;
> +}
> +
> +static int snd_sh_dac_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
> +{
> +	struct snd_sh_dac *chip = snd_pcm_substream_chip(substream);
> +
> +	switch (cmd) {
> +	case SNDRV_PCM_TRIGGER_START:
> +		dac_audio_start_timer(chip);
> +		break;
> +	case SNDRV_PCM_TRIGGER_STOP:
> +		chip->buffer_begin = chip->buffer_end = chip->data_buffer;
> +		chip->processed = 0;
> +		chip->empty = 1;
> +		dac_audio_stop_timer(chip);
> +		break;
> +	default:
> +		 return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static int snd_sh_dac_pcm_copy(struct snd_pcm_substream *substream, int channel,
> +	snd_pcm_uframes_t pos, void __user *src, snd_pcm_uframes_t count)
> +{
> +	/* channel is not used (interleaved data) */
> +	struct snd_sh_dac *chip = snd_pcm_substream_chip(substream);
> +	struct snd_pcm_runtime *runtime = substream->runtime;
> +	ssize_t b_count = frames_to_bytes(runtime , count);
> +	ssize_t b_pos = frames_to_bytes(runtime , pos);
> +
> +	if (count < 0)
> +		return -EINVAL;
> +
> +	if (!count) {
> +		dac_audio_sync(chip);
> +		return 0;
> +	}
> +
> +	memcpy_toio(chip->data_buffer + b_pos, src, b_count);
> +	chip->buffer_end = chip->data_buffer + b_pos + b_count;
> +
> +	if (chip->empty) {
> +		chip->empty = 0;
> +		dac_audio_start_timer(chip);
> +	}
> +
> +	return 0;
> +}
> +
> +static int snd_sh_dac_pcm_silence(struct snd_pcm_substream *substream,
> +				  int channel, snd_pcm_uframes_t pos,
> +				  snd_pcm_uframes_t count)
> +{
> +	/* channel is not used (interleaved data) */
> +	struct snd_sh_dac *chip = snd_pcm_substream_chip(substream);
> +	struct snd_pcm_runtime *runtime = substream->runtime;
> +	ssize_t b_count = frames_to_bytes(runtime , count);
> +	ssize_t b_pos = frames_to_bytes(runtime , pos);
> +
> +	if (count < 0)
> +		return -EINVAL;
> +
> +	if (!count) {
> +		dac_audio_sync(chip);
> +		return 0;
> +	}
> +
> +	memset_io(chip->data_buffer + b_pos, 0, b_count);
> +	chip->buffer_end = chip->data_buffer + b_pos + b_count;
> +
> +	if (chip->empty) {
> +		chip->empty = 0;
> +		dac_audio_start_timer(chip);
> +	}
> +
> +	return 0;
> +}
> +
> +static
> +snd_pcm_uframes_t snd_sh_dac_pcm_pointer(struct snd_pcm_substream *substream)
> +{
> +	struct snd_sh_dac *chip = snd_pcm_substream_chip(substream);
> +	int pointer = chip->buffer_begin - chip->data_buffer;
> +
> +	return pointer;
> +}
> +
> +/* pcm ops */
> +static struct snd_pcm_ops snd_sh_dac_pcm_ops = {
> +	.open		= snd_sh_dac_pcm_open,
> +	.close		= snd_sh_dac_pcm_close,
> +	.ioctl		= snd_pcm_lib_ioctl,
> +	.hw_params	= snd_sh_dac_pcm_hw_params,
> +	.hw_free	= snd_sh_dac_pcm_hw_free,
> +	.prepare	= snd_sh_dac_pcm_prepare,
> +	.trigger	= snd_sh_dac_pcm_trigger,
> +	.pointer	= snd_sh_dac_pcm_pointer,
> +	.copy		= snd_sh_dac_pcm_copy,
> +	.silence	= snd_sh_dac_pcm_silence,
> +	.mmap		= snd_pcm_lib_mmap_iomem,
> +};
> +
> +static int __devinit snd_sh_dac_pcm(struct snd_sh_dac *chip, int device)
> +{
> +	int err;
> +	struct snd_pcm *pcm;
> +
> +	/* device should be always 0 for us */
> +	err = snd_pcm_new(chip->card, "SH_DAC PCM", device, 1, 0, &pcm);
> +	if (err < 0)
> +		return err;
> +
> +	pcm->private_data = chip;
> +	strcpy(pcm->name, "SH_DAC PCM");
> +	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_sh_dac_pcm_ops);
> +
> +	/* buffer sizeHK */
> +	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
> +					  snd_dma_continuous_data(GFP_KERNEL),
> +							48 * 1024,
> +							48 * 1024);
> +
> +	return 0;
> +}
> +/* END OF PCM INTERFACE */
> +
> +
> +/* driver .remove  --  destructor */
> +static int snd_sh_dac_remove(struct platform_device *devptr)
> +{
> +	snd_card_free(platform_get_drvdata(devptr));
> +	platform_set_drvdata(devptr, NULL);
> +
> +	return 0;
> +}
> +
> +/* free -- it has been defined by create */
> +static int snd_sh_dac_free(struct snd_sh_dac *chip)
> +{
> +	/* release the data */
> +	kfree(chip->data_buffer);
> +	kfree(chip);
> +
> +	return 0;
> +}
> +
> +static int snd_sh_dac_dev_free(struct snd_device *device)
> +{
> +	struct snd_sh_dac *chip = device->device_data;
> +
> +	return snd_sh_dac_free(chip);
> +}
> +
> +static enum hrtimer_restart sh_dac_audio_timer(struct hrtimer *handle)
> +{
> +	struct snd_sh_dac *chip = container_of(handle, struct snd_sh_dac,
> +					       hrtimer);
> +	struct snd_pcm_runtime *runtime = chip->substream->runtime;
> +	ssize_t b_ps = frames_to_bytes(runtime, runtime->period_size);
> +
> +	if (!chip->empty) {
> +		sh_dac_output(*chip->buffer_begin, SH_DAC_AUDIO_CHANNEL);
> +		chip->buffer_begin++;
> +
> +		chip->processed++;
> +		if (chip->processed >= b_ps) {
> +			chip->processed -= b_ps;
> +			snd_pcm_period_elapsed(chip->substream);
> +		}
> +
> +		if (chip->buffer_begin = (chip->data_buffer +
> +					   chip->buffer_size - 1))
> +			chip->buffer_begin = chip->data_buffer;
> +
> +		if (chip->buffer_begin = chip->buffer_end)
> +			chip->empty = 1;
> +
> +	}
> +
> +	if (!chip->empty)
> +		hrtimer_start(&chip->hrtimer, chip->wakeups_per_second,
> +			      HRTIMER_MODE_REL);
> +
> +	return HRTIMER_NORESTART;
> +}
> +
> +/* create  --  chip-specific constructor for the cards components */
> +static int __devinit snd_sh_dac_create(struct snd_card *card,
> +				       struct platform_device *devptr,
> +				       struct snd_sh_dac **rchip)
> +{
> +	struct snd_sh_dac *chip;
> +	int err;
> +
> +	static struct snd_device_ops ops = {
> +		   .dev_free = snd_sh_dac_dev_free,
> +	};
> +
> +	*rchip = NULL;
> +
> +	chip = kzalloc(sizeof(*chip), GFP_KERNEL);
> +	if (chip = NULL)
> +		return -ENOMEM;
> +
> +	chip->card = card;
> +
> +	hrtimer_init(&chip->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
> +	chip->hrtimer.function = sh_dac_audio_timer;
> +
> +	dac_audio_reset(chip);
> +	chip->rate = 8000;
> +	dac_audio_set_rate(chip);
> +
> +	chip->data_buffer = kmalloc(BUFFER_SIZE, GFP_KERNEL);
> +	if (chip->data_buffer = NULL)
> +		return -ENOMEM;
> +
> +	err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
> +	if (err < 0) {
> +		snd_sh_dac_free(chip);
> +		return err;
> +	}
> +
> +	*rchip = chip;
> +
> +	return 0;
> +}
> +
> +/* driver .probe  --  constructor */
> +static int __devinit snd_sh_dac_probe(struct platform_device *devptr)
> +{
> +	struct snd_sh_dac *chip;
> +	struct snd_card *card;
> +	int err;
> +
> +	err = snd_card_create(index, id, THIS_MODULE, 0, &card);
> +	if (err < 0) {
> +			snd_printk(KERN_ERR "cannot allocate the card\n");
> +			return err;
> +	}
> +
> +	err = snd_sh_dac_create(card, devptr, &chip);
> +	if (err < 0)
> +		goto probe_error;
> +
> +	err = snd_sh_dac_pcm(chip, 0);
> +	if (err < 0)
> +		goto probe_error;
> +
> +	strcpy(card->driver, "snd_sh_dac");
> +	strcpy(card->shortname, "SuperH DAC audio driver");
> +	printk(KERN_INFO "%s %s", card->longname, card->shortname);
> +
> +	err = snd_card_register(card);
> +	if (err < 0)
> +		goto probe_error;
> +
> +	snd_printk("ALSA driver for SuperH DAC audio");
> +
> +	platform_set_drvdata(devptr, card);
> +	return 0;
> +
> +probe_error:
> +	snd_card_free(card);
> +	return err;
> +}
> +
> +/*
> + * "driver" definition
> + */
> +static struct platform_driver driver = {
> +	.probe	= snd_sh_dac_probe,
> +	.remove = snd_sh_dac_remove,
> +	.driver = {
> +		.name = SND_SH_DAC_DRIVER,
> +	},
> +};
> +
> +/* clean up the module */
> +static void __exit sh_dac_exit(void)
> +{
> +	platform_device_unregister(pd);
> +	platform_driver_unregister(&driver);
> +}
> +
> +
> +static int __init sh_dac_init(void)
> +{
> +	int err;
> +
> +	err = platform_driver_register(&driver);
> +	if (unlikely(err < 0))
> +		return err;
> +
> +	pd = platform_device_register_simple(SND_SH_DAC_DRIVER, -1, NULL, 0);
> +	if (unlikely(IS_ERR(pd))) {
> +		platform_driver_unregister(&driver);
> +		return PTR_ERR(pd);
> +	}
> +
> +	return 0;
> +}
> +
> +module_init(sh_dac_init);
> +module_exit(sh_dac_exit);
> --
> To unsubscribe from this list: send the line "unsubscribe linux-sh" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html


-- 
Kristoffer Ericson <kristoffer.ericson@gmail.com>

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH] sh: add SuperH DAC audio driver for ALSA
  2009-10-08  1:34 [PATCH] sh: add SuperH DAC audio driver for ALSA Rafael Ignacio Zurita
  2009-10-08  8:38 ` Kristoffer Ericson
@ 2009-10-09  1:22 ` Paul Mundt
  2009-10-16 15:22 ` Rafael Ignacio Zurita
                   ` (5 subsequent siblings)
  7 siblings, 0 replies; 21+ messages in thread
From: Paul Mundt @ 2009-10-09  1:22 UTC (permalink / raw)
  To: linux-sh

On Wed, Oct 07, 2009 at 10:34:23PM -0300, Rafael Ignacio Zurita wrote:
> diff --git a/sound/sh/Kconfig b/sound/sh/Kconfig
> index aed0f90..fecb198 100644
> --- a/sound/sh/Kconfig
> +++ b/sound/sh/Kconfig
> @@ -19,5 +19,14 @@ config SND_AICA
>  	help
>  	  ALSA Sound driver for the SEGA Dreamcast console.
>  
> +config SND_SH_DAC_AUDIO
> +	tristate "SuperH DAC audio support"
> +	depends on SND
> +	depends on CPU_SH3 && HIGH_RES_TIMERS
> +	select SND_PCM
> +	help
> +	  Alsa Sound driver for the HP Palmtop 620lx/660lx
> +	  and HP Jornada 680/690.
> +
This Kconfig entry is in need of some help. For starters, there is
nothing hp6xx dependent about this that can't be easily abstracted. The
whole reason this is called SH DAC audio in the first place is because it
is a driver for the on-chip DAC found in many legacy SH parts. While it's
true that hp6xx is the only in-tree user of this at the moment, it's
forseeable that other SH-3 boards may make use of this as well.

> +#include <linux/hrtimer.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/platform_device.h>
> +#include <sound/core.h>
> +#include <sound/initval.h>
> +#include <sound/pcm.h>
> +#include <asm/clock.h>
> +#include <asm/hd64461.h>
> +#include <mach-common/mach/hp6xx.h>

#include <mach/hp6xx.h> only.

> +/* Simple platform device */
> +static struct platform_device *pd;
> +
This shouldn't be in the driver, the board code should be registering its
own platform device.

> +#define SND_SH_DAC_DRIVER "SH_DAC"
> +#define BUFFER_SIZE	64000
> +#define SH_DAC_AUDIO_CHANNEL 1
> +
Buffer size and audio channel are things that ideally belong in platform
data, as these will not be consistent on other platforms.

> +static void dac_audio_sync(struct snd_sh_dac *chip)
> +{
> +	while (!chip->empty)
> +		schedule();

This should have a timeout?

> +static void dac_audio_start(void)
> +{
> +#ifdef CONFIG_SH_HP6XX
> +	u16 v;
> +	u8 v8;
> +
> +	/* HP Jornada 680/690 speaker on */
> +	v = inw(HD64461_GPADR);
> +	v &= ~HD64461_GPADR_SPEAKER;
> +	outw(v, HD64461_GPADR);
> +
> +	/* HP Palmtop 620lx/660lx speaker on */
> +	v8 = inb(PKDR);
> +	v8 &= ~PKDR_SPEAKER;
> +	outb(v8, PKDR);
> +#endif
> +	sh_dac_enable(SH_DAC_AUDIO_CHANNEL);
> +}
> +
> +static void dac_audio_stop(struct snd_sh_dac *chip)
> +{
> +
> +#ifdef CONFIG_SH_HP6XX
> +	u16 v;
> +	u8 v8;
> +#endif
> +
> +	dac_audio_stop_timer(chip);
> +
> +#ifdef CONFIG_SH_HP6XX
> +	/* HP Jornada 680/690 speaker off */
> +	v = inw(HD64461_GPADR);
> +	v |= HD64461_GPADR_SPEAKER;
> +	outw(v, HD64461_GPADR);
> +
> +	/* HP Palmtop 620lx/660lx speaker off */
> +	v8 = inb(PKDR);
> +	v8 |= PKDR_SPEAKER;
> +	outb(v8, PKDR);
> +#endif
> +	sh_dac_output(0, SH_DAC_AUDIO_CHANNEL);
> +	sh_dac_disable(SH_DAC_AUDIO_CHANNEL);
> +}
> +
This shows that at least the start/stop routines need to be defined by
the platform, while the driver only takes care of the hrtimer management.
As such, these need to be moved in to function pointers in the platform
data and moved in to the platform code.

> +static int snd_sh_dac_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));
> +}
> +
This is a very unusual format, please break it up in a more coherent
fashion, ie:

	return snd_pcm_lib_malloc_pages(substream,
			params_buffer_bytes(hw_params));

> +static int __devinit snd_sh_dac_create(struct snd_card *card,
> +				       struct platform_device *devptr,
> +				       struct snd_sh_dac **rchip)
> +{
[snip]
> +	chip = kzalloc(sizeof(*chip), GFP_KERNEL);
> +	if (chip = NULL)
> +		return -ENOMEM;
> +
> +	chip->card = card;
> +
> +	hrtimer_init(&chip->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
> +	chip->hrtimer.function = sh_dac_audio_timer;
> +
> +	dac_audio_reset(chip);
> +	chip->rate = 8000;
> +	dac_audio_set_rate(chip);
> +
> +	chip->data_buffer = kmalloc(BUFFER_SIZE, GFP_KERNEL);
> +	if (chip->data_buffer = NULL)
> +		return -ENOMEM;
> +
This is insufficient for an error path, chip is leaked.

> +/* clean up the module */
> +static void __exit sh_dac_exit(void)
> +{
> +	platform_device_unregister(pd);
> +	platform_driver_unregister(&driver);
> +}
> +
> +
> +static int __init sh_dac_init(void)
> +{

For some reason you have these defined in reverse order.

> +	int err;
> +
> +	err = platform_driver_register(&driver);
> +	if (unlikely(err < 0))
> +		return err;
> +
> +	pd = platform_device_register_simple(SND_SH_DAC_DRIVER, -1, NULL, 0);
> +	if (unlikely(IS_ERR(pd))) {
> +		platform_driver_unregister(&driver);
> +		return PTR_ERR(pd);
> +	}
> +
> +	return 0;
> +}

Once you kill off the platform device silliness, this can just be a
simple

	return platform_driver_register(&driver);

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH] sh: add SuperH DAC audio driver for ALSA
  2009-10-08  1:34 [PATCH] sh: add SuperH DAC audio driver for ALSA Rafael Ignacio Zurita
  2009-10-08  8:38 ` Kristoffer Ericson
  2009-10-09  1:22 ` Paul Mundt
@ 2009-10-16 15:22 ` Rafael Ignacio Zurita
  2009-10-19  7:01 ` Paul Mundt
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 21+ messages in thread
From: Rafael Ignacio Zurita @ 2009-10-16 15:22 UTC (permalink / raw)
  To: linux-sh

Hello Paul,
  
On Fri, Oct 09, 2009 at 10:22:24AM +0900, Paul Mundt wrote:
> On Wed, Oct 07, 2009 at 10:34:23PM -0300, Rafael Ignacio Zurita wrote:
[snip]
> > +#include <linux/hrtimer.h>
> > +#include <linux/interrupt.h>
> > +#include <linux/io.h>
> > +#include <linux/platform_device.h>
> > +#include <sound/core.h>
> > +#include <sound/initval.h>
> > +#include <sound/pcm.h>
> > +#include <asm/clock.h>
> > +#include <asm/hd64461.h>
> > +#include <mach-common/mach/hp6xx.h>
> 
> #include <mach/hp6xx.h> only.
> 
> > +/* Simple platform device */
> > +static struct platform_device *pd;
> > +
> This shouldn't be in the driver, the board code should be registering its
> own platform device.
> 
> > +#define SND_SH_DAC_DRIVER "SH_DAC"
> > +#define BUFFER_SIZE	64000
> > +#define SH_DAC_AUDIO_CHANNEL 1
> > +
> Buffer size and audio channel are things that ideally belong in platform
> data, as these will not be consistent on other platforms.
> 
[snip]
> > +static void dac_audio_start(void)
> > +{
> > +#ifdef CONFIG_SH_HP6XX
> > +	u16 v;
> > +	u8 v8;
> > +
> > +	/* HP Jornada 680/690 speaker on */
> > +	v = inw(HD64461_GPADR);
> > +	v &= ~HD64461_GPADR_SPEAKER;
> > +	outw(v, HD64461_GPADR);
> > +
> > +	/* HP Palmtop 620lx/660lx speaker on */
> > +	v8 = inb(PKDR);
> > +	v8 &= ~PKDR_SPEAKER;
> > +	outb(v8, PKDR);
> > +#endif
> > +	sh_dac_enable(SH_DAC_AUDIO_CHANNEL);
> > +}
> > +
> > +static void dac_audio_stop(struct snd_sh_dac *chip)
> > +{
> > +
> > +#ifdef CONFIG_SH_HP6XX
> > +	u16 v;
> > +	u8 v8;
> > +#endif
> > +
> > +	dac_audio_stop_timer(chip);
> > +
> > +#ifdef CONFIG_SH_HP6XX
> > +	/* HP Jornada 680/690 speaker off */
> > +	v = inw(HD64461_GPADR);
> > +	v |= HD64461_GPADR_SPEAKER;
> > +	outw(v, HD64461_GPADR);
> > +
> > +	/* HP Palmtop 620lx/660lx speaker off */
> > +	v8 = inb(PKDR);
> > +	v8 |= PKDR_SPEAKER;
> > +	outb(v8, PKDR);
> > +#endif
> > +	sh_dac_output(0, SH_DAC_AUDIO_CHANNEL);
> > +	sh_dac_disable(SH_DAC_AUDIO_CHANNEL);
> > +}
> > +
> This shows that at least the start/stop routines need to be defined by
> the platform, while the driver only takes care of the hrtimer management.
> As such, these need to be moved in to function pointers in the platform
> data and moved in to the platform code.
> 
[snip]
> 
> > +/* clean up the module */
> > +static void __exit sh_dac_exit(void)
> > +{
> > +	platform_device_unregister(pd);
> > +	platform_driver_unregister(&driver);
> > +}
> > +
> > +
> > +static int __init sh_dac_init(void)
> > +{
> 
> For some reason you have these defined in reverse order.
> 
> > +	int err;
> > +
> > +	err = platform_driver_register(&driver);
> > +	if (unlikely(err < 0))
> > +		return err;
> > +
> > +	pd = platform_device_register_simple(SND_SH_DAC_DRIVER, -1, NULL, 0);
> > +	if (unlikely(IS_ERR(pd))) {
> > +		platform_driver_unregister(&driver);
> > +		return PTR_ERR(pd);
> > +	}
> > +
> > +	return 0;
> > +}
> 
> Once you kill off the platform device silliness, this can just be a
> simple
> 
> 	return platform_driver_register(&driver);

Regarding platform bits, let me show you just the current idea to 
put the platform bits in the proper place : 

 arch/sh/boards/mach-hp6xx/setup.c        |   58 ++++
 include/sound/dac_audio.h                |   21 ++
 sound/sh/snd_sh_dac_audio.c              |  471 ++++++++++++++++++++++++++++++

diff --git a/arch/sh/boards/mach-hp6xx/setup.c b/arch/sh/boards/mach-hp6xx/setup.c
index 8f305b3..826a499 100644
--- a/arch/sh/boards/mach-hp6xx/setup.c
+++ b/arch/sh/boards/mach-hp6xx/setup.c
@@ -13,6 +13,7 @@
 #include <linux/init.h>
 #include <linux/platform_device.h>
 #include <linux/irq.h>
+#include <sound/dac_audio.h>
 #include <asm/hd64461.h>
 #include <asm/io.h>
 #include <mach/hp6xx.h>
@@ -51,9 +52,66 @@ static struct platform_device jornadakbd_device = {
 	.id		= -1,
 };
 
+static void dac_audio_start(struct dac_audio_pdata *pdata);
+static void dac_audio_stop(struct dac_audio_pdata *pdata);
+
+static struct dac_audio_pdata dac_audio_platform_data = {
+	.buffer_size		= 64000,
+	.channel		= 1,
+	.start			= dac_audio_start,
+	.stop			= dac_audio_stop,
+};
+
+static void dac_audio_start(struct dac_audio_pdata *pdata)
+{
+	u16 v;
+	u8 v8;
+
+	/* HP Jornada 680/690 speaker on */
+	v = inw(HD64461_GPADR);
+	v &= ~HD64461_GPADR_SPEAKER;
+	outw(v, HD64461_GPADR);
+
+	/* HP Palmtop 620lx/660lx speaker on */
+	v8 = inb(PKDR);
+	v8 &= ~PKDR_SPEAKER;
+	outb(v8, PKDR);
+
+	sh_dac_enable(pdata->channel);
+}
+
+static void dac_audio_stop(struct dac_audio_pdata *pdata)
+{
+	u16 v;
+	u8 v8;
+
+	/* HP Jornada 680/690 speaker off */
+	v = inw(HD64461_GPADR);
+	v |= HD64461_GPADR_SPEAKER;
+	outw(v, HD64461_GPADR);
+
+	/* HP Palmtop 620lx/660lx speaker off */
+	v8 = inb(PKDR);
+	v8 |= PKDR_SPEAKER;
+	outb(v8, PKDR);
+
+	sh_dac_output(0, pdata->channel);
+	sh_dac_disable(pdata->channel);
+}
+
+static struct platform_device dac_audio_device = {
+	.name		= "dac_audio",
+	.id		= -1,
+	.dev		= {
+		.platform_data	= &dac_audio_platform_data,
+	}
+
+};
+
 static struct platform_device *hp6xx_devices[] __initdata = {
 	&cf_ide_device,
 	&jornadakbd_device,
+	&dac_audio_device,
 };
 
 static void __init hp6xx_init_irq(void)
diff --git a/include/sound/dac_audio.h b/include/sound/dac_audio.h
new file mode 100644
index 0000000..98324e9
--- /dev/null
+++ b/include/sound/dac_audio.h
@@ -0,0 +1,21 @@
+/*
+ * SH_DAC specific configuration, for the dac_audio platform_device
+ *
+ * Copyright (C) 2009 Rafael Ignacio Zurita <rizurita@yahoo.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 __INCLUDE_DAC_AUDIO_H
+#define __INCLUDE_DAC_AUDIO_H
+
+struct dac_audio_pdata {
+	int buffer_size;
+	int channel;
+	void (*start)(struct dac_audio_pdata *pd);
+	void (*stop)(struct dac_audio_pdata *pd);
+};
+
+#endif /* __INCLUDE_DAC_AUDIO_H */
diff --git a/sound/sh/snd_sh_dac_audio.c b/sound/sh/snd_sh_dac_audio.c
new file mode 100644
index 0000000..63eda29
--- /dev/null
+++ b/sound/sh/snd_sh_dac_audio.c
@@ -0,0 +1,471 @@
+/*
+ * snd_sh_dac_audio.c - SuperH DAC audio driver for ALSA
+ *
[snip]
+ */
+
+#include <linux/hrtimer.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/dac_audio.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <asm/clock.h>
+#include <asm/hd64461.h>
+#include <mach/hp6xx.h>
+#include <cpu/dac.h>
+
[snip]
+
+/*
+ * "driver" definition
+ */
+static struct platform_driver driver = {
+	.probe	= snd_sh_dac_probe,
+	.remove = snd_sh_dac_remove,
+	.driver = {
+		.name = SND_SH_DAC_DRIVER,
+	},
+};
+
+static int __init sh_dac_init(void)
+{
+	return platform_driver_register(&driver);
+}
+
+/* clean up the module */
+static void __exit sh_dac_exit(void)
+{
+	platform_driver_unregister(&driver);
+}
+
+module_init(sh_dac_init);
+module_exit(sh_dac_exit);

Is that idea better for the platform device? The new include/sound/dac_audio.h
file has the struct for the platform data. Perhaps another platform would like
to use the same struct, if it needs to use the driver (it should set the values
for the members in its board code).

I am also fixing the other problems that you pointed in your mail.
If you are okey with the idea I am going to resend a new version of the patch.
Thanks a lot for the review.

Rafael

^ permalink raw reply related	[flat|nested] 21+ messages in thread

* Re: [PATCH] sh: add SuperH DAC audio driver for ALSA
  2009-10-08  1:34 [PATCH] sh: add SuperH DAC audio driver for ALSA Rafael Ignacio Zurita
                   ` (2 preceding siblings ...)
  2009-10-16 15:22 ` Rafael Ignacio Zurita
@ 2009-10-19  7:01 ` Paul Mundt
  2009-10-21  1:38 ` [PATCH] sh: add SuperH DAC audio driver for ALSA V2 Rafael Ignacio Zurita
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 21+ messages in thread
From: Paul Mundt @ 2009-10-19  7:01 UTC (permalink / raw)
  To: linux-sh

On Fri, Oct 16, 2009 at 12:22:36PM -0300, Rafael Ignacio Zurita wrote:
> Regarding platform bits, let me show you just the current idea to 
> put the platform bits in the proper place : 
> 
>  arch/sh/boards/mach-hp6xx/setup.c        |   58 ++++
>  include/sound/dac_audio.h                |   21 ++
>  sound/sh/snd_sh_dac_audio.c              |  471 ++++++++++++++++++++++++++++++
> 
> diff --git a/arch/sh/boards/mach-hp6xx/setup.c b/arch/sh/boards/mach-hp6xx/setup.c
> index 8f305b3..826a499 100644
> --- a/arch/sh/boards/mach-hp6xx/setup.c
> +++ b/arch/sh/boards/mach-hp6xx/setup.c
> @@ -13,6 +13,7 @@
>  #include <linux/init.h>
>  #include <linux/platform_device.h>
>  #include <linux/irq.h>
> +#include <sound/dac_audio.h>
>  #include <asm/hd64461.h>
>  #include <asm/io.h>
>  #include <mach/hp6xx.h>
> @@ -51,9 +52,66 @@ static struct platform_device jornadakbd_device = {
>  	.id		= -1,
>  };
>  
> +static void dac_audio_start(struct dac_audio_pdata *pdata);
> +static void dac_audio_stop(struct dac_audio_pdata *pdata);
> +
> +static struct dac_audio_pdata dac_audio_platform_data = {
> +	.buffer_size		= 64000,
> +	.channel		= 1,
> +	.start			= dac_audio_start,
> +	.stop			= dac_audio_stop,
> +};
> +
You don't need the prototype definitions, just move the platform data
definition further down.

> Is that idea better for the platform device? The new include/sound/dac_audio.h
> file has the struct for the platform data. Perhaps another platform would like
> to use the same struct, if it needs to use the driver (it should set the values
> for the members in its board code).
> 
Yes, that looks fine.

^ permalink raw reply	[flat|nested] 21+ messages in thread

* [PATCH] sh: add SuperH DAC audio driver for ALSA V2
  2009-10-08  1:34 [PATCH] sh: add SuperH DAC audio driver for ALSA Rafael Ignacio Zurita
                   ` (3 preceding siblings ...)
  2009-10-19  7:01 ` Paul Mundt
@ 2009-10-21  1:38 ` Rafael Ignacio Zurita
  2009-10-22  1:56 ` Paul Mundt
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 21+ messages in thread
From: Rafael Ignacio Zurita @ 2009-10-21  1:38 UTC (permalink / raw)
  To: linux-sh

This is a port of the sound/oss/sh_dac_audio.c driver.
The driver uses an on-chip 8-bit D/A converter, which has a speaker connected
to one of its channels, found in several ancient HP machines.
For interrupts it uses a high-resolution timer (hrtimer).
Tested on SH7709 based hp6xx (HP Jornada 680/690 and HP Palmtop 620lx/660lx).
 
Also, since OSS Emulation works, the old OSS sound/oss/sh_dac_audio.c driver
would be obsolete soon, and it could be removed.
 
Changes since V1:
 - moved the platform device code into board code.
 - dropped dac_audio_sync(), since that code is not needed (it was inherited of
   the old oss driver).
 - rewrote the code with style problems.
 
Signed-off-by: Rafael Ignacio Zurita <rizurita@yahoo.com>
---
 arch/sh/boards/mach-hp6xx/setup.c        |   55 ++++
 arch/sh/include/mach-common/mach/hp6xx.h |    4 +
 include/sound/dac_audio.h                |   21 ++
 sound/sh/Kconfig                         |    8 +
 sound/sh/Makefile                        |    1 +
 sound/sh/snd_sh_dac_audio.c              |  453 ++++++++++++++++++++++++++++++
 6 files changed, 542 insertions(+), 0 deletions(-)

diff --git a/arch/sh/boards/mach-hp6xx/setup.c b/arch/sh/boards/mach-hp6xx/setup.c
index 8f305b3..081e327 100644
--- a/arch/sh/boards/mach-hp6xx/setup.c
+++ b/arch/sh/boards/mach-hp6xx/setup.c
@@ -13,6 +13,7 @@
 #include <linux/init.h>
 #include <linux/platform_device.h>
 #include <linux/irq.h>
+#include <sound/dac_audio.h>
 #include <asm/hd64461.h>
 #include <asm/io.h>
 #include <mach/hp6xx.h>
@@ -51,9 +52,63 @@ static struct platform_device jornadakbd_device = {
 	.id		= -1,
 };
 
+static void dac_audio_start(struct dac_audio_pdata *pdata)
+{
+	u16 v;
+	u8 v8;
+
+	/* HP Jornada 680/690 speaker on */
+	v = inw(HD64461_GPADR);
+	v &= ~HD64461_GPADR_SPEAKER;
+	outw(v, HD64461_GPADR);
+
+	/* HP Palmtop 620lx/660lx speaker on */
+	v8 = inb(PKDR);
+	v8 &= ~PKDR_SPEAKER;
+	outb(v8, PKDR);
+
+	sh_dac_enable(pdata->channel);
+}
+
+static void dac_audio_stop(struct dac_audio_pdata *pdata)
+{
+	u16 v;
+	u8 v8;
+
+	/* HP Jornada 680/690 speaker off */
+	v = inw(HD64461_GPADR);
+	v |= HD64461_GPADR_SPEAKER;
+	outw(v, HD64461_GPADR);
+
+	/* HP Palmtop 620lx/660lx speaker off */
+	v8 = inb(PKDR);
+	v8 |= PKDR_SPEAKER;
+	outb(v8, PKDR);
+
+	sh_dac_output(0, pdata->channel);
+	sh_dac_disable(pdata->channel);
+}
+
+static struct dac_audio_pdata dac_audio_platform_data = {
+	.buffer_size		= 64000,
+	.channel		= 1,
+	.start			= dac_audio_start,
+	.stop			= dac_audio_stop,
+};
+
+static struct platform_device dac_audio_device = {
+	.name		= "dac_audio",
+	.id		= -1,
+	.dev		= {
+		.platform_data	= &dac_audio_platform_data,
+	}
+
+};
+
 static struct platform_device *hp6xx_devices[] __initdata = {
 	&cf_ide_device,
 	&jornadakbd_device,
+	&dac_audio_device,
 };
 
 static void __init hp6xx_init_irq(void)
diff --git a/arch/sh/include/mach-common/mach/hp6xx.h b/arch/sh/include/mach-common/mach/hp6xx.h
index 0d4165a..bcc301a 100644
--- a/arch/sh/include/mach-common/mach/hp6xx.h
+++ b/arch/sh/include/mach-common/mach/hp6xx.h
@@ -29,6 +29,9 @@
 
 #define PKDR_LED_GREEN		0x10
 
+/* HP Palmtop 620lx/660lx speaker on/off */
+#define PKDR_SPEAKER		0x20
+
 #define SCPDR_TS_SCAN_ENABLE	0x20
 #define SCPDR_TS_SCAN_Y		0x02
 #define SCPDR_TS_SCAN_X		0x01
@@ -42,6 +45,7 @@
 #define ADC_CHANNEL_BACKUP	4
 #define ADC_CHANNEL_CHARGE	5
 
+/* HP Jornada 680/690 speaker on/off */
 #define HD64461_GPADR_SPEAKER	0x01
 #define HD64461_GPADR_PCMCIA0	(0x02|0x08)
 
diff --git a/include/sound/dac_audio.h b/include/sound/dac_audio.h
new file mode 100644
index 0000000..98324e9
--- /dev/null
+++ b/include/sound/dac_audio.h
@@ -0,0 +1,21 @@
+/*
+ * SH_DAC specific configuration, for the dac_audio platform_device
+ *
+ * Copyright (C) 2009 Rafael Ignacio Zurita <rizurita@yahoo.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 __INCLUDE_DAC_AUDIO_H
+#define __INCLUDE_DAC_AUDIO_H
+
+struct dac_audio_pdata {
+	int buffer_size;
+	int channel;
+	void (*start)(struct dac_audio_pdata *pd);
+	void (*stop)(struct dac_audio_pdata *pd);
+};
+
+#endif /* __INCLUDE_DAC_AUDIO_H */
diff --git a/sound/sh/Kconfig b/sound/sh/Kconfig
index aed0f90..61139f3 100644
--- a/sound/sh/Kconfig
+++ b/sound/sh/Kconfig
@@ -19,5 +19,13 @@ config SND_AICA
 	help
 	  ALSA Sound driver for the SEGA Dreamcast console.
 
+config SND_SH_DAC_AUDIO
+	tristate "SuperH DAC audio support"
+	depends on SND
+	depends on CPU_SH3 && HIGH_RES_TIMERS
+	select SND_PCM
+	help
+	  Say Y here to include support for the on-chip DAC.
+
 endif	# SND_SUPERH
 
diff --git a/sound/sh/Makefile b/sound/sh/Makefile
index 8fdcb6e..c4b81c4 100644
--- a/sound/sh/Makefile
+++ b/sound/sh/Makefile
@@ -6,3 +6,4 @@ snd-aica-objs := aica.o
 
 # Toplevel Module Dependency
 obj-$(CONFIG_SND_AICA) += snd-aica.o
+obj-$(CONFIG_SND_SH_DAC_AUDIO) += snd_sh_dac_audio.o
diff --git a/sound/sh/snd_sh_dac_audio.c b/sound/sh/snd_sh_dac_audio.c
new file mode 100644
index 0000000..8fa824b
--- /dev/null
+++ b/sound/sh/snd_sh_dac_audio.c
@@ -0,0 +1,453 @@
+/*
+ * snd_sh_dac_audio.c - SuperH DAC audio driver for ALSA
+ *
+ * Copyright (c) 2009 by Rafael Ignacio Zurita <rizurita@yahoo.com>
+ *
+ *
+ * Based on sh_dac_audio.c (Copyright (C) 2004, 2005 by Andriy Skulysh)
+ *
+ *   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; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <linux/hrtimer.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/dac_audio.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <asm/clock.h>
+#include <asm/hd64461.h>
+#include <mach/hp6xx.h>
+#include <cpu/dac.h>
+
+MODULE_AUTHOR("Rafael Ignacio Zurita <rizurita@yahoo.com>");
+MODULE_DESCRIPTION("SuperH DAC audio driver");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{SuperH DAC audio support}}");
+
+/* Module Parameters */
+static int index = SNDRV_DEFAULT_IDX1;
+static char *id = SNDRV_DEFAULT_STR1;
+module_param(index, int, 0444);
+MODULE_PARM_DESC(index, "Index value for SuperH DAC audio.");
+module_param(id, charp, 0444);
+MODULE_PARM_DESC(id, "ID string for SuperH DAC audio.");
+
+/* main struct */
+struct snd_sh_dac {
+	struct snd_card *card;
+	struct snd_pcm_substream *substream;
+	struct hrtimer hrtimer;
+	ktime_t wakeups_per_second;
+
+	int rate;
+	int empty;
+	char *data_buffer, *buffer_begin, *buffer_end;
+	int processed; /* bytes proccesed, to compare with period_size */
+	int buffer_size;
+	struct dac_audio_pdata *pdata;
+};
+
+
+static void dac_audio_start_timer(struct snd_sh_dac *chip)
+{
+	hrtimer_start(&chip->hrtimer, chip->wakeups_per_second,
+		      HRTIMER_MODE_REL);
+}
+
+static void dac_audio_stop_timer(struct snd_sh_dac *chip)
+{
+	hrtimer_cancel(&chip->hrtimer);
+}
+
+static void dac_audio_reset(struct snd_sh_dac *chip)
+{
+	dac_audio_stop_timer(chip);
+	chip->buffer_begin = chip->buffer_end = chip->data_buffer;
+	chip->processed = 0;
+	chip->empty = 1;
+}
+
+static void dac_audio_set_rate(struct snd_sh_dac *chip)
+{
+	chip->wakeups_per_second = ktime_set(0, 1000000000 / chip->rate);
+}
+
+
+/* PCM INTERFACE */
+
+static struct snd_pcm_hardware snd_sh_dac_pcm_hw = {
+	.info			= (SNDRV_PCM_INFO_MMAP |
+					SNDRV_PCM_INFO_MMAP_VALID |
+					SNDRV_PCM_INFO_INTERLEAVED |
+					SNDRV_PCM_INFO_HALF_DUPLEX),
+	.formats		= SNDRV_PCM_FMTBIT_U8,
+	.rates			= SNDRV_PCM_RATE_8000,
+	.rate_min		= 8000,
+	.rate_max		= 8000,
+	.channels_min		= 1,
+	.channels_max		= 1,
+	.buffer_bytes_max	= (48*1024),
+	.period_bytes_min	= 1,
+	.period_bytes_max	= (48*1024),
+	.periods_min		= 1,
+	.periods_max		= 1024,
+};
+
+static int snd_sh_dac_pcm_open(struct snd_pcm_substream *substream)
+{
+	struct snd_sh_dac *chip = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+
+	runtime->hw = snd_sh_dac_pcm_hw;
+
+	chip->substream = substream;
+	chip->buffer_begin = chip->buffer_end = chip->data_buffer;
+	chip->processed = 0;
+	chip->empty = 1;
+
+	chip->pdata->start(chip->pdata);
+
+	return 0;
+}
+
+static int snd_sh_dac_pcm_close(struct snd_pcm_substream *substream)
+{
+	struct snd_sh_dac *chip = snd_pcm_substream_chip(substream);
+
+	chip->substream = NULL;
+
+	dac_audio_stop_timer(chip);
+	chip->pdata->stop(chip->pdata);
+
+	return 0;
+}
+
+static int snd_sh_dac_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 snd_sh_dac_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+	return snd_pcm_lib_free_pages(substream);
+}
+
+static int snd_sh_dac_pcm_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_sh_dac *chip = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = chip->substream->runtime;
+
+	chip->buffer_size = runtime->buffer_size;
+	memset(chip->data_buffer, 0, chip->pdata->buffer_size);
+
+	return 0;
+}
+
+static int snd_sh_dac_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	struct snd_sh_dac *chip = snd_pcm_substream_chip(substream);
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		dac_audio_start_timer(chip);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+		chip->buffer_begin = chip->buffer_end = chip->data_buffer;
+		chip->processed = 0;
+		chip->empty = 1;
+		dac_audio_stop_timer(chip);
+		break;
+	default:
+		 return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int snd_sh_dac_pcm_copy(struct snd_pcm_substream *substream, int channel,
+	snd_pcm_uframes_t pos, void __user *src, snd_pcm_uframes_t count)
+{
+	/* channel is not used (interleaved data) */
+	struct snd_sh_dac *chip = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	ssize_t b_count = frames_to_bytes(runtime , count);
+	ssize_t b_pos = frames_to_bytes(runtime , pos);
+
+	if (count < 0)
+		return -EINVAL;
+
+	if (!count)
+		return 0;
+
+	memcpy_toio(chip->data_buffer + b_pos, src, b_count);
+	chip->buffer_end = chip->data_buffer + b_pos + b_count;
+
+	if (chip->empty) {
+		chip->empty = 0;
+		dac_audio_start_timer(chip);
+	}
+
+	return 0;
+}
+
+static int snd_sh_dac_pcm_silence(struct snd_pcm_substream *substream,
+				  int channel, snd_pcm_uframes_t pos,
+				  snd_pcm_uframes_t count)
+{
+	/* channel is not used (interleaved data) */
+	struct snd_sh_dac *chip = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	ssize_t b_count = frames_to_bytes(runtime , count);
+	ssize_t b_pos = frames_to_bytes(runtime , pos);
+
+	if (count < 0)
+		return -EINVAL;
+
+	if (!count)
+		return 0;
+
+	memset_io(chip->data_buffer + b_pos, 0, b_count);
+	chip->buffer_end = chip->data_buffer + b_pos + b_count;
+
+	if (chip->empty) {
+		chip->empty = 0;
+		dac_audio_start_timer(chip);
+	}
+
+	return 0;
+}
+
+static
+snd_pcm_uframes_t snd_sh_dac_pcm_pointer(struct snd_pcm_substream *substream)
+{
+	struct snd_sh_dac *chip = snd_pcm_substream_chip(substream);
+	int pointer = chip->buffer_begin - chip->data_buffer;
+
+	return pointer;
+}
+
+/* pcm ops */
+static struct snd_pcm_ops snd_sh_dac_pcm_ops = {
+	.open		= snd_sh_dac_pcm_open,
+	.close		= snd_sh_dac_pcm_close,
+	.ioctl		= snd_pcm_lib_ioctl,
+	.hw_params	= snd_sh_dac_pcm_hw_params,
+	.hw_free	= snd_sh_dac_pcm_hw_free,
+	.prepare	= snd_sh_dac_pcm_prepare,
+	.trigger	= snd_sh_dac_pcm_trigger,
+	.pointer	= snd_sh_dac_pcm_pointer,
+	.copy		= snd_sh_dac_pcm_copy,
+	.silence	= snd_sh_dac_pcm_silence,
+	.mmap		= snd_pcm_lib_mmap_iomem,
+};
+
+static int __devinit snd_sh_dac_pcm(struct snd_sh_dac *chip, int device)
+{
+	int err;
+	struct snd_pcm *pcm;
+
+	/* device should be always 0 for us */
+	err = snd_pcm_new(chip->card, "SH_DAC PCM", device, 1, 0, &pcm);
+	if (err < 0)
+		return err;
+
+	pcm->private_data = chip;
+	strcpy(pcm->name, "SH_DAC PCM");
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_sh_dac_pcm_ops);
+
+	/* buffer sizeHK */
+	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
+					  snd_dma_continuous_data(GFP_KERNEL),
+							48 * 1024,
+							48 * 1024);
+
+	return 0;
+}
+/* END OF PCM INTERFACE */
+
+
+/* driver .remove  --  destructor */
+static int snd_sh_dac_remove(struct platform_device *devptr)
+{
+	snd_card_free(platform_get_drvdata(devptr));
+	platform_set_drvdata(devptr, NULL);
+
+	return 0;
+}
+
+/* free -- it has been defined by create */
+static int snd_sh_dac_free(struct snd_sh_dac *chip)
+{
+	/* release the data */
+	kfree(chip->data_buffer);
+	kfree(chip);
+
+	return 0;
+}
+
+static int snd_sh_dac_dev_free(struct snd_device *device)
+{
+	struct snd_sh_dac *chip = device->device_data;
+
+	return snd_sh_dac_free(chip);
+}
+
+static enum hrtimer_restart sh_dac_audio_timer(struct hrtimer *handle)
+{
+	struct snd_sh_dac *chip = container_of(handle, struct snd_sh_dac,
+					       hrtimer);
+	struct snd_pcm_runtime *runtime = chip->substream->runtime;
+	ssize_t b_ps = frames_to_bytes(runtime, runtime->period_size);
+
+	if (!chip->empty) {
+		sh_dac_output(*chip->buffer_begin, chip->pdata->channel);
+		chip->buffer_begin++;
+
+		chip->processed++;
+		if (chip->processed >= b_ps) {
+			chip->processed -= b_ps;
+			snd_pcm_period_elapsed(chip->substream);
+		}
+
+		if (chip->buffer_begin = (chip->data_buffer +
+					   chip->buffer_size - 1))
+			chip->buffer_begin = chip->data_buffer;
+
+		if (chip->buffer_begin = chip->buffer_end)
+			chip->empty = 1;
+
+	}
+
+	if (!chip->empty)
+		hrtimer_start(&chip->hrtimer, chip->wakeups_per_second,
+			      HRTIMER_MODE_REL);
+
+	return HRTIMER_NORESTART;
+}
+
+/* create  --  chip-specific constructor for the cards components */
+static int __devinit snd_sh_dac_create(struct snd_card *card,
+				       struct platform_device *devptr,
+				       struct snd_sh_dac **rchip)
+{
+	struct snd_sh_dac *chip;
+	int err;
+
+	static struct snd_device_ops ops = {
+		   .dev_free = snd_sh_dac_dev_free,
+	};
+
+	*rchip = NULL;
+
+	chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+	if (chip = NULL)
+		return -ENOMEM;
+
+	chip->card = card;
+
+	hrtimer_init(&chip->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+	chip->hrtimer.function = sh_dac_audio_timer;
+
+	dac_audio_reset(chip);
+	chip->rate = 8000;
+	dac_audio_set_rate(chip);
+
+	chip->pdata = devptr->dev.platform_data;
+
+	chip->data_buffer = kmalloc(chip->pdata->buffer_size, GFP_KERNEL);
+	if (chip->data_buffer = NULL) {
+		kfree(chip);
+		return -ENOMEM;
+	}
+
+	err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
+	if (err < 0) {
+		snd_sh_dac_free(chip);
+		return err;
+	}
+
+	*rchip = chip;
+
+	return 0;
+}
+
+/* driver .probe  --  constructor */
+static int __devinit snd_sh_dac_probe(struct platform_device *devptr)
+{
+	struct snd_sh_dac *chip;
+	struct snd_card *card;
+	int err;
+
+	err = snd_card_create(index, id, THIS_MODULE, 0, &card);
+	if (err < 0) {
+			snd_printk(KERN_ERR "cannot allocate the card\n");
+			return err;
+	}
+
+	err = snd_sh_dac_create(card, devptr, &chip);
+	if (err < 0)
+		goto probe_error;
+
+	err = snd_sh_dac_pcm(chip, 0);
+	if (err < 0)
+		goto probe_error;
+
+	strcpy(card->driver, "snd_sh_dac");
+	strcpy(card->shortname, "SuperH DAC audio driver");
+	printk(KERN_INFO "%s %s", card->longname, card->shortname);
+
+	err = snd_card_register(card);
+	if (err < 0)
+		goto probe_error;
+
+	snd_printk("ALSA driver for SuperH DAC audio");
+
+	platform_set_drvdata(devptr, card);
+	return 0;
+
+probe_error:
+	snd_card_free(card);
+	return err;
+}
+
+/*
+ * "driver" definition
+ */
+static struct platform_driver driver = {
+	.probe	= snd_sh_dac_probe,
+	.remove = snd_sh_dac_remove,
+	.driver = {
+		.name = "dac_audio",
+	},
+};
+
+static int __init sh_dac_init(void)
+{
+	return platform_driver_register(&driver);
+}
+
+static void __exit sh_dac_exit(void)
+{
+	platform_driver_unregister(&driver);
+}
+
+module_init(sh_dac_init);
+module_exit(sh_dac_exit);

^ permalink raw reply related	[flat|nested] 21+ messages in thread

* Re: [PATCH] sh: add SuperH DAC audio driver for ALSA V2
  2009-10-08  1:34 [PATCH] sh: add SuperH DAC audio driver for ALSA Rafael Ignacio Zurita
                   ` (4 preceding siblings ...)
  2009-10-21  1:38 ` [PATCH] sh: add SuperH DAC audio driver for ALSA V2 Rafael Ignacio Zurita
@ 2009-10-22  1:56 ` Paul Mundt
  2009-10-22 20:25   ` Rafael Ignacio Zurita
  2009-11-03 20:16   ` Rafael Ignacio Zurita
  7 siblings, 0 replies; 21+ messages in thread
From: Paul Mundt @ 2009-10-22  1:56 UTC (permalink / raw)
  To: linux-sh

On Tue, Oct 20, 2009 at 10:38:36PM -0300, Rafael Ignacio Zurita wrote:
> This is a port of the sound/oss/sh_dac_audio.c driver.
> The driver uses an on-chip 8-bit D/A converter, which has a speaker connected
> to one of its channels, found in several ancient HP machines.
> For interrupts it uses a high-resolution timer (hrtimer).
> Tested on SH7709 based hp6xx (HP Jornada 680/690 and HP Palmtop 620lx/660lx).
>  
> Also, since OSS Emulation works, the old OSS sound/oss/sh_dac_audio.c driver
> would be obsolete soon, and it could be removed.
>  
> Changes since V1:
>  - moved the platform device code into board code.
>  - dropped dac_audio_sync(), since that code is not needed (it was inherited of
>    the old oss driver).
>  - rewrote the code with style problems.
>  
> Signed-off-by: Rafael Ignacio Zurita <rizurita@yahoo.com>
> ---
>  arch/sh/boards/mach-hp6xx/setup.c        |   55 ++++
>  arch/sh/include/mach-common/mach/hp6xx.h |    4 +
>  include/sound/dac_audio.h                |   21 ++
>  sound/sh/Kconfig                         |    8 +
>  sound/sh/Makefile                        |    1 +
>  sound/sh/snd_sh_dac_audio.c              |  453 ++++++++++++++++++++++++++++++
>  6 files changed, 542 insertions(+), 0 deletions(-)
> 
Looks ok to me in general, but you really should rename the header to
sh_dac_audio.h or something similar, since dac_audio is an absurdly
generic name desite being tied entirely to the SH DAC.

Once you have fixed that up you should resend it and make sure to include
the ALSA folks, as they may have some comments on the actual audio bits.

^ permalink raw reply	[flat|nested] 21+ messages in thread

* [PATCH] sh: add SuperH DAC audio driver for ALSA V3
@ 2009-10-22 20:25   ` Rafael Ignacio Zurita
  0 siblings, 0 replies; 21+ messages in thread
From: Rafael Ignacio Zurita @ 2009-10-22 20:25 UTC (permalink / raw)
  To: linux-sh; +Cc: Paul Mundt, Takashi Iwai, alsa-devel

This is a port of the sound/oss/sh_dac_audio.c driver.
The driver uses an on-chip 8-bit D/A converter, which has a speaker connected
to one of its channels, found in several ancient HP machines.
For interrupts it uses a high-resolution timer (hrtimer).
Tested on SH7709 based hp6xx (HP Jornada 680/690 and HP Palmtop 620lx/660lx).
 
Also, since OSS Emulation works, the old OSS sound/oss/sh_dac_audio.c driver
would be obsolete soon, and it could be removed.
 
Changes since V2:
 - renamed include/sound/dac_audio.h to include/sound/sh_dac_audio.h

Changes since V1:
 - moved the platform device code into board code.
 - dropped dac_audio_sync(), since that code is not needed (it was inherited of
   the old oss driver).
 - rewrote the code with style problems.
 
Signed-off-by: Rafael Ignacio Zurita <rizurita@yahoo.com>
---
 arch/sh/boards/mach-hp6xx/setup.c        |   55 ++++
 arch/sh/include/mach-common/mach/hp6xx.h |    4 +
 include/sound/sh_dac_audio.h             |   21 ++
 sound/sh/Kconfig                         |    8 +
 sound/sh/Makefile                        |    1 +
 sound/sh/snd_sh_dac_audio.c              |  453 ++++++++++++++++++++++++++++++
 6 files changed, 542 insertions(+), 0 deletions(-)

diff --git a/arch/sh/boards/mach-hp6xx/setup.c b/arch/sh/boards/mach-hp6xx/setup.c
index 8f305b3..e6dd5e9 100644
--- a/arch/sh/boards/mach-hp6xx/setup.c
+++ b/arch/sh/boards/mach-hp6xx/setup.c
@@ -13,6 +13,7 @@
 #include <linux/init.h>
 #include <linux/platform_device.h>
 #include <linux/irq.h>
+#include <sound/sh_dac_audio.h>
 #include <asm/hd64461.h>
 #include <asm/io.h>
 #include <mach/hp6xx.h>
@@ -51,9 +52,63 @@ static struct platform_device jornadakbd_device = {
 	.id		= -1,
 };
 
+static void dac_audio_start(struct dac_audio_pdata *pdata)
+{
+	u16 v;
+	u8 v8;
+
+	/* HP Jornada 680/690 speaker on */
+	v = inw(HD64461_GPADR);
+	v &= ~HD64461_GPADR_SPEAKER;
+	outw(v, HD64461_GPADR);
+
+	/* HP Palmtop 620lx/660lx speaker on */
+	v8 = inb(PKDR);
+	v8 &= ~PKDR_SPEAKER;
+	outb(v8, PKDR);
+
+	sh_dac_enable(pdata->channel);
+}
+
+static void dac_audio_stop(struct dac_audio_pdata *pdata)
+{
+	u16 v;
+	u8 v8;
+
+	/* HP Jornada 680/690 speaker off */
+	v = inw(HD64461_GPADR);
+	v |= HD64461_GPADR_SPEAKER;
+	outw(v, HD64461_GPADR);
+
+	/* HP Palmtop 620lx/660lx speaker off */
+	v8 = inb(PKDR);
+	v8 |= PKDR_SPEAKER;
+	outb(v8, PKDR);
+
+	sh_dac_output(0, pdata->channel);
+	sh_dac_disable(pdata->channel);
+}
+
+static struct dac_audio_pdata dac_audio_platform_data = {
+	.buffer_size		= 64000,
+	.channel		= 1,
+	.start			= dac_audio_start,
+	.stop			= dac_audio_stop,
+};
+
+static struct platform_device dac_audio_device = {
+	.name		= "dac_audio",
+	.id		= -1,
+	.dev		= {
+		.platform_data	= &dac_audio_platform_data,
+	}
+
+};
+
 static struct platform_device *hp6xx_devices[] __initdata = {
 	&cf_ide_device,
 	&jornadakbd_device,
+	&dac_audio_device,
 };
 
 static void __init hp6xx_init_irq(void)
diff --git a/arch/sh/include/mach-common/mach/hp6xx.h b/arch/sh/include/mach-common/mach/hp6xx.h
index 0d4165a..bcc301a 100644
--- a/arch/sh/include/mach-common/mach/hp6xx.h
+++ b/arch/sh/include/mach-common/mach/hp6xx.h
@@ -29,6 +29,9 @@
 
 #define PKDR_LED_GREEN		0x10
 
+/* HP Palmtop 620lx/660lx speaker on/off */
+#define PKDR_SPEAKER		0x20
+
 #define SCPDR_TS_SCAN_ENABLE	0x20
 #define SCPDR_TS_SCAN_Y		0x02
 #define SCPDR_TS_SCAN_X		0x01
@@ -42,6 +45,7 @@
 #define ADC_CHANNEL_BACKUP	4
 #define ADC_CHANNEL_CHARGE	5
 
+/* HP Jornada 680/690 speaker on/off */
 #define HD64461_GPADR_SPEAKER	0x01
 #define HD64461_GPADR_PCMCIA0	(0x02|0x08)
 
diff --git a/include/sound/sh_dac_audio.h b/include/sound/sh_dac_audio.h
new file mode 100644
index 0000000..f5deaf1
--- /dev/null
+++ b/include/sound/sh_dac_audio.h
@@ -0,0 +1,21 @@
+/*
+ * SH_DAC specific configuration, for the dac_audio platform_device
+ *
+ * Copyright (C) 2009 Rafael Ignacio Zurita <rizurita@yahoo.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 __INCLUDE_SH_DAC_AUDIO_H
+#define __INCLUDE_SH_DAC_AUDIO_H
+
+struct dac_audio_pdata {
+	int buffer_size;
+	int channel;
+	void (*start)(struct dac_audio_pdata *pd);
+	void (*stop)(struct dac_audio_pdata *pd);
+};
+
+#endif /* __INCLUDE_SH_DAC_AUDIO_H */
diff --git a/sound/sh/Kconfig b/sound/sh/Kconfig
index aed0f90..61139f3 100644
--- a/sound/sh/Kconfig
+++ b/sound/sh/Kconfig
@@ -19,5 +19,13 @@ config SND_AICA
 	help
 	  ALSA Sound driver for the SEGA Dreamcast console.
 
+config SND_SH_DAC_AUDIO
+	tristate "SuperH DAC audio support"
+	depends on SND
+	depends on CPU_SH3 && HIGH_RES_TIMERS
+	select SND_PCM
+	help
+	  Say Y here to include support for the on-chip DAC.
+
 endif	# SND_SUPERH
 
diff --git a/sound/sh/Makefile b/sound/sh/Makefile
index 8fdcb6e..c4b81c4 100644
--- a/sound/sh/Makefile
+++ b/sound/sh/Makefile
@@ -6,3 +6,4 @@ snd-aica-objs := aica.o
 
 # Toplevel Module Dependency
 obj-$(CONFIG_SND_AICA) += snd-aica.o
+obj-$(CONFIG_SND_SH_DAC_AUDIO) += snd_sh_dac_audio.o
diff --git a/sound/sh/snd_sh_dac_audio.c b/sound/sh/snd_sh_dac_audio.c
new file mode 100644
index 0000000..ec3ca66
--- /dev/null
+++ b/sound/sh/snd_sh_dac_audio.c
@@ -0,0 +1,453 @@
+/*
+ * snd_sh_dac_audio.c - SuperH DAC audio driver for ALSA
+ *
+ * Copyright (c) 2009 by Rafael Ignacio Zurita <rizurita@yahoo.com>
+ *
+ *
+ * Based on sh_dac_audio.c (Copyright (C) 2004, 2005 by Andriy Skulysh)
+ *
+ *   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; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <linux/hrtimer.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/sh_dac_audio.h>
+#include <asm/clock.h>
+#include <asm/hd64461.h>
+#include <mach/hp6xx.h>
+#include <cpu/dac.h>
+
+MODULE_AUTHOR("Rafael Ignacio Zurita <rizurita@yahoo.com>");
+MODULE_DESCRIPTION("SuperH DAC audio driver");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{SuperH DAC audio support}}");
+
+/* Module Parameters */
+static int index = SNDRV_DEFAULT_IDX1;
+static char *id = SNDRV_DEFAULT_STR1;
+module_param(index, int, 0444);
+MODULE_PARM_DESC(index, "Index value for SuperH DAC audio.");
+module_param(id, charp, 0444);
+MODULE_PARM_DESC(id, "ID string for SuperH DAC audio.");
+
+/* main struct */
+struct snd_sh_dac {
+	struct snd_card *card;
+	struct snd_pcm_substream *substream;
+	struct hrtimer hrtimer;
+	ktime_t wakeups_per_second;
+
+	int rate;
+	int empty;
+	char *data_buffer, *buffer_begin, *buffer_end;
+	int processed; /* bytes proccesed, to compare with period_size */
+	int buffer_size;
+	struct dac_audio_pdata *pdata;
+};
+
+
+static void dac_audio_start_timer(struct snd_sh_dac *chip)
+{
+	hrtimer_start(&chip->hrtimer, chip->wakeups_per_second,
+		      HRTIMER_MODE_REL);
+}
+
+static void dac_audio_stop_timer(struct snd_sh_dac *chip)
+{
+	hrtimer_cancel(&chip->hrtimer);
+}
+
+static void dac_audio_reset(struct snd_sh_dac *chip)
+{
+	dac_audio_stop_timer(chip);
+	chip->buffer_begin = chip->buffer_end = chip->data_buffer;
+	chip->processed = 0;
+	chip->empty = 1;
+}
+
+static void dac_audio_set_rate(struct snd_sh_dac *chip)
+{
+	chip->wakeups_per_second = ktime_set(0, 1000000000 / chip->rate);
+}
+
+
+/* PCM INTERFACE */
+
+static struct snd_pcm_hardware snd_sh_dac_pcm_hw = {
+	.info			= (SNDRV_PCM_INFO_MMAP |
+					SNDRV_PCM_INFO_MMAP_VALID |
+					SNDRV_PCM_INFO_INTERLEAVED |
+					SNDRV_PCM_INFO_HALF_DUPLEX),
+	.formats		= SNDRV_PCM_FMTBIT_U8,
+	.rates			= SNDRV_PCM_RATE_8000,
+	.rate_min		= 8000,
+	.rate_max		= 8000,
+	.channels_min		= 1,
+	.channels_max		= 1,
+	.buffer_bytes_max	= (48*1024),
+	.period_bytes_min	= 1,
+	.period_bytes_max	= (48*1024),
+	.periods_min		= 1,
+	.periods_max		= 1024,
+};
+
+static int snd_sh_dac_pcm_open(struct snd_pcm_substream *substream)
+{
+	struct snd_sh_dac *chip = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+
+	runtime->hw = snd_sh_dac_pcm_hw;
+
+	chip->substream = substream;
+	chip->buffer_begin = chip->buffer_end = chip->data_buffer;
+	chip->processed = 0;
+	chip->empty = 1;
+
+	chip->pdata->start(chip->pdata);
+
+	return 0;
+}
+
+static int snd_sh_dac_pcm_close(struct snd_pcm_substream *substream)
+{
+	struct snd_sh_dac *chip = snd_pcm_substream_chip(substream);
+
+	chip->substream = NULL;
+
+	dac_audio_stop_timer(chip);
+	chip->pdata->stop(chip->pdata);
+
+	return 0;
+}
+
+static int snd_sh_dac_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 snd_sh_dac_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+	return snd_pcm_lib_free_pages(substream);
+}
+
+static int snd_sh_dac_pcm_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_sh_dac *chip = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = chip->substream->runtime;
+
+	chip->buffer_size = runtime->buffer_size;
+	memset(chip->data_buffer, 0, chip->pdata->buffer_size);
+
+	return 0;
+}
+
+static int snd_sh_dac_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	struct snd_sh_dac *chip = snd_pcm_substream_chip(substream);
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		dac_audio_start_timer(chip);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+		chip->buffer_begin = chip->buffer_end = chip->data_buffer;
+		chip->processed = 0;
+		chip->empty = 1;
+		dac_audio_stop_timer(chip);
+		break;
+	default:
+		 return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int snd_sh_dac_pcm_copy(struct snd_pcm_substream *substream, int channel,
+	snd_pcm_uframes_t pos, void __user *src, snd_pcm_uframes_t count)
+{
+	/* channel is not used (interleaved data) */
+	struct snd_sh_dac *chip = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	ssize_t b_count = frames_to_bytes(runtime , count);
+	ssize_t b_pos = frames_to_bytes(runtime , pos);
+
+	if (count < 0)
+		return -EINVAL;
+
+	if (!count)
+		return 0;
+
+	memcpy_toio(chip->data_buffer + b_pos, src, b_count);
+	chip->buffer_end = chip->data_buffer + b_pos + b_count;
+
+	if (chip->empty) {
+		chip->empty = 0;
+		dac_audio_start_timer(chip);
+	}
+
+	return 0;
+}
+
+static int snd_sh_dac_pcm_silence(struct snd_pcm_substream *substream,
+				  int channel, snd_pcm_uframes_t pos,
+				  snd_pcm_uframes_t count)
+{
+	/* channel is not used (interleaved data) */
+	struct snd_sh_dac *chip = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	ssize_t b_count = frames_to_bytes(runtime , count);
+	ssize_t b_pos = frames_to_bytes(runtime , pos);
+
+	if (count < 0)
+		return -EINVAL;
+
+	if (!count)
+		return 0;
+
+	memset_io(chip->data_buffer + b_pos, 0, b_count);
+	chip->buffer_end = chip->data_buffer + b_pos + b_count;
+
+	if (chip->empty) {
+		chip->empty = 0;
+		dac_audio_start_timer(chip);
+	}
+
+	return 0;
+}
+
+static
+snd_pcm_uframes_t snd_sh_dac_pcm_pointer(struct snd_pcm_substream *substream)
+{
+	struct snd_sh_dac *chip = snd_pcm_substream_chip(substream);
+	int pointer = chip->buffer_begin - chip->data_buffer;
+
+	return pointer;
+}
+
+/* pcm ops */
+static struct snd_pcm_ops snd_sh_dac_pcm_ops = {
+	.open		= snd_sh_dac_pcm_open,
+	.close		= snd_sh_dac_pcm_close,
+	.ioctl		= snd_pcm_lib_ioctl,
+	.hw_params	= snd_sh_dac_pcm_hw_params,
+	.hw_free	= snd_sh_dac_pcm_hw_free,
+	.prepare	= snd_sh_dac_pcm_prepare,
+	.trigger	= snd_sh_dac_pcm_trigger,
+	.pointer	= snd_sh_dac_pcm_pointer,
+	.copy		= snd_sh_dac_pcm_copy,
+	.silence	= snd_sh_dac_pcm_silence,
+	.mmap		= snd_pcm_lib_mmap_iomem,
+};
+
+static int __devinit snd_sh_dac_pcm(struct snd_sh_dac *chip, int device)
+{
+	int err;
+	struct snd_pcm *pcm;
+
+	/* device should be always 0 for us */
+	err = snd_pcm_new(chip->card, "SH_DAC PCM", device, 1, 0, &pcm);
+	if (err < 0)
+		return err;
+
+	pcm->private_data = chip;
+	strcpy(pcm->name, "SH_DAC PCM");
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_sh_dac_pcm_ops);
+
+	/* buffer sizeHK */
+	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
+					  snd_dma_continuous_data(GFP_KERNEL),
+							48 * 1024,
+							48 * 1024);
+
+	return 0;
+}
+/* END OF PCM INTERFACE */
+
+
+/* driver .remove  --  destructor */
+static int snd_sh_dac_remove(struct platform_device *devptr)
+{
+	snd_card_free(platform_get_drvdata(devptr));
+	platform_set_drvdata(devptr, NULL);
+
+	return 0;
+}
+
+/* free -- it has been defined by create */
+static int snd_sh_dac_free(struct snd_sh_dac *chip)
+{
+	/* release the data */
+	kfree(chip->data_buffer);
+	kfree(chip);
+
+	return 0;
+}
+
+static int snd_sh_dac_dev_free(struct snd_device *device)
+{
+	struct snd_sh_dac *chip = device->device_data;
+
+	return snd_sh_dac_free(chip);
+}
+
+static enum hrtimer_restart sh_dac_audio_timer(struct hrtimer *handle)
+{
+	struct snd_sh_dac *chip = container_of(handle, struct snd_sh_dac,
+					       hrtimer);
+	struct snd_pcm_runtime *runtime = chip->substream->runtime;
+	ssize_t b_ps = frames_to_bytes(runtime, runtime->period_size);
+
+	if (!chip->empty) {
+		sh_dac_output(*chip->buffer_begin, chip->pdata->channel);
+		chip->buffer_begin++;
+
+		chip->processed++;
+		if (chip->processed >= b_ps) {
+			chip->processed -= b_ps;
+			snd_pcm_period_elapsed(chip->substream);
+		}
+
+		if (chip->buffer_begin = (chip->data_buffer +
+					   chip->buffer_size - 1))
+			chip->buffer_begin = chip->data_buffer;
+
+		if (chip->buffer_begin = chip->buffer_end)
+			chip->empty = 1;
+
+	}
+
+	if (!chip->empty)
+		hrtimer_start(&chip->hrtimer, chip->wakeups_per_second,
+			      HRTIMER_MODE_REL);
+
+	return HRTIMER_NORESTART;
+}
+
+/* create  --  chip-specific constructor for the cards components */
+static int __devinit snd_sh_dac_create(struct snd_card *card,
+				       struct platform_device *devptr,
+				       struct snd_sh_dac **rchip)
+{
+	struct snd_sh_dac *chip;
+	int err;
+
+	static struct snd_device_ops ops = {
+		   .dev_free = snd_sh_dac_dev_free,
+	};
+
+	*rchip = NULL;
+
+	chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+	if (chip = NULL)
+		return -ENOMEM;
+
+	chip->card = card;
+
+	hrtimer_init(&chip->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+	chip->hrtimer.function = sh_dac_audio_timer;
+
+	dac_audio_reset(chip);
+	chip->rate = 8000;
+	dac_audio_set_rate(chip);
+
+	chip->pdata = devptr->dev.platform_data;
+
+	chip->data_buffer = kmalloc(chip->pdata->buffer_size, GFP_KERNEL);
+	if (chip->data_buffer = NULL) {
+		kfree(chip);
+		return -ENOMEM;
+	}
+
+	err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
+	if (err < 0) {
+		snd_sh_dac_free(chip);
+		return err;
+	}
+
+	*rchip = chip;
+
+	return 0;
+}
+
+/* driver .probe  --  constructor */
+static int __devinit snd_sh_dac_probe(struct platform_device *devptr)
+{
+	struct snd_sh_dac *chip;
+	struct snd_card *card;
+	int err;
+
+	err = snd_card_create(index, id, THIS_MODULE, 0, &card);
+	if (err < 0) {
+			snd_printk(KERN_ERR "cannot allocate the card\n");
+			return err;
+	}
+
+	err = snd_sh_dac_create(card, devptr, &chip);
+	if (err < 0)
+		goto probe_error;
+
+	err = snd_sh_dac_pcm(chip, 0);
+	if (err < 0)
+		goto probe_error;
+
+	strcpy(card->driver, "snd_sh_dac");
+	strcpy(card->shortname, "SuperH DAC audio driver");
+	printk(KERN_INFO "%s %s", card->longname, card->shortname);
+
+	err = snd_card_register(card);
+	if (err < 0)
+		goto probe_error;
+
+	snd_printk("ALSA driver for SuperH DAC audio");
+
+	platform_set_drvdata(devptr, card);
+	return 0;
+
+probe_error:
+	snd_card_free(card);
+	return err;
+}
+
+/*
+ * "driver" definition
+ */
+static struct platform_driver driver = {
+	.probe	= snd_sh_dac_probe,
+	.remove = snd_sh_dac_remove,
+	.driver = {
+		.name = "dac_audio",
+	},
+};
+
+static int __init sh_dac_init(void)
+{
+	return platform_driver_register(&driver);
+}
+
+static void __exit sh_dac_exit(void)
+{
+	platform_driver_unregister(&driver);
+}
+
+module_init(sh_dac_init);
+module_exit(sh_dac_exit);

^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH] sh: add SuperH DAC audio driver for ALSA V3
@ 2009-10-22 20:25   ` Rafael Ignacio Zurita
  0 siblings, 0 replies; 21+ messages in thread
From: Rafael Ignacio Zurita @ 2009-10-22 20:25 UTC (permalink / raw)
  To: linux-sh; +Cc: Paul Mundt, Takashi Iwai, alsa-devel

This is a port of the sound/oss/sh_dac_audio.c driver.
The driver uses an on-chip 8-bit D/A converter, which has a speaker connected
to one of its channels, found in several ancient HP machines.
For interrupts it uses a high-resolution timer (hrtimer).
Tested on SH7709 based hp6xx (HP Jornada 680/690 and HP Palmtop 620lx/660lx).
 
Also, since OSS Emulation works, the old OSS sound/oss/sh_dac_audio.c driver
would be obsolete soon, and it could be removed.
 
Changes since V2:
 - renamed include/sound/dac_audio.h to include/sound/sh_dac_audio.h

Changes since V1:
 - moved the platform device code into board code.
 - dropped dac_audio_sync(), since that code is not needed (it was inherited of
   the old oss driver).
 - rewrote the code with style problems.
 
Signed-off-by: Rafael Ignacio Zurita <rizurita@yahoo.com>
---
 arch/sh/boards/mach-hp6xx/setup.c        |   55 ++++
 arch/sh/include/mach-common/mach/hp6xx.h |    4 +
 include/sound/sh_dac_audio.h             |   21 ++
 sound/sh/Kconfig                         |    8 +
 sound/sh/Makefile                        |    1 +
 sound/sh/snd_sh_dac_audio.c              |  453 ++++++++++++++++++++++++++++++
 6 files changed, 542 insertions(+), 0 deletions(-)

diff --git a/arch/sh/boards/mach-hp6xx/setup.c b/arch/sh/boards/mach-hp6xx/setup.c
index 8f305b3..e6dd5e9 100644
--- a/arch/sh/boards/mach-hp6xx/setup.c
+++ b/arch/sh/boards/mach-hp6xx/setup.c
@@ -13,6 +13,7 @@
 #include <linux/init.h>
 #include <linux/platform_device.h>
 #include <linux/irq.h>
+#include <sound/sh_dac_audio.h>
 #include <asm/hd64461.h>
 #include <asm/io.h>
 #include <mach/hp6xx.h>
@@ -51,9 +52,63 @@ static struct platform_device jornadakbd_device = {
 	.id		= -1,
 };
 
+static void dac_audio_start(struct dac_audio_pdata *pdata)
+{
+	u16 v;
+	u8 v8;
+
+	/* HP Jornada 680/690 speaker on */
+	v = inw(HD64461_GPADR);
+	v &= ~HD64461_GPADR_SPEAKER;
+	outw(v, HD64461_GPADR);
+
+	/* HP Palmtop 620lx/660lx speaker on */
+	v8 = inb(PKDR);
+	v8 &= ~PKDR_SPEAKER;
+	outb(v8, PKDR);
+
+	sh_dac_enable(pdata->channel);
+}
+
+static void dac_audio_stop(struct dac_audio_pdata *pdata)
+{
+	u16 v;
+	u8 v8;
+
+	/* HP Jornada 680/690 speaker off */
+	v = inw(HD64461_GPADR);
+	v |= HD64461_GPADR_SPEAKER;
+	outw(v, HD64461_GPADR);
+
+	/* HP Palmtop 620lx/660lx speaker off */
+	v8 = inb(PKDR);
+	v8 |= PKDR_SPEAKER;
+	outb(v8, PKDR);
+
+	sh_dac_output(0, pdata->channel);
+	sh_dac_disable(pdata->channel);
+}
+
+static struct dac_audio_pdata dac_audio_platform_data = {
+	.buffer_size		= 64000,
+	.channel		= 1,
+	.start			= dac_audio_start,
+	.stop			= dac_audio_stop,
+};
+
+static struct platform_device dac_audio_device = {
+	.name		= "dac_audio",
+	.id		= -1,
+	.dev		= {
+		.platform_data	= &dac_audio_platform_data,
+	}
+
+};
+
 static struct platform_device *hp6xx_devices[] __initdata = {
 	&cf_ide_device,
 	&jornadakbd_device,
+	&dac_audio_device,
 };
 
 static void __init hp6xx_init_irq(void)
diff --git a/arch/sh/include/mach-common/mach/hp6xx.h b/arch/sh/include/mach-common/mach/hp6xx.h
index 0d4165a..bcc301a 100644
--- a/arch/sh/include/mach-common/mach/hp6xx.h
+++ b/arch/sh/include/mach-common/mach/hp6xx.h
@@ -29,6 +29,9 @@
 
 #define PKDR_LED_GREEN		0x10
 
+/* HP Palmtop 620lx/660lx speaker on/off */
+#define PKDR_SPEAKER		0x20
+
 #define SCPDR_TS_SCAN_ENABLE	0x20
 #define SCPDR_TS_SCAN_Y		0x02
 #define SCPDR_TS_SCAN_X		0x01
@@ -42,6 +45,7 @@
 #define ADC_CHANNEL_BACKUP	4
 #define ADC_CHANNEL_CHARGE	5
 
+/* HP Jornada 680/690 speaker on/off */
 #define HD64461_GPADR_SPEAKER	0x01
 #define HD64461_GPADR_PCMCIA0	(0x02|0x08)
 
diff --git a/include/sound/sh_dac_audio.h b/include/sound/sh_dac_audio.h
new file mode 100644
index 0000000..f5deaf1
--- /dev/null
+++ b/include/sound/sh_dac_audio.h
@@ -0,0 +1,21 @@
+/*
+ * SH_DAC specific configuration, for the dac_audio platform_device
+ *
+ * Copyright (C) 2009 Rafael Ignacio Zurita <rizurita@yahoo.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 __INCLUDE_SH_DAC_AUDIO_H
+#define __INCLUDE_SH_DAC_AUDIO_H
+
+struct dac_audio_pdata {
+	int buffer_size;
+	int channel;
+	void (*start)(struct dac_audio_pdata *pd);
+	void (*stop)(struct dac_audio_pdata *pd);
+};
+
+#endif /* __INCLUDE_SH_DAC_AUDIO_H */
diff --git a/sound/sh/Kconfig b/sound/sh/Kconfig
index aed0f90..61139f3 100644
--- a/sound/sh/Kconfig
+++ b/sound/sh/Kconfig
@@ -19,5 +19,13 @@ config SND_AICA
 	help
 	  ALSA Sound driver for the SEGA Dreamcast console.
 
+config SND_SH_DAC_AUDIO
+	tristate "SuperH DAC audio support"
+	depends on SND
+	depends on CPU_SH3 && HIGH_RES_TIMERS
+	select SND_PCM
+	help
+	  Say Y here to include support for the on-chip DAC.
+
 endif	# SND_SUPERH
 
diff --git a/sound/sh/Makefile b/sound/sh/Makefile
index 8fdcb6e..c4b81c4 100644
--- a/sound/sh/Makefile
+++ b/sound/sh/Makefile
@@ -6,3 +6,4 @@ snd-aica-objs := aica.o
 
 # Toplevel Module Dependency
 obj-$(CONFIG_SND_AICA) += snd-aica.o
+obj-$(CONFIG_SND_SH_DAC_AUDIO) += snd_sh_dac_audio.o
diff --git a/sound/sh/snd_sh_dac_audio.c b/sound/sh/snd_sh_dac_audio.c
new file mode 100644
index 0000000..ec3ca66
--- /dev/null
+++ b/sound/sh/snd_sh_dac_audio.c
@@ -0,0 +1,453 @@
+/*
+ * snd_sh_dac_audio.c - SuperH DAC audio driver for ALSA
+ *
+ * Copyright (c) 2009 by Rafael Ignacio Zurita <rizurita@yahoo.com>
+ *
+ *
+ * Based on sh_dac_audio.c (Copyright (C) 2004, 2005 by Andriy Skulysh)
+ *
+ *   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; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <linux/hrtimer.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/sh_dac_audio.h>
+#include <asm/clock.h>
+#include <asm/hd64461.h>
+#include <mach/hp6xx.h>
+#include <cpu/dac.h>
+
+MODULE_AUTHOR("Rafael Ignacio Zurita <rizurita@yahoo.com>");
+MODULE_DESCRIPTION("SuperH DAC audio driver");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{SuperH DAC audio support}}");
+
+/* Module Parameters */
+static int index = SNDRV_DEFAULT_IDX1;
+static char *id = SNDRV_DEFAULT_STR1;
+module_param(index, int, 0444);
+MODULE_PARM_DESC(index, "Index value for SuperH DAC audio.");
+module_param(id, charp, 0444);
+MODULE_PARM_DESC(id, "ID string for SuperH DAC audio.");
+
+/* main struct */
+struct snd_sh_dac {
+	struct snd_card *card;
+	struct snd_pcm_substream *substream;
+	struct hrtimer hrtimer;
+	ktime_t wakeups_per_second;
+
+	int rate;
+	int empty;
+	char *data_buffer, *buffer_begin, *buffer_end;
+	int processed; /* bytes proccesed, to compare with period_size */
+	int buffer_size;
+	struct dac_audio_pdata *pdata;
+};
+
+
+static void dac_audio_start_timer(struct snd_sh_dac *chip)
+{
+	hrtimer_start(&chip->hrtimer, chip->wakeups_per_second,
+		      HRTIMER_MODE_REL);
+}
+
+static void dac_audio_stop_timer(struct snd_sh_dac *chip)
+{
+	hrtimer_cancel(&chip->hrtimer);
+}
+
+static void dac_audio_reset(struct snd_sh_dac *chip)
+{
+	dac_audio_stop_timer(chip);
+	chip->buffer_begin = chip->buffer_end = chip->data_buffer;
+	chip->processed = 0;
+	chip->empty = 1;
+}
+
+static void dac_audio_set_rate(struct snd_sh_dac *chip)
+{
+	chip->wakeups_per_second = ktime_set(0, 1000000000 / chip->rate);
+}
+
+
+/* PCM INTERFACE */
+
+static struct snd_pcm_hardware snd_sh_dac_pcm_hw = {
+	.info			= (SNDRV_PCM_INFO_MMAP |
+					SNDRV_PCM_INFO_MMAP_VALID |
+					SNDRV_PCM_INFO_INTERLEAVED |
+					SNDRV_PCM_INFO_HALF_DUPLEX),
+	.formats		= SNDRV_PCM_FMTBIT_U8,
+	.rates			= SNDRV_PCM_RATE_8000,
+	.rate_min		= 8000,
+	.rate_max		= 8000,
+	.channels_min		= 1,
+	.channels_max		= 1,
+	.buffer_bytes_max	= (48*1024),
+	.period_bytes_min	= 1,
+	.period_bytes_max	= (48*1024),
+	.periods_min		= 1,
+	.periods_max		= 1024,
+};
+
+static int snd_sh_dac_pcm_open(struct snd_pcm_substream *substream)
+{
+	struct snd_sh_dac *chip = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+
+	runtime->hw = snd_sh_dac_pcm_hw;
+
+	chip->substream = substream;
+	chip->buffer_begin = chip->buffer_end = chip->data_buffer;
+	chip->processed = 0;
+	chip->empty = 1;
+
+	chip->pdata->start(chip->pdata);
+
+	return 0;
+}
+
+static int snd_sh_dac_pcm_close(struct snd_pcm_substream *substream)
+{
+	struct snd_sh_dac *chip = snd_pcm_substream_chip(substream);
+
+	chip->substream = NULL;
+
+	dac_audio_stop_timer(chip);
+	chip->pdata->stop(chip->pdata);
+
+	return 0;
+}
+
+static int snd_sh_dac_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 snd_sh_dac_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+	return snd_pcm_lib_free_pages(substream);
+}
+
+static int snd_sh_dac_pcm_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_sh_dac *chip = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = chip->substream->runtime;
+
+	chip->buffer_size = runtime->buffer_size;
+	memset(chip->data_buffer, 0, chip->pdata->buffer_size);
+
+	return 0;
+}
+
+static int snd_sh_dac_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	struct snd_sh_dac *chip = snd_pcm_substream_chip(substream);
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		dac_audio_start_timer(chip);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+		chip->buffer_begin = chip->buffer_end = chip->data_buffer;
+		chip->processed = 0;
+		chip->empty = 1;
+		dac_audio_stop_timer(chip);
+		break;
+	default:
+		 return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int snd_sh_dac_pcm_copy(struct snd_pcm_substream *substream, int channel,
+	snd_pcm_uframes_t pos, void __user *src, snd_pcm_uframes_t count)
+{
+	/* channel is not used (interleaved data) */
+	struct snd_sh_dac *chip = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	ssize_t b_count = frames_to_bytes(runtime , count);
+	ssize_t b_pos = frames_to_bytes(runtime , pos);
+
+	if (count < 0)
+		return -EINVAL;
+
+	if (!count)
+		return 0;
+
+	memcpy_toio(chip->data_buffer + b_pos, src, b_count);
+	chip->buffer_end = chip->data_buffer + b_pos + b_count;
+
+	if (chip->empty) {
+		chip->empty = 0;
+		dac_audio_start_timer(chip);
+	}
+
+	return 0;
+}
+
+static int snd_sh_dac_pcm_silence(struct snd_pcm_substream *substream,
+				  int channel, snd_pcm_uframes_t pos,
+				  snd_pcm_uframes_t count)
+{
+	/* channel is not used (interleaved data) */
+	struct snd_sh_dac *chip = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	ssize_t b_count = frames_to_bytes(runtime , count);
+	ssize_t b_pos = frames_to_bytes(runtime , pos);
+
+	if (count < 0)
+		return -EINVAL;
+
+	if (!count)
+		return 0;
+
+	memset_io(chip->data_buffer + b_pos, 0, b_count);
+	chip->buffer_end = chip->data_buffer + b_pos + b_count;
+
+	if (chip->empty) {
+		chip->empty = 0;
+		dac_audio_start_timer(chip);
+	}
+
+	return 0;
+}
+
+static
+snd_pcm_uframes_t snd_sh_dac_pcm_pointer(struct snd_pcm_substream *substream)
+{
+	struct snd_sh_dac *chip = snd_pcm_substream_chip(substream);
+	int pointer = chip->buffer_begin - chip->data_buffer;
+
+	return pointer;
+}
+
+/* pcm ops */
+static struct snd_pcm_ops snd_sh_dac_pcm_ops = {
+	.open		= snd_sh_dac_pcm_open,
+	.close		= snd_sh_dac_pcm_close,
+	.ioctl		= snd_pcm_lib_ioctl,
+	.hw_params	= snd_sh_dac_pcm_hw_params,
+	.hw_free	= snd_sh_dac_pcm_hw_free,
+	.prepare	= snd_sh_dac_pcm_prepare,
+	.trigger	= snd_sh_dac_pcm_trigger,
+	.pointer	= snd_sh_dac_pcm_pointer,
+	.copy		= snd_sh_dac_pcm_copy,
+	.silence	= snd_sh_dac_pcm_silence,
+	.mmap		= snd_pcm_lib_mmap_iomem,
+};
+
+static int __devinit snd_sh_dac_pcm(struct snd_sh_dac *chip, int device)
+{
+	int err;
+	struct snd_pcm *pcm;
+
+	/* device should be always 0 for us */
+	err = snd_pcm_new(chip->card, "SH_DAC PCM", device, 1, 0, &pcm);
+	if (err < 0)
+		return err;
+
+	pcm->private_data = chip;
+	strcpy(pcm->name, "SH_DAC PCM");
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_sh_dac_pcm_ops);
+
+	/* buffer size=48K */
+	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
+					  snd_dma_continuous_data(GFP_KERNEL),
+							48 * 1024,
+							48 * 1024);
+
+	return 0;
+}
+/* END OF PCM INTERFACE */
+
+
+/* driver .remove  --  destructor */
+static int snd_sh_dac_remove(struct platform_device *devptr)
+{
+	snd_card_free(platform_get_drvdata(devptr));
+	platform_set_drvdata(devptr, NULL);
+
+	return 0;
+}
+
+/* free -- it has been defined by create */
+static int snd_sh_dac_free(struct snd_sh_dac *chip)
+{
+	/* release the data */
+	kfree(chip->data_buffer);
+	kfree(chip);
+
+	return 0;
+}
+
+static int snd_sh_dac_dev_free(struct snd_device *device)
+{
+	struct snd_sh_dac *chip = device->device_data;
+
+	return snd_sh_dac_free(chip);
+}
+
+static enum hrtimer_restart sh_dac_audio_timer(struct hrtimer *handle)
+{
+	struct snd_sh_dac *chip = container_of(handle, struct snd_sh_dac,
+					       hrtimer);
+	struct snd_pcm_runtime *runtime = chip->substream->runtime;
+	ssize_t b_ps = frames_to_bytes(runtime, runtime->period_size);
+
+	if (!chip->empty) {
+		sh_dac_output(*chip->buffer_begin, chip->pdata->channel);
+		chip->buffer_begin++;
+
+		chip->processed++;
+		if (chip->processed >= b_ps) {
+			chip->processed -= b_ps;
+			snd_pcm_period_elapsed(chip->substream);
+		}
+
+		if (chip->buffer_begin == (chip->data_buffer +
+					   chip->buffer_size - 1))
+			chip->buffer_begin = chip->data_buffer;
+
+		if (chip->buffer_begin == chip->buffer_end)
+			chip->empty = 1;
+
+	}
+
+	if (!chip->empty)
+		hrtimer_start(&chip->hrtimer, chip->wakeups_per_second,
+			      HRTIMER_MODE_REL);
+
+	return HRTIMER_NORESTART;
+}
+
+/* create  --  chip-specific constructor for the cards components */
+static int __devinit snd_sh_dac_create(struct snd_card *card,
+				       struct platform_device *devptr,
+				       struct snd_sh_dac **rchip)
+{
+	struct snd_sh_dac *chip;
+	int err;
+
+	static struct snd_device_ops ops = {
+		   .dev_free = snd_sh_dac_dev_free,
+	};
+
+	*rchip = NULL;
+
+	chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+	if (chip == NULL)
+		return -ENOMEM;
+
+	chip->card = card;
+
+	hrtimer_init(&chip->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+	chip->hrtimer.function = sh_dac_audio_timer;
+
+	dac_audio_reset(chip);
+	chip->rate = 8000;
+	dac_audio_set_rate(chip);
+
+	chip->pdata = devptr->dev.platform_data;
+
+	chip->data_buffer = kmalloc(chip->pdata->buffer_size, GFP_KERNEL);
+	if (chip->data_buffer == NULL) {
+		kfree(chip);
+		return -ENOMEM;
+	}
+
+	err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
+	if (err < 0) {
+		snd_sh_dac_free(chip);
+		return err;
+	}
+
+	*rchip = chip;
+
+	return 0;
+}
+
+/* driver .probe  --  constructor */
+static int __devinit snd_sh_dac_probe(struct platform_device *devptr)
+{
+	struct snd_sh_dac *chip;
+	struct snd_card *card;
+	int err;
+
+	err = snd_card_create(index, id, THIS_MODULE, 0, &card);
+	if (err < 0) {
+			snd_printk(KERN_ERR "cannot allocate the card\n");
+			return err;
+	}
+
+	err = snd_sh_dac_create(card, devptr, &chip);
+	if (err < 0)
+		goto probe_error;
+
+	err = snd_sh_dac_pcm(chip, 0);
+	if (err < 0)
+		goto probe_error;
+
+	strcpy(card->driver, "snd_sh_dac");
+	strcpy(card->shortname, "SuperH DAC audio driver");
+	printk(KERN_INFO "%s %s", card->longname, card->shortname);
+
+	err = snd_card_register(card);
+	if (err < 0)
+		goto probe_error;
+
+	snd_printk("ALSA driver for SuperH DAC audio");
+
+	platform_set_drvdata(devptr, card);
+	return 0;
+
+probe_error:
+	snd_card_free(card);
+	return err;
+}
+
+/*
+ * "driver" definition
+ */
+static struct platform_driver driver = {
+	.probe	= snd_sh_dac_probe,
+	.remove = snd_sh_dac_remove,
+	.driver = {
+		.name = "dac_audio",
+	},
+};
+
+static int __init sh_dac_init(void)
+{
+	return platform_driver_register(&driver);
+}
+
+static void __exit sh_dac_exit(void)
+{
+	platform_driver_unregister(&driver);
+}
+
+module_init(sh_dac_init);
+module_exit(sh_dac_exit);

^ permalink raw reply related	[flat|nested] 21+ messages in thread

* Re: [PATCH] sh: add SuperH DAC audio driver for ALSA V3
  2009-10-22 20:25   ` Rafael Ignacio Zurita
@ 2009-10-26  0:31     ` Paul Mundt
  -1 siblings, 0 replies; 21+ messages in thread
From: Paul Mundt @ 2009-10-26  0:31 UTC (permalink / raw)
  To: Rafael Ignacio Zurita; +Cc: linux-sh, Takashi Iwai, alsa-devel

On Thu, Oct 22, 2009 at 05:25:06PM -0300, Rafael Ignacio Zurita wrote:
> This is a port of the sound/oss/sh_dac_audio.c driver.
> The driver uses an on-chip 8-bit D/A converter, which has a speaker connected
> to one of its channels, found in several ancient HP machines.
> For interrupts it uses a high-resolution timer (hrtimer).
> Tested on SH7709 based hp6xx (HP Jornada 680/690 and HP Palmtop 620lx/660lx).
>  
> Also, since OSS Emulation works, the old OSS sound/oss/sh_dac_audio.c driver
> would be obsolete soon, and it could be removed.
>  
> Changes since V2:
>  - renamed include/sound/dac_audio.h to include/sound/sh_dac_audio.h
> 
> Changes since V1:
>  - moved the platform device code into board code.
>  - dropped dac_audio_sync(), since that code is not needed (it was inherited of
>    the old oss driver).
>  - rewrote the code with style problems.
>  
> Signed-off-by: Rafael Ignacio Zurita <rizurita@yahoo.com>

As I've already reviewed the first 2 versions, this one is fine with me,
especially since it lets us kill off the old OSS driver.  Hopefully
someone from the ALSA side can comment on the non-architecture/platform
bits.

Reviewed-by: Paul Mundt <lethal@linux-sh.org>

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH] sh: add SuperH DAC audio driver for ALSA V3
@ 2009-10-26  0:31     ` Paul Mundt
  0 siblings, 0 replies; 21+ messages in thread
From: Paul Mundt @ 2009-10-26  0:31 UTC (permalink / raw)
  To: Rafael Ignacio Zurita; +Cc: linux-sh, Takashi Iwai, alsa-devel

On Thu, Oct 22, 2009 at 05:25:06PM -0300, Rafael Ignacio Zurita wrote:
> This is a port of the sound/oss/sh_dac_audio.c driver.
> The driver uses an on-chip 8-bit D/A converter, which has a speaker connected
> to one of its channels, found in several ancient HP machines.
> For interrupts it uses a high-resolution timer (hrtimer).
> Tested on SH7709 based hp6xx (HP Jornada 680/690 and HP Palmtop 620lx/660lx).
>  
> Also, since OSS Emulation works, the old OSS sound/oss/sh_dac_audio.c driver
> would be obsolete soon, and it could be removed.
>  
> Changes since V2:
>  - renamed include/sound/dac_audio.h to include/sound/sh_dac_audio.h
> 
> Changes since V1:
>  - moved the platform device code into board code.
>  - dropped dac_audio_sync(), since that code is not needed (it was inherited of
>    the old oss driver).
>  - rewrote the code with style problems.
>  
> Signed-off-by: Rafael Ignacio Zurita <rizurita@yahoo.com>

As I've already reviewed the first 2 versions, this one is fine with me,
especially since it lets us kill off the old OSS driver.  Hopefully
someone from the ALSA side can comment on the non-architecture/platform
bits.

Reviewed-by: Paul Mundt <lethal@linux-sh.org>

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH] sh: add SuperH DAC audio driver for ALSA V3
  2009-10-22 20:25   ` Rafael Ignacio Zurita
  (?)
  (?)
@ 2009-10-30 11:24   ` Takashi Iwai
  2009-10-30 11:59     ` Paul Mundt
  -1 siblings, 1 reply; 21+ messages in thread
From: Takashi Iwai @ 2009-10-30 11:24 UTC (permalink / raw)
  To: Rafael Ignacio Zurita; +Cc: alsa-devel, Paul Mundt

At Thu, 22 Oct 2009 17:25:06 -0300,
Rafael Ignacio Zurita wrote:
> 
> This is a port of the sound/oss/sh_dac_audio.c driver.
> The driver uses an on-chip 8-bit D/A converter, which has a speaker connected
> to one of its channels, found in several ancient HP machines.
> For interrupts it uses a high-resolution timer (hrtimer).
> Tested on SH7709 based hp6xx (HP Jornada 680/690 and HP Palmtop 620lx/660lx).
>  
> Also, since OSS Emulation works, the old OSS sound/oss/sh_dac_audio.c driver
> would be obsolete soon, and it could be removed.
>  
> Changes since V2:
>  - renamed include/sound/dac_audio.h to include/sound/sh_dac_audio.h
> 
> Changes since V1:
>  - moved the platform device code into board code.
>  - dropped dac_audio_sync(), since that code is not needed (it was inherited of
>    the old oss driver).
>  - rewrote the code with style problems.
>  
> Signed-off-by: Rafael Ignacio Zurita <rizurita@yahoo.com>

Thanks for the patch.  

> --- /dev/null
> +++ b/include/sound/sh_dac_audio.h
> @@ -0,0 +1,21 @@
> +/*
> + * SH_DAC specific configuration, for the dac_audio platform_device
> + *
> + * Copyright (C) 2009 Rafael Ignacio Zurita <rizurita@yahoo.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 __INCLUDE_SH_DAC_AUDIO_H
> +#define __INCLUDE_SH_DAC_AUDIO_H
> +
> +struct dac_audio_pdata {
> +	int buffer_size;
> +	int channel;
> +	void (*start)(struct dac_audio_pdata *pd);
> +	void (*stop)(struct dac_audio_pdata *pd);
> +};
> +
> +#endif /* __INCLUDE_SH_DAC_AUDIO_H */

Isn't it better to include this piece into mach/hp6xx.h or so?
Since it's pretty specific to the platform, it's not necessarily in
the common sound include path.

snd_sh_dac_audio.c looks good to me.
One little concern is the module name.  Other ALSA modules are named
as snd-xxx (no underscore), so it's better to follow that.  Also a file
name should be without snd_ prefix.


thanks,

Takashi

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH] sh: add SuperH DAC audio driver for ALSA V3
  2009-10-30 11:24   ` Takashi Iwai
@ 2009-10-30 11:59     ` Paul Mundt
  2009-11-02 17:03       ` Rafael Ignacio Zurita
  0 siblings, 1 reply; 21+ messages in thread
From: Paul Mundt @ 2009-10-30 11:59 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: alsa-devel, Rafael Ignacio Zurita

On Fri, Oct 30, 2009 at 12:24:03PM +0100, Takashi Iwai wrote:
> At Thu, 22 Oct 2009 17:25:06 -0300,
> Rafael Ignacio Zurita wrote:
> > --- /dev/null
> > +++ b/include/sound/sh_dac_audio.h
> > @@ -0,0 +1,21 @@
> > +/*
> > + * SH_DAC specific configuration, for the dac_audio platform_device
> > + *
> > + * Copyright (C) 2009 Rafael Ignacio Zurita <rizurita@yahoo.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 __INCLUDE_SH_DAC_AUDIO_H
> > +#define __INCLUDE_SH_DAC_AUDIO_H
> > +
> > +struct dac_audio_pdata {
> > +	int buffer_size;
> > +	int channel;
> > +	void (*start)(struct dac_audio_pdata *pd);
> > +	void (*stop)(struct dac_audio_pdata *pd);
> > +};
> > +
> > +#endif /* __INCLUDE_SH_DAC_AUDIO_H */
> 
> Isn't it better to include this piece into mach/hp6xx.h or so?
> Since it's pretty specific to the platform, it's not necessarily in
> the common sound include path.
> 
It's not really platform specific, it's a generic driver for the SH
on-chip DAC. hp6xx is the only platform that is using this at the moment,
but it's forseeable that other SH-3 platforms will enable this, too.

Of course we can hide this header in the sh includes somewhere if you
don't want it in include/sound, but include/sound certainly seems like a
reasonable place for it.

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH] sh: add SuperH DAC audio driver for ALSA V3
  2009-10-30 11:59     ` Paul Mundt
@ 2009-11-02 17:03       ` Rafael Ignacio Zurita
  2009-11-03  6:01         ` Takashi Iwai
  0 siblings, 1 reply; 21+ messages in thread
From: Rafael Ignacio Zurita @ 2009-11-02 17:03 UTC (permalink / raw)
  To: Paul Mundt, Takashi Iwai; +Cc: alsa-devel

Hello Takashi,
  thanks for the review in your previous mail.

On Fri, Oct 30, 2009 at 08:59:56PM +0900, Paul Mundt wrote:
> On Fri, Oct 30, 2009 at 12:24:03PM +0100, Takashi Iwai wrote:
> > At Thu, 22 Oct 2009 17:25:06 -0300,
> > Rafael Ignacio Zurita wrote:
> > > --- /dev/null
> > > +++ b/include/sound/sh_dac_audio.h
> > > @@ -0,0 +1,21 @@
> > > +/*
> > > + * SH_DAC specific configuration, for the dac_audio platform_device
> > > + *
> > > + * Copyright (C) 2009 Rafael Ignacio Zurita <rizurita@yahoo.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 __INCLUDE_SH_DAC_AUDIO_H
> > > +#define __INCLUDE_SH_DAC_AUDIO_H
> > > +
> > > +struct dac_audio_pdata {
> > > +	int buffer_size;
> > > +	int channel;
> > > +	void (*start)(struct dac_audio_pdata *pd);
> > > +	void (*stop)(struct dac_audio_pdata *pd);
> > > +};
> > > +
> > > +#endif /* __INCLUDE_SH_DAC_AUDIO_H */
> > 
> > Isn't it better to include this piece into mach/hp6xx.h or so?
> > Since it's pretty specific to the platform, it's not necessarily in
> > the common sound include path.
> > 
> It's not really platform specific, it's a generic driver for the SH
> on-chip DAC. hp6xx is the only platform that is using this at the moment,
> but it's forseeable that other SH-3 platforms will enable this, too.
> 
> Of course we can hide this header in the sh includes somewhere if you
> don't want it in include/sound, but include/sound certainly seems like a
> reasonable place for it.

After Pauls comments I would like to ask :
Is the sh_dac_audio.h file in include/sound okey with you?

If so, then I am going to send a new V4 of the patch, with the proper 
snd-* driver file name.

Thanks in advance,
Rafael

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH] sh: add SuperH DAC audio driver for ALSA V3
  2009-11-02 17:03       ` Rafael Ignacio Zurita
@ 2009-11-03  6:01         ` Takashi Iwai
  0 siblings, 0 replies; 21+ messages in thread
From: Takashi Iwai @ 2009-11-03  6:01 UTC (permalink / raw)
  To: Rafael Ignacio Zurita; +Cc: alsa-devel, Paul Mundt

At Mon, 2 Nov 2009 14:03:42 -0300,
Rafael Ignacio Zurita wrote:
> 
> Hello Takashi,
>   thanks for the review in your previous mail.
> 
> On Fri, Oct 30, 2009 at 08:59:56PM +0900, Paul Mundt wrote:
> > On Fri, Oct 30, 2009 at 12:24:03PM +0100, Takashi Iwai wrote:
> > > At Thu, 22 Oct 2009 17:25:06 -0300,
> > > Rafael Ignacio Zurita wrote:
> > > > --- /dev/null
> > > > +++ b/include/sound/sh_dac_audio.h
> > > > @@ -0,0 +1,21 @@
> > > > +/*
> > > > + * SH_DAC specific configuration, for the dac_audio platform_device
> > > > + *
> > > > + * Copyright (C) 2009 Rafael Ignacio Zurita <rizurita@yahoo.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 __INCLUDE_SH_DAC_AUDIO_H
> > > > +#define __INCLUDE_SH_DAC_AUDIO_H
> > > > +
> > > > +struct dac_audio_pdata {
> > > > +	int buffer_size;
> > > > +	int channel;
> > > > +	void (*start)(struct dac_audio_pdata *pd);
> > > > +	void (*stop)(struct dac_audio_pdata *pd);
> > > > +};
> > > > +
> > > > +#endif /* __INCLUDE_SH_DAC_AUDIO_H */
> > > 
> > > Isn't it better to include this piece into mach/hp6xx.h or so?
> > > Since it's pretty specific to the platform, it's not necessarily in
> > > the common sound include path.
> > > 
> > It's not really platform specific, it's a generic driver for the SH
> > on-chip DAC. hp6xx is the only platform that is using this at the moment,
> > but it's forseeable that other SH-3 platforms will enable this, too.
> > 
> > Of course we can hide this header in the sh includes somewhere if you
> > don't want it in include/sound, but include/sound certainly seems like a
> > reasonable place for it.
> 
> After Pauls comments I would like to ask :
> Is the sh_dac_audio.h file in include/sound okey with you?
> 
> If so, then I am going to send a new V4 of the patch, with the proper 
> snd-* driver file name.

It's fine to have it in include/sound.  I just wondered it because
this is used only in two places, and it's not necessarily to be
exported to the user-space, too.  But, include/sound is a reasonable
place, that's pretty OK.  (And, if any, we can move the code later on
:)

Please go ahead.  I'll apply the patch after reviewing.


thanks,

Takashi

^ permalink raw reply	[flat|nested] 21+ messages in thread

* [PATCH] sh: add SuperH DAC audio driver for ALSA V4
@ 2009-11-03 20:16   ` Rafael Ignacio Zurita
  0 siblings, 0 replies; 21+ messages in thread
From: Rafael Ignacio Zurita @ 2009-11-03 20:16 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: Paul Mundt, linux-sh, alsa-devel

This is a port of the sound/oss/sh_dac_audio.c driver.
The driver uses an on-chip 8-bit D/A converter, which has a speaker connected
to one of its channels, found in several ancient HP machines.
For interrupts it uses a high-resolution timer (hrtimer).
Tested on SH7709 based hp6xx (HP Jornada 680/690 and HP Palmtop 620lx/660lx).
 
Also, since OSS Emulation works, the old OSS sound/oss/sh_dac_audio.c driver
would be obsolete soon, and it could be removed.
 
Changes since V3:
 - renamed sound/sh/snd_sh_dac_audio.c to sound/sh/sh_dac_audio.c
 
Changes since V2:
 - renamed include/sound/dac_audio.h to include/sound/sh_dac_audio.h

Changes since V1:
 - moved the platform device code into board code.
 - dropped dac_audio_sync(), since that code is not needed (it was inherited of
   the old oss driver).
 - rewrote the code with style problems.

Signed-off-by: Rafael Ignacio Zurita <rizurita@yahoo.com>
---
 arch/sh/boards/mach-hp6xx/setup.c        |   55 ++++
 arch/sh/include/mach-common/mach/hp6xx.h |    4 +
 include/sound/sh_dac_audio.h             |   21 ++
 sound/sh/Kconfig                         |    8 +
 sound/sh/Makefile                        |    2 +
 sound/sh/sh_dac_audio.c                  |  453 ++++++++++++++++++++++++++++++
 6 files changed, 543 insertions(+), 0 deletions(-)

diff --git a/arch/sh/boards/mach-hp6xx/setup.c b/arch/sh/boards/mach-hp6xx/setup.c
index 8f305b3..e6dd5e9 100644
--- a/arch/sh/boards/mach-hp6xx/setup.c
+++ b/arch/sh/boards/mach-hp6xx/setup.c
@@ -13,6 +13,7 @@
 #include <linux/init.h>
 #include <linux/platform_device.h>
 #include <linux/irq.h>
+#include <sound/sh_dac_audio.h>
 #include <asm/hd64461.h>
 #include <asm/io.h>
 #include <mach/hp6xx.h>
@@ -51,9 +52,63 @@ static struct platform_device jornadakbd_device = {
 	.id		= -1,
 };
 
+static void dac_audio_start(struct dac_audio_pdata *pdata)
+{
+	u16 v;
+	u8 v8;
+
+	/* HP Jornada 680/690 speaker on */
+	v = inw(HD64461_GPADR);
+	v &= ~HD64461_GPADR_SPEAKER;
+	outw(v, HD64461_GPADR);
+
+	/* HP Palmtop 620lx/660lx speaker on */
+	v8 = inb(PKDR);
+	v8 &= ~PKDR_SPEAKER;
+	outb(v8, PKDR);
+
+	sh_dac_enable(pdata->channel);
+}
+
+static void dac_audio_stop(struct dac_audio_pdata *pdata)
+{
+	u16 v;
+	u8 v8;
+
+	/* HP Jornada 680/690 speaker off */
+	v = inw(HD64461_GPADR);
+	v |= HD64461_GPADR_SPEAKER;
+	outw(v, HD64461_GPADR);
+
+	/* HP Palmtop 620lx/660lx speaker off */
+	v8 = inb(PKDR);
+	v8 |= PKDR_SPEAKER;
+	outb(v8, PKDR);
+
+	sh_dac_output(0, pdata->channel);
+	sh_dac_disable(pdata->channel);
+}
+
+static struct dac_audio_pdata dac_audio_platform_data = {
+	.buffer_size		= 64000,
+	.channel		= 1,
+	.start			= dac_audio_start,
+	.stop			= dac_audio_stop,
+};
+
+static struct platform_device dac_audio_device = {
+	.name		= "dac_audio",
+	.id		= -1,
+	.dev		= {
+		.platform_data	= &dac_audio_platform_data,
+	}
+
+};
+
 static struct platform_device *hp6xx_devices[] __initdata = {
 	&cf_ide_device,
 	&jornadakbd_device,
+	&dac_audio_device,
 };
 
 static void __init hp6xx_init_irq(void)
diff --git a/arch/sh/include/mach-common/mach/hp6xx.h b/arch/sh/include/mach-common/mach/hp6xx.h
index 0d4165a..bcc301a 100644
--- a/arch/sh/include/mach-common/mach/hp6xx.h
+++ b/arch/sh/include/mach-common/mach/hp6xx.h
@@ -29,6 +29,9 @@
 
 #define PKDR_LED_GREEN		0x10
 
+/* HP Palmtop 620lx/660lx speaker on/off */
+#define PKDR_SPEAKER		0x20
+
 #define SCPDR_TS_SCAN_ENABLE	0x20
 #define SCPDR_TS_SCAN_Y		0x02
 #define SCPDR_TS_SCAN_X		0x01
@@ -42,6 +45,7 @@
 #define ADC_CHANNEL_BACKUP	4
 #define ADC_CHANNEL_CHARGE	5
 
+/* HP Jornada 680/690 speaker on/off */
 #define HD64461_GPADR_SPEAKER	0x01
 #define HD64461_GPADR_PCMCIA0	(0x02|0x08)
 
diff --git a/include/sound/sh_dac_audio.h b/include/sound/sh_dac_audio.h
new file mode 100644
index 0000000..f5deaf1
--- /dev/null
+++ b/include/sound/sh_dac_audio.h
@@ -0,0 +1,21 @@
+/*
+ * SH_DAC specific configuration, for the dac_audio platform_device
+ *
+ * Copyright (C) 2009 Rafael Ignacio Zurita <rizurita@yahoo.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 __INCLUDE_SH_DAC_AUDIO_H
+#define __INCLUDE_SH_DAC_AUDIO_H
+
+struct dac_audio_pdata {
+	int buffer_size;
+	int channel;
+	void (*start)(struct dac_audio_pdata *pd);
+	void (*stop)(struct dac_audio_pdata *pd);
+};
+
+#endif /* __INCLUDE_SH_DAC_AUDIO_H */
diff --git a/sound/sh/Kconfig b/sound/sh/Kconfig
index aed0f90..61139f3 100644
--- a/sound/sh/Kconfig
+++ b/sound/sh/Kconfig
@@ -19,5 +19,13 @@ config SND_AICA
 	help
 	  ALSA Sound driver for the SEGA Dreamcast console.
 
+config SND_SH_DAC_AUDIO
+	tristate "SuperH DAC audio support"
+	depends on SND
+	depends on CPU_SH3 && HIGH_RES_TIMERS
+	select SND_PCM
+	help
+	  Say Y here to include support for the on-chip DAC.
+
 endif	# SND_SUPERH
 
diff --git a/sound/sh/Makefile b/sound/sh/Makefile
index 8fdcb6e..7d09b51 100644
--- a/sound/sh/Makefile
+++ b/sound/sh/Makefile
@@ -3,6 +3,8 @@
 #
 
 snd-aica-objs := aica.o
+snd-sh_dac_audio-objs := sh_dac_audio.o
 
 # Toplevel Module Dependency
 obj-$(CONFIG_SND_AICA) += snd-aica.o
+obj-$(CONFIG_SND_SH_DAC_AUDIO) += snd-sh_dac_audio.o
diff --git a/sound/sh/sh_dac_audio.c b/sound/sh/sh_dac_audio.c
new file mode 100644
index 0000000..76d9ad2
--- /dev/null
+++ b/sound/sh/sh_dac_audio.c
@@ -0,0 +1,453 @@
+/*
+ * sh_dac_audio.c - SuperH DAC audio driver for ALSA
+ *
+ * Copyright (c) 2009 by Rafael Ignacio Zurita <rizurita@yahoo.com>
+ *
+ *
+ * Based on sh_dac_audio.c (Copyright (C) 2004, 2005 by Andriy Skulysh)
+ *
+ *   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; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <linux/hrtimer.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/sh_dac_audio.h>
+#include <asm/clock.h>
+#include <asm/hd64461.h>
+#include <mach/hp6xx.h>
+#include <cpu/dac.h>
+
+MODULE_AUTHOR("Rafael Ignacio Zurita <rizurita@yahoo.com>");
+MODULE_DESCRIPTION("SuperH DAC audio driver");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{SuperH DAC audio support}}");
+
+/* Module Parameters */
+static int index = SNDRV_DEFAULT_IDX1;
+static char *id = SNDRV_DEFAULT_STR1;
+module_param(index, int, 0444);
+MODULE_PARM_DESC(index, "Index value for SuperH DAC audio.");
+module_param(id, charp, 0444);
+MODULE_PARM_DESC(id, "ID string for SuperH DAC audio.");
+
+/* main struct */
+struct snd_sh_dac {
+	struct snd_card *card;
+	struct snd_pcm_substream *substream;
+	struct hrtimer hrtimer;
+	ktime_t wakeups_per_second;
+
+	int rate;
+	int empty;
+	char *data_buffer, *buffer_begin, *buffer_end;
+	int processed; /* bytes proccesed, to compare with period_size */
+	int buffer_size;
+	struct dac_audio_pdata *pdata;
+};
+
+
+static void dac_audio_start_timer(struct snd_sh_dac *chip)
+{
+	hrtimer_start(&chip->hrtimer, chip->wakeups_per_second,
+		      HRTIMER_MODE_REL);
+}
+
+static void dac_audio_stop_timer(struct snd_sh_dac *chip)
+{
+	hrtimer_cancel(&chip->hrtimer);
+}
+
+static void dac_audio_reset(struct snd_sh_dac *chip)
+{
+	dac_audio_stop_timer(chip);
+	chip->buffer_begin = chip->buffer_end = chip->data_buffer;
+	chip->processed = 0;
+	chip->empty = 1;
+}
+
+static void dac_audio_set_rate(struct snd_sh_dac *chip)
+{
+	chip->wakeups_per_second = ktime_set(0, 1000000000 / chip->rate);
+}
+
+
+/* PCM INTERFACE */
+
+static struct snd_pcm_hardware snd_sh_dac_pcm_hw = {
+	.info			= (SNDRV_PCM_INFO_MMAP |
+					SNDRV_PCM_INFO_MMAP_VALID |
+					SNDRV_PCM_INFO_INTERLEAVED |
+					SNDRV_PCM_INFO_HALF_DUPLEX),
+	.formats		= SNDRV_PCM_FMTBIT_U8,
+	.rates			= SNDRV_PCM_RATE_8000,
+	.rate_min		= 8000,
+	.rate_max		= 8000,
+	.channels_min		= 1,
+	.channels_max		= 1,
+	.buffer_bytes_max	= (48*1024),
+	.period_bytes_min	= 1,
+	.period_bytes_max	= (48*1024),
+	.periods_min		= 1,
+	.periods_max		= 1024,
+};
+
+static int snd_sh_dac_pcm_open(struct snd_pcm_substream *substream)
+{
+	struct snd_sh_dac *chip = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+
+	runtime->hw = snd_sh_dac_pcm_hw;
+
+	chip->substream = substream;
+	chip->buffer_begin = chip->buffer_end = chip->data_buffer;
+	chip->processed = 0;
+	chip->empty = 1;
+
+	chip->pdata->start(chip->pdata);
+
+	return 0;
+}
+
+static int snd_sh_dac_pcm_close(struct snd_pcm_substream *substream)
+{
+	struct snd_sh_dac *chip = snd_pcm_substream_chip(substream);
+
+	chip->substream = NULL;
+
+	dac_audio_stop_timer(chip);
+	chip->pdata->stop(chip->pdata);
+
+	return 0;
+}
+
+static int snd_sh_dac_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 snd_sh_dac_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+	return snd_pcm_lib_free_pages(substream);
+}
+
+static int snd_sh_dac_pcm_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_sh_dac *chip = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = chip->substream->runtime;
+
+	chip->buffer_size = runtime->buffer_size;
+	memset(chip->data_buffer, 0, chip->pdata->buffer_size);
+
+	return 0;
+}
+
+static int snd_sh_dac_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	struct snd_sh_dac *chip = snd_pcm_substream_chip(substream);
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		dac_audio_start_timer(chip);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+		chip->buffer_begin = chip->buffer_end = chip->data_buffer;
+		chip->processed = 0;
+		chip->empty = 1;
+		dac_audio_stop_timer(chip);
+		break;
+	default:
+		 return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int snd_sh_dac_pcm_copy(struct snd_pcm_substream *substream, int channel,
+	snd_pcm_uframes_t pos, void __user *src, snd_pcm_uframes_t count)
+{
+	/* channel is not used (interleaved data) */
+	struct snd_sh_dac *chip = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	ssize_t b_count = frames_to_bytes(runtime , count);
+	ssize_t b_pos = frames_to_bytes(runtime , pos);
+
+	if (count < 0)
+		return -EINVAL;
+
+	if (!count)
+		return 0;
+
+	memcpy_toio(chip->data_buffer + b_pos, src, b_count);
+	chip->buffer_end = chip->data_buffer + b_pos + b_count;
+
+	if (chip->empty) {
+		chip->empty = 0;
+		dac_audio_start_timer(chip);
+	}
+
+	return 0;
+}
+
+static int snd_sh_dac_pcm_silence(struct snd_pcm_substream *substream,
+				  int channel, snd_pcm_uframes_t pos,
+				  snd_pcm_uframes_t count)
+{
+	/* channel is not used (interleaved data) */
+	struct snd_sh_dac *chip = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	ssize_t b_count = frames_to_bytes(runtime , count);
+	ssize_t b_pos = frames_to_bytes(runtime , pos);
+
+	if (count < 0)
+		return -EINVAL;
+
+	if (!count)
+		return 0;
+
+	memset_io(chip->data_buffer + b_pos, 0, b_count);
+	chip->buffer_end = chip->data_buffer + b_pos + b_count;
+
+	if (chip->empty) {
+		chip->empty = 0;
+		dac_audio_start_timer(chip);
+	}
+
+	return 0;
+}
+
+static
+snd_pcm_uframes_t snd_sh_dac_pcm_pointer(struct snd_pcm_substream *substream)
+{
+	struct snd_sh_dac *chip = snd_pcm_substream_chip(substream);
+	int pointer = chip->buffer_begin - chip->data_buffer;
+
+	return pointer;
+}
+
+/* pcm ops */
+static struct snd_pcm_ops snd_sh_dac_pcm_ops = {
+	.open		= snd_sh_dac_pcm_open,
+	.close		= snd_sh_dac_pcm_close,
+	.ioctl		= snd_pcm_lib_ioctl,
+	.hw_params	= snd_sh_dac_pcm_hw_params,
+	.hw_free	= snd_sh_dac_pcm_hw_free,
+	.prepare	= snd_sh_dac_pcm_prepare,
+	.trigger	= snd_sh_dac_pcm_trigger,
+	.pointer	= snd_sh_dac_pcm_pointer,
+	.copy		= snd_sh_dac_pcm_copy,
+	.silence	= snd_sh_dac_pcm_silence,
+	.mmap		= snd_pcm_lib_mmap_iomem,
+};
+
+static int __devinit snd_sh_dac_pcm(struct snd_sh_dac *chip, int device)
+{
+	int err;
+	struct snd_pcm *pcm;
+
+	/* device should be always 0 for us */
+	err = snd_pcm_new(chip->card, "SH_DAC PCM", device, 1, 0, &pcm);
+	if (err < 0)
+		return err;
+
+	pcm->private_data = chip;
+	strcpy(pcm->name, "SH_DAC PCM");
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_sh_dac_pcm_ops);
+
+	/* buffer sizeHK */
+	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
+					  snd_dma_continuous_data(GFP_KERNEL),
+							48 * 1024,
+							48 * 1024);
+
+	return 0;
+}
+/* END OF PCM INTERFACE */
+
+
+/* driver .remove  --  destructor */
+static int snd_sh_dac_remove(struct platform_device *devptr)
+{
+	snd_card_free(platform_get_drvdata(devptr));
+	platform_set_drvdata(devptr, NULL);
+
+	return 0;
+}
+
+/* free -- it has been defined by create */
+static int snd_sh_dac_free(struct snd_sh_dac *chip)
+{
+	/* release the data */
+	kfree(chip->data_buffer);
+	kfree(chip);
+
+	return 0;
+}
+
+static int snd_sh_dac_dev_free(struct snd_device *device)
+{
+	struct snd_sh_dac *chip = device->device_data;
+
+	return snd_sh_dac_free(chip);
+}
+
+static enum hrtimer_restart sh_dac_audio_timer(struct hrtimer *handle)
+{
+	struct snd_sh_dac *chip = container_of(handle, struct snd_sh_dac,
+					       hrtimer);
+	struct snd_pcm_runtime *runtime = chip->substream->runtime;
+	ssize_t b_ps = frames_to_bytes(runtime, runtime->period_size);
+
+	if (!chip->empty) {
+		sh_dac_output(*chip->buffer_begin, chip->pdata->channel);
+		chip->buffer_begin++;
+
+		chip->processed++;
+		if (chip->processed >= b_ps) {
+			chip->processed -= b_ps;
+			snd_pcm_period_elapsed(chip->substream);
+		}
+
+		if (chip->buffer_begin = (chip->data_buffer +
+					   chip->buffer_size - 1))
+			chip->buffer_begin = chip->data_buffer;
+
+		if (chip->buffer_begin = chip->buffer_end)
+			chip->empty = 1;
+
+	}
+
+	if (!chip->empty)
+		hrtimer_start(&chip->hrtimer, chip->wakeups_per_second,
+			      HRTIMER_MODE_REL);
+
+	return HRTIMER_NORESTART;
+}
+
+/* create  --  chip-specific constructor for the cards components */
+static int __devinit snd_sh_dac_create(struct snd_card *card,
+				       struct platform_device *devptr,
+				       struct snd_sh_dac **rchip)
+{
+	struct snd_sh_dac *chip;
+	int err;
+
+	static struct snd_device_ops ops = {
+		   .dev_free = snd_sh_dac_dev_free,
+	};
+
+	*rchip = NULL;
+
+	chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+	if (chip = NULL)
+		return -ENOMEM;
+
+	chip->card = card;
+
+	hrtimer_init(&chip->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+	chip->hrtimer.function = sh_dac_audio_timer;
+
+	dac_audio_reset(chip);
+	chip->rate = 8000;
+	dac_audio_set_rate(chip);
+
+	chip->pdata = devptr->dev.platform_data;
+
+	chip->data_buffer = kmalloc(chip->pdata->buffer_size, GFP_KERNEL);
+	if (chip->data_buffer = NULL) {
+		kfree(chip);
+		return -ENOMEM;
+	}
+
+	err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
+	if (err < 0) {
+		snd_sh_dac_free(chip);
+		return err;
+	}
+
+	*rchip = chip;
+
+	return 0;
+}
+
+/* driver .probe  --  constructor */
+static int __devinit snd_sh_dac_probe(struct platform_device *devptr)
+{
+	struct snd_sh_dac *chip;
+	struct snd_card *card;
+	int err;
+
+	err = snd_card_create(index, id, THIS_MODULE, 0, &card);
+	if (err < 0) {
+			snd_printk(KERN_ERR "cannot allocate the card\n");
+			return err;
+	}
+
+	err = snd_sh_dac_create(card, devptr, &chip);
+	if (err < 0)
+		goto probe_error;
+
+	err = snd_sh_dac_pcm(chip, 0);
+	if (err < 0)
+		goto probe_error;
+
+	strcpy(card->driver, "snd_sh_dac");
+	strcpy(card->shortname, "SuperH DAC audio driver");
+	printk(KERN_INFO "%s %s", card->longname, card->shortname);
+
+	err = snd_card_register(card);
+	if (err < 0)
+		goto probe_error;
+
+	snd_printk("ALSA driver for SuperH DAC audio");
+
+	platform_set_drvdata(devptr, card);
+	return 0;
+
+probe_error:
+	snd_card_free(card);
+	return err;
+}
+
+/*
+ * "driver" definition
+ */
+static struct platform_driver driver = {
+	.probe	= snd_sh_dac_probe,
+	.remove = snd_sh_dac_remove,
+	.driver = {
+		.name = "dac_audio",
+	},
+};
+
+static int __init sh_dac_init(void)
+{
+	return platform_driver_register(&driver);
+}
+
+static void __exit sh_dac_exit(void)
+{
+	platform_driver_unregister(&driver);
+}
+
+module_init(sh_dac_init);
+module_exit(sh_dac_exit);

^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH] sh: add SuperH DAC audio driver for ALSA V4
@ 2009-11-03 20:16   ` Rafael Ignacio Zurita
  0 siblings, 0 replies; 21+ messages in thread
From: Rafael Ignacio Zurita @ 2009-11-03 20:16 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: Paul Mundt, linux-sh, alsa-devel

This is a port of the sound/oss/sh_dac_audio.c driver.
The driver uses an on-chip 8-bit D/A converter, which has a speaker connected
to one of its channels, found in several ancient HP machines.
For interrupts it uses a high-resolution timer (hrtimer).
Tested on SH7709 based hp6xx (HP Jornada 680/690 and HP Palmtop 620lx/660lx).
 
Also, since OSS Emulation works, the old OSS sound/oss/sh_dac_audio.c driver
would be obsolete soon, and it could be removed.
 
Changes since V3:
 - renamed sound/sh/snd_sh_dac_audio.c to sound/sh/sh_dac_audio.c
 
Changes since V2:
 - renamed include/sound/dac_audio.h to include/sound/sh_dac_audio.h

Changes since V1:
 - moved the platform device code into board code.
 - dropped dac_audio_sync(), since that code is not needed (it was inherited of
   the old oss driver).
 - rewrote the code with style problems.

Signed-off-by: Rafael Ignacio Zurita <rizurita@yahoo.com>
---
 arch/sh/boards/mach-hp6xx/setup.c        |   55 ++++
 arch/sh/include/mach-common/mach/hp6xx.h |    4 +
 include/sound/sh_dac_audio.h             |   21 ++
 sound/sh/Kconfig                         |    8 +
 sound/sh/Makefile                        |    2 +
 sound/sh/sh_dac_audio.c                  |  453 ++++++++++++++++++++++++++++++
 6 files changed, 543 insertions(+), 0 deletions(-)

diff --git a/arch/sh/boards/mach-hp6xx/setup.c b/arch/sh/boards/mach-hp6xx/setup.c
index 8f305b3..e6dd5e9 100644
--- a/arch/sh/boards/mach-hp6xx/setup.c
+++ b/arch/sh/boards/mach-hp6xx/setup.c
@@ -13,6 +13,7 @@
 #include <linux/init.h>
 #include <linux/platform_device.h>
 #include <linux/irq.h>
+#include <sound/sh_dac_audio.h>
 #include <asm/hd64461.h>
 #include <asm/io.h>
 #include <mach/hp6xx.h>
@@ -51,9 +52,63 @@ static struct platform_device jornadakbd_device = {
 	.id		= -1,
 };
 
+static void dac_audio_start(struct dac_audio_pdata *pdata)
+{
+	u16 v;
+	u8 v8;
+
+	/* HP Jornada 680/690 speaker on */
+	v = inw(HD64461_GPADR);
+	v &= ~HD64461_GPADR_SPEAKER;
+	outw(v, HD64461_GPADR);
+
+	/* HP Palmtop 620lx/660lx speaker on */
+	v8 = inb(PKDR);
+	v8 &= ~PKDR_SPEAKER;
+	outb(v8, PKDR);
+
+	sh_dac_enable(pdata->channel);
+}
+
+static void dac_audio_stop(struct dac_audio_pdata *pdata)
+{
+	u16 v;
+	u8 v8;
+
+	/* HP Jornada 680/690 speaker off */
+	v = inw(HD64461_GPADR);
+	v |= HD64461_GPADR_SPEAKER;
+	outw(v, HD64461_GPADR);
+
+	/* HP Palmtop 620lx/660lx speaker off */
+	v8 = inb(PKDR);
+	v8 |= PKDR_SPEAKER;
+	outb(v8, PKDR);
+
+	sh_dac_output(0, pdata->channel);
+	sh_dac_disable(pdata->channel);
+}
+
+static struct dac_audio_pdata dac_audio_platform_data = {
+	.buffer_size		= 64000,
+	.channel		= 1,
+	.start			= dac_audio_start,
+	.stop			= dac_audio_stop,
+};
+
+static struct platform_device dac_audio_device = {
+	.name		= "dac_audio",
+	.id		= -1,
+	.dev		= {
+		.platform_data	= &dac_audio_platform_data,
+	}
+
+};
+
 static struct platform_device *hp6xx_devices[] __initdata = {
 	&cf_ide_device,
 	&jornadakbd_device,
+	&dac_audio_device,
 };
 
 static void __init hp6xx_init_irq(void)
diff --git a/arch/sh/include/mach-common/mach/hp6xx.h b/arch/sh/include/mach-common/mach/hp6xx.h
index 0d4165a..bcc301a 100644
--- a/arch/sh/include/mach-common/mach/hp6xx.h
+++ b/arch/sh/include/mach-common/mach/hp6xx.h
@@ -29,6 +29,9 @@
 
 #define PKDR_LED_GREEN		0x10
 
+/* HP Palmtop 620lx/660lx speaker on/off */
+#define PKDR_SPEAKER		0x20
+
 #define SCPDR_TS_SCAN_ENABLE	0x20
 #define SCPDR_TS_SCAN_Y		0x02
 #define SCPDR_TS_SCAN_X		0x01
@@ -42,6 +45,7 @@
 #define ADC_CHANNEL_BACKUP	4
 #define ADC_CHANNEL_CHARGE	5
 
+/* HP Jornada 680/690 speaker on/off */
 #define HD64461_GPADR_SPEAKER	0x01
 #define HD64461_GPADR_PCMCIA0	(0x02|0x08)
 
diff --git a/include/sound/sh_dac_audio.h b/include/sound/sh_dac_audio.h
new file mode 100644
index 0000000..f5deaf1
--- /dev/null
+++ b/include/sound/sh_dac_audio.h
@@ -0,0 +1,21 @@
+/*
+ * SH_DAC specific configuration, for the dac_audio platform_device
+ *
+ * Copyright (C) 2009 Rafael Ignacio Zurita <rizurita@yahoo.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 __INCLUDE_SH_DAC_AUDIO_H
+#define __INCLUDE_SH_DAC_AUDIO_H
+
+struct dac_audio_pdata {
+	int buffer_size;
+	int channel;
+	void (*start)(struct dac_audio_pdata *pd);
+	void (*stop)(struct dac_audio_pdata *pd);
+};
+
+#endif /* __INCLUDE_SH_DAC_AUDIO_H */
diff --git a/sound/sh/Kconfig b/sound/sh/Kconfig
index aed0f90..61139f3 100644
--- a/sound/sh/Kconfig
+++ b/sound/sh/Kconfig
@@ -19,5 +19,13 @@ config SND_AICA
 	help
 	  ALSA Sound driver for the SEGA Dreamcast console.
 
+config SND_SH_DAC_AUDIO
+	tristate "SuperH DAC audio support"
+	depends on SND
+	depends on CPU_SH3 && HIGH_RES_TIMERS
+	select SND_PCM
+	help
+	  Say Y here to include support for the on-chip DAC.
+
 endif	# SND_SUPERH
 
diff --git a/sound/sh/Makefile b/sound/sh/Makefile
index 8fdcb6e..7d09b51 100644
--- a/sound/sh/Makefile
+++ b/sound/sh/Makefile
@@ -3,6 +3,8 @@
 #
 
 snd-aica-objs := aica.o
+snd-sh_dac_audio-objs := sh_dac_audio.o
 
 # Toplevel Module Dependency
 obj-$(CONFIG_SND_AICA) += snd-aica.o
+obj-$(CONFIG_SND_SH_DAC_AUDIO) += snd-sh_dac_audio.o
diff --git a/sound/sh/sh_dac_audio.c b/sound/sh/sh_dac_audio.c
new file mode 100644
index 0000000..76d9ad2
--- /dev/null
+++ b/sound/sh/sh_dac_audio.c
@@ -0,0 +1,453 @@
+/*
+ * sh_dac_audio.c - SuperH DAC audio driver for ALSA
+ *
+ * Copyright (c) 2009 by Rafael Ignacio Zurita <rizurita@yahoo.com>
+ *
+ *
+ * Based on sh_dac_audio.c (Copyright (C) 2004, 2005 by Andriy Skulysh)
+ *
+ *   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; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <linux/hrtimer.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/sh_dac_audio.h>
+#include <asm/clock.h>
+#include <asm/hd64461.h>
+#include <mach/hp6xx.h>
+#include <cpu/dac.h>
+
+MODULE_AUTHOR("Rafael Ignacio Zurita <rizurita@yahoo.com>");
+MODULE_DESCRIPTION("SuperH DAC audio driver");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{SuperH DAC audio support}}");
+
+/* Module Parameters */
+static int index = SNDRV_DEFAULT_IDX1;
+static char *id = SNDRV_DEFAULT_STR1;
+module_param(index, int, 0444);
+MODULE_PARM_DESC(index, "Index value for SuperH DAC audio.");
+module_param(id, charp, 0444);
+MODULE_PARM_DESC(id, "ID string for SuperH DAC audio.");
+
+/* main struct */
+struct snd_sh_dac {
+	struct snd_card *card;
+	struct snd_pcm_substream *substream;
+	struct hrtimer hrtimer;
+	ktime_t wakeups_per_second;
+
+	int rate;
+	int empty;
+	char *data_buffer, *buffer_begin, *buffer_end;
+	int processed; /* bytes proccesed, to compare with period_size */
+	int buffer_size;
+	struct dac_audio_pdata *pdata;
+};
+
+
+static void dac_audio_start_timer(struct snd_sh_dac *chip)
+{
+	hrtimer_start(&chip->hrtimer, chip->wakeups_per_second,
+		      HRTIMER_MODE_REL);
+}
+
+static void dac_audio_stop_timer(struct snd_sh_dac *chip)
+{
+	hrtimer_cancel(&chip->hrtimer);
+}
+
+static void dac_audio_reset(struct snd_sh_dac *chip)
+{
+	dac_audio_stop_timer(chip);
+	chip->buffer_begin = chip->buffer_end = chip->data_buffer;
+	chip->processed = 0;
+	chip->empty = 1;
+}
+
+static void dac_audio_set_rate(struct snd_sh_dac *chip)
+{
+	chip->wakeups_per_second = ktime_set(0, 1000000000 / chip->rate);
+}
+
+
+/* PCM INTERFACE */
+
+static struct snd_pcm_hardware snd_sh_dac_pcm_hw = {
+	.info			= (SNDRV_PCM_INFO_MMAP |
+					SNDRV_PCM_INFO_MMAP_VALID |
+					SNDRV_PCM_INFO_INTERLEAVED |
+					SNDRV_PCM_INFO_HALF_DUPLEX),
+	.formats		= SNDRV_PCM_FMTBIT_U8,
+	.rates			= SNDRV_PCM_RATE_8000,
+	.rate_min		= 8000,
+	.rate_max		= 8000,
+	.channels_min		= 1,
+	.channels_max		= 1,
+	.buffer_bytes_max	= (48*1024),
+	.period_bytes_min	= 1,
+	.period_bytes_max	= (48*1024),
+	.periods_min		= 1,
+	.periods_max		= 1024,
+};
+
+static int snd_sh_dac_pcm_open(struct snd_pcm_substream *substream)
+{
+	struct snd_sh_dac *chip = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+
+	runtime->hw = snd_sh_dac_pcm_hw;
+
+	chip->substream = substream;
+	chip->buffer_begin = chip->buffer_end = chip->data_buffer;
+	chip->processed = 0;
+	chip->empty = 1;
+
+	chip->pdata->start(chip->pdata);
+
+	return 0;
+}
+
+static int snd_sh_dac_pcm_close(struct snd_pcm_substream *substream)
+{
+	struct snd_sh_dac *chip = snd_pcm_substream_chip(substream);
+
+	chip->substream = NULL;
+
+	dac_audio_stop_timer(chip);
+	chip->pdata->stop(chip->pdata);
+
+	return 0;
+}
+
+static int snd_sh_dac_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 snd_sh_dac_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+	return snd_pcm_lib_free_pages(substream);
+}
+
+static int snd_sh_dac_pcm_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_sh_dac *chip = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = chip->substream->runtime;
+
+	chip->buffer_size = runtime->buffer_size;
+	memset(chip->data_buffer, 0, chip->pdata->buffer_size);
+
+	return 0;
+}
+
+static int snd_sh_dac_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	struct snd_sh_dac *chip = snd_pcm_substream_chip(substream);
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		dac_audio_start_timer(chip);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+		chip->buffer_begin = chip->buffer_end = chip->data_buffer;
+		chip->processed = 0;
+		chip->empty = 1;
+		dac_audio_stop_timer(chip);
+		break;
+	default:
+		 return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int snd_sh_dac_pcm_copy(struct snd_pcm_substream *substream, int channel,
+	snd_pcm_uframes_t pos, void __user *src, snd_pcm_uframes_t count)
+{
+	/* channel is not used (interleaved data) */
+	struct snd_sh_dac *chip = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	ssize_t b_count = frames_to_bytes(runtime , count);
+	ssize_t b_pos = frames_to_bytes(runtime , pos);
+
+	if (count < 0)
+		return -EINVAL;
+
+	if (!count)
+		return 0;
+
+	memcpy_toio(chip->data_buffer + b_pos, src, b_count);
+	chip->buffer_end = chip->data_buffer + b_pos + b_count;
+
+	if (chip->empty) {
+		chip->empty = 0;
+		dac_audio_start_timer(chip);
+	}
+
+	return 0;
+}
+
+static int snd_sh_dac_pcm_silence(struct snd_pcm_substream *substream,
+				  int channel, snd_pcm_uframes_t pos,
+				  snd_pcm_uframes_t count)
+{
+	/* channel is not used (interleaved data) */
+	struct snd_sh_dac *chip = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	ssize_t b_count = frames_to_bytes(runtime , count);
+	ssize_t b_pos = frames_to_bytes(runtime , pos);
+
+	if (count < 0)
+		return -EINVAL;
+
+	if (!count)
+		return 0;
+
+	memset_io(chip->data_buffer + b_pos, 0, b_count);
+	chip->buffer_end = chip->data_buffer + b_pos + b_count;
+
+	if (chip->empty) {
+		chip->empty = 0;
+		dac_audio_start_timer(chip);
+	}
+
+	return 0;
+}
+
+static
+snd_pcm_uframes_t snd_sh_dac_pcm_pointer(struct snd_pcm_substream *substream)
+{
+	struct snd_sh_dac *chip = snd_pcm_substream_chip(substream);
+	int pointer = chip->buffer_begin - chip->data_buffer;
+
+	return pointer;
+}
+
+/* pcm ops */
+static struct snd_pcm_ops snd_sh_dac_pcm_ops = {
+	.open		= snd_sh_dac_pcm_open,
+	.close		= snd_sh_dac_pcm_close,
+	.ioctl		= snd_pcm_lib_ioctl,
+	.hw_params	= snd_sh_dac_pcm_hw_params,
+	.hw_free	= snd_sh_dac_pcm_hw_free,
+	.prepare	= snd_sh_dac_pcm_prepare,
+	.trigger	= snd_sh_dac_pcm_trigger,
+	.pointer	= snd_sh_dac_pcm_pointer,
+	.copy		= snd_sh_dac_pcm_copy,
+	.silence	= snd_sh_dac_pcm_silence,
+	.mmap		= snd_pcm_lib_mmap_iomem,
+};
+
+static int __devinit snd_sh_dac_pcm(struct snd_sh_dac *chip, int device)
+{
+	int err;
+	struct snd_pcm *pcm;
+
+	/* device should be always 0 for us */
+	err = snd_pcm_new(chip->card, "SH_DAC PCM", device, 1, 0, &pcm);
+	if (err < 0)
+		return err;
+
+	pcm->private_data = chip;
+	strcpy(pcm->name, "SH_DAC PCM");
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_sh_dac_pcm_ops);
+
+	/* buffer size=48K */
+	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
+					  snd_dma_continuous_data(GFP_KERNEL),
+							48 * 1024,
+							48 * 1024);
+
+	return 0;
+}
+/* END OF PCM INTERFACE */
+
+
+/* driver .remove  --  destructor */
+static int snd_sh_dac_remove(struct platform_device *devptr)
+{
+	snd_card_free(platform_get_drvdata(devptr));
+	platform_set_drvdata(devptr, NULL);
+
+	return 0;
+}
+
+/* free -- it has been defined by create */
+static int snd_sh_dac_free(struct snd_sh_dac *chip)
+{
+	/* release the data */
+	kfree(chip->data_buffer);
+	kfree(chip);
+
+	return 0;
+}
+
+static int snd_sh_dac_dev_free(struct snd_device *device)
+{
+	struct snd_sh_dac *chip = device->device_data;
+
+	return snd_sh_dac_free(chip);
+}
+
+static enum hrtimer_restart sh_dac_audio_timer(struct hrtimer *handle)
+{
+	struct snd_sh_dac *chip = container_of(handle, struct snd_sh_dac,
+					       hrtimer);
+	struct snd_pcm_runtime *runtime = chip->substream->runtime;
+	ssize_t b_ps = frames_to_bytes(runtime, runtime->period_size);
+
+	if (!chip->empty) {
+		sh_dac_output(*chip->buffer_begin, chip->pdata->channel);
+		chip->buffer_begin++;
+
+		chip->processed++;
+		if (chip->processed >= b_ps) {
+			chip->processed -= b_ps;
+			snd_pcm_period_elapsed(chip->substream);
+		}
+
+		if (chip->buffer_begin == (chip->data_buffer +
+					   chip->buffer_size - 1))
+			chip->buffer_begin = chip->data_buffer;
+
+		if (chip->buffer_begin == chip->buffer_end)
+			chip->empty = 1;
+
+	}
+
+	if (!chip->empty)
+		hrtimer_start(&chip->hrtimer, chip->wakeups_per_second,
+			      HRTIMER_MODE_REL);
+
+	return HRTIMER_NORESTART;
+}
+
+/* create  --  chip-specific constructor for the cards components */
+static int __devinit snd_sh_dac_create(struct snd_card *card,
+				       struct platform_device *devptr,
+				       struct snd_sh_dac **rchip)
+{
+	struct snd_sh_dac *chip;
+	int err;
+
+	static struct snd_device_ops ops = {
+		   .dev_free = snd_sh_dac_dev_free,
+	};
+
+	*rchip = NULL;
+
+	chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+	if (chip == NULL)
+		return -ENOMEM;
+
+	chip->card = card;
+
+	hrtimer_init(&chip->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+	chip->hrtimer.function = sh_dac_audio_timer;
+
+	dac_audio_reset(chip);
+	chip->rate = 8000;
+	dac_audio_set_rate(chip);
+
+	chip->pdata = devptr->dev.platform_data;
+
+	chip->data_buffer = kmalloc(chip->pdata->buffer_size, GFP_KERNEL);
+	if (chip->data_buffer == NULL) {
+		kfree(chip);
+		return -ENOMEM;
+	}
+
+	err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
+	if (err < 0) {
+		snd_sh_dac_free(chip);
+		return err;
+	}
+
+	*rchip = chip;
+
+	return 0;
+}
+
+/* driver .probe  --  constructor */
+static int __devinit snd_sh_dac_probe(struct platform_device *devptr)
+{
+	struct snd_sh_dac *chip;
+	struct snd_card *card;
+	int err;
+
+	err = snd_card_create(index, id, THIS_MODULE, 0, &card);
+	if (err < 0) {
+			snd_printk(KERN_ERR "cannot allocate the card\n");
+			return err;
+	}
+
+	err = snd_sh_dac_create(card, devptr, &chip);
+	if (err < 0)
+		goto probe_error;
+
+	err = snd_sh_dac_pcm(chip, 0);
+	if (err < 0)
+		goto probe_error;
+
+	strcpy(card->driver, "snd_sh_dac");
+	strcpy(card->shortname, "SuperH DAC audio driver");
+	printk(KERN_INFO "%s %s", card->longname, card->shortname);
+
+	err = snd_card_register(card);
+	if (err < 0)
+		goto probe_error;
+
+	snd_printk("ALSA driver for SuperH DAC audio");
+
+	platform_set_drvdata(devptr, card);
+	return 0;
+
+probe_error:
+	snd_card_free(card);
+	return err;
+}
+
+/*
+ * "driver" definition
+ */
+static struct platform_driver driver = {
+	.probe	= snd_sh_dac_probe,
+	.remove = snd_sh_dac_remove,
+	.driver = {
+		.name = "dac_audio",
+	},
+};
+
+static int __init sh_dac_init(void)
+{
+	return platform_driver_register(&driver);
+}
+
+static void __exit sh_dac_exit(void)
+{
+	platform_driver_unregister(&driver);
+}
+
+module_init(sh_dac_init);
+module_exit(sh_dac_exit);

^ permalink raw reply related	[flat|nested] 21+ messages in thread

* Re: [PATCH] sh: add SuperH DAC audio driver for ALSA V4
  2009-11-03 20:16   ` Rafael Ignacio Zurita
@ 2009-11-04  3:13     ` Paul Mundt
  -1 siblings, 0 replies; 21+ messages in thread
From: Paul Mundt @ 2009-11-04  3:13 UTC (permalink / raw)
  To: Rafael Ignacio Zurita; +Cc: Takashi Iwai, linux-sh, alsa-devel

On Tue, Nov 03, 2009 at 05:16:27PM -0300, Rafael Ignacio Zurita wrote:
> This is a port of the sound/oss/sh_dac_audio.c driver.
> The driver uses an on-chip 8-bit D/A converter, which has a speaker connected
> to one of its channels, found in several ancient HP machines.
> For interrupts it uses a high-resolution timer (hrtimer).
> Tested on SH7709 based hp6xx (HP Jornada 680/690 and HP Palmtop 620lx/660lx).
>  
> Also, since OSS Emulation works, the old OSS sound/oss/sh_dac_audio.c driver
> would be obsolete soon, and it could be removed.
>  
> Changes since V3:
>  - renamed sound/sh/snd_sh_dac_audio.c to sound/sh/sh_dac_audio.c
>  
> Changes since V2:
>  - renamed include/sound/dac_audio.h to include/sound/sh_dac_audio.h
> 
> Changes since V1:
>  - moved the platform device code into board code.
>  - dropped dac_audio_sync(), since that code is not needed (it was inherited of
>    the old oss driver).
>  - rewrote the code with style problems.
> 
> Signed-off-by: Rafael Ignacio Zurita <rizurita@yahoo.com>

Acked-by: Paul Mundt <lethal@linux-sh.org>

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH] sh: add SuperH DAC audio driver for ALSA V4
@ 2009-11-04  3:13     ` Paul Mundt
  0 siblings, 0 replies; 21+ messages in thread
From: Paul Mundt @ 2009-11-04  3:13 UTC (permalink / raw)
  To: Rafael Ignacio Zurita; +Cc: Takashi Iwai, linux-sh, alsa-devel

On Tue, Nov 03, 2009 at 05:16:27PM -0300, Rafael Ignacio Zurita wrote:
> This is a port of the sound/oss/sh_dac_audio.c driver.
> The driver uses an on-chip 8-bit D/A converter, which has a speaker connected
> to one of its channels, found in several ancient HP machines.
> For interrupts it uses a high-resolution timer (hrtimer).
> Tested on SH7709 based hp6xx (HP Jornada 680/690 and HP Palmtop 620lx/660lx).
>  
> Also, since OSS Emulation works, the old OSS sound/oss/sh_dac_audio.c driver
> would be obsolete soon, and it could be removed.
>  
> Changes since V3:
>  - renamed sound/sh/snd_sh_dac_audio.c to sound/sh/sh_dac_audio.c
>  
> Changes since V2:
>  - renamed include/sound/dac_audio.h to include/sound/sh_dac_audio.h
> 
> Changes since V1:
>  - moved the platform device code into board code.
>  - dropped dac_audio_sync(), since that code is not needed (it was inherited of
>    the old oss driver).
>  - rewrote the code with style problems.
> 
> Signed-off-by: Rafael Ignacio Zurita <rizurita@yahoo.com>

Acked-by: Paul Mundt <lethal@linux-sh.org>

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [alsa-devel] [PATCH] sh: add SuperH DAC audio driver for ALSA V4
  2009-11-03 20:16   ` Rafael Ignacio Zurita
@ 2009-11-04  8:19     ` Takashi Iwai
  -1 siblings, 0 replies; 21+ messages in thread
From: Takashi Iwai @ 2009-11-04  8:19 UTC (permalink / raw)
  To: Rafael Ignacio Zurita; +Cc: alsa-devel, Paul Mundt, linux-sh

At Tue, 3 Nov 2009 17:16:27 -0300,
Rafael Ignacio Zurita wrote:
> 
> This is a port of the sound/oss/sh_dac_audio.c driver.
> The driver uses an on-chip 8-bit D/A converter, which has a speaker connected
> to one of its channels, found in several ancient HP machines.
> For interrupts it uses a high-resolution timer (hrtimer).
> Tested on SH7709 based hp6xx (HP Jornada 680/690 and HP Palmtop 620lx/660lx).
>  
> Also, since OSS Emulation works, the old OSS sound/oss/sh_dac_audio.c driver
> would be obsolete soon, and it could be removed.
>  
> Changes since V3:
>  - renamed sound/sh/snd_sh_dac_audio.c to sound/sh/sh_dac_audio.c
>  
> Changes since V2:
>  - renamed include/sound/dac_audio.h to include/sound/sh_dac_audio.h
> 
> Changes since V1:
>  - moved the platform device code into board code.
>  - dropped dac_audio_sync(), since that code is not needed (it was inherited of
>    the old oss driver).
>  - rewrote the code with style problems.
> 
> Signed-off-by: Rafael Ignacio Zurita <rizurita@yahoo.com>

Thanks!  Applied now to sound git tree.


Takashi

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [alsa-devel] [PATCH] sh: add SuperH DAC audio driver for ALSA V4
@ 2009-11-04  8:19     ` Takashi Iwai
  0 siblings, 0 replies; 21+ messages in thread
From: Takashi Iwai @ 2009-11-04  8:19 UTC (permalink / raw)
  To: Rafael Ignacio Zurita; +Cc: alsa-devel, Paul Mundt, linux-sh

At Tue, 3 Nov 2009 17:16:27 -0300,
Rafael Ignacio Zurita wrote:
> 
> This is a port of the sound/oss/sh_dac_audio.c driver.
> The driver uses an on-chip 8-bit D/A converter, which has a speaker connected
> to one of its channels, found in several ancient HP machines.
> For interrupts it uses a high-resolution timer (hrtimer).
> Tested on SH7709 based hp6xx (HP Jornada 680/690 and HP Palmtop 620lx/660lx).
>  
> Also, since OSS Emulation works, the old OSS sound/oss/sh_dac_audio.c driver
> would be obsolete soon, and it could be removed.
>  
> Changes since V3:
>  - renamed sound/sh/snd_sh_dac_audio.c to sound/sh/sh_dac_audio.c
>  
> Changes since V2:
>  - renamed include/sound/dac_audio.h to include/sound/sh_dac_audio.h
> 
> Changes since V1:
>  - moved the platform device code into board code.
>  - dropped dac_audio_sync(), since that code is not needed (it was inherited of
>    the old oss driver).
>  - rewrote the code with style problems.
> 
> Signed-off-by: Rafael Ignacio Zurita <rizurita@yahoo.com>

Thanks!  Applied now to sound git tree.


Takashi

^ permalink raw reply	[flat|nested] 21+ messages in thread

end of thread, other threads:[~2009-11-04  8:19 UTC | newest]

Thread overview: 21+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-10-08  1:34 [PATCH] sh: add SuperH DAC audio driver for ALSA Rafael Ignacio Zurita
2009-10-08  8:38 ` Kristoffer Ericson
2009-10-09  1:22 ` Paul Mundt
2009-10-16 15:22 ` Rafael Ignacio Zurita
2009-10-19  7:01 ` Paul Mundt
2009-10-21  1:38 ` [PATCH] sh: add SuperH DAC audio driver for ALSA V2 Rafael Ignacio Zurita
2009-10-22  1:56 ` Paul Mundt
2009-10-22 20:25 ` [PATCH] sh: add SuperH DAC audio driver for ALSA V3 Rafael Ignacio Zurita
2009-10-22 20:25   ` Rafael Ignacio Zurita
2009-10-26  0:31   ` Paul Mundt
2009-10-26  0:31     ` Paul Mundt
2009-10-30 11:24   ` Takashi Iwai
2009-10-30 11:59     ` Paul Mundt
2009-11-02 17:03       ` Rafael Ignacio Zurita
2009-11-03  6:01         ` Takashi Iwai
2009-11-03 20:16 ` [PATCH] sh: add SuperH DAC audio driver for ALSA V4 Rafael Ignacio Zurita
2009-11-03 20:16   ` Rafael Ignacio Zurita
2009-11-04  3:13   ` Paul Mundt
2009-11-04  3:13     ` Paul Mundt
2009-11-04  8:19   ` [alsa-devel] " Takashi Iwai
2009-11-04  8:19     ` Takashi Iwai

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.