dmasound_q40.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638
  1. /*
  2. * linux/sound/oss/dmasound/dmasound_q40.c
  3. *
  4. * Q40 DMA Sound Driver
  5. *
  6. * See linux/sound/oss/dmasound/dmasound_core.c for copyright and credits
  7. * prior to 28/01/2001
  8. *
  9. * 28/01/2001 [0.1] Iain Sandoe
  10. * - added versioning
  11. * - put in and populated the hardware_afmts field.
  12. * [0.2] - put in SNDCTL_DSP_GETCAPS value.
  13. * [0.3] - put in default hard/soft settings.
  14. */
  15. #include <linux/module.h>
  16. #include <linux/init.h>
  17. #include <linux/slab.h>
  18. #include <linux/soundcard.h>
  19. #include <linux/interrupt.h>
  20. #include <asm/uaccess.h>
  21. #include <asm/q40ints.h>
  22. #include <asm/q40_master.h>
  23. #include "dmasound.h"
  24. #define DMASOUND_Q40_REVISION 0
  25. #define DMASOUND_Q40_EDITION 3
  26. static int expand_bal; /* Balance factor for expanding (not volume!) */
  27. static int expand_data; /* Data for expanding */
  28. /*** Low level stuff *********************************************************/
  29. static void *Q40Alloc(unsigned int size, gfp_t flags);
  30. static void Q40Free(void *, unsigned int);
  31. static int Q40IrqInit(void);
  32. #ifdef MODULE
  33. static void Q40IrqCleanUp(void);
  34. #endif
  35. static void Q40Silence(void);
  36. static void Q40Init(void);
  37. static int Q40SetFormat(int format);
  38. static int Q40SetVolume(int volume);
  39. static void Q40PlayNextFrame(int index);
  40. static void Q40Play(void);
  41. static irqreturn_t Q40StereoInterrupt(int irq, void *dummy);
  42. static irqreturn_t Q40MonoInterrupt(int irq, void *dummy);
  43. static void Q40Interrupt(void);
  44. /*** Mid level stuff *********************************************************/
  45. /* userCount, frameUsed, frameLeft == byte counts */
  46. static ssize_t q40_ct_law(const u_char __user *userPtr, size_t userCount,
  47. u_char frame[], ssize_t *frameUsed,
  48. ssize_t frameLeft)
  49. {
  50. char *table = dmasound.soft.format == AFMT_MU_LAW ? dmasound_ulaw2dma8: dmasound_alaw2dma8;
  51. ssize_t count, used;
  52. u_char *p = (u_char *) &frame[*frameUsed];
  53. used = count = min_t(size_t, userCount, frameLeft);
  54. if (copy_from_user(p,userPtr,count))
  55. return -EFAULT;
  56. while (count > 0) {
  57. *p = table[*p]+128;
  58. p++;
  59. count--;
  60. }
  61. *frameUsed += used ;
  62. return used;
  63. }
  64. static ssize_t q40_ct_s8(const u_char __user *userPtr, size_t userCount,
  65. u_char frame[], ssize_t *frameUsed,
  66. ssize_t frameLeft)
  67. {
  68. ssize_t count, used;
  69. u_char *p = (u_char *) &frame[*frameUsed];
  70. used = count = min_t(size_t, userCount, frameLeft);
  71. if (copy_from_user(p,userPtr,count))
  72. return -EFAULT;
  73. while (count > 0) {
  74. *p = *p + 128;
  75. p++;
  76. count--;
  77. }
  78. *frameUsed += used;
  79. return used;
  80. }
  81. static ssize_t q40_ct_u8(const u_char __user *userPtr, size_t userCount,
  82. u_char frame[], ssize_t *frameUsed,
  83. ssize_t frameLeft)
  84. {
  85. ssize_t count, used;
  86. u_char *p = (u_char *) &frame[*frameUsed];
  87. used = count = min_t(size_t, userCount, frameLeft);
  88. if (copy_from_user(p,userPtr,count))
  89. return -EFAULT;
  90. *frameUsed += used;
  91. return used;
  92. }
  93. /* a bit too complicated to optimise right now ..*/
  94. static ssize_t q40_ctx_law(const u_char __user *userPtr, size_t userCount,
  95. u_char frame[], ssize_t *frameUsed,
  96. ssize_t frameLeft)
  97. {
  98. unsigned char *table = (unsigned char *)
  99. (dmasound.soft.format == AFMT_MU_LAW ? dmasound_ulaw2dma8: dmasound_alaw2dma8);
  100. unsigned int data = expand_data;
  101. u_char *p = (u_char *) &frame[*frameUsed];
  102. int bal = expand_bal;
  103. int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
  104. int utotal, ftotal;
  105. ftotal = frameLeft;
  106. utotal = userCount;
  107. while (frameLeft) {
  108. u_char c;
  109. if (bal < 0) {
  110. if (userCount == 0)
  111. break;
  112. if (get_user(c, userPtr++))
  113. return -EFAULT;
  114. data = table[c];
  115. data += 0x80;
  116. userCount--;
  117. bal += hSpeed;
  118. }
  119. *p++ = data;
  120. frameLeft--;
  121. bal -= sSpeed;
  122. }
  123. expand_bal = bal;
  124. expand_data = data;
  125. *frameUsed += (ftotal - frameLeft);
  126. utotal -= userCount;
  127. return utotal;
  128. }
  129. static ssize_t q40_ctx_s8(const u_char __user *userPtr, size_t userCount,
  130. u_char frame[], ssize_t *frameUsed,
  131. ssize_t frameLeft)
  132. {
  133. u_char *p = (u_char *) &frame[*frameUsed];
  134. unsigned int data = expand_data;
  135. int bal = expand_bal;
  136. int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
  137. int utotal, ftotal;
  138. ftotal = frameLeft;
  139. utotal = userCount;
  140. while (frameLeft) {
  141. u_char c;
  142. if (bal < 0) {
  143. if (userCount == 0)
  144. break;
  145. if (get_user(c, userPtr++))
  146. return -EFAULT;
  147. data = c ;
  148. data += 0x80;
  149. userCount--;
  150. bal += hSpeed;
  151. }
  152. *p++ = data;
  153. frameLeft--;
  154. bal -= sSpeed;
  155. }
  156. expand_bal = bal;
  157. expand_data = data;
  158. *frameUsed += (ftotal - frameLeft);
  159. utotal -= userCount;
  160. return utotal;
  161. }
  162. static ssize_t q40_ctx_u8(const u_char __user *userPtr, size_t userCount,
  163. u_char frame[], ssize_t *frameUsed,
  164. ssize_t frameLeft)
  165. {
  166. u_char *p = (u_char *) &frame[*frameUsed];
  167. unsigned int data = expand_data;
  168. int bal = expand_bal;
  169. int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
  170. int utotal, ftotal;
  171. ftotal = frameLeft;
  172. utotal = userCount;
  173. while (frameLeft) {
  174. u_char c;
  175. if (bal < 0) {
  176. if (userCount == 0)
  177. break;
  178. if (get_user(c, userPtr++))
  179. return -EFAULT;
  180. data = c ;
  181. userCount--;
  182. bal += hSpeed;
  183. }
  184. *p++ = data;
  185. frameLeft--;
  186. bal -= sSpeed;
  187. }
  188. expand_bal = bal;
  189. expand_data = data;
  190. *frameUsed += (ftotal - frameLeft) ;
  191. utotal -= userCount;
  192. return utotal;
  193. }
  194. /* compressing versions */
  195. static ssize_t q40_ctc_law(const u_char __user *userPtr, size_t userCount,
  196. u_char frame[], ssize_t *frameUsed,
  197. ssize_t frameLeft)
  198. {
  199. unsigned char *table = (unsigned char *)
  200. (dmasound.soft.format == AFMT_MU_LAW ? dmasound_ulaw2dma8: dmasound_alaw2dma8);
  201. unsigned int data = expand_data;
  202. u_char *p = (u_char *) &frame[*frameUsed];
  203. int bal = expand_bal;
  204. int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
  205. int utotal, ftotal;
  206. ftotal = frameLeft;
  207. utotal = userCount;
  208. while (frameLeft) {
  209. u_char c;
  210. while(bal<0) {
  211. if (userCount == 0)
  212. goto lout;
  213. if (!(bal<(-hSpeed))) {
  214. if (get_user(c, userPtr))
  215. return -EFAULT;
  216. data = 0x80 + table[c];
  217. }
  218. userPtr++;
  219. userCount--;
  220. bal += hSpeed;
  221. }
  222. *p++ = data;
  223. frameLeft--;
  224. bal -= sSpeed;
  225. }
  226. lout:
  227. expand_bal = bal;
  228. expand_data = data;
  229. *frameUsed += (ftotal - frameLeft);
  230. utotal -= userCount;
  231. return utotal;
  232. }
  233. static ssize_t q40_ctc_s8(const u_char __user *userPtr, size_t userCount,
  234. u_char frame[], ssize_t *frameUsed,
  235. ssize_t frameLeft)
  236. {
  237. u_char *p = (u_char *) &frame[*frameUsed];
  238. unsigned int data = expand_data;
  239. int bal = expand_bal;
  240. int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
  241. int utotal, ftotal;
  242. ftotal = frameLeft;
  243. utotal = userCount;
  244. while (frameLeft) {
  245. u_char c;
  246. while (bal < 0) {
  247. if (userCount == 0)
  248. goto lout;
  249. if (!(bal<(-hSpeed))) {
  250. if (get_user(c, userPtr))
  251. return -EFAULT;
  252. data = c + 0x80;
  253. }
  254. userPtr++;
  255. userCount--;
  256. bal += hSpeed;
  257. }
  258. *p++ = data;
  259. frameLeft--;
  260. bal -= sSpeed;
  261. }
  262. lout:
  263. expand_bal = bal;
  264. expand_data = data;
  265. *frameUsed += (ftotal - frameLeft);
  266. utotal -= userCount;
  267. return utotal;
  268. }
  269. static ssize_t q40_ctc_u8(const u_char __user *userPtr, size_t userCount,
  270. u_char frame[], ssize_t *frameUsed,
  271. ssize_t frameLeft)
  272. {
  273. u_char *p = (u_char *) &frame[*frameUsed];
  274. unsigned int data = expand_data;
  275. int bal = expand_bal;
  276. int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
  277. int utotal, ftotal;
  278. ftotal = frameLeft;
  279. utotal = userCount;
  280. while (frameLeft) {
  281. u_char c;
  282. while (bal < 0) {
  283. if (userCount == 0)
  284. goto lout;
  285. if (!(bal<(-hSpeed))) {
  286. if (get_user(c, userPtr))
  287. return -EFAULT;
  288. data = c ;
  289. }
  290. userPtr++;
  291. userCount--;
  292. bal += hSpeed;
  293. }
  294. *p++ = data;
  295. frameLeft--;
  296. bal -= sSpeed;
  297. }
  298. lout:
  299. expand_bal = bal;
  300. expand_data = data;
  301. *frameUsed += (ftotal - frameLeft) ;
  302. utotal -= userCount;
  303. return utotal;
  304. }
  305. static TRANS transQ40Normal = {
  306. q40_ct_law, q40_ct_law, q40_ct_s8, q40_ct_u8, NULL, NULL, NULL, NULL
  307. };
  308. static TRANS transQ40Expanding = {
  309. q40_ctx_law, q40_ctx_law, q40_ctx_s8, q40_ctx_u8, NULL, NULL, NULL, NULL
  310. };
  311. static TRANS transQ40Compressing = {
  312. q40_ctc_law, q40_ctc_law, q40_ctc_s8, q40_ctc_u8, NULL, NULL, NULL, NULL
  313. };
  314. /*** Low level stuff *********************************************************/
  315. static void *Q40Alloc(unsigned int size, gfp_t flags)
  316. {
  317. return kmalloc(size, flags); /* change to vmalloc */
  318. }
  319. static void Q40Free(void *ptr, unsigned int size)
  320. {
  321. kfree(ptr);
  322. }
  323. static int __init Q40IrqInit(void)
  324. {
  325. /* Register interrupt handler. */
  326. if (request_irq(Q40_IRQ_SAMPLE, Q40StereoInterrupt, 0,
  327. "DMA sound", Q40Interrupt))
  328. return 0;
  329. return(1);
  330. }
  331. #ifdef MODULE
  332. static void Q40IrqCleanUp(void)
  333. {
  334. master_outb(0,SAMPLE_ENABLE_REG);
  335. free_irq(Q40_IRQ_SAMPLE, Q40Interrupt);
  336. }
  337. #endif /* MODULE */
  338. static void Q40Silence(void)
  339. {
  340. master_outb(0,SAMPLE_ENABLE_REG);
  341. *DAC_LEFT=*DAC_RIGHT=127;
  342. }
  343. static char *q40_pp;
  344. static unsigned int q40_sc;
  345. static void Q40PlayNextFrame(int index)
  346. {
  347. u_char *start;
  348. u_long size;
  349. u_char speed;
  350. int error;
  351. /* used by Q40Play() if all doubts whether there really is something
  352. * to be played are already wiped out.
  353. */
  354. start = write_sq.buffers[write_sq.front];
  355. size = (write_sq.count == index ? write_sq.rear_size : write_sq.block_size);
  356. q40_pp=start;
  357. q40_sc=size;
  358. write_sq.front = (write_sq.front+1) % write_sq.max_count;
  359. write_sq.active++;
  360. speed=(dmasound.hard.speed==10000 ? 0 : 1);
  361. master_outb( 0,SAMPLE_ENABLE_REG);
  362. free_irq(Q40_IRQ_SAMPLE, Q40Interrupt);
  363. if (dmasound.soft.stereo)
  364. error = request_irq(Q40_IRQ_SAMPLE, Q40StereoInterrupt, 0,
  365. "Q40 sound", Q40Interrupt);
  366. else
  367. error = request_irq(Q40_IRQ_SAMPLE, Q40MonoInterrupt, 0,
  368. "Q40 sound", Q40Interrupt);
  369. if (error && printk_ratelimit())
  370. pr_err("Couldn't register sound interrupt\n");
  371. master_outb( speed, SAMPLE_RATE_REG);
  372. master_outb( 1,SAMPLE_CLEAR_REG);
  373. master_outb( 1,SAMPLE_ENABLE_REG);
  374. }
  375. static void Q40Play(void)
  376. {
  377. unsigned long flags;
  378. if (write_sq.active || write_sq.count<=0 ) {
  379. /* There's already a frame loaded */
  380. return;
  381. }
  382. /* nothing in the queue */
  383. if (write_sq.count <= 1 && write_sq.rear_size < write_sq.block_size && !write_sq.syncing) {
  384. /* hmmm, the only existing frame is not
  385. * yet filled and we're not syncing?
  386. */
  387. return;
  388. }
  389. spin_lock_irqsave(&dmasound.lock, flags);
  390. Q40PlayNextFrame(1);
  391. spin_unlock_irqrestore(&dmasound.lock, flags);
  392. }
  393. static irqreturn_t Q40StereoInterrupt(int irq, void *dummy)
  394. {
  395. spin_lock(&dmasound.lock);
  396. if (q40_sc>1){
  397. *DAC_LEFT=*q40_pp++;
  398. *DAC_RIGHT=*q40_pp++;
  399. q40_sc -=2;
  400. master_outb(1,SAMPLE_CLEAR_REG);
  401. }else Q40Interrupt();
  402. spin_unlock(&dmasound.lock);
  403. return IRQ_HANDLED;
  404. }
  405. static irqreturn_t Q40MonoInterrupt(int irq, void *dummy)
  406. {
  407. spin_lock(&dmasound.lock);
  408. if (q40_sc>0){
  409. *DAC_LEFT=*q40_pp;
  410. *DAC_RIGHT=*q40_pp++;
  411. q40_sc --;
  412. master_outb(1,SAMPLE_CLEAR_REG);
  413. }else Q40Interrupt();
  414. spin_unlock(&dmasound.lock);
  415. return IRQ_HANDLED;
  416. }
  417. static void Q40Interrupt(void)
  418. {
  419. if (!write_sq.active) {
  420. /* playing was interrupted and sq_reset() has already cleared
  421. * the sq variables, so better don't do anything here.
  422. */
  423. WAKE_UP(write_sq.sync_queue);
  424. master_outb(0,SAMPLE_ENABLE_REG); /* better safe */
  425. goto exit;
  426. } else write_sq.active=0;
  427. write_sq.count--;
  428. Q40Play();
  429. if (q40_sc<2)
  430. { /* there was nothing to play, disable irq */
  431. master_outb(0,SAMPLE_ENABLE_REG);
  432. *DAC_LEFT=*DAC_RIGHT=127;
  433. }
  434. WAKE_UP(write_sq.action_queue);
  435. exit:
  436. master_outb(1,SAMPLE_CLEAR_REG);
  437. }
  438. static void Q40Init(void)
  439. {
  440. int i, idx;
  441. const int freq[] = {10000, 20000};
  442. /* search a frequency that fits into the allowed error range */
  443. idx = -1;
  444. for (i = 0; i < 2; i++)
  445. if ((100 * abs(dmasound.soft.speed - freq[i]) / freq[i]) <= catchRadius)
  446. idx = i;
  447. dmasound.hard = dmasound.soft;
  448. /*sound.hard.stereo=1;*/ /* no longer true */
  449. dmasound.hard.size=8;
  450. if (idx > -1) {
  451. dmasound.soft.speed = freq[idx];
  452. dmasound.trans_write = &transQ40Normal;
  453. } else
  454. dmasound.trans_write = &transQ40Expanding;
  455. Q40Silence();
  456. if (dmasound.hard.speed > 20200) {
  457. /* squeeze the sound, we do that */
  458. dmasound.hard.speed = 20000;
  459. dmasound.trans_write = &transQ40Compressing;
  460. } else if (dmasound.hard.speed > 10000) {
  461. dmasound.hard.speed = 20000;
  462. } else {
  463. dmasound.hard.speed = 10000;
  464. }
  465. expand_bal = -dmasound.soft.speed;
  466. }
  467. static int Q40SetFormat(int format)
  468. {
  469. /* Q40 sound supports only 8bit modes */
  470. switch (format) {
  471. case AFMT_QUERY:
  472. return(dmasound.soft.format);
  473. case AFMT_MU_LAW:
  474. case AFMT_A_LAW:
  475. case AFMT_S8:
  476. case AFMT_U8:
  477. break;
  478. default:
  479. format = AFMT_S8;
  480. }
  481. dmasound.soft.format = format;
  482. dmasound.soft.size = 8;
  483. if (dmasound.minDev == SND_DEV_DSP) {
  484. dmasound.dsp.format = format;
  485. dmasound.dsp.size = 8;
  486. }
  487. Q40Init();
  488. return(format);
  489. }
  490. static int Q40SetVolume(int volume)
  491. {
  492. return 0;
  493. }
  494. /*** Machine definitions *****************************************************/
  495. static SETTINGS def_hard = {
  496. .format = AFMT_U8,
  497. .stereo = 0,
  498. .size = 8,
  499. .speed = 10000
  500. } ;
  501. static SETTINGS def_soft = {
  502. .format = AFMT_U8,
  503. .stereo = 0,
  504. .size = 8,
  505. .speed = 8000
  506. } ;
  507. static MACHINE machQ40 = {
  508. .name = "Q40",
  509. .name2 = "Q40",
  510. .owner = THIS_MODULE,
  511. .dma_alloc = Q40Alloc,
  512. .dma_free = Q40Free,
  513. .irqinit = Q40IrqInit,
  514. #ifdef MODULE
  515. .irqcleanup = Q40IrqCleanUp,
  516. #endif /* MODULE */
  517. .init = Q40Init,
  518. .silence = Q40Silence,
  519. .setFormat = Q40SetFormat,
  520. .setVolume = Q40SetVolume,
  521. .play = Q40Play,
  522. .min_dsp_speed = 10000,
  523. .version = ((DMASOUND_Q40_REVISION<<8) | DMASOUND_Q40_EDITION),
  524. .hardware_afmts = AFMT_U8, /* h'ware-supported formats *only* here */
  525. .capabilities = DSP_CAP_BATCH /* As per SNDCTL_DSP_GETCAPS */
  526. };
  527. /*** Config & Setup **********************************************************/
  528. static int __init dmasound_q40_init(void)
  529. {
  530. if (MACH_IS_Q40) {
  531. dmasound.mach = machQ40;
  532. dmasound.mach.default_hard = def_hard ;
  533. dmasound.mach.default_soft = def_soft ;
  534. return dmasound_init();
  535. } else
  536. return -ENODEV;
  537. }
  538. static void __exit dmasound_q40_cleanup(void)
  539. {
  540. dmasound_deinit();
  541. }
  542. module_init(dmasound_q40_init);
  543. module_exit(dmasound_q40_cleanup);
  544. MODULE_DESCRIPTION("Q40/Q60 sound driver");
  545. MODULE_LICENSE("GPL");