pcsp_lib.c 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347
  1. /*
  2. * PC-Speaker driver for Linux
  3. *
  4. * Copyright (C) 1993-1997 Michael Beck
  5. * Copyright (C) 1997-2001 David Woodhouse
  6. * Copyright (C) 2001-2008 Stas Sergeev
  7. */
  8. #include <linux/module.h>
  9. #include <linux/moduleparam.h>
  10. #include <sound/pcm.h>
  11. #include <sound/pcm_params.h>
  12. #include <linux/interrupt.h>
  13. #include <asm/io.h>
  14. #include <asm/i8253.h>
  15. #include "pcsp.h"
  16. static int nforce_wa;
  17. module_param(nforce_wa, bool, 0444);
  18. MODULE_PARM_DESC(nforce_wa, "Apply NForce chipset workaround "
  19. "(expect bad sound)");
  20. #define DMIX_WANTS_S16 1
  21. static void pcsp_start_timer(unsigned long dummy)
  22. {
  23. hrtimer_start(&pcsp_chip.timer, ktime_set(0, 0), HRTIMER_MODE_REL);
  24. }
  25. /*
  26. * We need the hrtimer_start as a tasklet to avoid
  27. * the nasty locking problem. :(
  28. * The problem:
  29. * - The timer handler is called with the cpu_base->lock
  30. * already held by hrtimer code.
  31. * - snd_pcm_period_elapsed() takes the
  32. * substream->self_group.lock.
  33. * So far so good.
  34. * But the snd_pcsp_trigger() is called with the
  35. * substream->self_group.lock held, and it calls
  36. * hrtimer_start(), which takes the cpu_base->lock.
  37. * You see the problem. We have the code pathes
  38. * which take two locks in a reverse order. This
  39. * can deadlock and the lock validator complains.
  40. * The only solution I could find was to move the
  41. * hrtimer_start() into a tasklet. -stsp
  42. */
  43. DECLARE_TASKLET(pcsp_start_timer_tasklet, pcsp_start_timer, 0);
  44. enum hrtimer_restart pcsp_do_timer(struct hrtimer *handle)
  45. {
  46. unsigned long flags;
  47. unsigned char timer_cnt, val;
  48. int fmt_size, periods_elapsed;
  49. u64 ns;
  50. size_t period_bytes, buffer_bytes;
  51. struct snd_pcm_substream *substream;
  52. struct snd_pcm_runtime *runtime;
  53. struct snd_pcsp *chip = container_of(handle, struct snd_pcsp, timer);
  54. if (chip->thalf) {
  55. outb(chip->val61, 0x61);
  56. chip->thalf = 0;
  57. if (!atomic_read(&chip->timer_active))
  58. return HRTIMER_NORESTART;
  59. hrtimer_forward(&chip->timer, chip->timer.expires,
  60. ktime_set(0, chip->ns_rem));
  61. return HRTIMER_RESTART;
  62. }
  63. /* hrtimer calls us from both hardirq and softirq contexts,
  64. * so irqsave :( */
  65. spin_lock_irqsave(&chip->substream_lock, flags);
  66. /* Takashi Iwai says regarding this extra lock:
  67. If the irq handler handles some data on the DMA buffer, it should
  68. do snd_pcm_stream_lock().
  69. That protects basically against all races among PCM callbacks, yes.
  70. However, there are two remaining issues:
  71. 1. The substream pointer you try to lock isn't protected _before_
  72. this lock yet.
  73. 2. snd_pcm_period_elapsed() itself acquires the lock.
  74. The requirement of another lock is because of 1. When you get
  75. chip->playback_substream, it's not protected.
  76. Keeping this lock while snd_pcm_period_elapsed() assures the substream
  77. is still protected (at least, not released). And the other status is
  78. handled properly inside snd_pcm_stream_lock() in
  79. snd_pcm_period_elapsed().
  80. */
  81. if (!chip->playback_substream)
  82. goto exit_nr_unlock1;
  83. substream = chip->playback_substream;
  84. snd_pcm_stream_lock(substream);
  85. if (!atomic_read(&chip->timer_active))
  86. goto exit_nr_unlock2;
  87. runtime = substream->runtime;
  88. fmt_size = snd_pcm_format_physical_width(runtime->format) >> 3;
  89. /* assume it is mono! */
  90. val = runtime->dma_area[chip->playback_ptr + fmt_size - 1];
  91. if (snd_pcm_format_signed(runtime->format))
  92. val ^= 0x80;
  93. timer_cnt = val * CUR_DIV() / 256;
  94. if (timer_cnt && chip->enable) {
  95. spin_lock(&i8253_lock);
  96. if (!nforce_wa) {
  97. outb_p(chip->val61, 0x61);
  98. outb_p(timer_cnt, 0x42);
  99. outb(chip->val61 ^ 1, 0x61);
  100. } else {
  101. outb(chip->val61 ^ 2, 0x61);
  102. chip->thalf = 1;
  103. }
  104. spin_unlock(&i8253_lock);
  105. }
  106. period_bytes = snd_pcm_lib_period_bytes(substream);
  107. buffer_bytes = snd_pcm_lib_buffer_bytes(substream);
  108. chip->playback_ptr += PCSP_INDEX_INC() * fmt_size;
  109. periods_elapsed = chip->playback_ptr - chip->period_ptr;
  110. if (periods_elapsed < 0) {
  111. printk(KERN_WARNING "PCSP: playback_ptr inconsistent "
  112. "(%zi %zi %zi)\n",
  113. chip->playback_ptr, period_bytes, buffer_bytes);
  114. periods_elapsed += buffer_bytes;
  115. }
  116. periods_elapsed /= period_bytes;
  117. /* wrap the pointer _before_ calling snd_pcm_period_elapsed(),
  118. * or ALSA will BUG on us. */
  119. chip->playback_ptr %= buffer_bytes;
  120. snd_pcm_stream_unlock(substream);
  121. if (periods_elapsed) {
  122. snd_pcm_period_elapsed(substream);
  123. chip->period_ptr += periods_elapsed * period_bytes;
  124. chip->period_ptr %= buffer_bytes;
  125. }
  126. spin_unlock_irqrestore(&chip->substream_lock, flags);
  127. if (!atomic_read(&chip->timer_active))
  128. return HRTIMER_NORESTART;
  129. chip->ns_rem = PCSP_PERIOD_NS();
  130. ns = (chip->thalf ? PCSP_CALC_NS(timer_cnt) : chip->ns_rem);
  131. chip->ns_rem -= ns;
  132. hrtimer_forward(&chip->timer, chip->timer.expires, ktime_set(0, ns));
  133. return HRTIMER_RESTART;
  134. exit_nr_unlock2:
  135. snd_pcm_stream_unlock(substream);
  136. exit_nr_unlock1:
  137. spin_unlock_irqrestore(&chip->substream_lock, flags);
  138. return HRTIMER_NORESTART;
  139. }
  140. static void pcsp_start_playing(struct snd_pcsp *chip)
  141. {
  142. #if PCSP_DEBUG
  143. printk(KERN_INFO "PCSP: start_playing called\n");
  144. #endif
  145. if (atomic_read(&chip->timer_active)) {
  146. printk(KERN_ERR "PCSP: Timer already active\n");
  147. return;
  148. }
  149. spin_lock(&i8253_lock);
  150. chip->val61 = inb(0x61) | 0x03;
  151. outb_p(0x92, 0x43); /* binary, mode 1, LSB only, ch 2 */
  152. spin_unlock(&i8253_lock);
  153. atomic_set(&chip->timer_active, 1);
  154. chip->thalf = 0;
  155. tasklet_schedule(&pcsp_start_timer_tasklet);
  156. }
  157. static void pcsp_stop_playing(struct snd_pcsp *chip)
  158. {
  159. #if PCSP_DEBUG
  160. printk(KERN_INFO "PCSP: stop_playing called\n");
  161. #endif
  162. if (!atomic_read(&chip->timer_active))
  163. return;
  164. atomic_set(&chip->timer_active, 0);
  165. spin_lock(&i8253_lock);
  166. /* restore the timer */
  167. outb_p(0xb6, 0x43); /* binary, mode 3, LSB/MSB, ch 2 */
  168. outb(chip->val61 & 0xFC, 0x61);
  169. spin_unlock(&i8253_lock);
  170. }
  171. static int snd_pcsp_playback_close(struct snd_pcm_substream *substream)
  172. {
  173. struct snd_pcsp *chip = snd_pcm_substream_chip(substream);
  174. #if PCSP_DEBUG
  175. printk(KERN_INFO "PCSP: close called\n");
  176. #endif
  177. if (atomic_read(&chip->timer_active)) {
  178. printk(KERN_ERR "PCSP: timer still active\n");
  179. pcsp_stop_playing(chip);
  180. }
  181. spin_lock_irq(&chip->substream_lock);
  182. chip->playback_substream = NULL;
  183. spin_unlock_irq(&chip->substream_lock);
  184. return 0;
  185. }
  186. static int snd_pcsp_playback_hw_params(struct snd_pcm_substream *substream,
  187. struct snd_pcm_hw_params *hw_params)
  188. {
  189. int err;
  190. err = snd_pcm_lib_malloc_pages(substream,
  191. params_buffer_bytes(hw_params));
  192. if (err < 0)
  193. return err;
  194. return 0;
  195. }
  196. static int snd_pcsp_playback_hw_free(struct snd_pcm_substream *substream)
  197. {
  198. #if PCSP_DEBUG
  199. printk(KERN_INFO "PCSP: hw_free called\n");
  200. #endif
  201. return snd_pcm_lib_free_pages(substream);
  202. }
  203. static int snd_pcsp_playback_prepare(struct snd_pcm_substream *substream)
  204. {
  205. struct snd_pcsp *chip = snd_pcm_substream_chip(substream);
  206. #if PCSP_DEBUG
  207. printk(KERN_INFO "PCSP: prepare called, "
  208. "size=%zi psize=%zi f=%zi f1=%i\n",
  209. snd_pcm_lib_buffer_bytes(substream),
  210. snd_pcm_lib_period_bytes(substream),
  211. snd_pcm_lib_buffer_bytes(substream) /
  212. snd_pcm_lib_period_bytes(substream),
  213. substream->runtime->periods);
  214. #endif
  215. chip->playback_ptr = 0;
  216. chip->period_ptr = 0;
  217. return 0;
  218. }
  219. static int snd_pcsp_trigger(struct snd_pcm_substream *substream, int cmd)
  220. {
  221. struct snd_pcsp *chip = snd_pcm_substream_chip(substream);
  222. #if PCSP_DEBUG
  223. printk(KERN_INFO "PCSP: trigger called\n");
  224. #endif
  225. switch (cmd) {
  226. case SNDRV_PCM_TRIGGER_START:
  227. case SNDRV_PCM_TRIGGER_RESUME:
  228. pcsp_start_playing(chip);
  229. break;
  230. case SNDRV_PCM_TRIGGER_STOP:
  231. case SNDRV_PCM_TRIGGER_SUSPEND:
  232. pcsp_stop_playing(chip);
  233. break;
  234. default:
  235. return -EINVAL;
  236. }
  237. return 0;
  238. }
  239. static snd_pcm_uframes_t snd_pcsp_playback_pointer(struct snd_pcm_substream
  240. *substream)
  241. {
  242. struct snd_pcsp *chip = snd_pcm_substream_chip(substream);
  243. return bytes_to_frames(substream->runtime, chip->playback_ptr);
  244. }
  245. static struct snd_pcm_hardware snd_pcsp_playback = {
  246. .info = (SNDRV_PCM_INFO_INTERLEAVED |
  247. SNDRV_PCM_INFO_HALF_DUPLEX |
  248. SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID),
  249. .formats = (SNDRV_PCM_FMTBIT_U8
  250. #if DMIX_WANTS_S16
  251. | SNDRV_PCM_FMTBIT_S16_LE
  252. #endif
  253. ),
  254. .rates = SNDRV_PCM_RATE_KNOT,
  255. .rate_min = PCSP_DEFAULT_SRATE,
  256. .rate_max = PCSP_DEFAULT_SRATE,
  257. .channels_min = 1,
  258. .channels_max = 1,
  259. .buffer_bytes_max = PCSP_BUFFER_SIZE,
  260. .period_bytes_min = 64,
  261. .period_bytes_max = PCSP_MAX_PERIOD_SIZE,
  262. .periods_min = 2,
  263. .periods_max = PCSP_MAX_PERIODS,
  264. .fifo_size = 0,
  265. };
  266. static int snd_pcsp_playback_open(struct snd_pcm_substream *substream)
  267. {
  268. struct snd_pcsp *chip = snd_pcm_substream_chip(substream);
  269. struct snd_pcm_runtime *runtime = substream->runtime;
  270. #if PCSP_DEBUG
  271. printk(KERN_INFO "PCSP: open called\n");
  272. #endif
  273. if (atomic_read(&chip->timer_active)) {
  274. printk(KERN_ERR "PCSP: still active!!\n");
  275. return -EBUSY;
  276. }
  277. runtime->hw = snd_pcsp_playback;
  278. chip->playback_substream = substream;
  279. return 0;
  280. }
  281. static struct snd_pcm_ops snd_pcsp_playback_ops = {
  282. .open = snd_pcsp_playback_open,
  283. .close = snd_pcsp_playback_close,
  284. .ioctl = snd_pcm_lib_ioctl,
  285. .hw_params = snd_pcsp_playback_hw_params,
  286. .hw_free = snd_pcsp_playback_hw_free,
  287. .prepare = snd_pcsp_playback_prepare,
  288. .trigger = snd_pcsp_trigger,
  289. .pointer = snd_pcsp_playback_pointer,
  290. };
  291. int __devinit snd_pcsp_new_pcm(struct snd_pcsp *chip)
  292. {
  293. int err;
  294. err = snd_pcm_new(chip->card, "pcspeaker", 0, 1, 0, &chip->pcm);
  295. if (err < 0)
  296. return err;
  297. snd_pcm_set_ops(chip->pcm, SNDRV_PCM_STREAM_PLAYBACK,
  298. &snd_pcsp_playback_ops);
  299. chip->pcm->private_data = chip;
  300. chip->pcm->info_flags = SNDRV_PCM_INFO_HALF_DUPLEX;
  301. strcpy(chip->pcm->name, "pcsp");
  302. snd_pcm_lib_preallocate_pages_for_all(chip->pcm,
  303. SNDRV_DMA_TYPE_CONTINUOUS,
  304. snd_dma_continuous_data
  305. (GFP_KERNEL), PCSP_BUFFER_SIZE,
  306. PCSP_BUFFER_SIZE);
  307. return 0;
  308. }