diff options
Diffstat (limited to 'sound')
-rw-r--r-- | sound/pci/ctxfi/Makefile | 2 | ||||
-rw-r--r-- | sound/pci/ctxfi/ct20k1reg.h | 2 | ||||
-rw-r--r-- | sound/pci/ctxfi/ctatc.c | 21 | ||||
-rw-r--r-- | sound/pci/ctxfi/ctatc.h | 9 | ||||
-rw-r--r-- | sound/pci/ctxfi/cthardware.h | 19 | ||||
-rw-r--r-- | sound/pci/ctxfi/cthw20k1.c | 43 | ||||
-rw-r--r-- | sound/pci/ctxfi/ctpcm.c | 106 | ||||
-rw-r--r-- | sound/pci/ctxfi/cttimer.c | 417 | ||||
-rw-r--r-- | sound/pci/ctxfi/cttimer.h | 29 |
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 @@ | |||
1 | snd-ctxfi-objs := xfi.o ctatc.o ctvmem.o ctpcm.o ctmixer.o ctresource.o \ | 1 | snd-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 | ||
5 | obj-$(CONFIG_SND_CTXFI) += snd-ctxfi.o | 5 | obj-$(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 | ||
312 | error1: | 315 | error1: |
@@ -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 | ||
61 | struct ct_atc; | 61 | struct ct_atc; |
62 | struct ct_timer; | ||
63 | struct ct_timer_instance; | ||
62 | 64 | ||
63 | /* alsa pcm stream descriptor */ | 65 | /* alsa pcm stream descriptor */ |
64 | struct ct_atc_pcm { | 66 | struct 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); | |||
157 | unsigned int get_field(unsigned int data, unsigned int field); | 163 | unsigned int get_field(unsigned int data, unsigned int field); |
158 | void set_field(unsigned int *data, unsigned int field, unsigned int value); | 164 | void 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 */ | ||
1175 | static 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 | |||
1181 | static 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 */ |
1175 | struct dac_conf { | 1190 | struct 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 | ||
1896 | static 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 | |||
1881 | static int hw_card_start(struct hw *hw) | 1912 | static 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: | |||
1936 | static int hw_card_stop(struct hw *hw) | 1968 | static 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 | ||
185 | static 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 | |||
214 | static 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 | |||
234 | static 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 | |||
255 | static 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 | ||
269 | static int ct_pcm_playback_prepare(struct snd_pcm_substream *substream) | 190 | static 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 | |||
16 | struct 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 */ | ||
27 | struct 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 */ | ||
42 | struct 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 | |||
59 | static 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 | |||
87 | static void ct_systimer_init(struct ct_timer_instance *ti) | ||
88 | { | ||
89 | setup_timer(&ti->timer, ct_systimer_callback, | ||
90 | (unsigned long)ti); | ||
91 | } | ||
92 | |||
93 | static 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 | |||
106 | static 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 | |||
116 | static 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 | |||
124 | static 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 | |||
140 | static 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 | |||
151 | static 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 | */ | ||
169 | static 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 */ | ||
208 | static 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 */ | ||
224 | static 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 | |||
242 | static 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 */ | ||
250 | static 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 | |||
269 | static 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 | |||
280 | static 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 | |||
292 | static void ct_xfitimer_free_global(struct ct_timer *atimer) | ||
293 | { | ||
294 | ct_xfitimer_irq_stop(atimer); | ||
295 | } | ||
296 | |||
297 | static 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 | |||
309 | struct ct_timer_instance * | ||
310 | ct_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 | |||
333 | void 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 | |||
341 | void ct_timer_start(struct ct_timer_instance *ti) | ||
342 | { | ||
343 | struct ct_timer *atimer = ti->timer_base; | ||
344 | atimer->ops->start(ti); | ||
345 | } | ||
346 | |||
347 | void ct_timer_stop(struct ct_timer_instance *ti) | ||
348 | { | ||
349 | struct ct_timer *atimer = ti->timer_base; | ||
350 | atimer->ops->stop(ti); | ||
351 | } | ||
352 | |||
353 | void 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 | |||
374 | static 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 | |||
383 | struct 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 | |||
409 | void 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 | |||
12 | struct snd_pcm_substream; | ||
13 | struct ct_atc; | ||
14 | struct ct_atc_pcm; | ||
15 | |||
16 | struct ct_timer; | ||
17 | struct ct_timer_instance; | ||
18 | |||
19 | struct ct_timer *ct_timer_new(struct ct_atc *atc); | ||
20 | void ct_timer_free(struct ct_timer *atimer); | ||
21 | |||
22 | struct ct_timer_instance * | ||
23 | ct_timer_instance_new(struct ct_timer *atimer, struct ct_atc_pcm *apcm); | ||
24 | void ct_timer_instance_free(struct ct_timer_instance *ti); | ||
25 | void ct_timer_start(struct ct_timer_instance *ti); | ||
26 | void ct_timer_stop(struct ct_timer_instance *ti); | ||
27 | void ct_timer_prepare(struct ct_timer_instance *ti); | ||
28 | |||
29 | #endif /* __CTTIMER_H */ | ||