linux-old/drivers/char/dsp56k.c
<<
>>
Prefs
   1/*
   2 * The DSP56001 Device Driver, saviour of the Free World(tm)
   3 *
   4 * Authors: Fredrik Noring   <noring@nocrew.org>
   5 *          lars brinkhoff   <lars@nocrew.org>
   6 *          Tomas Berndtsson <tomas@nocrew.org>
   7 *
   8 * First version May 1996
   9 *
  10 * History:
  11 *  97-01-29   Tomas Berndtsson,
  12 *               Integrated with Linux 2.1.21 kernel sources.
  13 *  97-02-15   Tomas Berndtsson,
  14 *               Fixed for kernel 2.1.26
  15 *
  16 * BUGS:
  17 *  Hmm... there must be something here :)
  18 *
  19 * Copyright (C) 1996,1997 Fredrik Noring, lars brinkhoff & Tomas Berndtsson
  20 *
  21 * This file is subject to the terms and conditions of the GNU General Public
  22 * License.  See the file COPYING in the main directory of this archive
  23 * for more details.
  24 */
  25
  26#include <linux/module.h>
  27#include <linux/version.h>
  28#include <linux/slab.h> /* for kmalloc() and kfree() */
  29#include <linux/sched.h>        /* for struct wait_queue etc */
  30#include <linux/major.h>
  31#include <linux/types.h>
  32#include <linux/errno.h>
  33#include <linux/delay.h>        /* guess what */
  34#include <linux/fs.h>
  35#include <linux/mm.h>
  36#include <linux/init.h>
  37#include <linux/devfs_fs_kernel.h>
  38#include <linux/smp_lock.h>
  39
  40#include <asm/segment.h>
  41#include <asm/atarihw.h>
  42#include <asm/traps.h>
  43#include <asm/uaccess.h>        /* For put_user and get_user */
  44
  45#include <asm/dsp56k.h>
  46
  47/* minor devices */
  48#define DSP56K_DEV_56001        0    /* The only device so far */
  49
  50#define TIMEOUT    10   /* Host port timeout in number of tries */
  51#define MAXIO    2048   /* Maximum number of words before sleep */
  52#define DSP56K_MAX_BINARY_LENGTH (3*64*1024)
  53
  54#define DSP56K_TX_INT_ON        dsp56k_host_interface.icr |=  DSP56K_ICR_TREQ
  55#define DSP56K_RX_INT_ON        dsp56k_host_interface.icr |=  DSP56K_ICR_RREQ
  56#define DSP56K_TX_INT_OFF       dsp56k_host_interface.icr &= ~DSP56K_ICR_TREQ
  57#define DSP56K_RX_INT_OFF       dsp56k_host_interface.icr &= ~DSP56K_ICR_RREQ
  58
  59#define DSP56K_TRANSMIT         (dsp56k_host_interface.isr & DSP56K_ISR_TXDE)
  60#define DSP56K_RECEIVE          (dsp56k_host_interface.isr & DSP56K_ISR_RXDF)
  61
  62#define wait_some(n) \
  63{ \
  64        set_current_state(TASK_INTERRUPTIBLE); \
  65        schedule_timeout(n); \
  66}
  67
  68#define handshake(count, maxio, timeout, ENABLE, f) \
  69{ \
  70        long i, t, m; \
  71        while (count > 0) { \
  72                m = min_t(unsigned long, count, maxio); \
  73                for (i = 0; i < m; i++) { \
  74                        for (t = 0; t < timeout && !ENABLE; t++) \
  75                                wait_some(HZ/50); \
  76                        if(!ENABLE) \
  77                                return -EIO; \
  78                        f; \
  79                } \
  80                count -= m; \
  81                if (m == maxio) wait_some(HZ/50); \
  82        } \
  83}
  84
  85#define tx_wait(n) \
  86{ \
  87        int t; \
  88        for(t = 0; t < n && !DSP56K_TRANSMIT; t++) \
  89                wait_some(HZ/100); \
  90        if(!DSP56K_TRANSMIT) { \
  91                return -EIO; \
  92        } \
  93}
  94
  95#define rx_wait(n) \
  96{ \
  97        int t; \
  98        for(t = 0; t < n && !DSP56K_RECEIVE; t++) \
  99                wait_some(HZ/100); \
 100        if(!DSP56K_RECEIVE) { \
 101                return -EIO; \
 102        } \
 103}
 104
 105/* DSP56001 bootstrap code */
 106static char bootstrap[] = {
 107        0x0c, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 108        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 109        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 110        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 111        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 112        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 113        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 114        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 115        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 116        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 117        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 118        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 119        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 120        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 121        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 122        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 123        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 124        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 125        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 126        0x00, 0x00, 0x60, 0xf4, 0x00, 0x00, 0x00, 0x4f, 0x61, 0xf4,
 127        0x00, 0x00, 0x7e, 0xa9, 0x06, 0x2e, 0x80, 0x00, 0x00, 0x47,
 128        0x07, 0xd8, 0x84, 0x07, 0x59, 0x84, 0x08, 0xf4, 0xa8, 0x00,
 129        0x00, 0x04, 0x08, 0xf4, 0xbf, 0x00, 0x0c, 0x00, 0x00, 0xfe,
 130        0xb8, 0x0a, 0xf0, 0x80, 0x00, 0x7e, 0xa9, 0x08, 0xf4, 0xa0,
 131        0x00, 0x00, 0x01, 0x08, 0xf4, 0xbe, 0x00, 0x00, 0x00, 0x0a,
 132        0xa9, 0x80, 0x00, 0x7e, 0xad, 0x08, 0x4e, 0x2b, 0x44, 0xf4,
 133        0x00, 0x00, 0x00, 0x03, 0x44, 0xf4, 0x45, 0x00, 0x00, 0x01,
 134        0x0e, 0xa0, 0x00, 0x0a, 0xa9, 0x80, 0x00, 0x7e, 0xb5, 0x08,
 135        0x50, 0x2b, 0x0a, 0xa9, 0x80, 0x00, 0x7e, 0xb8, 0x08, 0x46,
 136        0x2b, 0x44, 0xf4, 0x45, 0x00, 0x00, 0x02, 0x0a, 0xf0, 0xaa,
 137        0x00, 0x7e, 0xc9, 0x20, 0x00, 0x45, 0x0a, 0xf0, 0xaa, 0x00,
 138        0x7e, 0xd0, 0x06, 0xc6, 0x00, 0x00, 0x7e, 0xc6, 0x0a, 0xa9,
 139        0x80, 0x00, 0x7e, 0xc4, 0x08, 0x58, 0x6b, 0x0a, 0xf0, 0x80,
 140        0x00, 0x7e, 0xad, 0x06, 0xc6, 0x00, 0x00, 0x7e, 0xcd, 0x0a,
 141        0xa9, 0x80, 0x00, 0x7e, 0xcb, 0x08, 0x58, 0xab, 0x0a, 0xf0,
 142        0x80, 0x00, 0x7e, 0xad, 0x06, 0xc6, 0x00, 0x00, 0x7e, 0xd4,
 143        0x0a, 0xa9, 0x80, 0x00, 0x7e, 0xd2, 0x08, 0x58, 0xeb, 0x0a,
 144        0xf0, 0x80, 0x00, 0x7e, 0xad};
 145static int sizeof_bootstrap = 375;
 146
 147
 148static struct dsp56k_device {
 149        long in_use;
 150        long maxio, timeout;
 151        int tx_wsize, rx_wsize;
 152} dsp56k;
 153
 154static int dsp56k_reset(void)
 155{
 156        u_char status;
 157        
 158        /* Power down the DSP */
 159        sound_ym.rd_data_reg_sel = 14;
 160        status = sound_ym.rd_data_reg_sel & 0xef;
 161        sound_ym.wd_data = status;
 162        sound_ym.wd_data = status | 0x10;
 163  
 164        udelay(10);
 165  
 166        /* Power up the DSP */
 167        sound_ym.rd_data_reg_sel = 14;
 168        sound_ym.wd_data = sound_ym.rd_data_reg_sel & 0xef;
 169
 170        return 0;
 171}
 172
 173static int dsp56k_upload(u_char *bin, int len)
 174{
 175        int i;
 176        u_char *p;
 177        
 178        dsp56k_reset();
 179  
 180        p = bootstrap;
 181        for (i = 0; i < sizeof_bootstrap/3; i++) {
 182                /* tx_wait(10); */
 183                dsp56k_host_interface.data.b[1] = *p++;
 184                dsp56k_host_interface.data.b[2] = *p++;
 185                dsp56k_host_interface.data.b[3] = *p++;
 186        }
 187        for (; i < 512; i++) {
 188                /* tx_wait(10); */
 189                dsp56k_host_interface.data.b[1] = 0;
 190                dsp56k_host_interface.data.b[2] = 0;
 191                dsp56k_host_interface.data.b[3] = 0;
 192        }
 193  
 194        for (i = 0; i < len; i++) {
 195                tx_wait(10);
 196                get_user(dsp56k_host_interface.data.b[1], bin++);
 197                get_user(dsp56k_host_interface.data.b[2], bin++);
 198                get_user(dsp56k_host_interface.data.b[3], bin++);
 199        }
 200
 201        tx_wait(10);
 202        dsp56k_host_interface.data.l = 3;    /* Magic execute */
 203
 204        return 0;
 205}
 206
 207static ssize_t dsp56k_read(struct file *file, char *buf, size_t count,
 208                           loff_t *ppos)
 209{
 210        struct inode *inode = file->f_dentry->d_inode;
 211        int dev = MINOR(inode->i_rdev) & 0x0f;
 212
 213        switch(dev)
 214        {
 215        case DSP56K_DEV_56001:
 216        {
 217
 218                long n;
 219
 220                /* Don't do anything if nothing is to be done */
 221                if (!count) return 0;
 222
 223                n = 0;
 224                switch (dsp56k.rx_wsize) {
 225                case 1:  /* 8 bit */
 226                {
 227                        handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_RECEIVE,
 228                                  put_user(dsp56k_host_interface.data.b[3], buf+n++));
 229                        return n;
 230                }
 231                case 2:  /* 16 bit */
 232                {
 233                        short *data;
 234
 235                        count /= 2;
 236                        data = (short*) buf;
 237                        handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_RECEIVE,
 238                                  put_user(dsp56k_host_interface.data.w[1], data+n++));
 239                        return 2*n;
 240                }
 241                case 3:  /* 24 bit */
 242                {
 243                        count /= 3;
 244                        handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_RECEIVE,
 245                                  put_user(dsp56k_host_interface.data.b[1], buf+n++);
 246                                  put_user(dsp56k_host_interface.data.b[2], buf+n++);
 247                                  put_user(dsp56k_host_interface.data.b[3], buf+n++));
 248                        return 3*n;
 249                }
 250                case 4:  /* 32 bit */
 251                {
 252                        long *data;
 253
 254                        count /= 4;
 255                        data = (long*) buf;
 256                        handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_RECEIVE,
 257                                  put_user(dsp56k_host_interface.data.l, data+n++));
 258                        return 4*n;
 259                }
 260                }
 261                return -EFAULT;
 262        }
 263
 264        default:
 265                printk(KERN_ERR "DSP56k driver: Unknown minor device: %d\n", dev);
 266                return -ENXIO;
 267        }
 268}
 269
 270static ssize_t dsp56k_write(struct file *file, const char *buf, size_t count,
 271                            loff_t *ppos)
 272{
 273        struct inode *inode = file->f_dentry->d_inode;
 274        int dev = MINOR(inode->i_rdev) & 0x0f;
 275
 276        switch(dev)
 277        {
 278        case DSP56K_DEV_56001:
 279        {
 280                long n;
 281
 282                /* Don't do anything if nothing is to be done */
 283                if (!count) return 0;
 284
 285                n = 0;
 286                switch (dsp56k.tx_wsize) {
 287                case 1:  /* 8 bit */
 288                {
 289                        handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_TRANSMIT,
 290                                  get_user(dsp56k_host_interface.data.b[3], buf+n++));
 291                        return n;
 292                }
 293                case 2:  /* 16 bit */
 294                {
 295                        short *data;
 296
 297                        count /= 2;
 298                        data = (short*) buf;
 299                        handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_TRANSMIT,
 300                                  get_user(dsp56k_host_interface.data.w[1], data+n++));
 301                        return 2*n;
 302                }
 303                case 3:  /* 24 bit */
 304                {
 305                        count /= 3;
 306                        handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_TRANSMIT,
 307                                  get_user(dsp56k_host_interface.data.b[1], buf+n++);
 308                                  get_user(dsp56k_host_interface.data.b[2], buf+n++);
 309                                  get_user(dsp56k_host_interface.data.b[3], buf+n++));
 310                        return 3*n;
 311                }
 312                case 4:  /* 32 bit */
 313                {
 314                        long *data;
 315
 316                        count /= 4;
 317                        data = (long*) buf;
 318                        handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_TRANSMIT,
 319                                  get_user(dsp56k_host_interface.data.l, data+n++));
 320                        return 4*n;
 321                }
 322                }
 323
 324                return -EFAULT;
 325        }
 326        default:
 327                printk(KERN_ERR "DSP56k driver: Unknown minor device: %d\n", dev);
 328                return -ENXIO;
 329        }
 330}
 331
 332static int dsp56k_ioctl(struct inode *inode, struct file *file,
 333                        unsigned int cmd, unsigned long arg)
 334{
 335        int dev = MINOR(inode->i_rdev) & 0x0f;
 336
 337        switch(dev)
 338        {
 339        case DSP56K_DEV_56001:
 340
 341                switch(cmd) {
 342                case DSP56K_UPLOAD:
 343                {
 344                        char *bin;
 345                        int r, len;
 346                        struct dsp56k_upload *binary = (struct dsp56k_upload *) arg;
 347    
 348                        if(get_user(len, &binary->len) < 0)
 349                                return -EFAULT;
 350                        if(get_user(bin, &binary->bin) < 0)
 351                                return -EFAULT;
 352                
 353                        if (len == 0) {
 354                                return -EINVAL;      /* nothing to upload?!? */
 355                        }
 356                        if (len > DSP56K_MAX_BINARY_LENGTH) {
 357                                return -EINVAL;
 358                        }
 359    
 360                        r = dsp56k_upload(bin, len);
 361                        if (r < 0) {
 362                                return r;
 363                        }
 364    
 365                        break;
 366                }
 367                case DSP56K_SET_TX_WSIZE:
 368                        if (arg > 4 || arg < 1)
 369                                return -EINVAL;
 370                        dsp56k.tx_wsize = (int) arg;
 371                        break;
 372                case DSP56K_SET_RX_WSIZE:
 373                        if (arg > 4 || arg < 1)
 374                                return -EINVAL;
 375                        dsp56k.rx_wsize = (int) arg;
 376                        break;
 377                case DSP56K_HOST_FLAGS:
 378                {
 379                        int dir, out, status;
 380                        struct dsp56k_host_flags *hf = (struct dsp56k_host_flags*) arg;
 381    
 382                        if(get_user(dir, &hf->dir) < 0)
 383                                return -EFAULT;
 384                        if(get_user(out, &hf->out) < 0)
 385                                return -EFAULT;
 386
 387                        if ((dir & 0x1) && (out & 0x1))
 388                                dsp56k_host_interface.icr |= DSP56K_ICR_HF0;
 389                        else if (dir & 0x1)
 390                                dsp56k_host_interface.icr &= ~DSP56K_ICR_HF0;
 391                        if ((dir & 0x2) && (out & 0x2))
 392                                dsp56k_host_interface.icr |= DSP56K_ICR_HF1;
 393                        else if (dir & 0x2)
 394                                dsp56k_host_interface.icr &= ~DSP56K_ICR_HF1;
 395
 396                        status = 0;
 397                        if (dsp56k_host_interface.icr & DSP56K_ICR_HF0) status |= 0x1;
 398                        if (dsp56k_host_interface.icr & DSP56K_ICR_HF1) status |= 0x2;
 399                        if (dsp56k_host_interface.isr & DSP56K_ISR_HF2) status |= 0x4;
 400                        if (dsp56k_host_interface.isr & DSP56K_ISR_HF3) status |= 0x8;
 401
 402                        return put_user(status, &hf->status);
 403                }
 404                case DSP56K_HOST_CMD:
 405                        if (arg > 31 || arg < 0)
 406                                return -EINVAL;
 407                        dsp56k_host_interface.cvr = (u_char)((arg & DSP56K_CVR_HV_MASK) |
 408                                                             DSP56K_CVR_HC);
 409                        break;
 410                default:
 411                        return -EINVAL;
 412                }
 413                return 0;
 414
 415        default:
 416                printk(KERN_ERR "DSP56k driver: Unknown minor device: %d\n", dev);
 417                return -ENXIO;
 418        }
 419}
 420
 421/* As of 2.1.26 this should be dsp56k_poll,
 422 * but how do I then check device minor number?
 423 * Do I need this function at all???
 424 */
 425#if 0
 426static unsigned int dsp56k_poll(struct file *file, poll_table *wait)
 427{
 428        int dev = MINOR(file->f_dentry->d_inode->i_rdev) & 0x0f;
 429
 430        switch(dev)
 431        {
 432        case DSP56K_DEV_56001:
 433                /* poll_wait(file, ???, wait); */
 434                return POLLIN | POLLRDNORM | POLLOUT;
 435
 436        default:
 437                printk("DSP56k driver: Unknown minor device: %d\n", dev);
 438                return 0;
 439        }
 440}
 441#endif
 442
 443static int dsp56k_open(struct inode *inode, struct file *file)
 444{
 445        int dev = MINOR(inode->i_rdev) & 0x0f;
 446
 447        switch(dev)
 448        {
 449        case DSP56K_DEV_56001:
 450
 451                if (test_and_set_bit(0, &dsp56k.in_use))
 452                        return -EBUSY;
 453
 454                dsp56k.timeout = TIMEOUT;
 455                dsp56k.maxio = MAXIO;
 456                dsp56k.rx_wsize = dsp56k.tx_wsize = 4; 
 457
 458                DSP56K_TX_INT_OFF;
 459                DSP56K_RX_INT_OFF;
 460
 461                /* Zero host flags */
 462                dsp56k_host_interface.icr &= ~DSP56K_ICR_HF0;
 463                dsp56k_host_interface.icr &= ~DSP56K_ICR_HF1;
 464
 465                break;
 466
 467        default:
 468                return -ENODEV;
 469        }
 470
 471        return 0;
 472}
 473
 474static int dsp56k_release(struct inode *inode, struct file *file)
 475{
 476        int dev = MINOR(inode->i_rdev) & 0x0f;
 477
 478        switch(dev)
 479        {
 480        case DSP56K_DEV_56001:
 481                clear_bit(0, &dsp56k.in_use);
 482                break;
 483        default:
 484                printk(KERN_ERR "DSP56k driver: Unknown minor device: %d\n", dev);
 485                return -ENXIO;
 486        }
 487
 488        return 0;
 489}
 490
 491static struct file_operations dsp56k_fops = {
 492        owner:          THIS_MODULE,
 493        read:           dsp56k_read,
 494        write:          dsp56k_write,
 495        ioctl:          dsp56k_ioctl,
 496        open:           dsp56k_open,
 497        release:        dsp56k_release,
 498};
 499
 500
 501/****** Init and module functions ******/
 502
 503static devfs_handle_t devfs_handle;
 504
 505static char banner[] __initdata = KERN_INFO "DSP56k driver installed\n";
 506
 507static int __init dsp56k_init_driver(void)
 508{
 509        if(!MACH_IS_ATARI || !ATARIHW_PRESENT(DSP56K)) {
 510                printk("DSP56k driver: Hardware not present\n");
 511                return -ENODEV;
 512        }
 513
 514        if(devfs_register_chrdev(DSP56K_MAJOR, "dsp56k", &dsp56k_fops)) {
 515                printk("DSP56k driver: Unable to register driver\n");
 516                return -ENODEV;
 517        }
 518        devfs_handle = devfs_register(NULL, "dsp56k", DEVFS_FL_DEFAULT,
 519                                      DSP56K_MAJOR, 0,
 520                                      S_IFCHR | S_IRUSR | S_IWUSR,
 521                                      &dsp56k_fops, NULL);
 522
 523        printk(banner);
 524        return 0;
 525}
 526module_init(dsp56k_init_driver);
 527
 528static void __exit dsp56k_cleanup_driver(void)
 529{
 530        devfs_unregister_chrdev(DSP56K_MAJOR, "dsp56k");
 531        devfs_unregister(devfs_handle);
 532}
 533module_exit(dsp56k_cleanup_driver);
 534
 535MODULE_LICENSE("GPL");
 536
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.