diff options
author | Takashi Iwai <tiwai@suse.de> | 2008-11-26 08:13:03 -0500 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2008-11-26 08:13:03 -0500 |
commit | eea0579fc85e64e9f05361d5aacf496fe7a151aa (patch) | |
tree | 3671619779847120761225c679fd2e9a2f117205 /sound/drivers/pcsp/pcsp_lib.c | |
parent | e7dd8c1bdacf658b0ade51facb2f7eaf40eb0ac4 (diff) |
ALSA: pcsp - Fix starting the stream with HRTIMER_CB_IRQSAFE_UNLOCK
With the callback mode HRTIMER_CB_IRQSAFE_UNLOCK, the start of the
stream with zero delay doesn't work. Since IRQSAFE mode is removed,
we have to change the pcsp start-up code.
This patch splits the callback function to two parts, the triggering
of the port and the calculation of the expire time, and the update of
the ALSA PCM core. The first part is called both from the trigger-start
and the hrtimer callback while the latter is handled only in the
hrtimer callback.
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/drivers/pcsp/pcsp_lib.c')
-rw-r--r-- | sound/drivers/pcsp/pcsp_lib.c | 87 |
1 files changed, 56 insertions, 31 deletions
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); |