f8d8470861dac9fa99dc4f7a4db179dd20e9a962
[linux-2.6.git] / sound / drivers / pcsp / pcsp_lib.c
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
9 #include <linux/module.h>
10 #include <linux/moduleparam.h>
11 #include <linux/interrupt.h>
12 #include <sound/pcm.h>
13 #include <asm/io.h>
14 #include "pcsp.h"
15
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
21 #define DMIX_WANTS_S16  1
22
23 /*
24  * Call snd_pcm_period_elapsed in a tasklet
25  * This avoids spinlock messes and long-running irq contexts
26  */
27 static void pcsp_call_pcm_elapsed(unsigned long priv)
28 {
29         if (atomic_read(&pcsp_chip.timer_active)) {
30                 struct snd_pcm_substream *substream;
31                 substream = pcsp_chip.playback_substream;
32                 if (substream)
33                         snd_pcm_period_elapsed(substream);
34         }
35 }
36
37 static DECLARE_TASKLET(pcsp_pcm_tasklet, pcsp_call_pcm_elapsed, 0);
38
39 enum hrtimer_restart pcsp_do_timer(struct hrtimer *handle)
40 {
41         unsigned char timer_cnt, val;
42         int fmt_size, periods_elapsed;
43         u64 ns;
44         size_t period_bytes, buffer_bytes;
45         struct snd_pcm_substream *substream;
46         struct snd_pcm_runtime *runtime;
47         struct snd_pcsp *chip = container_of(handle, struct snd_pcsp, timer);
48         unsigned long flags;
49
50         if (chip->thalf) {
51                 outb(chip->val61, 0x61);
52                 chip->thalf = 0;
53                 if (!atomic_read(&chip->timer_active))
54                         goto stop;
55                 hrtimer_forward(&chip->timer, hrtimer_get_expires(&chip->timer),
56                                 ktime_set(0, chip->ns_rem));
57                 return HRTIMER_RESTART;
58         }
59
60         if (!atomic_read(&chip->timer_active))
61                 goto stop;
62         substream = chip->playback_substream;
63         if (!substream)
64                 goto stop;
65
66         runtime = substream->runtime;
67         fmt_size = snd_pcm_format_physical_width(runtime->format) >> 3;
68         /* assume it is mono! */
69         val = runtime->dma_area[chip->playback_ptr + fmt_size - 1];
70         if (snd_pcm_format_signed(runtime->format))
71                 val ^= 0x80;
72         timer_cnt = val * CUR_DIV() / 256;
73
74         if (timer_cnt && chip->enable) {
75                 spin_lock(&i8253_lock);
76                 if (!nforce_wa) {
77                         outb_p(chip->val61, 0x61);
78                         outb_p(timer_cnt, 0x42);
79                         outb(chip->val61 ^ 1, 0x61);
80                 } else {
81                         outb(chip->val61 ^ 2, 0x61);
82                         chip->thalf = 1;
83                 }
84                 spin_unlock(&i8253_lock);
85         }
86
87         period_bytes = snd_pcm_lib_period_bytes(substream);
88         buffer_bytes = snd_pcm_lib_buffer_bytes(substream);
89
90         spin_lock_irqsave(&chip->substream_lock, flags);
91         chip->playback_ptr += PCSP_INDEX_INC() * fmt_size;
92         periods_elapsed = chip->playback_ptr - chip->period_ptr;
93         if (periods_elapsed < 0) {
94 #if PCSP_DEBUG
95                 printk(KERN_INFO "PCSP: buffer_bytes mod period_bytes != 0 ? "
96                         "(%zi %zi %zi)\n",
97                         chip->playback_ptr, period_bytes, buffer_bytes);
98 #endif
99                 periods_elapsed += buffer_bytes;
100         }
101         periods_elapsed /= period_bytes;
102         /* wrap the pointer _before_ calling snd_pcm_period_elapsed(),
103          * or ALSA will BUG on us. */
104         chip->playback_ptr %= buffer_bytes;
105
106         if (periods_elapsed) {
107                 chip->period_ptr += periods_elapsed * period_bytes;
108                 chip->period_ptr %= buffer_bytes;
109                 tasklet_schedule(&pcsp_pcm_tasklet);
110         }
111         spin_unlock_irqrestore(&chip->substream_lock, flags);
112
113         if (!atomic_read(&chip->timer_active))
114                 goto stop;
115
116         chip->ns_rem = PCSP_PERIOD_NS();
117         ns = (chip->thalf ? PCSP_CALC_NS(timer_cnt) : chip->ns_rem);
118         chip->ns_rem -= ns;
119         hrtimer_forward(&chip->timer, hrtimer_get_expires(&chip->timer),
120                                                         ktime_set(0, ns));
121         return HRTIMER_RESTART;
122
123  stop:
124         return HRTIMER_NORESTART;
125 }
126
127 static void pcsp_start_playing(struct snd_pcsp *chip)
128 {
129 #if PCSP_DEBUG
130         printk(KERN_INFO "PCSP: start_playing called\n");
131 #endif
132         if (atomic_read(&chip->timer_active)) {
133                 printk(KERN_ERR "PCSP: Timer already active\n");
134                 return;
135         }
136
137         spin_lock(&i8253_lock);
138         chip->val61 = inb(0x61) | 0x03;
139         outb_p(0x92, 0x43);     /* binary, mode 1, LSB only, ch 2 */
140         spin_unlock(&i8253_lock);
141         atomic_set(&chip->timer_active, 1);
142         chip->thalf = 0;
143
144         hrtimer_start(&pcsp_chip.timer, ktime_set(0, 0), HRTIMER_MODE_REL);
145 }
146
147 static void pcsp_stop_playing(struct snd_pcsp *chip)
148 {
149 #if PCSP_DEBUG
150         printk(KERN_INFO "PCSP: stop_playing called\n");
151 #endif
152         if (!atomic_read(&chip->timer_active))
153                 return;
154
155         atomic_set(&chip->timer_active, 0);
156         spin_lock(&i8253_lock);
157         /* restore the timer */
158         outb_p(0xb6, 0x43);     /* binary, mode 3, LSB/MSB, ch 2 */
159         outb(chip->val61 & 0xFC, 0x61);
160         spin_unlock(&i8253_lock);
161 }
162
163 /*
164  * Force to stop and sync the stream
165  */
166 void pcsp_sync_stop(struct snd_pcsp *chip)
167 {
168         local_irq_disable();
169         pcsp_stop_playing(chip);
170         local_irq_enable();
171         hrtimer_cancel(&chip->timer);
172         tasklet_kill(&pcsp_pcm_tasklet);
173 }
174
175 static int snd_pcsp_playback_close(struct snd_pcm_substream *substream)
176 {
177         struct snd_pcsp *chip = snd_pcm_substream_chip(substream);
178 #if PCSP_DEBUG
179         printk(KERN_INFO "PCSP: close called\n");
180 #endif
181         pcsp_sync_stop(chip);
182         chip->playback_substream = NULL;
183         return 0;
184 }
185
186 static int snd_pcsp_playback_hw_params(struct snd_pcm_substream *substream,
187                                        struct snd_pcm_hw_params *hw_params)
188 {
189         struct snd_pcsp *chip = snd_pcm_substream_chip(substream);
190         int err;
191         pcsp_sync_stop(chip);
192         err = snd_pcm_lib_malloc_pages(substream,
193                                       params_buffer_bytes(hw_params));
194         if (err < 0)
195                 return err;
196         return 0;
197 }
198
199 static int snd_pcsp_playback_hw_free(struct snd_pcm_substream *substream)
200 {
201         struct snd_pcsp *chip = snd_pcm_substream_chip(substream);
202 #if PCSP_DEBUG
203         printk(KERN_INFO "PCSP: hw_free called\n");
204 #endif
205         pcsp_sync_stop(chip);
206         return snd_pcm_lib_free_pages(substream);
207 }
208
209 static int snd_pcsp_playback_prepare(struct snd_pcm_substream *substream)
210 {
211         struct snd_pcsp *chip = snd_pcm_substream_chip(substream);
212 #if PCSP_DEBUG
213         printk(KERN_INFO "PCSP: prepare called, "
214                         "size=%zi psize=%zi f=%zi f1=%i\n",
215                         snd_pcm_lib_buffer_bytes(substream),
216                         snd_pcm_lib_period_bytes(substream),
217                         snd_pcm_lib_buffer_bytes(substream) /
218                         snd_pcm_lib_period_bytes(substream),
219                         substream->runtime->periods);
220 #endif
221         pcsp_sync_stop(chip);
222         chip->playback_ptr = 0;
223         chip->period_ptr = 0;
224         return 0;
225 }
226
227 static int snd_pcsp_trigger(struct snd_pcm_substream *substream, int cmd)
228 {
229         struct snd_pcsp *chip = snd_pcm_substream_chip(substream);
230 #if PCSP_DEBUG
231         printk(KERN_INFO "PCSP: trigger called\n");
232 #endif
233         switch (cmd) {
234         case SNDRV_PCM_TRIGGER_START:
235         case SNDRV_PCM_TRIGGER_RESUME:
236                 pcsp_start_playing(chip);
237                 break;
238         case SNDRV_PCM_TRIGGER_STOP:
239         case SNDRV_PCM_TRIGGER_SUSPEND:
240                 pcsp_stop_playing(chip);
241                 break;
242         default:
243                 return -EINVAL;
244         }
245         return 0;
246 }
247
248 static snd_pcm_uframes_t snd_pcsp_playback_pointer(struct snd_pcm_substream
249                                                    *substream)
250 {
251         struct snd_pcsp *chip = snd_pcm_substream_chip(substream);
252         unsigned int pos;
253         spin_lock(&chip->substream_lock);
254         pos = chip->playback_ptr;
255         spin_unlock(&chip->substream_lock);
256         return bytes_to_frames(substream->runtime, pos);
257 }
258
259 static struct snd_pcm_hardware snd_pcsp_playback = {
260         .info = (SNDRV_PCM_INFO_INTERLEAVED |
261                  SNDRV_PCM_INFO_HALF_DUPLEX |
262                  SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID),
263         .formats = (SNDRV_PCM_FMTBIT_U8
264 #if DMIX_WANTS_S16
265                     | SNDRV_PCM_FMTBIT_S16_LE
266 #endif
267             ),
268         .rates = SNDRV_PCM_RATE_KNOT,
269         .rate_min = PCSP_DEFAULT_SRATE,
270         .rate_max = PCSP_DEFAULT_SRATE,
271         .channels_min = 1,
272         .channels_max = 1,
273         .buffer_bytes_max = PCSP_BUFFER_SIZE,
274         .period_bytes_min = 64,
275         .period_bytes_max = PCSP_MAX_PERIOD_SIZE,
276         .periods_min = 2,
277         .periods_max = PCSP_MAX_PERIODS,
278         .fifo_size = 0,
279 };
280
281 static int snd_pcsp_playback_open(struct snd_pcm_substream *substream)
282 {
283         struct snd_pcsp *chip = snd_pcm_substream_chip(substream);
284         struct snd_pcm_runtime *runtime = substream->runtime;
285 #if PCSP_DEBUG
286         printk(KERN_INFO "PCSP: open called\n");
287 #endif
288         if (atomic_read(&chip->timer_active)) {
289                 printk(KERN_ERR "PCSP: still active!!\n");
290                 return -EBUSY;
291         }
292         runtime->hw = snd_pcsp_playback;
293         chip->playback_substream = substream;
294         return 0;
295 }
296
297 static struct snd_pcm_ops snd_pcsp_playback_ops = {
298         .open = snd_pcsp_playback_open,
299         .close = snd_pcsp_playback_close,
300         .ioctl = snd_pcm_lib_ioctl,
301         .hw_params = snd_pcsp_playback_hw_params,
302         .hw_free = snd_pcsp_playback_hw_free,
303         .prepare = snd_pcsp_playback_prepare,
304         .trigger = snd_pcsp_trigger,
305         .pointer = snd_pcsp_playback_pointer,
306 };
307
308 int __devinit snd_pcsp_new_pcm(struct snd_pcsp *chip)
309 {
310         int err;
311
312         err = snd_pcm_new(chip->card, "pcspeaker", 0, 1, 0, &chip->pcm);
313         if (err < 0)
314                 return err;
315
316         snd_pcm_set_ops(chip->pcm, SNDRV_PCM_STREAM_PLAYBACK,
317                         &snd_pcsp_playback_ops);
318
319         chip->pcm->private_data = chip;
320         chip->pcm->info_flags = SNDRV_PCM_INFO_HALF_DUPLEX;
321         strcpy(chip->pcm->name, "pcsp");
322
323         snd_pcm_lib_preallocate_pages_for_all(chip->pcm,
324                                               SNDRV_DMA_TYPE_CONTINUOUS,
325                                               snd_dma_continuous_data
326                                               (GFP_KERNEL), PCSP_BUFFER_SIZE,
327                                               PCSP_BUFFER_SIZE);
328
329         return 0;
330 }