536 lines · c
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 19969 *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.2615 *16 * BUGS:17 * Hmm... there must be something here :)18 *19 * Copyright (C) 1996,1997 Fredrik Noring, lars brinkhoff & Tomas Berndtsson20 *21 * This file is subject to the terms and conditions of the GNU General Public22 * License. See the file COPYING in the main directory of this archive23 * for more details.24 */25 26#include <linux/module.h>27#include <linux/major.h>28#include <linux/types.h>29#include <linux/errno.h>30#include <linux/delay.h> /* guess what */31#include <linux/fs.h>32#include <linux/mm.h>33#include <linux/init.h>34#include <linux/device.h>35#include <linux/mutex.h>36#include <linux/firmware.h>37#include <linux/platform_device.h>38#include <linux/uaccess.h> /* For put_user and get_user */39 40#include <asm/atarihw.h>41#include <asm/traps.h>42 43#include <asm/dsp56k.h>44 45/* minor devices */46#define DSP56K_DEV_56001 0 /* The only device so far */47 48#define TIMEOUT 10 /* Host port timeout in number of tries */49#define MAXIO 2048 /* Maximum number of words before sleep */50#define DSP56K_MAX_BINARY_LENGTH (3*64*1024)51 52#define DSP56K_TX_INT_ON dsp56k_host_interface.icr |= DSP56K_ICR_TREQ53#define DSP56K_RX_INT_ON dsp56k_host_interface.icr |= DSP56K_ICR_RREQ54#define DSP56K_TX_INT_OFF dsp56k_host_interface.icr &= ~DSP56K_ICR_TREQ55#define DSP56K_RX_INT_OFF dsp56k_host_interface.icr &= ~DSP56K_ICR_RREQ56 57#define DSP56K_TRANSMIT (dsp56k_host_interface.isr & DSP56K_ISR_TXDE)58#define DSP56K_RECEIVE (dsp56k_host_interface.isr & DSP56K_ISR_RXDF)59 60#define handshake(count, maxio, timeout, ENABLE, f) \61{ \62 long i, t, m; \63 while (count > 0) { \64 m = min_t(unsigned long, count, maxio); \65 for (i = 0; i < m; i++) { \66 for (t = 0; t < timeout && !ENABLE; t++) \67 msleep(20); \68 if(!ENABLE) \69 return -EIO; \70 f; \71 } \72 count -= m; \73 if (m == maxio) msleep(20); \74 } \75}76 77#define tx_wait(n) \78{ \79 int t; \80 for(t = 0; t < n && !DSP56K_TRANSMIT; t++) \81 msleep(10); \82 if(!DSP56K_TRANSMIT) { \83 return -EIO; \84 } \85}86 87#define rx_wait(n) \88{ \89 int t; \90 for(t = 0; t < n && !DSP56K_RECEIVE; t++) \91 msleep(10); \92 if(!DSP56K_RECEIVE) { \93 return -EIO; \94 } \95}96 97static DEFINE_MUTEX(dsp56k_mutex);98static struct dsp56k_device {99 unsigned long in_use;100 long maxio, timeout;101 int tx_wsize, rx_wsize;102} dsp56k;103 104static const struct class dsp56k_class = {105 .name = "dsp56k",106};107 108static int dsp56k_reset(void)109{110 u_char status;111 112 /* Power down the DSP */113 sound_ym.rd_data_reg_sel = 14;114 status = sound_ym.rd_data_reg_sel & 0xef;115 sound_ym.wd_data = status;116 sound_ym.wd_data = status | 0x10;117 118 udelay(10);119 120 /* Power up the DSP */121 sound_ym.rd_data_reg_sel = 14;122 sound_ym.wd_data = sound_ym.rd_data_reg_sel & 0xef;123 124 return 0;125}126 127static int dsp56k_upload(u_char __user *bin, int len)128{129 struct platform_device *pdev;130 const struct firmware *fw;131 const char fw_name[] = "dsp56k/bootstrap.bin";132 int err;133 int i;134 135 dsp56k_reset();136 137 pdev = platform_device_register_simple("dsp56k", 0, NULL, 0);138 if (IS_ERR(pdev)) {139 printk(KERN_ERR "Failed to register device for \"%s\"\n",140 fw_name);141 return -EINVAL;142 }143 err = request_firmware(&fw, fw_name, &pdev->dev);144 platform_device_unregister(pdev);145 if (err) {146 printk(KERN_ERR "Failed to load image \"%s\" err %d\n",147 fw_name, err);148 return err;149 }150 if (fw->size % 3) {151 printk(KERN_ERR "Bogus length %d in image \"%s\"\n",152 fw->size, fw_name);153 release_firmware(fw);154 return -EINVAL;155 }156 for (i = 0; i < fw->size; i = i + 3) {157 /* tx_wait(10); */158 dsp56k_host_interface.data.b[1] = fw->data[i];159 dsp56k_host_interface.data.b[2] = fw->data[i + 1];160 dsp56k_host_interface.data.b[3] = fw->data[i + 2];161 }162 release_firmware(fw);163 for (; i < 512; i++) {164 /* tx_wait(10); */165 dsp56k_host_interface.data.b[1] = 0;166 dsp56k_host_interface.data.b[2] = 0;167 dsp56k_host_interface.data.b[3] = 0;168 }169 170 for (i = 0; i < len; i++) {171 tx_wait(10);172 get_user(dsp56k_host_interface.data.b[1], bin++);173 get_user(dsp56k_host_interface.data.b[2], bin++);174 get_user(dsp56k_host_interface.data.b[3], bin++);175 }176 177 tx_wait(10);178 dsp56k_host_interface.data.l = 3; /* Magic execute */179 180 return 0;181}182 183static ssize_t dsp56k_read(struct file *file, char __user *buf, size_t count,184 loff_t *ppos)185{186 struct inode *inode = file_inode(file);187 int dev = iminor(inode) & 0x0f;188 189 switch(dev)190 {191 case DSP56K_DEV_56001:192 {193 194 long n;195 196 /* Don't do anything if nothing is to be done */197 if (!count) return 0;198 199 n = 0;200 switch (dsp56k.rx_wsize) {201 case 1: /* 8 bit */202 {203 handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_RECEIVE,204 put_user(dsp56k_host_interface.data.b[3], buf+n++));205 return n;206 }207 case 2: /* 16 bit */208 {209 short __user *data;210 211 count /= 2;212 data = (short __user *) buf;213 handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_RECEIVE,214 put_user(dsp56k_host_interface.data.w[1], data+n++));215 return 2*n;216 }217 case 3: /* 24 bit */218 {219 count /= 3;220 handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_RECEIVE,221 put_user(dsp56k_host_interface.data.b[1], buf+n++);222 put_user(dsp56k_host_interface.data.b[2], buf+n++);223 put_user(dsp56k_host_interface.data.b[3], buf+n++));224 return 3*n;225 }226 case 4: /* 32 bit */227 {228 long __user *data;229 230 count /= 4;231 data = (long __user *) buf;232 handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_RECEIVE,233 put_user(dsp56k_host_interface.data.l, data+n++));234 return 4*n;235 }236 }237 return -EFAULT;238 }239 240 default:241 printk(KERN_ERR "DSP56k driver: Unknown minor device: %d\n", dev);242 return -ENXIO;243 }244}245 246static ssize_t dsp56k_write(struct file *file, const char __user *buf, size_t count,247 loff_t *ppos)248{249 struct inode *inode = file_inode(file);250 int dev = iminor(inode) & 0x0f;251 252 switch(dev)253 {254 case DSP56K_DEV_56001:255 {256 long n;257 258 /* Don't do anything if nothing is to be done */259 if (!count) return 0;260 261 n = 0;262 switch (dsp56k.tx_wsize) {263 case 1: /* 8 bit */264 {265 handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_TRANSMIT,266 get_user(dsp56k_host_interface.data.b[3], buf+n++));267 return n;268 }269 case 2: /* 16 bit */270 {271 const short __user *data;272 273 count /= 2;274 data = (const short __user *)buf;275 handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_TRANSMIT,276 get_user(dsp56k_host_interface.data.w[1], data+n++));277 return 2*n;278 }279 case 3: /* 24 bit */280 {281 count /= 3;282 handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_TRANSMIT,283 get_user(dsp56k_host_interface.data.b[1], buf+n++);284 get_user(dsp56k_host_interface.data.b[2], buf+n++);285 get_user(dsp56k_host_interface.data.b[3], buf+n++));286 return 3*n;287 }288 case 4: /* 32 bit */289 {290 const long __user *data;291 292 count /= 4;293 data = (const long __user *)buf;294 handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_TRANSMIT,295 get_user(dsp56k_host_interface.data.l, data+n++));296 return 4*n;297 }298 }299 300 return -EFAULT;301 }302 default:303 printk(KERN_ERR "DSP56k driver: Unknown minor device: %d\n", dev);304 return -ENXIO;305 }306}307 308static long dsp56k_ioctl(struct file *file, unsigned int cmd,309 unsigned long arg)310{311 int dev = iminor(file_inode(file)) & 0x0f;312 void __user *argp = (void __user *)arg;313 314 switch(dev)315 {316 case DSP56K_DEV_56001:317 318 switch(cmd) {319 case DSP56K_UPLOAD:320 {321 char __user *bin;322 int r, len;323 struct dsp56k_upload __user *binary = argp;324 325 if(get_user(len, &binary->len) < 0)326 return -EFAULT;327 if(get_user(bin, &binary->bin) < 0)328 return -EFAULT;329 330 if (len <= 0) {331 return -EINVAL; /* nothing to upload?!? */332 }333 if (len > DSP56K_MAX_BINARY_LENGTH) {334 return -EINVAL;335 }336 mutex_lock(&dsp56k_mutex);337 r = dsp56k_upload(bin, len);338 mutex_unlock(&dsp56k_mutex);339 if (r < 0) {340 return r;341 }342 343 break;344 }345 case DSP56K_SET_TX_WSIZE:346 if (arg > 4 || arg < 1)347 return -EINVAL;348 mutex_lock(&dsp56k_mutex);349 dsp56k.tx_wsize = (int) arg;350 mutex_unlock(&dsp56k_mutex);351 break;352 case DSP56K_SET_RX_WSIZE:353 if (arg > 4 || arg < 1)354 return -EINVAL;355 mutex_lock(&dsp56k_mutex);356 dsp56k.rx_wsize = (int) arg;357 mutex_unlock(&dsp56k_mutex);358 break;359 case DSP56K_HOST_FLAGS:360 {361 int dir, out, status;362 struct dsp56k_host_flags __user *hf = argp;363 364 if(get_user(dir, &hf->dir) < 0)365 return -EFAULT;366 if(get_user(out, &hf->out) < 0)367 return -EFAULT;368 369 mutex_lock(&dsp56k_mutex);370 if ((dir & 0x1) && (out & 0x1))371 dsp56k_host_interface.icr |= DSP56K_ICR_HF0;372 else if (dir & 0x1)373 dsp56k_host_interface.icr &= ~DSP56K_ICR_HF0;374 if ((dir & 0x2) && (out & 0x2))375 dsp56k_host_interface.icr |= DSP56K_ICR_HF1;376 else if (dir & 0x2)377 dsp56k_host_interface.icr &= ~DSP56K_ICR_HF1;378 379 status = 0;380 if (dsp56k_host_interface.icr & DSP56K_ICR_HF0) status |= 0x1;381 if (dsp56k_host_interface.icr & DSP56K_ICR_HF1) status |= 0x2;382 if (dsp56k_host_interface.isr & DSP56K_ISR_HF2) status |= 0x4;383 if (dsp56k_host_interface.isr & DSP56K_ISR_HF3) status |= 0x8;384 mutex_unlock(&dsp56k_mutex);385 return put_user(status, &hf->status);386 }387 case DSP56K_HOST_CMD:388 if (arg > 31)389 return -EINVAL;390 mutex_lock(&dsp56k_mutex);391 dsp56k_host_interface.cvr = (u_char)((arg & DSP56K_CVR_HV_MASK) |392 DSP56K_CVR_HC);393 mutex_unlock(&dsp56k_mutex);394 break;395 default:396 return -EINVAL;397 }398 return 0;399 400 default:401 printk(KERN_ERR "DSP56k driver: Unknown minor device: %d\n", dev);402 return -ENXIO;403 }404}405 406/* As of 2.1.26 this should be dsp56k_poll,407 * but how do I then check device minor number?408 * Do I need this function at all???409 */410#if 0411static __poll_t dsp56k_poll(struct file *file, poll_table *wait)412{413 int dev = iminor(file_inode(file)) & 0x0f;414 415 switch(dev)416 {417 case DSP56K_DEV_56001:418 /* poll_wait(file, ???, wait); */419 return EPOLLIN | EPOLLRDNORM | EPOLLOUT;420 421 default:422 printk("DSP56k driver: Unknown minor device: %d\n", dev);423 return 0;424 }425}426#endif427 428static int dsp56k_open(struct inode *inode, struct file *file)429{430 int dev = iminor(inode) & 0x0f;431 int ret = 0;432 433 mutex_lock(&dsp56k_mutex);434 switch(dev)435 {436 case DSP56K_DEV_56001:437 438 if (test_and_set_bit(0, &dsp56k.in_use)) {439 ret = -EBUSY;440 goto out;441 }442 443 dsp56k.timeout = TIMEOUT;444 dsp56k.maxio = MAXIO;445 dsp56k.rx_wsize = dsp56k.tx_wsize = 4; 446 447 DSP56K_TX_INT_OFF;448 DSP56K_RX_INT_OFF;449 450 /* Zero host flags */451 dsp56k_host_interface.icr &= ~DSP56K_ICR_HF0;452 dsp56k_host_interface.icr &= ~DSP56K_ICR_HF1;453 454 break;455 456 default:457 ret = -ENODEV;458 }459out:460 mutex_unlock(&dsp56k_mutex);461 return ret;462}463 464static int dsp56k_release(struct inode *inode, struct file *file)465{466 int dev = iminor(inode) & 0x0f;467 468 switch(dev)469 {470 case DSP56K_DEV_56001:471 clear_bit(0, &dsp56k.in_use);472 break;473 default:474 printk(KERN_ERR "DSP56k driver: Unknown minor device: %d\n", dev);475 return -ENXIO;476 }477 478 return 0;479}480 481static const struct file_operations dsp56k_fops = {482 .owner = THIS_MODULE,483 .read = dsp56k_read,484 .write = dsp56k_write,485 .unlocked_ioctl = dsp56k_ioctl,486 .open = dsp56k_open,487 .release = dsp56k_release,488 .llseek = noop_llseek,489};490 491 492/****** Init and module functions ******/493 494static const char banner[] __initconst = KERN_INFO "DSP56k driver installed\n";495 496static int __init dsp56k_init_driver(void)497{498 int err;499 500 if(!MACH_IS_ATARI || !ATARIHW_PRESENT(DSP56K)) {501 printk("DSP56k driver: Hardware not present\n");502 return -ENODEV;503 }504 505 if(register_chrdev(DSP56K_MAJOR, "dsp56k", &dsp56k_fops)) {506 printk("DSP56k driver: Unable to register driver\n");507 return -ENODEV;508 }509 err = class_register(&dsp56k_class);510 if (err)511 goto out_chrdev;512 device_create(&dsp56k_class, NULL, MKDEV(DSP56K_MAJOR, 0), NULL,513 "dsp56k");514 515 printk(banner);516 goto out;517 518out_chrdev:519 unregister_chrdev(DSP56K_MAJOR, "dsp56k");520out:521 return err;522}523module_init(dsp56k_init_driver);524 525static void __exit dsp56k_cleanup_driver(void)526{527 device_destroy(&dsp56k_class, MKDEV(DSP56K_MAJOR, 0));528 class_unregister(&dsp56k_class);529 unregister_chrdev(DSP56K_MAJOR, "dsp56k");530}531module_exit(dsp56k_cleanup_driver);532 533MODULE_DESCRIPTION("Atari DSP56001 Device Driver");534MODULE_LICENSE("GPL");535MODULE_FIRMWARE("dsp56k/bootstrap.bin");536