Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * PC-Speaker driver for Linux
0004  *
0005  * Copyright (C) 1993-1997  Michael Beck
0006  * Copyright (C) 1997-2001  David Woodhouse
0007  * Copyright (C) 2001-2008  Stas Sergeev
0008  */
0009 
0010 #include <linux/module.h>
0011 #include <linux/gfp.h>
0012 #include <linux/moduleparam.h>
0013 #include <linux/interrupt.h>
0014 #include <linux/io.h>
0015 #include <sound/pcm.h>
0016 #include "pcsp.h"
0017 
0018 static bool nforce_wa;
0019 module_param(nforce_wa, bool, 0444);
0020 MODULE_PARM_DESC(nforce_wa, "Apply NForce chipset workaround "
0021         "(expect bad sound)");
0022 
0023 #define DMIX_WANTS_S16  1
0024 
0025 /*
0026  * Call snd_pcm_period_elapsed in a work
0027  * This avoids spinlock messes and long-running irq contexts
0028  */
0029 static void pcsp_call_pcm_elapsed(struct work_struct *work)
0030 {
0031     if (atomic_read(&pcsp_chip.timer_active)) {
0032         struct snd_pcm_substream *substream;
0033         substream = pcsp_chip.playback_substream;
0034         if (substream)
0035             snd_pcm_period_elapsed(substream);
0036     }
0037 }
0038 
0039 static DECLARE_WORK(pcsp_pcm_work, pcsp_call_pcm_elapsed);
0040 
0041 /* write the port and returns the next expire time in ns;
0042  * called at the trigger-start and in hrtimer callback
0043  */
0044 static u64 pcsp_timer_update(struct snd_pcsp *chip)
0045 {
0046     unsigned char timer_cnt, val;
0047     u64 ns;
0048     struct snd_pcm_substream *substream;
0049     struct snd_pcm_runtime *runtime;
0050     unsigned long flags;
0051 
0052     if (chip->thalf) {
0053         outb(chip->val61, 0x61);
0054         chip->thalf = 0;
0055         return chip->ns_rem;
0056     }
0057 
0058     substream = chip->playback_substream;
0059     if (!substream)
0060         return 0;
0061 
0062     runtime = substream->runtime;
0063     /* assume it is mono! */
0064     val = runtime->dma_area[chip->playback_ptr + chip->fmt_size - 1];
0065     if (chip->is_signed)
0066         val ^= 0x80;
0067     timer_cnt = val * CUR_DIV() / 256;
0068 
0069     if (timer_cnt && chip->enable) {
0070         raw_spin_lock_irqsave(&i8253_lock, flags);
0071         if (!nforce_wa) {
0072             outb_p(chip->val61, 0x61);
0073             outb_p(timer_cnt, 0x42);
0074             outb(chip->val61 ^ 1, 0x61);
0075         } else {
0076             outb(chip->val61 ^ 2, 0x61);
0077             chip->thalf = 1;
0078         }
0079         raw_spin_unlock_irqrestore(&i8253_lock, flags);
0080     }
0081 
0082     chip->ns_rem = PCSP_PERIOD_NS();
0083     ns = (chip->thalf ? PCSP_CALC_NS(timer_cnt) : chip->ns_rem);
0084     chip->ns_rem -= ns;
0085     return ns;
0086 }
0087 
0088 static void pcsp_pointer_update(struct snd_pcsp *chip)
0089 {
0090     struct snd_pcm_substream *substream;
0091     size_t period_bytes, buffer_bytes;
0092     int periods_elapsed;
0093     unsigned long flags;
0094 
0095     /* update the playback position */
0096     substream = chip->playback_substream;
0097     if (!substream)
0098         return;
0099 
0100     period_bytes = snd_pcm_lib_period_bytes(substream);
0101     buffer_bytes = snd_pcm_lib_buffer_bytes(substream);
0102 
0103     spin_lock_irqsave(&chip->substream_lock, flags);
0104     chip->playback_ptr += PCSP_INDEX_INC() * chip->fmt_size;
0105     periods_elapsed = chip->playback_ptr - chip->period_ptr;
0106     if (periods_elapsed < 0) {
0107 #if PCSP_DEBUG
0108         printk(KERN_INFO "PCSP: buffer_bytes mod period_bytes != 0 ? "
0109             "(%zi %zi %zi)\n",
0110             chip->playback_ptr, period_bytes, buffer_bytes);
0111 #endif
0112         periods_elapsed += buffer_bytes;
0113     }
0114     periods_elapsed /= period_bytes;
0115     /* wrap the pointer _before_ calling snd_pcm_period_elapsed(),
0116      * or ALSA will BUG on us. */
0117     chip->playback_ptr %= buffer_bytes;
0118 
0119     if (periods_elapsed) {
0120         chip->period_ptr += periods_elapsed * period_bytes;
0121         chip->period_ptr %= buffer_bytes;
0122         queue_work(system_highpri_wq, &pcsp_pcm_work);
0123     }
0124     spin_unlock_irqrestore(&chip->substream_lock, flags);
0125 }
0126 
0127 enum hrtimer_restart pcsp_do_timer(struct hrtimer *handle)
0128 {
0129     struct snd_pcsp *chip = container_of(handle, struct snd_pcsp, timer);
0130     int pointer_update;
0131     u64 ns;
0132 
0133     if (!atomic_read(&chip->timer_active) || !chip->playback_substream)
0134         return HRTIMER_NORESTART;
0135 
0136     pointer_update = !chip->thalf;
0137     ns = pcsp_timer_update(chip);
0138     if (!ns) {
0139         printk(KERN_WARNING "PCSP: unexpected stop\n");
0140         return HRTIMER_NORESTART;
0141     }
0142 
0143     if (pointer_update)
0144         pcsp_pointer_update(chip);
0145 
0146     hrtimer_forward_now(handle, ns_to_ktime(ns));
0147 
0148     return HRTIMER_RESTART;
0149 }
0150 
0151 static int pcsp_start_playing(struct snd_pcsp *chip)
0152 {
0153 #if PCSP_DEBUG
0154     printk(KERN_INFO "PCSP: start_playing called\n");
0155 #endif
0156     if (atomic_read(&chip->timer_active)) {
0157         printk(KERN_ERR "PCSP: Timer already active\n");
0158         return -EIO;
0159     }
0160 
0161     raw_spin_lock(&i8253_lock);
0162     chip->val61 = inb(0x61) | 0x03;
0163     outb_p(0x92, 0x43); /* binary, mode 1, LSB only, ch 2 */
0164     raw_spin_unlock(&i8253_lock);
0165     atomic_set(&chip->timer_active, 1);
0166     chip->thalf = 0;
0167 
0168     hrtimer_start(&pcsp_chip.timer, 0, HRTIMER_MODE_REL);
0169     return 0;
0170 }
0171 
0172 static void pcsp_stop_playing(struct snd_pcsp *chip)
0173 {
0174 #if PCSP_DEBUG
0175     printk(KERN_INFO "PCSP: stop_playing called\n");
0176 #endif
0177     if (!atomic_read(&chip->timer_active))
0178         return;
0179 
0180     atomic_set(&chip->timer_active, 0);
0181     raw_spin_lock(&i8253_lock);
0182     /* restore the timer */
0183     outb_p(0xb6, 0x43); /* binary, mode 3, LSB/MSB, ch 2 */
0184     outb(chip->val61 & 0xFC, 0x61);
0185     raw_spin_unlock(&i8253_lock);
0186 }
0187 
0188 /*
0189  * Force to stop and sync the stream
0190  */
0191 void pcsp_sync_stop(struct snd_pcsp *chip)
0192 {
0193     local_irq_disable();
0194     pcsp_stop_playing(chip);
0195     local_irq_enable();
0196     hrtimer_cancel(&chip->timer);
0197     cancel_work_sync(&pcsp_pcm_work);
0198 }
0199 
0200 static int snd_pcsp_playback_close(struct snd_pcm_substream *substream)
0201 {
0202     struct snd_pcsp *chip = snd_pcm_substream_chip(substream);
0203 #if PCSP_DEBUG
0204     printk(KERN_INFO "PCSP: close called\n");
0205 #endif
0206     pcsp_sync_stop(chip);
0207     chip->playback_substream = NULL;
0208     return 0;
0209 }
0210 
0211 static int snd_pcsp_playback_hw_params(struct snd_pcm_substream *substream,
0212                        struct snd_pcm_hw_params *hw_params)
0213 {
0214     struct snd_pcsp *chip = snd_pcm_substream_chip(substream);
0215     pcsp_sync_stop(chip);
0216     return 0;
0217 }
0218 
0219 static int snd_pcsp_playback_hw_free(struct snd_pcm_substream *substream)
0220 {
0221     struct snd_pcsp *chip = snd_pcm_substream_chip(substream);
0222 #if PCSP_DEBUG
0223     printk(KERN_INFO "PCSP: hw_free called\n");
0224 #endif
0225     pcsp_sync_stop(chip);
0226     return 0;
0227 }
0228 
0229 static int snd_pcsp_playback_prepare(struct snd_pcm_substream *substream)
0230 {
0231     struct snd_pcsp *chip = snd_pcm_substream_chip(substream);
0232     pcsp_sync_stop(chip);
0233     chip->playback_ptr = 0;
0234     chip->period_ptr = 0;
0235     chip->fmt_size =
0236         snd_pcm_format_physical_width(substream->runtime->format) >> 3;
0237     chip->is_signed = snd_pcm_format_signed(substream->runtime->format);
0238 #if PCSP_DEBUG
0239     printk(KERN_INFO "PCSP: prepare called, "
0240             "size=%zi psize=%zi f=%zi f1=%i fsize=%i\n",
0241             snd_pcm_lib_buffer_bytes(substream),
0242             snd_pcm_lib_period_bytes(substream),
0243             snd_pcm_lib_buffer_bytes(substream) /
0244             snd_pcm_lib_period_bytes(substream),
0245             substream->runtime->periods,
0246             chip->fmt_size);
0247 #endif
0248     return 0;
0249 }
0250 
0251 static int snd_pcsp_trigger(struct snd_pcm_substream *substream, int cmd)
0252 {
0253     struct snd_pcsp *chip = snd_pcm_substream_chip(substream);
0254 #if PCSP_DEBUG
0255     printk(KERN_INFO "PCSP: trigger called\n");
0256 #endif
0257     switch (cmd) {
0258     case SNDRV_PCM_TRIGGER_START:
0259     case SNDRV_PCM_TRIGGER_RESUME:
0260         return pcsp_start_playing(chip);
0261     case SNDRV_PCM_TRIGGER_STOP:
0262     case SNDRV_PCM_TRIGGER_SUSPEND:
0263         pcsp_stop_playing(chip);
0264         break;
0265     default:
0266         return -EINVAL;
0267     }
0268     return 0;
0269 }
0270 
0271 static snd_pcm_uframes_t snd_pcsp_playback_pointer(struct snd_pcm_substream
0272                            *substream)
0273 {
0274     struct snd_pcsp *chip = snd_pcm_substream_chip(substream);
0275     unsigned int pos;
0276     spin_lock(&chip->substream_lock);
0277     pos = chip->playback_ptr;
0278     spin_unlock(&chip->substream_lock);
0279     return bytes_to_frames(substream->runtime, pos);
0280 }
0281 
0282 static const struct snd_pcm_hardware snd_pcsp_playback = {
0283     .info = (SNDRV_PCM_INFO_INTERLEAVED |
0284          SNDRV_PCM_INFO_HALF_DUPLEX |
0285          SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID),
0286     .formats = (SNDRV_PCM_FMTBIT_U8
0287 #if DMIX_WANTS_S16
0288             | SNDRV_PCM_FMTBIT_S16_LE
0289 #endif
0290         ),
0291     .rates = SNDRV_PCM_RATE_KNOT,
0292     .rate_min = PCSP_DEFAULT_SRATE,
0293     .rate_max = PCSP_DEFAULT_SRATE,
0294     .channels_min = 1,
0295     .channels_max = 1,
0296     .buffer_bytes_max = PCSP_BUFFER_SIZE,
0297     .period_bytes_min = 64,
0298     .period_bytes_max = PCSP_MAX_PERIOD_SIZE,
0299     .periods_min = 2,
0300     .periods_max = PCSP_MAX_PERIODS,
0301     .fifo_size = 0,
0302 };
0303 
0304 static int snd_pcsp_playback_open(struct snd_pcm_substream *substream)
0305 {
0306     struct snd_pcsp *chip = snd_pcm_substream_chip(substream);
0307     struct snd_pcm_runtime *runtime = substream->runtime;
0308 #if PCSP_DEBUG
0309     printk(KERN_INFO "PCSP: open called\n");
0310 #endif
0311     if (atomic_read(&chip->timer_active)) {
0312         printk(KERN_ERR "PCSP: still active!!\n");
0313         return -EBUSY;
0314     }
0315     runtime->hw = snd_pcsp_playback;
0316     chip->playback_substream = substream;
0317     return 0;
0318 }
0319 
0320 static const struct snd_pcm_ops snd_pcsp_playback_ops = {
0321     .open = snd_pcsp_playback_open,
0322     .close = snd_pcsp_playback_close,
0323     .hw_params = snd_pcsp_playback_hw_params,
0324     .hw_free = snd_pcsp_playback_hw_free,
0325     .prepare = snd_pcsp_playback_prepare,
0326     .trigger = snd_pcsp_trigger,
0327     .pointer = snd_pcsp_playback_pointer,
0328 };
0329 
0330 int snd_pcsp_new_pcm(struct snd_pcsp *chip)
0331 {
0332     int err;
0333 
0334     err = snd_pcm_new(chip->card, "pcspeaker", 0, 1, 0, &chip->pcm);
0335     if (err < 0)
0336         return err;
0337 
0338     snd_pcm_set_ops(chip->pcm, SNDRV_PCM_STREAM_PLAYBACK,
0339             &snd_pcsp_playback_ops);
0340 
0341     chip->pcm->private_data = chip;
0342     chip->pcm->info_flags = SNDRV_PCM_INFO_HALF_DUPLEX;
0343     strcpy(chip->pcm->name, "pcsp");
0344 
0345     snd_pcm_set_managed_buffer_all(chip->pcm,
0346                        SNDRV_DMA_TYPE_CONTINUOUS,
0347                        NULL,
0348                        PCSP_BUFFER_SIZE,
0349                        PCSP_BUFFER_SIZE);
0350 
0351     return 0;
0352 }