aboutsummaryrefslogtreecommitdiffstats
path: root/sound
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2008-11-26 08:13:03 -0500
committerTakashi Iwai <tiwai@suse.de>2008-11-26 08:13:03 -0500
commiteea0579fc85e64e9f05361d5aacf496fe7a151aa (patch)
tree3671619779847120761225c679fd2e9a2f117205 /sound
parente7dd8c1bdacf658b0ade51facb2f7eaf40eb0ac4 (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')
-rw-r--r--sound/drivers/pcsp/pcsp.h2
-rw-r--r--sound/drivers/pcsp/pcsp_lib.c87
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
37static DECLARE_TASKLET(pcsp_pcm_tasklet, pcsp_call_pcm_elapsed, 0); 37static DECLARE_TASKLET(pcsp_pcm_tasklet, pcsp_call_pcm_elapsed, 0);
38 38
39enum 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 */
42static 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
91enum 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
127static void pcsp_start_playing(struct snd_pcsp *chip) 143static 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
147static void pcsp_stop_playing(struct snd_pcsp *chip) 170static 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);