linux/sound/oss/sh_dac_audio.c
<<
>>
Prefs
   1/*
   2 * sound/oss/sh_dac_audio.c
   3 *
   4 * SH DAC based sound :(
   5 *
   6 *  Copyright (C) 2004,2005  Andriy Skulysh
   7 *
   8 * This file is subject to the terms and conditions of the GNU General Public
   9 * License.  See the file "COPYING" in the main directory of this archive
  10 * for more details.
  11 */
  12#include <linux/module.h>
  13#include <linux/init.h>
  14#include <linux/sched.h>
  15#include <linux/linkage.h>
  16#include <linux/slab.h>
  17#include <linux/fs.h>
  18#include <linux/sound.h>
  19#include <linux/soundcard.h>
  20#include <linux/interrupt.h>
  21#include <asm/io.h>
  22#include <asm/uaccess.h>
  23#include <asm/irq.h>
  24#include <asm/delay.h>
  25#include <asm/clock.h>
  26#include <asm/cpu/dac.h>
  27#include <asm/cpu/timer.h>
  28#include <asm/machvec.h>
  29#include <asm/hp6xx.h>
  30#include <asm/hd64461.h>
  31
  32#define MODNAME "sh_dac_audio"
  33
  34#define TMU_TOCR_INIT   0x00
  35
  36#define TMU1_TCR_INIT   0x0020  /* Clock/4, rising edge; interrupt on */
  37#define TMU1_TSTR_INIT  0x02    /* Bit to turn on TMU1 */
  38
  39#define BUFFER_SIZE 48000
  40
  41static int rate;
  42static int empty;
  43static char *data_buffer, *buffer_begin, *buffer_end;
  44static int in_use, device_major;
  45
  46static void dac_audio_start_timer(void)
  47{
  48        u8 tstr;
  49
  50        tstr = ctrl_inb(TMU_TSTR);
  51        tstr |= TMU1_TSTR_INIT;
  52        ctrl_outb(tstr, TMU_TSTR);
  53}
  54
  55static void dac_audio_stop_timer(void)
  56{
  57        u8 tstr;
  58
  59        tstr = ctrl_inb(TMU_TSTR);
  60        tstr &= ~TMU1_TSTR_INIT;
  61        ctrl_outb(tstr, TMU_TSTR);
  62}
  63
  64static void dac_audio_reset(void)
  65{
  66        dac_audio_stop_timer();
  67        buffer_begin = buffer_end = data_buffer;
  68        empty = 1;
  69}
  70
  71static void dac_audio_sync(void)
  72{
  73        while (!empty)
  74                schedule();
  75}
  76
  77static void dac_audio_start(void)
  78{
  79        if (mach_is_hp6xx()) {
  80                u16 v = inw(HD64461_GPADR);
  81                v &= ~HD64461_GPADR_SPEAKER;
  82                outw(v, HD64461_GPADR);
  83        }
  84
  85        sh_dac_enable(CONFIG_SOUND_SH_DAC_AUDIO_CHANNEL);
  86        ctrl_outw(TMU1_TCR_INIT, TMU1_TCR);
  87}
  88static void dac_audio_stop(void)
  89{
  90        dac_audio_stop_timer();
  91
  92        if (mach_is_hp6xx()) {
  93                u16 v = inw(HD64461_GPADR);
  94                v |= HD64461_GPADR_SPEAKER;
  95                outw(v, HD64461_GPADR);
  96        }
  97
  98        sh_dac_output(0, CONFIG_SOUND_SH_DAC_AUDIO_CHANNEL);
  99        sh_dac_disable(CONFIG_SOUND_SH_DAC_AUDIO_CHANNEL);
 100}
 101
 102static void dac_audio_set_rate(void)
 103{
 104        unsigned long interval;
 105        struct clk *clk;
 106
 107        clk = clk_get(NULL, "module_clk");
 108        interval = (clk_get_rate(clk) / 4) / rate;
 109        clk_put(clk);
 110        ctrl_outl(interval, TMU1_TCOR);
 111        ctrl_outl(interval, TMU1_TCNT);
 112}
 113
 114static int dac_audio_ioctl(struct inode *inode, struct file *file,
 115                           unsigned int cmd, unsigned long arg)
 116{
 117        int val;
 118
 119        switch (cmd) {
 120        case OSS_GETVERSION:
 121                return put_user(SOUND_VERSION, (int *)arg);
 122
 123        case SNDCTL_DSP_SYNC:
 124                dac_audio_sync();
 125                return 0;
 126
 127        case SNDCTL_DSP_RESET:
 128                dac_audio_reset();
 129                return 0;
 130
 131        case SNDCTL_DSP_GETFMTS:
 132                return put_user(AFMT_U8, (int *)arg);
 133
 134        case SNDCTL_DSP_SETFMT:
 135                return put_user(AFMT_U8, (int *)arg);
 136
 137        case SNDCTL_DSP_NONBLOCK:
 138                file->f_flags |= O_NONBLOCK;
 139                return 0;
 140
 141        case SNDCTL_DSP_GETCAPS:
 142                return 0;
 143
 144        case SOUND_PCM_WRITE_RATE:
 145                val = *(int *)arg;
 146                if (val > 0) {
 147                        rate = val;
 148                        dac_audio_set_rate();
 149                }
 150                return put_user(rate, (int *)arg);
 151
 152        case SNDCTL_DSP_STEREO:
 153                return put_user(0, (int *)arg);
 154
 155        case SOUND_PCM_WRITE_CHANNELS:
 156                return put_user(1, (int *)arg);
 157
 158        case SNDCTL_DSP_SETDUPLEX:
 159                return -EINVAL;
 160
 161        case SNDCTL_DSP_PROFILE:
 162                return -EINVAL;
 163
 164        case SNDCTL_DSP_GETBLKSIZE:
 165                return put_user(BUFFER_SIZE, (int *)arg);
 166
 167        case SNDCTL_DSP_SETFRAGMENT:
 168                return 0;
 169
 170        default:
 171                printk(KERN_ERR "sh_dac_audio: unimplemented ioctl=0x%x\n",
 172                       cmd);
 173                return -EINVAL;
 174        }
 175        return -EINVAL;
 176}
 177
 178static ssize_t dac_audio_write(struct file *file, const char *buf, size_t count,
 179                               loff_t * ppos)
 180{
 181        int free;
 182        int nbytes;
 183
 184        if (count < 0)
 185                return -EINVAL;
 186
 187        if (!count) {
 188                dac_audio_sync();
 189                return 0;
 190        }
 191
 192        free = buffer_begin - buffer_end;
 193
 194        if (free < 0)
 195                free += BUFFER_SIZE;
 196        if ((free == 0) && (empty))
 197                free = BUFFER_SIZE;
 198        if (count > free)
 199                count = free;
 200        if (buffer_begin > buffer_end) {
 201                if (copy_from_user((void *)buffer_end, buf, count))
 202                        return -EFAULT;
 203
 204                buffer_end += count;
 205        } else {
 206                nbytes = data_buffer + BUFFER_SIZE - buffer_end;
 207                if (nbytes > count) {
 208                        if (copy_from_user((void *)buffer_end, buf, count))
 209                                return -EFAULT;
 210                        buffer_end += count;
 211                } else {
 212                        if (copy_from_user((void *)buffer_end, buf, nbytes))
 213                                return -EFAULT;
 214                        if (copy_from_user
 215                            ((void *)data_buffer, buf + nbytes, count - nbytes))
 216                                return -EFAULT;
 217                        buffer_end = data_buffer + count - nbytes;
 218                }
 219        }
 220
 221        if (empty) {
 222                empty = 0;
 223                dac_audio_start_timer();
 224        }
 225
 226        return count;
 227}
 228
 229static ssize_t dac_audio_read(struct file *file, char *buf, size_t count,
 230                              loff_t * ppos)
 231{
 232        return -EINVAL;
 233}
 234
 235static int dac_audio_open(struct inode *inode, struct file *file)
 236{
 237        if (file->f_mode & FMODE_READ)
 238                return -ENODEV;
 239        if (in_use)
 240                return -EBUSY;
 241
 242        in_use = 1;
 243
 244        dac_audio_start();
 245
 246        return 0;
 247}
 248
 249static int dac_audio_release(struct inode *inode, struct file *file)
 250{
 251        dac_audio_sync();
 252        dac_audio_stop();
 253        in_use = 0;
 254
 255        return 0;
 256}
 257
 258const struct file_operations dac_audio_fops = {
 259      .read =           dac_audio_read,
 260      .write =  dac_audio_write,
 261      .ioctl =  dac_audio_ioctl,
 262      .open =           dac_audio_open,
 263      .release =        dac_audio_release,
 264};
 265
 266static irqreturn_t timer1_interrupt(int irq, void *dev)
 267{
 268        unsigned long timer_status;
 269
 270        timer_status = ctrl_inw(TMU1_TCR);
 271        timer_status &= ~0x100;
 272        ctrl_outw(timer_status, TMU1_TCR);
 273
 274        if (!empty) {
 275                sh_dac_output(*buffer_begin, CONFIG_SOUND_SH_DAC_AUDIO_CHANNEL);
 276                buffer_begin++;
 277
 278                if (buffer_begin == data_buffer + BUFFER_SIZE)
 279                        buffer_begin = data_buffer;
 280                if (buffer_begin == buffer_end) {
 281                        empty = 1;
 282                        dac_audio_stop_timer();
 283                }
 284        }
 285        return IRQ_HANDLED;
 286}
 287
 288static int __init dac_audio_init(void)
 289{
 290        int retval;
 291
 292        if ((device_major = register_sound_dsp(&dac_audio_fops, -1)) < 0) {
 293                printk(KERN_ERR "Cannot register dsp device");
 294                return device_major;
 295        }
 296
 297        in_use = 0;
 298
 299        data_buffer = kmalloc(BUFFER_SIZE, GFP_KERNEL);
 300        if (data_buffer == NULL)
 301                return -ENOMEM;
 302
 303        dac_audio_reset();
 304        rate = 8000;
 305        dac_audio_set_rate();
 306
 307        retval =
 308            request_irq(TIMER1_IRQ, timer1_interrupt, IRQF_DISABLED, MODNAME, 0);
 309        if (retval < 0) {
 310                printk(KERN_ERR "sh_dac_audio: IRQ %d request failed\n",
 311                       TIMER1_IRQ);
 312                return retval;
 313        }
 314
 315        return 0;
 316}
 317
 318static void __exit dac_audio_exit(void)
 319{
 320        free_irq(TIMER1_IRQ, 0);
 321
 322        unregister_sound_dsp(device_major);
 323        kfree((void *)data_buffer);
 324}
 325
 326module_init(dac_audio_init);
 327module_exit(dac_audio_exit);
 328
 329MODULE_AUTHOR("Andriy Skulysh, askulysh@image.kiev.ua");
 330MODULE_DESCRIPTION("SH DAC sound driver");
 331MODULE_LICENSE("GPL");
 332