diff options
Diffstat (limited to 'sound')
-rw-r--r-- | sound/drivers/pcsp/pcsp.h | 2 | ||||
-rw-r--r-- | sound/drivers/pcsp/pcsp_lib.c | 87 |
2 files changed, 58 insertions, 31 deletions
diff --git a/sound/drivers/pcsp/pcsp.h b/sound/drivers/pcsp/pcsp.h index 70533a333b5b..cdef2664218f 100644 --- a/sound/drivers/pcsp/pcsp.h +++ b/sound/drivers/pcsp/pcsp.h | |||
@@ -62,6 +62,8 @@ struct snd_pcsp { | |||
62 | unsigned short port, irq, dma; | 62 | unsigned short port, irq, dma; |
63 | spinlock_t substream_lock; | 63 | spinlock_t substream_lock; |
64 | struct snd_pcm_substream *playback_substream; | 64 | struct snd_pcm_substream *playback_substream; |
65 | unsigned int fmt_size; | ||
66 | unsigned int is_signed; | ||
65 | size_t playback_ptr; | 67 | size_t playback_ptr; |
66 | size_t period_ptr; | 68 | size_t period_ptr; |
67 | atomic_t timer_active; | 69 | atomic_t timer_active; |
diff --git a/sound/drivers/pcsp/pcsp_lib.c b/sound/drivers/pcsp/pcsp_lib.c index f8d8470861da..84cc2658c05b 100644 --- a/sound/drivers/pcsp/pcsp_lib.c +++ b/sound/drivers/pcsp/pcsp_lib.c | |||
@@ -36,12 +36,13 @@ static void pcsp_call_pcm_elapsed(unsigned long priv) | |||
36 | 36 | ||
37 | static DECLARE_TASKLET(pcsp_pcm_tasklet, pcsp_call_pcm_elapsed, 0); | 37 | static DECLARE_TASKLET(pcsp_pcm_tasklet, pcsp_call_pcm_elapsed, 0); |
38 | 38 | ||
39 | enum hrtimer_restart pcsp_do_timer(struct hrtimer *handle) | 39 | /* write the port and returns the next expire time in ns; |
40 | * called at the trigger-start and in hrtimer callback | ||
41 | */ | ||
42 | static unsigned long pcsp_timer_update(struct hrtimer *handle) | ||
40 | { | 43 | { |
41 | unsigned char timer_cnt, val; | 44 | unsigned char timer_cnt, val; |
42 | int fmt_size, periods_elapsed; | ||
43 | u64 ns; | 45 | u64 ns; |
44 | size_t period_bytes, buffer_bytes; | ||
45 | struct snd_pcm_substream *substream; | 46 | struct snd_pcm_substream *substream; |
46 | struct snd_pcm_runtime *runtime; | 47 | struct snd_pcm_runtime *runtime; |
47 | struct snd_pcsp *chip = container_of(handle, struct snd_pcsp, timer); | 48 | struct snd_pcsp *chip = container_of(handle, struct snd_pcsp, timer); |
@@ -51,28 +52,25 @@ enum hrtimer_restart pcsp_do_timer(struct hrtimer *handle) | |||
51 | outb(chip->val61, 0x61); | 52 | outb(chip->val61, 0x61); |
52 | chip->thalf = 0; | 53 | chip->thalf = 0; |
53 | if (!atomic_read(&chip->timer_active)) | 54 | if (!atomic_read(&chip->timer_active)) |
54 | goto stop; | 55 | return 0; |
55 | hrtimer_forward(&chip->timer, hrtimer_get_expires(&chip->timer), | 56 | return chip->ns_rem; |
56 | ktime_set(0, chip->ns_rem)); | ||
57 | return HRTIMER_RESTART; | ||
58 | } | 57 | } |
59 | 58 | ||
60 | if (!atomic_read(&chip->timer_active)) | 59 | if (!atomic_read(&chip->timer_active)) |
61 | goto stop; | 60 | return 0; |
62 | substream = chip->playback_substream; | 61 | substream = chip->playback_substream; |
63 | if (!substream) | 62 | if (!substream) |
64 | goto stop; | 63 | return 0; |
65 | 64 | ||
66 | runtime = substream->runtime; | 65 | runtime = substream->runtime; |
67 | fmt_size = snd_pcm_format_physical_width(runtime->format) >> 3; | ||
68 | /* assume it is mono! */ | 66 | /* assume it is mono! */ |
69 | val = runtime->dma_area[chip->playback_ptr + fmt_size - 1]; | 67 | val = runtime->dma_area[chip->playback_ptr + chip->fmt_size - 1]; |
70 | if (snd_pcm_format_signed(runtime->format)) | 68 | if (chip->is_signed) |
71 | val ^= 0x80; | 69 | val ^= 0x80; |
72 | timer_cnt = val * CUR_DIV() / 256; | 70 | timer_cnt = val * CUR_DIV() / 256; |
73 | 71 | ||
74 | if (timer_cnt && chip->enable) { | 72 | if (timer_cnt && chip->enable) { |
75 | spin_lock(&i8253_lock); | 73 | spin_lock_irqsave(&i8253_lock, flags); |
76 | if (!nforce_wa) { | 74 | if (!nforce_wa) { |
77 | outb_p(chip->val61, 0x61); | 75 | outb_p(chip->val61, 0x61); |
78 | outb_p(timer_cnt, 0x42); | 76 | outb_p(timer_cnt, 0x42); |
@@ -81,14 +79,39 @@ enum hrtimer_restart pcsp_do_timer(struct hrtimer *handle) | |||
81 | outb(chip->val61 ^ 2, 0x61); | 79 | outb(chip->val61 ^ 2, 0x61); |
82 | chip->thalf = 1; | 80 | chip->thalf = 1; |
83 | } | 81 | } |
84 | spin_unlock(&i8253_lock); | 82 | spin_unlock_irqrestore(&i8253_lock, flags); |
85 | } | 83 | } |
86 | 84 | ||
85 | chip->ns_rem = PCSP_PERIOD_NS(); | ||
86 | ns = (chip->thalf ? PCSP_CALC_NS(timer_cnt) : chip->ns_rem); | ||
87 | chip->ns_rem -= ns; | ||
88 | return ns; | ||
89 | } | ||
90 | |||
91 | enum hrtimer_restart pcsp_do_timer(struct hrtimer *handle) | ||
92 | { | ||
93 | struct snd_pcsp *chip = container_of(handle, struct snd_pcsp, timer); | ||
94 | struct snd_pcm_substream *substream; | ||
95 | int periods_elapsed, pointer_update; | ||
96 | size_t period_bytes, buffer_bytes; | ||
97 | unsigned long ns; | ||
98 | unsigned long flags; | ||
99 | |||
100 | pointer_update = !chip->thalf; | ||
101 | ns = pcsp_timer_update(handle); | ||
102 | if (!ns) | ||
103 | return HRTIMER_NORESTART; | ||
104 | |||
105 | /* update the playback position */ | ||
106 | substream = chip->playback_substream; | ||
107 | if (!substream) | ||
108 | return HRTIMER_NORESTART; | ||
109 | |||
87 | period_bytes = snd_pcm_lib_period_bytes(substream); | 110 | period_bytes = snd_pcm_lib_period_bytes(substream); |
88 | buffer_bytes = snd_pcm_lib_buffer_bytes(substream); | 111 | buffer_bytes = snd_pcm_lib_buffer_bytes(substream); |
89 | 112 | ||
90 | spin_lock_irqsave(&chip->substream_lock, flags); | 113 | spin_lock_irqsave(&chip->substream_lock, flags); |
91 | chip->playback_ptr += PCSP_INDEX_INC() * fmt_size; | 114 | chip->playback_ptr += PCSP_INDEX_INC() * chip->fmt_size; |
92 | periods_elapsed = chip->playback_ptr - chip->period_ptr; | 115 | periods_elapsed = chip->playback_ptr - chip->period_ptr; |
93 | if (periods_elapsed < 0) { | 116 | if (periods_elapsed < 0) { |
94 | #if PCSP_DEBUG | 117 | #if PCSP_DEBUG |
@@ -106,32 +129,27 @@ enum hrtimer_restart pcsp_do_timer(struct hrtimer *handle) | |||
106 | if (periods_elapsed) { | 129 | if (periods_elapsed) { |
107 | chip->period_ptr += periods_elapsed * period_bytes; | 130 | chip->period_ptr += periods_elapsed * period_bytes; |
108 | chip->period_ptr %= buffer_bytes; | 131 | chip->period_ptr %= buffer_bytes; |
109 | tasklet_schedule(&pcsp_pcm_tasklet); | ||
110 | } | 132 | } |
111 | spin_unlock_irqrestore(&chip->substream_lock, flags); | 133 | spin_unlock_irqrestore(&chip->substream_lock, flags); |
112 | 134 | ||
113 | if (!atomic_read(&chip->timer_active)) | 135 | if (periods_elapsed) |
114 | goto stop; | 136 | tasklet_schedule(&pcsp_pcm_tasklet); |
115 | 137 | ||
116 | chip->ns_rem = PCSP_PERIOD_NS(); | 138 | hrtimer_forward(handle, hrtimer_get_expires(handle), ns_to_ktime(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 | 139 | ||
123 | stop: | 140 | return HRTIMER_RESTART; |
124 | return HRTIMER_NORESTART; | ||
125 | } | 141 | } |
126 | 142 | ||
127 | static void pcsp_start_playing(struct snd_pcsp *chip) | 143 | static int pcsp_start_playing(struct snd_pcsp *chip) |
128 | { | 144 | { |
145 | unsigned long ns; | ||
146 | |||
129 | #if PCSP_DEBUG | 147 | #if PCSP_DEBUG |
130 | printk(KERN_INFO "PCSP: start_playing called\n"); | 148 | printk(KERN_INFO "PCSP: start_playing called\n"); |
131 | #endif | 149 | #endif |
132 | if (atomic_read(&chip->timer_active)) { | 150 | if (atomic_read(&chip->timer_active)) { |
133 | printk(KERN_ERR "PCSP: Timer already active\n"); | 151 | printk(KERN_ERR "PCSP: Timer already active\n"); |
134 | return; | 152 | return -EIO; |
135 | } | 153 | } |
136 | 154 | ||
137 | spin_lock(&i8253_lock); | 155 | spin_lock(&i8253_lock); |
@@ -141,7 +159,12 @@ static void pcsp_start_playing(struct snd_pcsp *chip) | |||
141 | atomic_set(&chip->timer_active, 1); | 159 | atomic_set(&chip->timer_active, 1); |
142 | chip->thalf = 0; | 160 | chip->thalf = 0; |
143 | 161 | ||
144 | hrtimer_start(&pcsp_chip.timer, ktime_set(0, 0), HRTIMER_MODE_REL); | 162 | ns = pcsp_timer_update(&pcsp_chip.timer); |
163 | if (!ns) | ||
164 | return -EIO; | ||
165 | |||
166 | hrtimer_start(&pcsp_chip.timer, ktime_set(0, ns), HRTIMER_MODE_REL); | ||
167 | return 0; | ||
145 | } | 168 | } |
146 | 169 | ||
147 | static void pcsp_stop_playing(struct snd_pcsp *chip) | 170 | static void pcsp_stop_playing(struct snd_pcsp *chip) |
@@ -221,6 +244,9 @@ static int snd_pcsp_playback_prepare(struct snd_pcm_substream *substream) | |||
221 | pcsp_sync_stop(chip); | 244 | pcsp_sync_stop(chip); |
222 | chip->playback_ptr = 0; | 245 | chip->playback_ptr = 0; |
223 | chip->period_ptr = 0; | 246 | chip->period_ptr = 0; |
247 | chip->fmt_size = | ||
248 | snd_pcm_format_physical_width(substream->runtime->format) >> 3; | ||
249 | chip->is_signed = snd_pcm_format_signed(substream->runtime->format); | ||
224 | return 0; | 250 | return 0; |
225 | } | 251 | } |
226 | 252 | ||
@@ -233,8 +259,7 @@ static int snd_pcsp_trigger(struct snd_pcm_substream *substream, int cmd) | |||
233 | switch (cmd) { | 259 | switch (cmd) { |
234 | case SNDRV_PCM_TRIGGER_START: | 260 | case SNDRV_PCM_TRIGGER_START: |
235 | case SNDRV_PCM_TRIGGER_RESUME: | 261 | case SNDRV_PCM_TRIGGER_RESUME: |
236 | pcsp_start_playing(chip); | 262 | return pcsp_start_playing(chip); |
237 | break; | ||
238 | case SNDRV_PCM_TRIGGER_STOP: | 263 | case SNDRV_PCM_TRIGGER_STOP: |
239 | case SNDRV_PCM_TRIGGER_SUSPEND: | 264 | case SNDRV_PCM_TRIGGER_SUSPEND: |
240 | pcsp_stop_playing(chip); | 265 | pcsp_stop_playing(chip); |