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 <mach/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                spin_lock(&file->f_lock);
 139                file->f_flags |= O_NONBLOCK;
 140                spin_unlock(&file->f_lock);
 141                return 0;
 142
 143        case SNDCTL_DSP_GETCAPS:
 144                return 0;
 145
 146        case SOUND_PCM_WRITE_RATE:
 147                val = *(int *)arg;
 148                if (val > 0) {
 149                        rate = val;
 150                        dac_audio_set_rate();
 151                }
 152                return put_user(rate, (int *)arg);
 153
 154        case SNDCTL_DSP_STEREO:
 155                return put_user(0, (int *)arg);
 156
 157        case SOUND_PCM_WRITE_CHANNELS:
 158                return put_user(1, (int *)arg);
 159
 160        case SNDCTL_DSP_SETDUPLEX:
 161                return -EINVAL;
 162
 163        case SNDCTL_DSP_PROFILE:
 164                return -EINVAL;
 165
 166        case SNDCTL_DSP_GETBLKSIZE:
 167                return put_user(BUFFER_SIZE, (int *)arg);
 168
 169        case SNDCTL_DSP_SETFRAGMENT:
 170                return 0;
 171
 172        default:
 173                printk(KERN_ERR "sh_dac_audio: unimplemented ioctl=0x%x\n",
 174                       cmd);
 175                return -EINVAL;
 176        }
 177        return -EINVAL;
 178}
 179
 180static ssize_t dac_audio_write(struct file *file, const char *buf, size_t count,
 181                               loff_t * ppos)
 182{
 183        int free;
 184        int nbytes;
 185
 186        if (count < 0)
 187                return -EINVAL;
 188
 189        if (!count) {
 190                dac_audio_sync();
 191                return 0;
 192        }
 193
 194        free = buffer_begin - buffer_end;
 195
 196        if (free < 0)
 197                free += BUFFER_SIZE;
 198        if ((free == 0) && (empty))
 199                free = BUFFER_SIZE;
 200        if (count > free)
 201                count = free;
 202        if (buffer_begin > buffer_end) {
 203                if (copy_from_user((void *)buffer_end, buf, count))
 204                        return -EFAULT;
 205
 206                buffer_end += count;
 207        } else {
 208                nbytes = data_buffer + BUFFER_SIZE - buffer_end;
 209                if (nbytes > count) {
 210                        if (copy_from_user((void *)buffer_end, buf, count))
 211                                return -EFAULT;
 212                        buffer_end += count;
 213                } else {
 214                        if (copy_from_user((void *)buffer_end, buf, nbytes))
 215                                return -EFAULT;
 216                        if (copy_from_user
 217                            ((void *)data_buffer, buf + nbytes, count - nbytes))
 218                                return -EFAULT;
 219                        buffer_end = data_buffer + count - nbytes;
 220                }
 221        }
 222
 223        if (empty) {
 224                empty = 0;
 225                dac_audio_start_timer();
 226        }
 227
 228        return count;
 229}
 230
 231static ssize_t dac_audio_read(struct file *file, char *buf, size_t count,
 232                              loff_t * ppos)
 233{
 234        return -EINVAL;
 235}
 236
 237static int dac_audio_open(struct inode *inode, struct file *file)
 238{
 239        if (file->f_mode & FMODE_READ)
 240                return -ENODEV;
 241        if (in_use)
 242                return -EBUSY;
 243
 244        in_use = 1;
 245
 246        dac_audio_start();
 247
 248        return 0;
 249}
 250
 251static int dac_audio_release(struct inode *inode, struct file *file)
 252{
 253        dac_audio_sync();
 254        dac_audio_stop();
 255        in_use = 0;
 256
 257        return 0;
 258}
 259
 260const struct file_operations dac_audio_fops = {
 261      .read =           dac_audio_read,
 262      .write =  dac_audio_write,
 263      .ioctl =  dac_audio_ioctl,
 264      .open =           dac_audio_open,
 265      .release =        dac_audio_release,
 266};
 267
 268static irqreturn_t timer1_interrupt(int irq, void *dev)
 269{
 270        unsigned long timer_status;
 271
 272        timer_status = ctrl_inw(TMU1_TCR);
 273        timer_status &= ~0x100;
 274        ctrl_outw(timer_status, TMU1_TCR);
 275
 276        if (!empty) {
 277                sh_dac_output(*buffer_begin, CONFIG_SOUND_SH_DAC_AUDIO_CHANNEL);
 278                buffer_begin++;
 279
 280                if (buffer_begin == data_buffer + BUFFER_SIZE)
 281                        buffer_begin = data_buffer;
 282                if (buffer_begin == buffer_end) {
 283                        empty = 1;
 284                        dac_audio_stop_timer();
 285                }
 286        }
 287        return IRQ_HANDLED;
 288}
 289
 290static int __init dac_audio_init(void)
 291{
 292        int retval;
 293
 294        if ((device_major = register_sound_dsp(&dac_audio_fops, -1)) < 0) {
 295                printk(KERN_ERR "Cannot register dsp device");
 296                return device_major;
 297        }
 298
 299        in_use = 0;
 300
 301        data_buffer = kmalloc(BUFFER_SIZE, GFP_KERNEL);
 302        if (data_buffer == NULL)
 303                return -ENOMEM;
 304
 305        dac_audio_reset();
 306        rate = 8000;
 307        dac_audio_set_rate();
 308
 309        retval =
 310            request_irq(TIMER1_IRQ, timer1_interrupt, IRQF_DISABLED, MODNAME, 0);
 311        if (retval < 0) {
 312                printk(KERN_ERR "sh_dac_audio: IRQ %d request failed\n",
 313                       TIMER1_IRQ);
 314                return retval;
 315        }
 316
 317        return 0;
 318}
 319
 320static void __exit dac_audio_exit(void)
 321{
 322        free_irq(TIMER1_IRQ, 0);
 323
 324        unregister_sound_dsp(device_major);
 325        kfree((void *)data_buffer);
 326}
 327
 328module_init(dac_audio_init);
 329module_exit(dac_audio_exit);
 330
 331MODULE_AUTHOR("Andriy Skulysh, askulysh@image.kiev.ua");
 332MODULE_DESCRIPTION("SH DAC sound driver");
 333MODULE_LICENSE("GPL");
 334