dmasound_q40.c 14 KB

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