vidc.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558
  1. /*
  2. * linux/drivers/sound/vidc.c
  3. *
  4. * Copyright (C) 1997-2000 by Russell King <rmk@arm.linux.org.uk>
  5. *
  6. * This program is free software; you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License version 2 as
  8. * published by the Free Software Foundation.
  9. *
  10. * VIDC20 audio driver.
  11. *
  12. * The VIDC20 sound hardware consists of the VIDC20 itself, a DAC and a DMA
  13. * engine. The DMA transfers fixed-format (16-bit little-endian linear)
  14. * samples to the VIDC20, which then transfers this data serially to the
  15. * DACs. The samplerate is controlled by the VIDC.
  16. *
  17. * We currently support a mixer device, but it is currently non-functional.
  18. */
  19. #include <linux/gfp.h>
  20. #include <linux/init.h>
  21. #include <linux/module.h>
  22. #include <linux/kernel.h>
  23. #include <linux/interrupt.h>
  24. #include <mach/hardware.h>
  25. #include <asm/dma.h>
  26. #include <asm/io.h>
  27. #include <asm/hardware/iomd.h>
  28. #include <asm/irq.h>
  29. #include "sound_config.h"
  30. #include "vidc.h"
  31. #ifndef _SIOC_TYPE
  32. #define _SIOC_TYPE(x) _IOC_TYPE(x)
  33. #endif
  34. #ifndef _SIOC_NR
  35. #define _SIOC_NR(x) _IOC_NR(x)
  36. #endif
  37. #define VIDC_SOUND_CLOCK (250000)
  38. #define VIDC_SOUND_CLOCK_EXT (176400)
  39. /*
  40. * When using SERIAL SOUND mode (external DAC), the number of physical
  41. * channels is fixed at 2.
  42. */
  43. static int vidc_busy;
  44. static int vidc_adev;
  45. static int vidc_audio_rate;
  46. static char vidc_audio_format;
  47. static char vidc_audio_channels;
  48. static unsigned char vidc_level_l[SOUND_MIXER_NRDEVICES] = {
  49. 85, /* master */
  50. 50, /* bass */
  51. 50, /* treble */
  52. 0, /* synth */
  53. 75, /* pcm */
  54. 0, /* speaker */
  55. 100, /* ext line */
  56. 0, /* mic */
  57. 100, /* CD */
  58. 0,
  59. };
  60. static unsigned char vidc_level_r[SOUND_MIXER_NRDEVICES] = {
  61. 85, /* master */
  62. 50, /* bass */
  63. 50, /* treble */
  64. 0, /* synth */
  65. 75, /* pcm */
  66. 0, /* speaker */
  67. 100, /* ext line */
  68. 0, /* mic */
  69. 100, /* CD */
  70. 0,
  71. };
  72. static unsigned int vidc_audio_volume_l; /* left PCM vol, 0 - 65536 */
  73. static unsigned int vidc_audio_volume_r; /* right PCM vol, 0 - 65536 */
  74. extern void vidc_update_filler(int bits, int channels);
  75. extern int softoss_dev;
  76. static void
  77. vidc_mixer_set(int mdev, unsigned int level)
  78. {
  79. unsigned int lev_l = level & 0x007f;
  80. unsigned int lev_r = (level & 0x7f00) >> 8;
  81. unsigned int mlev_l, mlev_r;
  82. if (lev_l > 100)
  83. lev_l = 100;
  84. if (lev_r > 100)
  85. lev_r = 100;
  86. #define SCALE(lev,master) ((lev) * (master) * 65536 / 10000)
  87. mlev_l = vidc_level_l[SOUND_MIXER_VOLUME];
  88. mlev_r = vidc_level_r[SOUND_MIXER_VOLUME];
  89. switch (mdev) {
  90. case SOUND_MIXER_VOLUME:
  91. case SOUND_MIXER_PCM:
  92. vidc_level_l[mdev] = lev_l;
  93. vidc_level_r[mdev] = lev_r;
  94. vidc_audio_volume_l = SCALE(lev_l, mlev_l);
  95. vidc_audio_volume_r = SCALE(lev_r, mlev_r);
  96. /*printk("VIDC: PCM vol %05X %05X\n", vidc_audio_volume_l, vidc_audio_volume_r);*/
  97. break;
  98. }
  99. #undef SCALE
  100. }
  101. static int vidc_mixer_ioctl(int dev, unsigned int cmd, void __user *arg)
  102. {
  103. unsigned int val;
  104. unsigned int mdev;
  105. if (_SIOC_TYPE(cmd) != 'M')
  106. return -EINVAL;
  107. mdev = _SIOC_NR(cmd);
  108. if (_SIOC_DIR(cmd) & _SIOC_WRITE) {
  109. if (get_user(val, (unsigned int __user *)arg))
  110. return -EFAULT;
  111. if (mdev < SOUND_MIXER_NRDEVICES)
  112. vidc_mixer_set(mdev, val);
  113. else
  114. return -EINVAL;
  115. }
  116. /*
  117. * Return parameters
  118. */
  119. switch (mdev) {
  120. case SOUND_MIXER_RECSRC:
  121. val = 0;
  122. break;
  123. case SOUND_MIXER_DEVMASK:
  124. val = SOUND_MASK_VOLUME | SOUND_MASK_PCM | SOUND_MASK_SYNTH;
  125. break;
  126. case SOUND_MIXER_STEREODEVS:
  127. val = SOUND_MASK_VOLUME | SOUND_MASK_PCM | SOUND_MASK_SYNTH;
  128. break;
  129. case SOUND_MIXER_RECMASK:
  130. val = 0;
  131. break;
  132. case SOUND_MIXER_CAPS:
  133. val = 0;
  134. break;
  135. default:
  136. if (mdev < SOUND_MIXER_NRDEVICES)
  137. val = vidc_level_l[mdev] | vidc_level_r[mdev] << 8;
  138. else
  139. return -EINVAL;
  140. }
  141. return put_user(val, (unsigned int __user *)arg) ? -EFAULT : 0;
  142. }
  143. static unsigned int vidc_audio_set_format(int dev, unsigned int fmt)
  144. {
  145. switch (fmt) {
  146. default:
  147. fmt = AFMT_S16_LE;
  148. case AFMT_U8:
  149. case AFMT_S8:
  150. case AFMT_S16_LE:
  151. vidc_audio_format = fmt;
  152. vidc_update_filler(vidc_audio_format, vidc_audio_channels);
  153. case AFMT_QUERY:
  154. break;
  155. }
  156. return vidc_audio_format;
  157. }
  158. #define my_abs(i) ((i)<0 ? -(i) : (i))
  159. static int vidc_audio_set_speed(int dev, int rate)
  160. {
  161. if (rate) {
  162. unsigned int hwctrl, hwrate, hwrate_ext, rate_int, rate_ext;
  163. unsigned int diff_int, diff_ext;
  164. unsigned int newsize, new2size;
  165. hwctrl = 0x00000003;
  166. /* Using internal clock */
  167. hwrate = (((VIDC_SOUND_CLOCK * 2) / rate) + 1) >> 1;
  168. if (hwrate < 3)
  169. hwrate = 3;
  170. if (hwrate > 255)
  171. hwrate = 255;
  172. /* Using exernal clock */
  173. hwrate_ext = (((VIDC_SOUND_CLOCK_EXT * 2) / rate) + 1) >> 1;
  174. if (hwrate_ext < 3)
  175. hwrate_ext = 3;
  176. if (hwrate_ext > 255)
  177. hwrate_ext = 255;
  178. rate_int = VIDC_SOUND_CLOCK / hwrate;
  179. rate_ext = VIDC_SOUND_CLOCK_EXT / hwrate_ext;
  180. /* Chose between external and internal clock */
  181. diff_int = my_abs(rate_ext-rate);
  182. diff_ext = my_abs(rate_int-rate);
  183. if (diff_ext < diff_int) {
  184. /*printk("VIDC: external %d %d %d\n", rate, rate_ext, hwrate_ext);*/
  185. hwrate=hwrate_ext;
  186. hwctrl=0x00000002;
  187. /* Allow roughly 0.4% tolerance */
  188. if (diff_ext > (rate/256))
  189. rate=rate_ext;
  190. } else {
  191. /*printk("VIDC: internal %d %d %d\n", rate, rate_int, hwrate);*/
  192. hwctrl=0x00000003;
  193. /* Allow roughly 0.4% tolerance */
  194. if (diff_int > (rate/256))
  195. rate=rate_int;
  196. }
  197. vidc_writel(0xb0000000 | (hwrate - 2));
  198. vidc_writel(0xb1000000 | hwctrl);
  199. newsize = (10000 / hwrate) & ~3;
  200. if (newsize < 208)
  201. newsize = 208;
  202. if (newsize > 4096)
  203. newsize = 4096;
  204. for (new2size = 128; new2size < newsize; new2size <<= 1);
  205. if (new2size - newsize > newsize - (new2size >> 1))
  206. new2size >>= 1;
  207. if (new2size > 4096) {
  208. printk(KERN_ERR "VIDC: error: dma buffer (%d) %d > 4K\n",
  209. newsize, new2size);
  210. new2size = 4096;
  211. }
  212. /*printk("VIDC: dma size %d\n", new2size);*/
  213. dma_bufsize = new2size;
  214. vidc_audio_rate = rate;
  215. }
  216. return vidc_audio_rate;
  217. }
  218. static short vidc_audio_set_channels(int dev, short channels)
  219. {
  220. switch (channels) {
  221. default:
  222. channels = 2;
  223. case 1:
  224. case 2:
  225. vidc_audio_channels = channels;
  226. vidc_update_filler(vidc_audio_format, vidc_audio_channels);
  227. case 0:
  228. break;
  229. }
  230. return vidc_audio_channels;
  231. }
  232. /*
  233. * Open the device
  234. */
  235. static int vidc_audio_open(int dev, int mode)
  236. {
  237. /* This audio device does not have recording capability */
  238. if (mode == OPEN_READ)
  239. return -EPERM;
  240. if (vidc_busy)
  241. return -EBUSY;
  242. vidc_busy = 1;
  243. return 0;
  244. }
  245. /*
  246. * Close the device
  247. */
  248. static void vidc_audio_close(int dev)
  249. {
  250. vidc_busy = 0;
  251. }
  252. /*
  253. * Output a block via DMA to sound device.
  254. *
  255. * We just set the DMA start and count; the DMA interrupt routine
  256. * will take care of formatting the samples (via the appropriate
  257. * vidc_filler routine), and flag via vidc_audio_dma_interrupt when
  258. * more data is required.
  259. */
  260. static void
  261. vidc_audio_output_block(int dev, unsigned long buf, int total_count, int one)
  262. {
  263. struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
  264. unsigned long flags;
  265. local_irq_save(flags);
  266. dma_start = buf - (unsigned long)dmap->raw_buf_phys + (unsigned long)dmap->raw_buf;
  267. dma_count = total_count;
  268. local_irq_restore(flags);
  269. }
  270. static void
  271. vidc_audio_start_input(int dev, unsigned long buf, int count, int intrflag)
  272. {
  273. }
  274. static int vidc_audio_prepare_for_input(int dev, int bsize, int bcount)
  275. {
  276. return -EINVAL;
  277. }
  278. static irqreturn_t vidc_audio_dma_interrupt(void)
  279. {
  280. DMAbuf_outputintr(vidc_adev, 1);
  281. return IRQ_HANDLED;
  282. }
  283. /*
  284. * Prepare for outputting samples.
  285. *
  286. * Each buffer that will be passed will be `bsize' bytes long,
  287. * with a total of `bcount' buffers.
  288. */
  289. static int vidc_audio_prepare_for_output(int dev, int bsize, int bcount)
  290. {
  291. struct audio_operations *adev = audio_devs[dev];
  292. dma_interrupt = NULL;
  293. adev->dmap_out->flags |= DMA_NODMA;
  294. return 0;
  295. }
  296. /*
  297. * Stop our current operation.
  298. */
  299. static void vidc_audio_reset(int dev)
  300. {
  301. dma_interrupt = NULL;
  302. }
  303. static int vidc_audio_local_qlen(int dev)
  304. {
  305. return /*dma_count !=*/ 0;
  306. }
  307. static void vidc_audio_trigger(int dev, int enable_bits)
  308. {
  309. struct audio_operations *adev = audio_devs[dev];
  310. if (enable_bits & PCM_ENABLE_OUTPUT) {
  311. if (!(adev->dmap_out->flags & DMA_ACTIVE)) {
  312. unsigned long flags;
  313. local_irq_save(flags);
  314. /* prevent recusion */
  315. adev->dmap_out->flags |= DMA_ACTIVE;
  316. dma_interrupt = vidc_audio_dma_interrupt;
  317. vidc_sound_dma_irq(0, NULL);
  318. iomd_writeb(DMA_CR_E | 0x10, IOMD_SD0CR);
  319. local_irq_restore(flags);
  320. }
  321. }
  322. }
  323. static struct audio_driver vidc_audio_driver =
  324. {
  325. .owner = THIS_MODULE,
  326. .open = vidc_audio_open,
  327. .close = vidc_audio_close,
  328. .output_block = vidc_audio_output_block,
  329. .start_input = vidc_audio_start_input,
  330. .prepare_for_input = vidc_audio_prepare_for_input,
  331. .prepare_for_output = vidc_audio_prepare_for_output,
  332. .halt_io = vidc_audio_reset,
  333. .local_qlen = vidc_audio_local_qlen,
  334. .trigger = vidc_audio_trigger,
  335. .set_speed = vidc_audio_set_speed,
  336. .set_bits = vidc_audio_set_format,
  337. .set_channels = vidc_audio_set_channels
  338. };
  339. static struct mixer_operations vidc_mixer_operations = {
  340. .owner = THIS_MODULE,
  341. .id = "VIDC",
  342. .name = "VIDCsound",
  343. .ioctl = vidc_mixer_ioctl
  344. };
  345. void vidc_update_filler(int format, int channels)
  346. {
  347. #define TYPE(fmt,ch) (((fmt)<<2) | ((ch)&3))
  348. switch (TYPE(format, channels)) {
  349. default:
  350. case TYPE(AFMT_U8, 1):
  351. vidc_filler = vidc_fill_1x8_u;
  352. break;
  353. case TYPE(AFMT_U8, 2):
  354. vidc_filler = vidc_fill_2x8_u;
  355. break;
  356. case TYPE(AFMT_S8, 1):
  357. vidc_filler = vidc_fill_1x8_s;
  358. break;
  359. case TYPE(AFMT_S8, 2):
  360. vidc_filler = vidc_fill_2x8_s;
  361. break;
  362. case TYPE(AFMT_S16_LE, 1):
  363. vidc_filler = vidc_fill_1x16_s;
  364. break;
  365. case TYPE(AFMT_S16_LE, 2):
  366. vidc_filler = vidc_fill_2x16_s;
  367. break;
  368. }
  369. }
  370. static void __init attach_vidc(struct address_info *hw_config)
  371. {
  372. char name[32];
  373. int i, adev;
  374. sprintf(name, "VIDC %d-bit sound", hw_config->card_subtype);
  375. conf_printf(name, hw_config);
  376. memset(dma_buf, 0, sizeof(dma_buf));
  377. adev = sound_install_audiodrv(AUDIO_DRIVER_VERSION, name,
  378. &vidc_audio_driver, sizeof(vidc_audio_driver),
  379. DMA_AUTOMODE, AFMT_U8 | AFMT_S8 | AFMT_S16_LE,
  380. NULL, hw_config->dma, hw_config->dma2);
  381. if (adev < 0)
  382. goto audio_failed;
  383. /*
  384. * 1024 bytes => 64 buffers
  385. */
  386. audio_devs[adev]->min_fragment = 10;
  387. audio_devs[adev]->mixer_dev = num_mixers;
  388. audio_devs[adev]->mixer_dev =
  389. sound_install_mixer(MIXER_DRIVER_VERSION,
  390. name, &vidc_mixer_operations,
  391. sizeof(vidc_mixer_operations), NULL);
  392. if (audio_devs[adev]->mixer_dev < 0)
  393. goto mixer_failed;
  394. for (i = 0; i < 2; i++) {
  395. dma_buf[i] = get_zeroed_page(GFP_KERNEL);
  396. if (!dma_buf[i]) {
  397. printk(KERN_ERR "%s: can't allocate required buffers\n",
  398. name);
  399. goto mem_failed;
  400. }
  401. dma_pbuf[i] = virt_to_phys((void *)dma_buf[i]);
  402. }
  403. if (sound_alloc_dma(hw_config->dma, hw_config->name)) {
  404. printk(KERN_ERR "%s: DMA %d is in use\n", name, hw_config->dma);
  405. goto dma_failed;
  406. }
  407. if (request_irq(hw_config->irq, vidc_sound_dma_irq, 0,
  408. hw_config->name, &dma_start)) {
  409. printk(KERN_ERR "%s: IRQ %d is in use\n", name, hw_config->irq);
  410. goto irq_failed;
  411. }
  412. vidc_adev = adev;
  413. vidc_mixer_set(SOUND_MIXER_VOLUME, (85 | 85 << 8));
  414. return;
  415. irq_failed:
  416. sound_free_dma(hw_config->dma);
  417. dma_failed:
  418. mem_failed:
  419. for (i = 0; i < 2; i++)
  420. free_page(dma_buf[i]);
  421. sound_unload_mixerdev(audio_devs[adev]->mixer_dev);
  422. mixer_failed:
  423. sound_unload_audiodev(adev);
  424. audio_failed:
  425. return;
  426. }
  427. static int __init probe_vidc(struct address_info *hw_config)
  428. {
  429. hw_config->irq = IRQ_DMAS0;
  430. hw_config->dma = DMA_VIRTUAL_SOUND;
  431. hw_config->dma2 = -1;
  432. hw_config->card_subtype = 16;
  433. hw_config->name = "VIDC20";
  434. return 1;
  435. }
  436. static void __exit unload_vidc(struct address_info *hw_config)
  437. {
  438. int i, adev = vidc_adev;
  439. vidc_adev = -1;
  440. free_irq(hw_config->irq, &dma_start);
  441. sound_free_dma(hw_config->dma);
  442. if (adev >= 0) {
  443. sound_unload_mixerdev(audio_devs[adev]->mixer_dev);
  444. sound_unload_audiodev(adev);
  445. for (i = 0; i < 2; i++)
  446. free_page(dma_buf[i]);
  447. }
  448. }
  449. static struct address_info cfg;
  450. static int __init init_vidc(void)
  451. {
  452. if (probe_vidc(&cfg) == 0)
  453. return -ENODEV;
  454. attach_vidc(&cfg);
  455. return 0;
  456. }
  457. static void __exit cleanup_vidc(void)
  458. {
  459. unload_vidc(&cfg);
  460. }
  461. module_init(init_vidc);
  462. module_exit(cleanup_vidc);
  463. MODULE_AUTHOR("Russell King");
  464. MODULE_DESCRIPTION("VIDC20 audio driver");
  465. MODULE_LICENSE("GPL");