Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ALSA: sh: add SuperH DAC audio driver for ALSA V4

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.

Signed-off-by: Rafael Ignacio Zurita <rizurita@yahoo.com>
Acked-by: Paul Mundt <lethal@linux-sh.org>
Signed-off-by: Takashi Iwai <tiwai@suse.de>

authored by

Rafael Ignacio Zurita and committed by
Takashi Iwai
9dcaa7b2 bcc2c6b7

+543
+55
arch/sh/boards/mach-hp6xx/setup.c
··· 13 13 #include <linux/init.h> 14 14 #include <linux/platform_device.h> 15 15 #include <linux/irq.h> 16 + #include <sound/sh_dac_audio.h> 16 17 #include <asm/hd64461.h> 17 18 #include <asm/io.h> 18 19 #include <mach/hp6xx.h> ··· 52 51 .id = -1, 53 52 }; 54 53 54 + static void dac_audio_start(struct dac_audio_pdata *pdata) 55 + { 56 + u16 v; 57 + u8 v8; 58 + 59 + /* HP Jornada 680/690 speaker on */ 60 + v = inw(HD64461_GPADR); 61 + v &= ~HD64461_GPADR_SPEAKER; 62 + outw(v, HD64461_GPADR); 63 + 64 + /* HP Palmtop 620lx/660lx speaker on */ 65 + v8 = inb(PKDR); 66 + v8 &= ~PKDR_SPEAKER; 67 + outb(v8, PKDR); 68 + 69 + sh_dac_enable(pdata->channel); 70 + } 71 + 72 + static void dac_audio_stop(struct dac_audio_pdata *pdata) 73 + { 74 + u16 v; 75 + u8 v8; 76 + 77 + /* HP Jornada 680/690 speaker off */ 78 + v = inw(HD64461_GPADR); 79 + v |= HD64461_GPADR_SPEAKER; 80 + outw(v, HD64461_GPADR); 81 + 82 + /* HP Palmtop 620lx/660lx speaker off */ 83 + v8 = inb(PKDR); 84 + v8 |= PKDR_SPEAKER; 85 + outb(v8, PKDR); 86 + 87 + sh_dac_output(0, pdata->channel); 88 + sh_dac_disable(pdata->channel); 89 + } 90 + 91 + static struct dac_audio_pdata dac_audio_platform_data = { 92 + .buffer_size = 64000, 93 + .channel = 1, 94 + .start = dac_audio_start, 95 + .stop = dac_audio_stop, 96 + }; 97 + 98 + static struct platform_device dac_audio_device = { 99 + .name = "dac_audio", 100 + .id = -1, 101 + .dev = { 102 + .platform_data = &dac_audio_platform_data, 103 + } 104 + 105 + }; 106 + 55 107 static struct platform_device *hp6xx_devices[] __initdata = { 56 108 &cf_ide_device, 57 109 &jornadakbd_device, 110 + &dac_audio_device, 58 111 }; 59 112 60 113 static void __init hp6xx_init_irq(void)
+4
arch/sh/include/mach-common/mach/hp6xx.h
··· 29 29 30 30 #define PKDR_LED_GREEN 0x10 31 31 32 + /* HP Palmtop 620lx/660lx speaker on/off */ 33 + #define PKDR_SPEAKER 0x20 34 + 32 35 #define SCPDR_TS_SCAN_ENABLE 0x20 33 36 #define SCPDR_TS_SCAN_Y 0x02 34 37 #define SCPDR_TS_SCAN_X 0x01 ··· 45 42 #define ADC_CHANNEL_BACKUP 4 46 43 #define ADC_CHANNEL_CHARGE 5 47 44 45 + /* HP Jornada 680/690 speaker on/off */ 48 46 #define HD64461_GPADR_SPEAKER 0x01 49 47 #define HD64461_GPADR_PCMCIA0 (0x02|0x08) 50 48
+21
include/sound/sh_dac_audio.h
··· 1 + /* 2 + * SH_DAC specific configuration, for the dac_audio platform_device 3 + * 4 + * Copyright (C) 2009 Rafael Ignacio Zurita <rizurita@yahoo.com> 5 + * 6 + * This program is free software; you can redistribute it and/or modify it 7 + * under the terms of the GNU General Public License version 2 as published 8 + * by the Free Software Foundation. 9 + */ 10 + 11 + #ifndef __INCLUDE_SH_DAC_AUDIO_H 12 + #define __INCLUDE_SH_DAC_AUDIO_H 13 + 14 + struct dac_audio_pdata { 15 + int buffer_size; 16 + int channel; 17 + void (*start)(struct dac_audio_pdata *pd); 18 + void (*stop)(struct dac_audio_pdata *pd); 19 + }; 20 + 21 + #endif /* __INCLUDE_SH_DAC_AUDIO_H */
+8
sound/sh/Kconfig
··· 19 19 help 20 20 ALSA Sound driver for the SEGA Dreamcast console. 21 21 22 + config SND_SH_DAC_AUDIO 23 + tristate "SuperH DAC audio support" 24 + depends on SND 25 + depends on CPU_SH3 && HIGH_RES_TIMERS 26 + select SND_PCM 27 + help 28 + Say Y here to include support for the on-chip DAC. 29 + 22 30 endif # SND_SUPERH 23 31
+2
sound/sh/Makefile
··· 3 3 # 4 4 5 5 snd-aica-objs := aica.o 6 + snd-sh_dac_audio-objs := sh_dac_audio.o 6 7 7 8 # Toplevel Module Dependency 8 9 obj-$(CONFIG_SND_AICA) += snd-aica.o 10 + obj-$(CONFIG_SND_SH_DAC_AUDIO) += snd-sh_dac_audio.o
+453
sound/sh/sh_dac_audio.c
··· 1 + /* 2 + * sh_dac_audio.c - SuperH DAC audio driver for ALSA 3 + * 4 + * Copyright (c) 2009 by Rafael Ignacio Zurita <rizurita@yahoo.com> 5 + * 6 + * 7 + * Based on sh_dac_audio.c (Copyright (C) 2004, 2005 by Andriy Skulysh) 8 + * 9 + * This program is free software; you can redistribute it and/or modify 10 + * it under the terms of the GNU General Public License as published by 11 + * the Free Software Foundation; either version 2 of the License, or 12 + * (at your option) any later version. 13 + * 14 + * This program is distributed in the hope that it will be useful, 15 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 + * GNU General Public License for more details. 18 + * 19 + * You should have received a copy of the GNU General Public License 20 + * along with this program; if not, write to the Free Software 21 + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 22 + * 23 + */ 24 + 25 + #include <linux/hrtimer.h> 26 + #include <linux/interrupt.h> 27 + #include <linux/io.h> 28 + #include <linux/platform_device.h> 29 + #include <sound/core.h> 30 + #include <sound/initval.h> 31 + #include <sound/pcm.h> 32 + #include <sound/sh_dac_audio.h> 33 + #include <asm/clock.h> 34 + #include <asm/hd64461.h> 35 + #include <mach/hp6xx.h> 36 + #include <cpu/dac.h> 37 + 38 + MODULE_AUTHOR("Rafael Ignacio Zurita <rizurita@yahoo.com>"); 39 + MODULE_DESCRIPTION("SuperH DAC audio driver"); 40 + MODULE_LICENSE("GPL"); 41 + MODULE_SUPPORTED_DEVICE("{{SuperH DAC audio support}}"); 42 + 43 + /* Module Parameters */ 44 + static int index = SNDRV_DEFAULT_IDX1; 45 + static char *id = SNDRV_DEFAULT_STR1; 46 + module_param(index, int, 0444); 47 + MODULE_PARM_DESC(index, "Index value for SuperH DAC audio."); 48 + module_param(id, charp, 0444); 49 + MODULE_PARM_DESC(id, "ID string for SuperH DAC audio."); 50 + 51 + /* main struct */ 52 + struct snd_sh_dac { 53 + struct snd_card *card; 54 + struct snd_pcm_substream *substream; 55 + struct hrtimer hrtimer; 56 + ktime_t wakeups_per_second; 57 + 58 + int rate; 59 + int empty; 60 + char *data_buffer, *buffer_begin, *buffer_end; 61 + int processed; /* bytes proccesed, to compare with period_size */ 62 + int buffer_size; 63 + struct dac_audio_pdata *pdata; 64 + }; 65 + 66 + 67 + static void dac_audio_start_timer(struct snd_sh_dac *chip) 68 + { 69 + hrtimer_start(&chip->hrtimer, chip->wakeups_per_second, 70 + HRTIMER_MODE_REL); 71 + } 72 + 73 + static void dac_audio_stop_timer(struct snd_sh_dac *chip) 74 + { 75 + hrtimer_cancel(&chip->hrtimer); 76 + } 77 + 78 + static void dac_audio_reset(struct snd_sh_dac *chip) 79 + { 80 + dac_audio_stop_timer(chip); 81 + chip->buffer_begin = chip->buffer_end = chip->data_buffer; 82 + chip->processed = 0; 83 + chip->empty = 1; 84 + } 85 + 86 + static void dac_audio_set_rate(struct snd_sh_dac *chip) 87 + { 88 + chip->wakeups_per_second = ktime_set(0, 1000000000 / chip->rate); 89 + } 90 + 91 + 92 + /* PCM INTERFACE */ 93 + 94 + static struct snd_pcm_hardware snd_sh_dac_pcm_hw = { 95 + .info = (SNDRV_PCM_INFO_MMAP | 96 + SNDRV_PCM_INFO_MMAP_VALID | 97 + SNDRV_PCM_INFO_INTERLEAVED | 98 + SNDRV_PCM_INFO_HALF_DUPLEX), 99 + .formats = SNDRV_PCM_FMTBIT_U8, 100 + .rates = SNDRV_PCM_RATE_8000, 101 + .rate_min = 8000, 102 + .rate_max = 8000, 103 + .channels_min = 1, 104 + .channels_max = 1, 105 + .buffer_bytes_max = (48*1024), 106 + .period_bytes_min = 1, 107 + .period_bytes_max = (48*1024), 108 + .periods_min = 1, 109 + .periods_max = 1024, 110 + }; 111 + 112 + static int snd_sh_dac_pcm_open(struct snd_pcm_substream *substream) 113 + { 114 + struct snd_sh_dac *chip = snd_pcm_substream_chip(substream); 115 + struct snd_pcm_runtime *runtime = substream->runtime; 116 + 117 + runtime->hw = snd_sh_dac_pcm_hw; 118 + 119 + chip->substream = substream; 120 + chip->buffer_begin = chip->buffer_end = chip->data_buffer; 121 + chip->processed = 0; 122 + chip->empty = 1; 123 + 124 + chip->pdata->start(chip->pdata); 125 + 126 + return 0; 127 + } 128 + 129 + static int snd_sh_dac_pcm_close(struct snd_pcm_substream *substream) 130 + { 131 + struct snd_sh_dac *chip = snd_pcm_substream_chip(substream); 132 + 133 + chip->substream = NULL; 134 + 135 + dac_audio_stop_timer(chip); 136 + chip->pdata->stop(chip->pdata); 137 + 138 + return 0; 139 + } 140 + 141 + static int snd_sh_dac_pcm_hw_params(struct snd_pcm_substream *substream, 142 + struct snd_pcm_hw_params *hw_params) 143 + { 144 + return snd_pcm_lib_malloc_pages(substream, 145 + params_buffer_bytes(hw_params)); 146 + } 147 + 148 + static int snd_sh_dac_pcm_hw_free(struct snd_pcm_substream *substream) 149 + { 150 + return snd_pcm_lib_free_pages(substream); 151 + } 152 + 153 + static int snd_sh_dac_pcm_prepare(struct snd_pcm_substream *substream) 154 + { 155 + struct snd_sh_dac *chip = snd_pcm_substream_chip(substream); 156 + struct snd_pcm_runtime *runtime = chip->substream->runtime; 157 + 158 + chip->buffer_size = runtime->buffer_size; 159 + memset(chip->data_buffer, 0, chip->pdata->buffer_size); 160 + 161 + return 0; 162 + } 163 + 164 + static int snd_sh_dac_pcm_trigger(struct snd_pcm_substream *substream, int cmd) 165 + { 166 + struct snd_sh_dac *chip = snd_pcm_substream_chip(substream); 167 + 168 + switch (cmd) { 169 + case SNDRV_PCM_TRIGGER_START: 170 + dac_audio_start_timer(chip); 171 + break; 172 + case SNDRV_PCM_TRIGGER_STOP: 173 + chip->buffer_begin = chip->buffer_end = chip->data_buffer; 174 + chip->processed = 0; 175 + chip->empty = 1; 176 + dac_audio_stop_timer(chip); 177 + break; 178 + default: 179 + return -EINVAL; 180 + } 181 + 182 + return 0; 183 + } 184 + 185 + static int snd_sh_dac_pcm_copy(struct snd_pcm_substream *substream, int channel, 186 + snd_pcm_uframes_t pos, void __user *src, snd_pcm_uframes_t count) 187 + { 188 + /* channel is not used (interleaved data) */ 189 + struct snd_sh_dac *chip = snd_pcm_substream_chip(substream); 190 + struct snd_pcm_runtime *runtime = substream->runtime; 191 + ssize_t b_count = frames_to_bytes(runtime , count); 192 + ssize_t b_pos = frames_to_bytes(runtime , pos); 193 + 194 + if (count < 0) 195 + return -EINVAL; 196 + 197 + if (!count) 198 + return 0; 199 + 200 + memcpy_toio(chip->data_buffer + b_pos, src, b_count); 201 + chip->buffer_end = chip->data_buffer + b_pos + b_count; 202 + 203 + if (chip->empty) { 204 + chip->empty = 0; 205 + dac_audio_start_timer(chip); 206 + } 207 + 208 + return 0; 209 + } 210 + 211 + static int snd_sh_dac_pcm_silence(struct snd_pcm_substream *substream, 212 + int channel, snd_pcm_uframes_t pos, 213 + snd_pcm_uframes_t count) 214 + { 215 + /* channel is not used (interleaved data) */ 216 + struct snd_sh_dac *chip = snd_pcm_substream_chip(substream); 217 + struct snd_pcm_runtime *runtime = substream->runtime; 218 + ssize_t b_count = frames_to_bytes(runtime , count); 219 + ssize_t b_pos = frames_to_bytes(runtime , pos); 220 + 221 + if (count < 0) 222 + return -EINVAL; 223 + 224 + if (!count) 225 + return 0; 226 + 227 + memset_io(chip->data_buffer + b_pos, 0, b_count); 228 + chip->buffer_end = chip->data_buffer + b_pos + b_count; 229 + 230 + if (chip->empty) { 231 + chip->empty = 0; 232 + dac_audio_start_timer(chip); 233 + } 234 + 235 + return 0; 236 + } 237 + 238 + static 239 + snd_pcm_uframes_t snd_sh_dac_pcm_pointer(struct snd_pcm_substream *substream) 240 + { 241 + struct snd_sh_dac *chip = snd_pcm_substream_chip(substream); 242 + int pointer = chip->buffer_begin - chip->data_buffer; 243 + 244 + return pointer; 245 + } 246 + 247 + /* pcm ops */ 248 + static struct snd_pcm_ops snd_sh_dac_pcm_ops = { 249 + .open = snd_sh_dac_pcm_open, 250 + .close = snd_sh_dac_pcm_close, 251 + .ioctl = snd_pcm_lib_ioctl, 252 + .hw_params = snd_sh_dac_pcm_hw_params, 253 + .hw_free = snd_sh_dac_pcm_hw_free, 254 + .prepare = snd_sh_dac_pcm_prepare, 255 + .trigger = snd_sh_dac_pcm_trigger, 256 + .pointer = snd_sh_dac_pcm_pointer, 257 + .copy = snd_sh_dac_pcm_copy, 258 + .silence = snd_sh_dac_pcm_silence, 259 + .mmap = snd_pcm_lib_mmap_iomem, 260 + }; 261 + 262 + static int __devinit snd_sh_dac_pcm(struct snd_sh_dac *chip, int device) 263 + { 264 + int err; 265 + struct snd_pcm *pcm; 266 + 267 + /* device should be always 0 for us */ 268 + err = snd_pcm_new(chip->card, "SH_DAC PCM", device, 1, 0, &pcm); 269 + if (err < 0) 270 + return err; 271 + 272 + pcm->private_data = chip; 273 + strcpy(pcm->name, "SH_DAC PCM"); 274 + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_sh_dac_pcm_ops); 275 + 276 + /* buffer size=48K */ 277 + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS, 278 + snd_dma_continuous_data(GFP_KERNEL), 279 + 48 * 1024, 280 + 48 * 1024); 281 + 282 + return 0; 283 + } 284 + /* END OF PCM INTERFACE */ 285 + 286 + 287 + /* driver .remove -- destructor */ 288 + static int snd_sh_dac_remove(struct platform_device *devptr) 289 + { 290 + snd_card_free(platform_get_drvdata(devptr)); 291 + platform_set_drvdata(devptr, NULL); 292 + 293 + return 0; 294 + } 295 + 296 + /* free -- it has been defined by create */ 297 + static int snd_sh_dac_free(struct snd_sh_dac *chip) 298 + { 299 + /* release the data */ 300 + kfree(chip->data_buffer); 301 + kfree(chip); 302 + 303 + return 0; 304 + } 305 + 306 + static int snd_sh_dac_dev_free(struct snd_device *device) 307 + { 308 + struct snd_sh_dac *chip = device->device_data; 309 + 310 + return snd_sh_dac_free(chip); 311 + } 312 + 313 + static enum hrtimer_restart sh_dac_audio_timer(struct hrtimer *handle) 314 + { 315 + struct snd_sh_dac *chip = container_of(handle, struct snd_sh_dac, 316 + hrtimer); 317 + struct snd_pcm_runtime *runtime = chip->substream->runtime; 318 + ssize_t b_ps = frames_to_bytes(runtime, runtime->period_size); 319 + 320 + if (!chip->empty) { 321 + sh_dac_output(*chip->buffer_begin, chip->pdata->channel); 322 + chip->buffer_begin++; 323 + 324 + chip->processed++; 325 + if (chip->processed >= b_ps) { 326 + chip->processed -= b_ps; 327 + snd_pcm_period_elapsed(chip->substream); 328 + } 329 + 330 + if (chip->buffer_begin == (chip->data_buffer + 331 + chip->buffer_size - 1)) 332 + chip->buffer_begin = chip->data_buffer; 333 + 334 + if (chip->buffer_begin == chip->buffer_end) 335 + chip->empty = 1; 336 + 337 + } 338 + 339 + if (!chip->empty) 340 + hrtimer_start(&chip->hrtimer, chip->wakeups_per_second, 341 + HRTIMER_MODE_REL); 342 + 343 + return HRTIMER_NORESTART; 344 + } 345 + 346 + /* create -- chip-specific constructor for the cards components */ 347 + static int __devinit snd_sh_dac_create(struct snd_card *card, 348 + struct platform_device *devptr, 349 + struct snd_sh_dac **rchip) 350 + { 351 + struct snd_sh_dac *chip; 352 + int err; 353 + 354 + static struct snd_device_ops ops = { 355 + .dev_free = snd_sh_dac_dev_free, 356 + }; 357 + 358 + *rchip = NULL; 359 + 360 + chip = kzalloc(sizeof(*chip), GFP_KERNEL); 361 + if (chip == NULL) 362 + return -ENOMEM; 363 + 364 + chip->card = card; 365 + 366 + hrtimer_init(&chip->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); 367 + chip->hrtimer.function = sh_dac_audio_timer; 368 + 369 + dac_audio_reset(chip); 370 + chip->rate = 8000; 371 + dac_audio_set_rate(chip); 372 + 373 + chip->pdata = devptr->dev.platform_data; 374 + 375 + chip->data_buffer = kmalloc(chip->pdata->buffer_size, GFP_KERNEL); 376 + if (chip->data_buffer == NULL) { 377 + kfree(chip); 378 + return -ENOMEM; 379 + } 380 + 381 + err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops); 382 + if (err < 0) { 383 + snd_sh_dac_free(chip); 384 + return err; 385 + } 386 + 387 + *rchip = chip; 388 + 389 + return 0; 390 + } 391 + 392 + /* driver .probe -- constructor */ 393 + static int __devinit snd_sh_dac_probe(struct platform_device *devptr) 394 + { 395 + struct snd_sh_dac *chip; 396 + struct snd_card *card; 397 + int err; 398 + 399 + err = snd_card_create(index, id, THIS_MODULE, 0, &card); 400 + if (err < 0) { 401 + snd_printk(KERN_ERR "cannot allocate the card\n"); 402 + return err; 403 + } 404 + 405 + err = snd_sh_dac_create(card, devptr, &chip); 406 + if (err < 0) 407 + goto probe_error; 408 + 409 + err = snd_sh_dac_pcm(chip, 0); 410 + if (err < 0) 411 + goto probe_error; 412 + 413 + strcpy(card->driver, "snd_sh_dac"); 414 + strcpy(card->shortname, "SuperH DAC audio driver"); 415 + printk(KERN_INFO "%s %s", card->longname, card->shortname); 416 + 417 + err = snd_card_register(card); 418 + if (err < 0) 419 + goto probe_error; 420 + 421 + snd_printk("ALSA driver for SuperH DAC audio"); 422 + 423 + platform_set_drvdata(devptr, card); 424 + return 0; 425 + 426 + probe_error: 427 + snd_card_free(card); 428 + return err; 429 + } 430 + 431 + /* 432 + * "driver" definition 433 + */ 434 + static struct platform_driver driver = { 435 + .probe = snd_sh_dac_probe, 436 + .remove = snd_sh_dac_remove, 437 + .driver = { 438 + .name = "dac_audio", 439 + }, 440 + }; 441 + 442 + static int __init sh_dac_init(void) 443 + { 444 + return platform_driver_register(&driver); 445 + } 446 + 447 + static void __exit sh_dac_exit(void) 448 + { 449 + platform_driver_unregister(&driver); 450 + } 451 + 452 + module_init(sh_dac_init); 453 + module_exit(sh_dac_exit);