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