aboutsummaryrefslogtreecommitdiffstats
path: root/sound/pci/ctxfi
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2009-06-05 10:11:07 -0400
committerTakashi Iwai <tiwai@suse.de>2009-06-05 10:44:13 -0400
commitb7bbf876087e0e2c0ba723a8398083c9a9ac1dfd (patch)
tree69a3e70658fc751ffc99eef5a6f047b19f61a4a2 /sound/pci/ctxfi
parent6bc5874a1ddf98ac0fe6c4eab7d286c11cb1c748 (diff)
ALSA: ctxfi - Use native timer interrupt on emu20k1
emu20k1 has a native timer interrupt based on the audio clock, which is more accurate than the system timer (from the synchronization POV). This patch adds the code to handle this with multiple streams. The system timer is still used on emu20k2, and can be used also for emu20k1 easily by changing USE_SYSTEM_TIMER to 1 in cttimer.c. Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/pci/ctxfi')
-rw-r--r--sound/pci/ctxfi/Makefile2
-rw-r--r--sound/pci/ctxfi/ct20k1reg.h2
-rw-r--r--sound/pci/ctxfi/ctatc.c21
-rw-r--r--sound/pci/ctxfi/ctatc.h9
-rw-r--r--sound/pci/ctxfi/cthardware.h19
-rw-r--r--sound/pci/ctxfi/cthw20k1.c43
-rw-r--r--sound/pci/ctxfi/ctpcm.c106
-rw-r--r--sound/pci/ctxfi/cttimer.c417
-rw-r--r--sound/pci/ctxfi/cttimer.h29
9 files changed, 543 insertions, 105 deletions
diff --git a/sound/pci/ctxfi/Makefile b/sound/pci/ctxfi/Makefile
index 29043237f9f8..15075f89e98a 100644
--- a/sound/pci/ctxfi/Makefile
+++ b/sound/pci/ctxfi/Makefile
@@ -1,5 +1,5 @@
1snd-ctxfi-objs := xfi.o ctatc.o ctvmem.o ctpcm.o ctmixer.o ctresource.o \ 1snd-ctxfi-objs := xfi.o ctatc.o ctvmem.o ctpcm.o ctmixer.o ctresource.o \
2 ctsrc.o ctamixer.o ctdaio.o ctimap.o cthardware.o \ 2 ctsrc.o ctamixer.o ctdaio.o ctimap.o cthardware.o cttimer.o \
3 cthw20k2.o cthw20k1.o 3 cthw20k2.o cthw20k1.o
4 4
5obj-$(CONFIG_SND_CTXFI) += snd-ctxfi.o 5obj-$(CONFIG_SND_CTXFI) += snd-ctxfi.o
diff --git a/sound/pci/ctxfi/ct20k1reg.h b/sound/pci/ctxfi/ct20k1reg.h
index c62e6775dab3..f2e34e3f27ee 100644
--- a/sound/pci/ctxfi/ct20k1reg.h
+++ b/sound/pci/ctxfi/ct20k1reg.h
@@ -589,6 +589,8 @@
589 589
590#define WC 0x1C6000 590#define WC 0x1C6000
591#define TIMR 0x1C6004 591#define TIMR 0x1C6004
592# define TIMR_IE (1<<15)
593# define TIMR_IP (1<<14)
592 594
593#define GIP 0x1C6010 595#define GIP 0x1C6010
594#define GIE 0x1C6014 596#define GIE 0x1C6014
diff --git a/sound/pci/ctxfi/ctatc.c b/sound/pci/ctxfi/ctatc.c
index 684947546d81..10b741977dd7 100644
--- a/sound/pci/ctxfi/ctatc.c
+++ b/sound/pci/ctxfi/ctatc.c
@@ -22,6 +22,7 @@
22#include "ctsrc.h" 22#include "ctsrc.h"
23#include "ctamixer.h" 23#include "ctamixer.h"
24#include "ctdaio.h" 24#include "ctdaio.h"
25#include "cttimer.h"
25#include <linux/delay.h> 26#include <linux/delay.h>
26#include <sound/pcm.h> 27#include <sound/pcm.h>
27#include <sound/control.h> 28#include <sound/control.h>
@@ -307,6 +308,8 @@ static int atc_pcm_playback_prepare(struct ct_atc *atc, struct ct_atc_pcm *apcm)
307 src = apcm->src; 308 src = apcm->src;
308 } 309 }
309 310
311 ct_timer_prepare(apcm->timer);
312
310 return 0; 313 return 0;
311 314
312error1: 315error1:
@@ -389,6 +392,7 @@ static int atc_pcm_playback_start(struct ct_atc *atc, struct ct_atc_pcm *apcm)
389 src->ops->set_state(src, SRC_STATE_INIT); 392 src->ops->set_state(src, SRC_STATE_INIT);
390 src->ops->commit_write(src); 393 src->ops->commit_write(src);
391 394
395 ct_timer_start(apcm->timer);
392 return 0; 396 return 0;
393} 397}
394 398
@@ -397,6 +401,8 @@ static int atc_pcm_stop(struct ct_atc *atc, struct ct_atc_pcm *apcm)
397 struct src *src = NULL; 401 struct src *src = NULL;
398 int i = 0; 402 int i = 0;
399 403
404 ct_timer_stop(apcm->timer);
405
400 src = apcm->src; 406 src = apcm->src;
401 src->ops->set_bm(src, 0); 407 src->ops->set_bm(src, 0);
402 src->ops->set_state(src, SRC_STATE_OFF); 408 src->ops->set_state(src, SRC_STATE_OFF);
@@ -701,6 +707,8 @@ static int atc_pcm_capture_prepare(struct ct_atc *atc, struct ct_atc_pcm *apcm)
701 } 707 }
702 } 708 }
703 709
710 ct_timer_prepare(apcm->timer);
711
704 return 0; 712 return 0;
705} 713}
706 714
@@ -749,6 +757,7 @@ static int atc_pcm_capture_start(struct ct_atc *atc, struct ct_atc_pcm *apcm)
749 /* Enable relevant SRCs synchronously */ 757 /* Enable relevant SRCs synchronously */
750 src_mgr->commit_write(src_mgr); 758 src_mgr->commit_write(src_mgr);
751 759
760 ct_timer_start(apcm->timer);
752 return 0; 761 return 0;
753} 762}
754 763
@@ -906,6 +915,8 @@ spdif_passthru_playback_prepare(struct ct_atc *atc, struct ct_atc_pcm *apcm)
906 dao->ops->set_right_input(dao, &amixer->rsc); 915 dao->ops->set_right_input(dao, &amixer->rsc);
907 spin_unlock_irqrestore(&atc->atc_lock, flags); 916 spin_unlock_irqrestore(&atc->atc_lock, flags);
908 917
918 ct_timer_prepare(apcm->timer);
919
909 return 0; 920 return 0;
910} 921}
911 922
@@ -1100,6 +1111,11 @@ static int ct_atc_destroy(struct ct_atc *atc)
1100 if (NULL == atc) 1111 if (NULL == atc)
1101 return 0; 1112 return 0;
1102 1113
1114 if (atc->timer) {
1115 ct_timer_free(atc->timer);
1116 atc->timer = NULL;
1117 }
1118
1103 /* Stop hardware and disable all interrupts */ 1119 /* Stop hardware and disable all interrupts */
1104 if (NULL != atc->hw) 1120 if (NULL != atc->hw)
1105 ((struct hw *)atc->hw)->card_stop(atc->hw); 1121 ((struct hw *)atc->hw)->card_stop(atc->hw);
@@ -1586,6 +1602,10 @@ int ct_atc_create(struct snd_card *card, struct pci_dev *pci,
1586 /* Build topology */ 1602 /* Build topology */
1587 atc_connect_resources(atc); 1603 atc_connect_resources(atc);
1588 1604
1605 atc->timer = ct_timer_new(atc);
1606 if (!atc->timer)
1607 goto error1;
1608
1589 atc->create_alsa_devs = ct_create_alsa_devs; 1609 atc->create_alsa_devs = ct_create_alsa_devs;
1590 1610
1591 err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, atc, &ops); 1611 err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, atc, &ops);
@@ -1602,4 +1622,3 @@ error1:
1602 printk(KERN_ERR "ctxfi: Something wrong!!!\n"); 1622 printk(KERN_ERR "ctxfi: Something wrong!!!\n");
1603 return err; 1623 return err;
1604} 1624}
1605
diff --git a/sound/pci/ctxfi/ctatc.h b/sound/pci/ctxfi/ctatc.h
index b86d12cd4a19..a3f9b1bc7dcc 100644
--- a/sound/pci/ctxfi/ctatc.h
+++ b/sound/pci/ctxfi/ctatc.h
@@ -59,16 +59,15 @@ struct ct_atc_chip_details {
59}; 59};
60 60
61struct ct_atc; 61struct ct_atc;
62struct ct_timer;
63struct ct_timer_instance;
62 64
63/* alsa pcm stream descriptor */ 65/* alsa pcm stream descriptor */
64struct ct_atc_pcm { 66struct ct_atc_pcm {
65 struct snd_pcm_substream *substream; 67 struct snd_pcm_substream *substream;
66 void (*interrupt)(struct ct_atc_pcm *apcm); 68 void (*interrupt)(struct ct_atc_pcm *apcm);
69 struct ct_timer_instance *timer;
67 unsigned int started:1; 70 unsigned int started:1;
68 unsigned int stop_timer:1;
69 struct timer_list timer;
70 spinlock_t timer_lock;
71 unsigned int position;
72 71
73 /* Only mono and interleaved modes are supported now. */ 72 /* Only mono and interleaved modes are supported now. */
74 struct ct_vm_block *vm_block; 73 struct ct_vm_block *vm_block;
@@ -144,6 +143,8 @@ struct ct_atc {
144 unsigned char n_src; 143 unsigned char n_src;
145 unsigned char n_srcimp; 144 unsigned char n_srcimp;
146 unsigned char n_pcm; 145 unsigned char n_pcm;
146
147 struct ct_timer *timer;
147}; 148};
148 149
149 150
diff --git a/sound/pci/ctxfi/cthardware.h b/sound/pci/ctxfi/cthardware.h
index b0512df8b334..35350cf9d2f2 100644
--- a/sound/pci/ctxfi/cthardware.h
+++ b/sound/pci/ctxfi/cthardware.h
@@ -145,6 +145,12 @@ struct hw {
145 int (*daio_mgr_set_imapaddr)(void *blk, unsigned int addr); 145 int (*daio_mgr_set_imapaddr)(void *blk, unsigned int addr);
146 int (*daio_mgr_commit_write)(struct hw *hw, void *blk); 146 int (*daio_mgr_commit_write)(struct hw *hw, void *blk);
147 147
148 int (*set_timer_irq)(struct hw *hw, int enable);
149 int (*set_timer_tick)(struct hw *hw, unsigned int tick);
150
151 void (*irq_callback)(void *data, unsigned int bit);
152 void *irq_callback_data;
153
148 struct pci_dev *pci; /* the pci kernel structure of this card */ 154 struct pci_dev *pci; /* the pci kernel structure of this card */
149 int irq; 155 int irq;
150 unsigned long io_base; 156 unsigned long io_base;
@@ -157,4 +163,17 @@ int destroy_hw_obj(struct hw *hw);
157unsigned int get_field(unsigned int data, unsigned int field); 163unsigned int get_field(unsigned int data, unsigned int field);
158void set_field(unsigned int *data, unsigned int field, unsigned int value); 164void set_field(unsigned int *data, unsigned int field, unsigned int value);
159 165
166/* IRQ bits */
167#define PLL_INT (1 << 10) /* PLL input-clock out-of-range */
168#define FI_INT (1 << 9) /* forced interrupt */
169#define IT_INT (1 << 8) /* timer interrupt */
170#define PCI_INT (1 << 7) /* PCI bus error pending */
171#define URT_INT (1 << 6) /* UART Tx/Rx */
172#define GPI_INT (1 << 5) /* GPI pin */
173#define MIX_INT (1 << 4) /* mixer parameter segment FIFO channels */
174#define DAI_INT (1 << 3) /* DAI (SR-tracker or SPDIF-receiver) */
175#define TP_INT (1 << 2) /* transport priority queue */
176#define DSP_INT (1 << 1) /* DSP */
177#define SRC_INT (1 << 0) /* SRC channels */
178
160#endif /* CTHARDWARE_H */ 179#endif /* CTHARDWARE_H */
diff --git a/sound/pci/ctxfi/cthw20k1.c b/sound/pci/ctxfi/cthw20k1.c
index e530a6d60422..550b30a2bcf1 100644
--- a/sound/pci/ctxfi/cthw20k1.c
+++ b/sound/pci/ctxfi/cthw20k1.c
@@ -1171,6 +1171,21 @@ static int daio_mgr_put_ctrl_blk(void *blk)
1171 return 0; 1171 return 0;
1172} 1172}
1173 1173
1174/* Timer interrupt */
1175static int set_timer_irq(struct hw *hw, int enable)
1176{
1177 hw_write_20kx(hw, GIE, enable ? IT_INT : 0);
1178 return 0;
1179}
1180
1181static int set_timer_tick(struct hw *hw, unsigned int ticks)
1182{
1183 if (ticks)
1184 ticks |= TIMR_IE | TIMR_IP;
1185 hw_write_20kx(hw, TIMR, ticks);
1186 return 0;
1187}
1188
1174/* Card hardware initialization block */ 1189/* Card hardware initialization block */
1175struct dac_conf { 1190struct dac_conf {
1176 unsigned int msr; /* master sample rate in rsrs */ 1191 unsigned int msr; /* master sample rate in rsrs */
@@ -1878,6 +1893,22 @@ static int uaa_to_xfi(struct pci_dev *pci)
1878 return 0; 1893 return 0;
1879} 1894}
1880 1895
1896static irqreturn_t ct_20k1_interrupt(int irq, void *dev_id)
1897{
1898 struct hw *hw = dev_id;
1899 unsigned int status;
1900
1901 status = hw_read_20kx(hw, GIP);
1902 if (!status)
1903 return IRQ_NONE;
1904
1905 if (hw->irq_callback)
1906 hw->irq_callback(hw->irq_callback_data, status);
1907
1908 hw_write_20kx(hw, GIP, status);
1909 return IRQ_HANDLED;
1910}
1911
1881static int hw_card_start(struct hw *hw) 1912static int hw_card_start(struct hw *hw)
1882{ 1913{
1883 int err = 0; 1914 int err = 0;
@@ -1914,12 +1945,13 @@ static int hw_card_start(struct hw *hw)
1914 hw->io_base = pci_resource_start(pci, 0); 1945 hw->io_base = pci_resource_start(pci, 0);
1915 } 1946 }
1916 1947
1917 /*if ((err = request_irq(pci->irq, ct_atc_interrupt, IRQF_SHARED, 1948 err = request_irq(pci->irq, ct_20k1_interrupt, IRQF_SHARED,
1918 atc->chip_details->nm_card, hw))) { 1949 "ctxfi", hw);
1950 if (err < 0) {
1951 printk(KERN_ERR "XFi: Cannot get irq %d\n", pci->irq);
1919 goto error2; 1952 goto error2;
1920 } 1953 }
1921 hw->irq = pci->irq; 1954 hw->irq = pci->irq;
1922 */
1923 1955
1924 pci_set_master(pci); 1956 pci_set_master(pci);
1925 1957
@@ -1936,6 +1968,8 @@ error1:
1936static int hw_card_stop(struct hw *hw) 1968static int hw_card_stop(struct hw *hw)
1937{ 1969{
1938 /* TODO: Disable interrupt and so on... */ 1970 /* TODO: Disable interrupt and so on... */
1971 if (hw->irq >= 0)
1972 synchronize_irq(hw->irq);
1939 return 0; 1973 return 0;
1940} 1974}
1941 1975
@@ -2215,6 +2249,9 @@ int create_20k1_hw_obj(struct hw **rhw)
2215 hw->daio_mgr_set_imapaddr = daio_mgr_set_imapaddr; 2249 hw->daio_mgr_set_imapaddr = daio_mgr_set_imapaddr;
2216 hw->daio_mgr_commit_write = daio_mgr_commit_write; 2250 hw->daio_mgr_commit_write = daio_mgr_commit_write;
2217 2251
2252 hw->set_timer_irq = set_timer_irq;
2253 hw->set_timer_tick = set_timer_tick;
2254
2218 *rhw = hw; 2255 *rhw = hw;
2219 2256
2220 return 0; 2257 return 0;
diff --git a/sound/pci/ctxfi/ctpcm.c b/sound/pci/ctxfi/ctpcm.c
index 52ddf19d83bb..32b742dcd538 100644
--- a/sound/pci/ctxfi/ctpcm.c
+++ b/sound/pci/ctxfi/ctpcm.c
@@ -16,6 +16,7 @@
16 */ 16 */
17 17
18#include "ctpcm.h" 18#include "ctpcm.h"
19#include "cttimer.h"
19#include <sound/pcm.h> 20#include <sound/pcm.h>
20 21
21/* Hardware descriptions for playback */ 22/* Hardware descriptions for playback */
@@ -108,6 +109,7 @@ static void ct_atc_pcm_free_substream(struct snd_pcm_runtime *runtime)
108 struct ct_atc *atc = snd_pcm_substream_chip(apcm->substream); 109 struct ct_atc *atc = snd_pcm_substream_chip(apcm->substream);
109 110
110 atc->pcm_release_resources(atc, apcm); 111 atc->pcm_release_resources(atc, apcm);
112 ct_timer_instance_free(apcm->timer);
111 kfree(apcm); 113 kfree(apcm);
112 runtime->private_data = NULL; 114 runtime->private_data = NULL;
113} 115}
@@ -124,8 +126,6 @@ static int ct_pcm_playback_open(struct snd_pcm_substream *substream)
124 if (NULL == apcm) 126 if (NULL == apcm)
125 return -ENOMEM; 127 return -ENOMEM;
126 128
127 spin_lock_init(&apcm->timer_lock);
128 apcm->stop_timer = 0;
129 apcm->substream = substream; 129 apcm->substream = substream;
130 apcm->interrupt = ct_atc_pcm_interrupt; 130 apcm->interrupt = ct_atc_pcm_interrupt;
131 runtime->private_data = apcm; 131 runtime->private_data = apcm;
@@ -153,6 +153,10 @@ static int ct_pcm_playback_open(struct snd_pcm_substream *substream)
153 return err; 153 return err;
154 } 154 }
155 155
156 apcm->timer = ct_timer_instance_new(atc->timer, apcm);
157 if (!apcm->timer)
158 return -ENOMEM;
159
156 return 0; 160 return 0;
157} 161}
158 162
@@ -182,89 +186,6 @@ static int ct_pcm_hw_free(struct snd_pcm_substream *substream)
182 return snd_pcm_lib_free_pages(substream); 186 return snd_pcm_lib_free_pages(substream);
183} 187}
184 188
185static void ct_pcm_timer_callback(unsigned long data)
186{
187 struct ct_atc_pcm *apcm = (struct ct_atc_pcm *)data;
188 struct snd_pcm_substream *substream = apcm->substream;
189 struct snd_pcm_runtime *runtime = substream->runtime;
190 unsigned int period_size = runtime->period_size;
191 unsigned int buffer_size = runtime->buffer_size;
192 unsigned long flags;
193 unsigned int position = 0, dist = 0, interval = 0;
194
195 position = substream->ops->pointer(substream);
196 dist = (position + buffer_size - apcm->position) % buffer_size;
197 if ((dist >= period_size) ||
198 (position/period_size != apcm->position/period_size)) {
199 apcm->interrupt(apcm);
200 apcm->position = position;
201 }
202 /* Add extra HZ*5/1000 to avoid overrun issue when recording
203 * at 8kHz in 8-bit format or at 88kHz in 24-bit format. */
204 interval = ((period_size - (position % period_size))
205 * HZ + (runtime->rate - 1)) / runtime->rate + HZ * 5 / 1000;
206 spin_lock_irqsave(&apcm->timer_lock, flags);
207 apcm->timer.expires = jiffies + interval;
208 if (!apcm->stop_timer)
209 add_timer(&apcm->timer);
210
211 spin_unlock_irqrestore(&apcm->timer_lock, flags);
212}
213
214static int ct_pcm_timer_prepare(struct ct_atc_pcm *apcm)
215{
216 unsigned long flags;
217
218 spin_lock_irqsave(&apcm->timer_lock, flags);
219 if (timer_pending(&apcm->timer)) {
220 /* The timer has already been started. */
221 spin_unlock_irqrestore(&apcm->timer_lock, flags);
222 return 0;
223 }
224
225 init_timer(&apcm->timer);
226 apcm->timer.data = (unsigned long)apcm;
227 apcm->timer.function = ct_pcm_timer_callback;
228 spin_unlock_irqrestore(&apcm->timer_lock, flags);
229 apcm->position = 0;
230
231 return 0;
232}
233
234static int ct_pcm_timer_start(struct ct_atc_pcm *apcm)
235{
236 struct snd_pcm_runtime *runtime = apcm->substream->runtime;
237 unsigned long flags;
238
239 spin_lock_irqsave(&apcm->timer_lock, flags);
240 if (timer_pending(&apcm->timer)) {
241 /* The timer has already been started. */
242 spin_unlock_irqrestore(&apcm->timer_lock, flags);
243 return 0;
244 }
245
246 apcm->timer.expires = jiffies + (runtime->period_size * HZ +
247 (runtime->rate - 1)) / runtime->rate;
248 apcm->stop_timer = 0;
249 add_timer(&apcm->timer);
250 spin_unlock_irqrestore(&apcm->timer_lock, flags);
251
252 return 0;
253}
254
255static int ct_pcm_timer_stop(struct ct_atc_pcm *apcm)
256{
257 unsigned long flags;
258
259 spin_lock_irqsave(&apcm->timer_lock, flags);
260 apcm->stop_timer = 1;
261 del_timer(&apcm->timer);
262 spin_unlock_irqrestore(&apcm->timer_lock, flags);
263
264 try_to_del_timer_sync(&apcm->timer);
265
266 return 0;
267}
268 189
269static int ct_pcm_playback_prepare(struct snd_pcm_substream *substream) 190static int ct_pcm_playback_prepare(struct snd_pcm_substream *substream)
270{ 191{
@@ -283,8 +204,6 @@ static int ct_pcm_playback_prepare(struct snd_pcm_substream *substream)
283 return err; 204 return err;
284 } 205 }
285 206
286 ct_pcm_timer_prepare(apcm);
287
288 return 0; 207 return 0;
289} 208}
290 209
@@ -300,12 +219,10 @@ ct_pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd)
300 case SNDRV_PCM_TRIGGER_RESUME: 219 case SNDRV_PCM_TRIGGER_RESUME:
301 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 220 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
302 atc->pcm_playback_start(atc, apcm); 221 atc->pcm_playback_start(atc, apcm);
303 ct_pcm_timer_start(apcm);
304 break; 222 break;
305 case SNDRV_PCM_TRIGGER_STOP: 223 case SNDRV_PCM_TRIGGER_STOP:
306 case SNDRV_PCM_TRIGGER_SUSPEND: 224 case SNDRV_PCM_TRIGGER_SUSPEND:
307 case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 225 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
308 ct_pcm_timer_stop(apcm);
309 atc->pcm_playback_stop(atc, apcm); 226 atc->pcm_playback_stop(atc, apcm);
310 break; 227 break;
311 default: 228 default:
@@ -341,9 +258,7 @@ static int ct_pcm_capture_open(struct snd_pcm_substream *substream)
341 if (NULL == apcm) 258 if (NULL == apcm)
342 return -ENOMEM; 259 return -ENOMEM;
343 260
344 spin_lock_init(&apcm->timer_lock);
345 apcm->started = 0; 261 apcm->started = 0;
346 apcm->stop_timer = 0;
347 apcm->substream = substream; 262 apcm->substream = substream;
348 apcm->interrupt = ct_atc_pcm_interrupt; 263 apcm->interrupt = ct_atc_pcm_interrupt;
349 runtime->private_data = apcm; 264 runtime->private_data = apcm;
@@ -365,6 +280,10 @@ static int ct_pcm_capture_open(struct snd_pcm_substream *substream)
365 return err; 280 return err;
366 } 281 }
367 282
283 apcm->timer = ct_timer_instance_new(atc->timer, apcm);
284 if (!apcm->timer)
285 return -ENOMEM;
286
368 return 0; 287 return 0;
369} 288}
370 289
@@ -388,8 +307,6 @@ static int ct_pcm_capture_prepare(struct snd_pcm_substream *substream)
388 return err; 307 return err;
389 } 308 }
390 309
391 ct_pcm_timer_prepare(apcm);
392
393 return 0; 310 return 0;
394} 311}
395 312
@@ -403,14 +320,11 @@ ct_pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd)
403 switch (cmd) { 320 switch (cmd) {
404 case SNDRV_PCM_TRIGGER_START: 321 case SNDRV_PCM_TRIGGER_START:
405 atc->pcm_capture_start(atc, apcm); 322 atc->pcm_capture_start(atc, apcm);
406 ct_pcm_timer_start(apcm);
407 break; 323 break;
408 case SNDRV_PCM_TRIGGER_STOP: 324 case SNDRV_PCM_TRIGGER_STOP:
409 ct_pcm_timer_stop(apcm);
410 atc->pcm_capture_stop(atc, apcm); 325 atc->pcm_capture_stop(atc, apcm);
411 break; 326 break;
412 default: 327 default:
413 ct_pcm_timer_stop(apcm);
414 atc->pcm_capture_stop(atc, apcm); 328 atc->pcm_capture_stop(atc, apcm);
415 break; 329 break;
416 } 330 }
diff --git a/sound/pci/ctxfi/cttimer.c b/sound/pci/ctxfi/cttimer.c
new file mode 100644
index 000000000000..3acb26d0c4cc
--- /dev/null
+++ b/sound/pci/ctxfi/cttimer.c
@@ -0,0 +1,417 @@
1/*
2 * PCM timer handling on ctxfi
3 *
4 * This source file is released under GPL v2 license (no other versions).
5 * See the COPYING file included in the main directory of this source
6 * distribution for the license terms and conditions.
7 */
8
9#include <linux/slab.h>
10#include <sound/core.h>
11#include <sound/pcm.h>
12#include "ctatc.h"
13#include "cthardware.h"
14#include "cttimer.h"
15
16struct ct_timer_ops {
17 void (*init)(struct ct_timer_instance *);
18 void (*prepare)(struct ct_timer_instance *);
19 void (*start)(struct ct_timer_instance *);
20 void (*stop)(struct ct_timer_instance *);
21 void (*free_instance)(struct ct_timer_instance *);
22 void (*interrupt)(struct ct_timer *);
23 void (*free_global)(struct ct_timer *);
24};
25
26/* timer instance -- assigned to each PCM stream */
27struct ct_timer_instance {
28 spinlock_t lock;
29 struct ct_timer *timer_base;
30 struct ct_atc_pcm *apcm;
31 struct snd_pcm_substream *substream;
32 struct timer_list timer;
33 struct list_head instance_list;
34 struct list_head running_list;
35 unsigned int position;
36 unsigned int frag_count;
37 unsigned int running:1;
38 unsigned int need_update:1;
39};
40
41/* timer instance manager */
42struct ct_timer {
43 spinlock_t lock; /* global timer lock (for xfitimer) */
44 spinlock_t list_lock; /* lock for instance list */
45 struct ct_atc *atc;
46 struct ct_timer_ops *ops;
47 struct list_head instance_head;
48 struct list_head running_head;
49 unsigned int irq_handling:1; /* in IRQ handling */
50 unsigned int reprogram:1; /* need to reprogram the internval */
51 unsigned int running:1; /* global timer running */
52};
53
54
55/*
56 * system-timer-based updates
57 */
58
59static void ct_systimer_callback(unsigned long data)
60{
61 struct ct_timer_instance *ti = (struct ct_timer_instance *)data;
62 struct snd_pcm_substream *substream = ti->substream;
63 struct snd_pcm_runtime *runtime = substream->runtime;
64 struct ct_atc_pcm *apcm = ti->apcm;
65 unsigned int period_size = runtime->period_size;
66 unsigned int buffer_size = runtime->buffer_size;
67 unsigned long flags;
68 unsigned int position, dist, interval;
69
70 position = substream->ops->pointer(substream);
71 dist = (position + buffer_size - ti->position) % buffer_size;
72 if (dist >= period_size ||
73 position / period_size != ti->position / period_size) {
74 apcm->interrupt(apcm);
75 ti->position = position;
76 }
77 /* Add extra HZ*5/1000 to avoid overrun issue when recording
78 * at 8kHz in 8-bit format or at 88kHz in 24-bit format. */
79 interval = ((period_size - (position % period_size))
80 * HZ + (runtime->rate - 1)) / runtime->rate + HZ * 5 / 1000;
81 spin_lock_irqsave(&ti->lock, flags);
82 if (ti->running)
83 mod_timer(&ti->timer, jiffies + interval);
84 spin_unlock_irqrestore(&ti->lock, flags);
85}
86
87static void ct_systimer_init(struct ct_timer_instance *ti)
88{
89 setup_timer(&ti->timer, ct_systimer_callback,
90 (unsigned long)ti);
91}
92
93static void ct_systimer_start(struct ct_timer_instance *ti)
94{
95 struct snd_pcm_runtime *runtime = ti->substream->runtime;
96 unsigned long flags;
97
98 spin_lock_irqsave(&ti->lock, flags);
99 ti->running = 1;
100 mod_timer(&ti->timer,
101 jiffies + (runtime->period_size * HZ +
102 (runtime->rate - 1)) / runtime->rate);
103 spin_unlock_irqrestore(&ti->lock, flags);
104}
105
106static void ct_systimer_stop(struct ct_timer_instance *ti)
107{
108 unsigned long flags;
109
110 spin_lock_irqsave(&ti->lock, flags);
111 ti->running = 0;
112 del_timer(&ti->timer);
113 spin_unlock_irqrestore(&ti->lock, flags);
114}
115
116static void ct_systimer_prepare(struct ct_timer_instance *ti)
117{
118 ct_systimer_stop(ti);
119 try_to_del_timer_sync(&ti->timer);
120}
121
122#define ct_systimer_free ct_systimer_prepare
123
124static struct ct_timer_ops ct_systimer_ops = {
125 .init = ct_systimer_init,
126 .free_instance = ct_systimer_free,
127 .prepare = ct_systimer_prepare,
128 .start = ct_systimer_start,
129 .stop = ct_systimer_stop,
130};
131
132
133/*
134 * Handling multiple streams using a global emu20k1 timer irq
135 */
136
137#define CT_TIMER_FREQ 48000
138#define MAX_TICKS ((1 << 13) - 1)
139
140static void ct_xfitimer_irq_rearm(struct ct_timer *atimer, int ticks)
141{
142 struct hw *hw = atimer->atc->hw;
143 if (ticks > MAX_TICKS)
144 ticks = MAX_TICKS;
145 hw->set_timer_tick(hw, ticks);
146 if (!atimer->running)
147 hw->set_timer_irq(hw, 1);
148 atimer->running = 1;
149}
150
151static void ct_xfitimer_irq_stop(struct ct_timer *atimer)
152{
153 if (atimer->running) {
154 struct hw *hw = atimer->atc->hw;
155 hw->set_timer_irq(hw, 0);
156 hw->set_timer_tick(hw, 0);
157 atimer->running = 0;
158 }
159}
160
161/*
162 * reprogram the timer interval;
163 * checks the running instance list and determines the next timer interval.
164 * also updates the each stream position, returns the number of streams
165 * to call snd_pcm_period_elapsed() appropriately
166 *
167 * call this inside the lock and irq disabled
168 */
169static int ct_xfitimer_reprogram(struct ct_timer *atimer)
170{
171 struct ct_timer_instance *ti;
172 int min_intr = -1;
173 int updates = 0;
174
175 list_for_each_entry(ti, &atimer->running_head, running_list) {
176 struct snd_pcm_runtime *runtime;
177 unsigned int pos, diff;
178 int intr;
179 runtime = ti->substream->runtime;
180 pos = ti->substream->ops->pointer(ti->substream);
181 if (pos < ti->position)
182 diff = runtime->buffer_size - ti->position + pos;
183 else
184 diff = pos - ti->position;
185 ti->position = pos;
186 while (diff >= ti->frag_count) {
187 ti->frag_count += runtime->period_size;
188 ti->need_update = 1;
189 updates++;
190 }
191 ti->frag_count -= diff;
192 intr = div_u64((u64)ti->frag_count * CT_TIMER_FREQ,
193 runtime->rate);
194 if (min_intr < 0 || intr < min_intr)
195 min_intr = intr;
196 }
197
198 if (min_intr > 0)
199 ct_xfitimer_irq_rearm(atimer, min_intr);
200 else
201 ct_xfitimer_irq_stop(atimer);
202
203 atimer->reprogram = 0; /* clear flag */
204 return updates;
205}
206
207/* look through the instance list and call period_elapsed if needed */
208static void ct_xfitimer_check_period(struct ct_timer *atimer)
209{
210 struct ct_timer_instance *ti;
211 unsigned long flags;
212
213 spin_lock_irqsave(&atimer->list_lock, flags);
214 list_for_each_entry(ti, &atimer->instance_head, instance_list) {
215 if (ti->need_update) {
216 ti->need_update = 0;
217 ti->apcm->interrupt(ti->apcm);
218 }
219 }
220 spin_unlock_irqrestore(&atimer->list_lock, flags);
221}
222
223/* Handle timer-interrupt */
224static void ct_xfitimer_callback(struct ct_timer *atimer)
225{
226 int update;
227 unsigned long flags;
228
229 spin_lock_irqsave(&atimer->lock, flags);
230 atimer->irq_handling = 1;
231 do {
232 update = ct_xfitimer_reprogram(atimer);
233 spin_unlock(&atimer->lock);
234 if (update)
235 ct_xfitimer_check_period(atimer);
236 spin_lock(&atimer->lock);
237 } while (atimer->reprogram);
238 atimer->irq_handling = 0;
239 spin_unlock_irqrestore(&atimer->lock, flags);
240}
241
242static void ct_xfitimer_prepare(struct ct_timer_instance *ti)
243{
244 ti->frag_count = ti->substream->runtime->period_size;
245 ti->need_update = 0;
246}
247
248
249/* start/stop the timer */
250static void ct_xfitimer_update(struct ct_timer *atimer)
251{
252 unsigned long flags;
253 int update;
254
255 if (atimer->irq_handling) {
256 /* reached from IRQ handler; let it handle later */
257 atimer->reprogram = 1;
258 return;
259 }
260
261 spin_lock_irqsave(&atimer->lock, flags);
262 ct_xfitimer_irq_stop(atimer);
263 update = ct_xfitimer_reprogram(atimer);
264 spin_unlock_irqrestore(&atimer->lock, flags);
265 if (update)
266 ct_xfitimer_check_period(atimer);
267}
268
269static void ct_xfitimer_start(struct ct_timer_instance *ti)
270{
271 struct ct_timer *atimer = ti->timer_base;
272 unsigned long flags;
273
274 spin_lock_irqsave(&atimer->lock, flags);
275 list_add(&ti->running_list, &atimer->running_head);
276 spin_unlock_irqrestore(&atimer->lock, flags);
277 ct_xfitimer_update(atimer);
278}
279
280static void ct_xfitimer_stop(struct ct_timer_instance *ti)
281{
282 struct ct_timer *atimer = ti->timer_base;
283 unsigned long flags;
284
285 spin_lock_irqsave(&atimer->lock, flags);
286 list_del_init(&ti->running_list);
287 ti->need_update = 0;
288 spin_unlock_irqrestore(&atimer->lock, flags);
289 ct_xfitimer_update(atimer);
290}
291
292static void ct_xfitimer_free_global(struct ct_timer *atimer)
293{
294 ct_xfitimer_irq_stop(atimer);
295}
296
297static struct ct_timer_ops ct_xfitimer_ops = {
298 .prepare = ct_xfitimer_prepare,
299 .start = ct_xfitimer_start,
300 .stop = ct_xfitimer_stop,
301 .interrupt = ct_xfitimer_callback,
302 .free_global = ct_xfitimer_free_global,
303};
304
305/*
306 * timer instance
307 */
308
309struct ct_timer_instance *
310ct_timer_instance_new(struct ct_timer *atimer, struct ct_atc_pcm *apcm)
311{
312 struct ct_timer_instance *ti;
313
314 ti = kzalloc(sizeof(*ti), GFP_KERNEL);
315 if (!ti)
316 return NULL;
317 spin_lock_init(&ti->lock);
318 INIT_LIST_HEAD(&ti->instance_list);
319 INIT_LIST_HEAD(&ti->running_list);
320 ti->timer_base = atimer;
321 ti->apcm = apcm;
322 ti->substream = apcm->substream;
323 if (atimer->ops->init)
324 atimer->ops->init(ti);
325
326 spin_lock_irq(&atimer->list_lock);
327 list_add(&ti->instance_list, &atimer->instance_head);
328 spin_unlock_irq(&atimer->list_lock);
329
330 return ti;
331}
332
333void ct_timer_prepare(struct ct_timer_instance *ti)
334{
335 if (ti->timer_base->ops->prepare)
336 ti->timer_base->ops->prepare(ti);
337 ti->position = 0;
338 ti->running = 0;
339}
340
341void ct_timer_start(struct ct_timer_instance *ti)
342{
343 struct ct_timer *atimer = ti->timer_base;
344 atimer->ops->start(ti);
345}
346
347void ct_timer_stop(struct ct_timer_instance *ti)
348{
349 struct ct_timer *atimer = ti->timer_base;
350 atimer->ops->stop(ti);
351}
352
353void ct_timer_instance_free(struct ct_timer_instance *ti)
354{
355 struct ct_timer *atimer = ti->timer_base;
356
357 atimer->ops->stop(ti); /* to be sure */
358 if (atimer->ops->free_instance)
359 atimer->ops->free_instance(ti);
360
361 spin_lock_irq(&atimer->list_lock);
362 list_del(&ti->instance_list);
363 spin_unlock_irq(&atimer->list_lock);
364
365 kfree(ti);
366}
367
368/*
369 * timer manager
370 */
371
372#define USE_SYSTEM_TIMER 0
373
374static void ct_timer_interrupt(void *data, unsigned int status)
375{
376 struct ct_timer *timer = data;
377
378 /* Interval timer interrupt */
379 if ((status & IT_INT) && timer->ops->interrupt)
380 timer->ops->interrupt(timer);
381}
382
383struct ct_timer *ct_timer_new(struct ct_atc *atc)
384{
385 struct ct_timer *atimer;
386 struct hw *hw;
387
388 atimer = kzalloc(sizeof(*atimer), GFP_KERNEL);
389 if (!atimer)
390 return NULL;
391 spin_lock_init(&atimer->lock);
392 spin_lock_init(&atimer->list_lock);
393 INIT_LIST_HEAD(&atimer->instance_head);
394 INIT_LIST_HEAD(&atimer->running_head);
395 atimer->atc = atc;
396 hw = atc->hw;
397 if (!USE_SYSTEM_TIMER && hw->set_timer_irq) {
398 printk(KERN_INFO "ctxfi: Use xfi-native timer\n");
399 atimer->ops = &ct_xfitimer_ops;
400 hw->irq_callback_data = atimer;
401 hw->irq_callback = ct_timer_interrupt;
402 } else {
403 printk(KERN_INFO "ctxfi: Use system timer\n");
404 atimer->ops = &ct_systimer_ops;
405 }
406 return atimer;
407}
408
409void ct_timer_free(struct ct_timer *atimer)
410{
411 struct hw *hw = atimer->atc->hw;
412 hw->irq_callback = NULL;
413 if (atimer->ops->free_global)
414 atimer->ops->free_global(atimer);
415 kfree(atimer);
416}
417
diff --git a/sound/pci/ctxfi/cttimer.h b/sound/pci/ctxfi/cttimer.h
new file mode 100644
index 000000000000..979348229291
--- /dev/null
+++ b/sound/pci/ctxfi/cttimer.h
@@ -0,0 +1,29 @@
1/*
2 * Timer handling
3 */
4
5#ifndef __CTTIMER_H
6#define __CTTIMER_H
7
8#include <linux/spinlock.h>
9#include <linux/timer.h>
10#include <linux/list.h>
11
12struct snd_pcm_substream;
13struct ct_atc;
14struct ct_atc_pcm;
15
16struct ct_timer;
17struct ct_timer_instance;
18
19struct ct_timer *ct_timer_new(struct ct_atc *atc);
20void ct_timer_free(struct ct_timer *atimer);
21
22struct ct_timer_instance *
23ct_timer_instance_new(struct ct_timer *atimer, struct ct_atc_pcm *apcm);
24void ct_timer_instance_free(struct ct_timer_instance *ti);
25void ct_timer_start(struct ct_timer_instance *ti);
26void ct_timer_stop(struct ct_timer_instance *ti);
27void ct_timer_prepare(struct ct_timer_instance *ti);
28
29#endif /* __CTTIMER_H */