f8d8470861dac9fa99dc4f7a4db179dd20e9a962
[GitHub/mt8127/android_kernel_alcatel_ttab.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 }