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 @@ snd-ctxfi-objs := xfi.o ctatc.o ctvmem.o ctpcm.o ctmixer.o ctresource.o \ - ctsrc.o ctamixer.o ctdaio.o ctimap.o cthardware.o \ + ctsrc.o ctamixer.o ctdaio.o ctimap.o cthardware.o cttimer.o \ cthw20k2.o cthw20k1.o 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 @@ #define WC 0x1C6000 #define TIMR 0x1C6004 +# define TIMR_IE (1<<15) +# define TIMR_IP (1<<14) #define GIP 0x1C6010 #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 @@ #include "ctsrc.h" #include "ctamixer.h" #include "ctdaio.h" +#include "cttimer.h" #include #include #include @@ -307,6 +308,8 @@ static int atc_pcm_playback_prepare(struct ct_atc *atc, struct ct_atc_pcm *apcm) src = apcm->src; } + ct_timer_prepare(apcm->timer); + return 0; error1: @@ -389,6 +392,7 @@ static int atc_pcm_playback_start(struct ct_atc *atc, struct ct_atc_pcm *apcm) src->ops->set_state(src, SRC_STATE_INIT); src->ops->commit_write(src); + ct_timer_start(apcm->timer); return 0; } @@ -397,6 +401,8 @@ static int atc_pcm_stop(struct ct_atc *atc, struct ct_atc_pcm *apcm) struct src *src = NULL; int i = 0; + ct_timer_stop(apcm->timer); + src = apcm->src; src->ops->set_bm(src, 0); 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) } } + ct_timer_prepare(apcm->timer); + return 0; } @@ -749,6 +757,7 @@ static int atc_pcm_capture_start(struct ct_atc *atc, struct ct_atc_pcm *apcm) /* Enable relevant SRCs synchronously */ src_mgr->commit_write(src_mgr); + ct_timer_start(apcm->timer); return 0; } @@ -906,6 +915,8 @@ spdif_passthru_playback_prepare(struct ct_atc *atc, struct ct_atc_pcm *apcm) dao->ops->set_right_input(dao, &amixer->rsc); spin_unlock_irqrestore(&atc->atc_lock, flags); + ct_timer_prepare(apcm->timer); + return 0; } @@ -1100,6 +1111,11 @@ static int ct_atc_destroy(struct ct_atc *atc) if (NULL == atc) return 0; + if (atc->timer) { + ct_timer_free(atc->timer); + atc->timer = NULL; + } + /* Stop hardware and disable all interrupts */ if (NULL != atc->hw) ((struct hw *)atc->hw)->card_stop(atc->hw); @@ -1586,6 +1602,10 @@ int ct_atc_create(struct snd_card *card, struct pci_dev *pci, /* Build topology */ atc_connect_resources(atc); + atc->timer = ct_timer_new(atc); + if (!atc->timer) + goto error1; + atc->create_alsa_devs = ct_create_alsa_devs; err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, atc, &ops); @@ -1602,4 +1622,3 @@ error1: printk(KERN_ERR "ctxfi: Something wrong!!!\n"); return err; } - 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 { }; struct ct_atc; +struct ct_timer; +struct ct_timer_instance; /* alsa pcm stream descriptor */ struct ct_atc_pcm { struct snd_pcm_substream *substream; void (*interrupt)(struct ct_atc_pcm *apcm); + struct ct_timer_instance *timer; unsigned int started:1; - unsigned int stop_timer:1; - struct timer_list timer; - spinlock_t timer_lock; - unsigned int position; /* Only mono and interleaved modes are supported now. */ struct ct_vm_block *vm_block; @@ -144,6 +143,8 @@ struct ct_atc { unsigned char n_src; unsigned char n_srcimp; unsigned char n_pcm; + + struct ct_timer *timer; }; 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 { int (*daio_mgr_set_imapaddr)(void *blk, unsigned int addr); int (*daio_mgr_commit_write)(struct hw *hw, void *blk); + int (*set_timer_irq)(struct hw *hw, int enable); + int (*set_timer_tick)(struct hw *hw, unsigned int tick); + + void (*irq_callback)(void *data, unsigned int bit); + void *irq_callback_data; + struct pci_dev *pci; /* the pci kernel structure of this card */ int irq; unsigned long io_base; @@ -157,4 +163,17 @@ int destroy_hw_obj(struct hw *hw); unsigned int get_field(unsigned int data, unsigned int field); void set_field(unsigned int *data, unsigned int field, unsigned int value); +/* IRQ bits */ +#define PLL_INT (1 << 10) /* PLL input-clock out-of-range */ +#define FI_INT (1 << 9) /* forced interrupt */ +#define IT_INT (1 << 8) /* timer interrupt */ +#define PCI_INT (1 << 7) /* PCI bus error pending */ +#define URT_INT (1 << 6) /* UART Tx/Rx */ +#define GPI_INT (1 << 5) /* GPI pin */ +#define MIX_INT (1 << 4) /* mixer parameter segment FIFO channels */ +#define DAI_INT (1 << 3) /* DAI (SR-tracker or SPDIF-receiver) */ +#define TP_INT (1 << 2) /* transport priority queue */ +#define DSP_INT (1 << 1) /* DSP */ +#define SRC_INT (1 << 0) /* SRC channels */ + #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) return 0; } +/* Timer interrupt */ +static int set_timer_irq(struct hw *hw, int enable) +{ + hw_write_20kx(hw, GIE, enable ? IT_INT : 0); + return 0; +} + +static int set_timer_tick(struct hw *hw, unsigned int ticks) +{ + if (ticks) + ticks |= TIMR_IE | TIMR_IP; + hw_write_20kx(hw, TIMR, ticks); + return 0; +} + /* Card hardware initialization block */ struct dac_conf { unsigned int msr; /* master sample rate in rsrs */ @@ -1878,6 +1893,22 @@ static int uaa_to_xfi(struct pci_dev *pci) return 0; } +static irqreturn_t ct_20k1_interrupt(int irq, void *dev_id) +{ + struct hw *hw = dev_id; + unsigned int status; + + status = hw_read_20kx(hw, GIP); + if (!status) + return IRQ_NONE; + + if (hw->irq_callback) + hw->irq_callback(hw->irq_callback_data, status); + + hw_write_20kx(hw, GIP, status); + return IRQ_HANDLED; +} + static int hw_card_start(struct hw *hw) { int err = 0; @@ -1914,12 +1945,13 @@ static int hw_card_start(struct hw *hw) hw->io_base = pci_resource_start(pci, 0); } - /*if ((err = request_irq(pci->irq, ct_atc_interrupt, IRQF_SHARED, - atc->chip_details->nm_card, hw))) { + err = request_irq(pci->irq, ct_20k1_interrupt, IRQF_SHARED, + "ctxfi", hw); + if (err < 0) { + printk(KERN_ERR "XFi: Cannot get irq %d\n", pci->irq); goto error2; } hw->irq = pci->irq; - */ pci_set_master(pci); @@ -1936,6 +1968,8 @@ error1: static int hw_card_stop(struct hw *hw) { /* TODO: Disable interrupt and so on... */ + if (hw->irq >= 0) + synchronize_irq(hw->irq); return 0; } @@ -2215,6 +2249,9 @@ int create_20k1_hw_obj(struct hw **rhw) hw->daio_mgr_set_imapaddr = daio_mgr_set_imapaddr; hw->daio_mgr_commit_write = daio_mgr_commit_write; + hw->set_timer_irq = set_timer_irq; + hw->set_timer_tick = set_timer_tick; + *rhw = hw; 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 @@ */ #include "ctpcm.h" +#include "cttimer.h" #include /* Hardware descriptions for playback */ @@ -108,6 +109,7 @@ static void ct_atc_pcm_free_substream(struct snd_pcm_runtime *runtime) struct ct_atc *atc = snd_pcm_substream_chip(apcm->substream); atc->pcm_release_resources(atc, apcm); + ct_timer_instance_free(apcm->timer); kfree(apcm); runtime->private_data = NULL; } @@ -124,8 +126,6 @@ static int ct_pcm_playback_open(struct snd_pcm_substream *substream) if (NULL == apcm) return -ENOMEM; - spin_lock_init(&apcm->timer_lock); - apcm->stop_timer = 0; apcm->substream = substream; apcm->interrupt = ct_atc_pcm_interrupt; runtime->private_data = apcm; @@ -153,6 +153,10 @@ static int ct_pcm_playback_open(struct snd_pcm_substream *substream) return err; } + apcm->timer = ct_timer_instance_new(atc->timer, apcm); + if (!apcm->timer) + return -ENOMEM; + return 0; } @@ -182,89 +186,6 @@ static int ct_pcm_hw_free(struct snd_pcm_substream *substream) return snd_pcm_lib_free_pages(substream); } -static void ct_pcm_timer_callback(unsigned long data) -{ - struct ct_atc_pcm *apcm = (struct ct_atc_pcm *)data; - struct snd_pcm_substream *substream = apcm->substream; - struct snd_pcm_runtime *runtime = substream->runtime; - unsigned int period_size = runtime->period_size; - unsigned int buffer_size = runtime->buffer_size; - unsigned long flags; - unsigned int position = 0, dist = 0, interval = 0; - - position = substream->ops->pointer(substream); - dist = (position + buffer_size - apcm->position) % buffer_size; - if ((dist >= period_size) || - (position/period_size != apcm->position/period_size)) { - apcm->interrupt(apcm); - apcm->position = position; - } - /* Add extra HZ*5/1000 to avoid overrun issue when recording - * at 8kHz in 8-bit format or at 88kHz in 24-bit format. */ - interval = ((period_size - (position % period_size)) - * HZ + (runtime->rate - 1)) / runtime->rate + HZ * 5 / 1000; - spin_lock_irqsave(&apcm->timer_lock, flags); - apcm->timer.expires = jiffies + interval; - if (!apcm->stop_timer) - add_timer(&apcm->timer); - - spin_unlock_irqrestore(&apcm->timer_lock, flags); -} - -static int ct_pcm_timer_prepare(struct ct_atc_pcm *apcm) -{ - unsigned long flags; - - spin_lock_irqsave(&apcm->timer_lock, flags); - if (timer_pending(&apcm->timer)) { - /* The timer has already been started. */ - spin_unlock_irqrestore(&apcm->timer_lock, flags); - return 0; - } - - init_timer(&apcm->timer); - apcm->timer.data = (unsigned long)apcm; - apcm->timer.function = ct_pcm_timer_callback; - spin_unlock_irqrestore(&apcm->timer_lock, flags); - apcm->position = 0; - - return 0; -} - -static int ct_pcm_timer_start(struct ct_atc_pcm *apcm) -{ - struct snd_pcm_runtime *runtime = apcm->substream->runtime; - unsigned long flags; - - spin_lock_irqsave(&apcm->timer_lock, flags); - if (timer_pending(&apcm->timer)) { - /* The timer has already been started. */ - spin_unlock_irqrestore(&apcm->timer_lock, flags); - return 0; - } - - apcm->timer.expires = jiffies + (runtime->period_size * HZ + - (runtime->rate - 1)) / runtime->rate; - apcm->stop_timer = 0; - add_timer(&apcm->timer); - spin_unlock_irqrestore(&apcm->timer_lock, flags); - - return 0; -} - -static int ct_pcm_timer_stop(struct ct_atc_pcm *apcm) -{ - unsigned long flags; - - spin_lock_irqsave(&apcm->timer_lock, flags); - apcm->stop_timer = 1; - del_timer(&apcm->timer); - spin_unlock_irqrestore(&apcm->timer_lock, flags); - - try_to_del_timer_sync(&apcm->timer); - - return 0; -} static int ct_pcm_playback_prepare(struct snd_pcm_substream *substream) { @@ -283,8 +204,6 @@ static int ct_pcm_playback_prepare(struct snd_pcm_substream *substream) return err; } - ct_pcm_timer_prepare(apcm); - return 0; } @@ -300,12 +219,10 @@ ct_pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd) case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: atc->pcm_playback_start(atc, apcm); - ct_pcm_timer_start(apcm); break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - ct_pcm_timer_stop(apcm); atc->pcm_playback_stop(atc, apcm); break; default: @@ -341,9 +258,7 @@ static int ct_pcm_capture_open(struct snd_pcm_substream *substream) if (NULL == apcm) return -ENOMEM; - spin_lock_init(&apcm->timer_lock); apcm->started = 0; - apcm->stop_timer = 0; apcm->substream = substream; apcm->interrupt = ct_atc_pcm_interrupt; runtime->private_data = apcm; @@ -365,6 +280,10 @@ static int ct_pcm_capture_open(struct snd_pcm_substream *substream) return err; } + apcm->timer = ct_timer_instance_new(atc->timer, apcm); + if (!apcm->timer) + return -ENOMEM; + return 0; } @@ -388,8 +307,6 @@ static int ct_pcm_capture_prepare(struct snd_pcm_substream *substream) return err; } - ct_pcm_timer_prepare(apcm); - return 0; } @@ -403,14 +320,11 @@ ct_pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd) switch (cmd) { case SNDRV_PCM_TRIGGER_START: atc->pcm_capture_start(atc, apcm); - ct_pcm_timer_start(apcm); break; case SNDRV_PCM_TRIGGER_STOP: - ct_pcm_timer_stop(apcm); atc->pcm_capture_stop(atc, apcm); break; default: - ct_pcm_timer_stop(apcm); atc->pcm_capture_stop(atc, apcm); break; } 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 @@ +/* + * PCM timer handling on ctxfi + * + * This source file is released under GPL v2 license (no other versions). + * See the COPYING file included in the main directory of this source + * distribution for the license terms and conditions. + */ + +#include +#include +#include +#include "ctatc.h" +#include "cthardware.h" +#include "cttimer.h" + +struct ct_timer_ops { + void (*init)(struct ct_timer_instance *); + void (*prepare)(struct ct_timer_instance *); + void (*start)(struct ct_timer_instance *); + void (*stop)(struct ct_timer_instance *); + void (*free_instance)(struct ct_timer_instance *); + void (*interrupt)(struct ct_timer *); + void (*free_global)(struct ct_timer *); +}; + +/* timer instance -- assigned to each PCM stream */ +struct ct_timer_instance { + spinlock_t lock; + struct ct_timer *timer_base; + struct ct_atc_pcm *apcm; + struct snd_pcm_substream *substream; + struct timer_list timer; + struct list_head instance_list; + struct list_head running_list; + unsigned int position; + unsigned int frag_count; + unsigned int running:1; + unsigned int need_update:1; +}; + +/* timer instance manager */ +struct ct_timer { + spinlock_t lock; /* global timer lock (for xfitimer) */ + spinlock_t list_lock; /* lock for instance list */ + struct ct_atc *atc; + struct ct_timer_ops *ops; + struct list_head instance_head; + struct list_head running_head; + unsigned int irq_handling:1; /* in IRQ handling */ + unsigned int reprogram:1; /* need to reprogram the internval */ + unsigned int running:1; /* global timer running */ +}; + + +/* + * system-timer-based updates + */ + +static void ct_systimer_callback(unsigned long data) +{ + struct ct_timer_instance *ti = (struct ct_timer_instance *)data; + struct snd_pcm_substream *substream = ti->substream; + struct snd_pcm_runtime *runtime = substream->runtime; + struct ct_atc_pcm *apcm = ti->apcm; + unsigned int period_size = runtime->period_size; + unsigned int buffer_size = runtime->buffer_size; + unsigned long flags; + unsigned int position, dist, interval; + + position = substream->ops->pointer(substream); + dist = (position + buffer_size - ti->position) % buffer_size; + if (dist >= period_size || + position / period_size != ti->position / period_size) { + apcm->interrupt(apcm); + ti->position = position; + } + /* Add extra HZ*5/1000 to avoid overrun issue when recording + * at 8kHz in 8-bit format or at 88kHz in 24-bit format. */ + interval = ((period_size - (position % period_size)) + * HZ + (runtime->rate - 1)) / runtime->rate + HZ * 5 / 1000; + spin_lock_irqsave(&ti->lock, flags); + if (ti->running) + mod_timer(&ti->timer, jiffies + interval); + spin_unlock_irqrestore(&ti->lock, flags); +} + +static void ct_systimer_init(struct ct_timer_instance *ti) +{ + setup_timer(&ti->timer, ct_systimer_callback, + (unsigned long)ti); +} + +static void ct_systimer_start(struct ct_timer_instance *ti) +{ + struct snd_pcm_runtime *runtime = ti->substream->runtime; + unsigned long flags; + + spin_lock_irqsave(&ti->lock, flags); + ti->running = 1; + mod_timer(&ti->timer, + jiffies + (runtime->period_size * HZ + + (runtime->rate - 1)) / runtime->rate); + spin_unlock_irqrestore(&ti->lock, flags); +} + +static void ct_systimer_stop(struct ct_timer_instance *ti) +{ + unsigned long flags; + + spin_lock_irqsave(&ti->lock, flags); + ti->running = 0; + del_timer(&ti->timer); + spin_unlock_irqrestore(&ti->lock, flags); +} + +static void ct_systimer_prepare(struct ct_timer_instance *ti) +{ + ct_systimer_stop(ti); + try_to_del_timer_sync(&ti->timer); +} + +#define ct_systimer_free ct_systimer_prepare + +static struct ct_timer_ops ct_systimer_ops = { + .init = ct_systimer_init, + .free_instance = ct_systimer_free, + .prepare = ct_systimer_prepare, + .start = ct_systimer_start, + .stop = ct_systimer_stop, +}; + + +/* + * Handling multiple streams using a global emu20k1 timer irq + */ + +#define CT_TIMER_FREQ 48000 +#define MAX_TICKS ((1 << 13) - 1) + +static void ct_xfitimer_irq_rearm(struct ct_timer *atimer, int ticks) +{ + struct hw *hw = atimer->atc->hw; + if (ticks > MAX_TICKS) + ticks = MAX_TICKS; + hw->set_timer_tick(hw, ticks); + if (!atimer->running) + hw->set_timer_irq(hw, 1); + atimer->running = 1; +} + +static void ct_xfitimer_irq_stop(struct ct_timer *atimer) +{ + if (atimer->running) { + struct hw *hw = atimer->atc->hw; + hw->set_timer_irq(hw, 0); + hw->set_timer_tick(hw, 0); + atimer->running = 0; + } +} + +/* + * reprogram the timer interval; + * checks the running instance list and determines the next timer interval. + * also updates the each stream position, returns the number of streams + * to call snd_pcm_period_elapsed() appropriately + * + * call this inside the lock and irq disabled + */ +static int ct_xfitimer_reprogram(struct ct_timer *atimer) +{ + struct ct_timer_instance *ti; + int min_intr = -1; + int updates = 0; + + list_for_each_entry(ti, &atimer->running_head, running_list) { + struct snd_pcm_runtime *runtime; + unsigned int pos, diff; + int intr; + runtime = ti->substream->runtime; + pos = ti->substream->ops->pointer(ti->substream); + if (pos < ti->position) + diff = runtime->buffer_size - ti->position + pos; + else + diff = pos - ti->position; + ti->position = pos; + while (diff >= ti->frag_count) { + ti->frag_count += runtime->period_size; + ti->need_update = 1; + updates++; + } + ti->frag_count -= diff; + intr = div_u64((u64)ti->frag_count * CT_TIMER_FREQ, + runtime->rate); + if (min_intr < 0 || intr < min_intr) + min_intr = intr; + } + + if (min_intr > 0) + ct_xfitimer_irq_rearm(atimer, min_intr); + else + ct_xfitimer_irq_stop(atimer); + + atimer->reprogram = 0; /* clear flag */ + return updates; +} + +/* look through the instance list and call period_elapsed if needed */ +static void ct_xfitimer_check_period(struct ct_timer *atimer) +{ + struct ct_timer_instance *ti; + unsigned long flags; + + spin_lock_irqsave(&atimer->list_lock, flags); + list_for_each_entry(ti, &atimer->instance_head, instance_list) { + if (ti->need_update) { + ti->need_update = 0; + ti->apcm->interrupt(ti->apcm); + } + } + spin_unlock_irqrestore(&atimer->list_lock, flags); +} + +/* Handle timer-interrupt */ +static void ct_xfitimer_callback(struct ct_timer *atimer) +{ + int update; + unsigned long flags; + + spin_lock_irqsave(&atimer->lock, flags); + atimer->irq_handling = 1; + do { + update = ct_xfitimer_reprogram(atimer); + spin_unlock(&atimer->lock); + if (update) + ct_xfitimer_check_period(atimer); + spin_lock(&atimer->lock); + } while (atimer->reprogram); + atimer->irq_handling = 0; + spin_unlock_irqrestore(&atimer->lock, flags); +} + +static void ct_xfitimer_prepare(struct ct_timer_instance *ti) +{ + ti->frag_count = ti->substream->runtime->period_size; + ti->need_update = 0; +} + + +/* start/stop the timer */ +static void ct_xfitimer_update(struct ct_timer *atimer) +{ + unsigned long flags; + int update; + + if (atimer->irq_handling) { + /* reached from IRQ handler; let it handle later */ + atimer->reprogram = 1; + return; + } + + spin_lock_irqsave(&atimer->lock, flags); + ct_xfitimer_irq_stop(atimer); + update = ct_xfitimer_reprogram(atimer); + spin_unlock_irqrestore(&atimer->lock, flags); + if (update) + ct_xfitimer_check_period(atimer); +} + +static void ct_xfitimer_start(struct ct_timer_instance *ti) +{ + struct ct_timer *atimer = ti->timer_base; + unsigned long flags; + + spin_lock_irqsave(&atimer->lock, flags); + list_add(&ti->running_list, &atimer->running_head); + spin_unlock_irqrestore(&atimer->lock, flags); + ct_xfitimer_update(atimer); +} + +static void ct_xfitimer_stop(struct ct_timer_instance *ti) +{ + struct ct_timer *atimer = ti->timer_base; + unsigned long flags; + + spin_lock_irqsave(&atimer->lock, flags); + list_del_init(&ti->running_list); + ti->need_update = 0; + spin_unlock_irqrestore(&atimer->lock, flags); + ct_xfitimer_update(atimer); +} + +static void ct_xfitimer_free_global(struct ct_timer *atimer) +{ + ct_xfitimer_irq_stop(atimer); +} + +static struct ct_timer_ops ct_xfitimer_ops = { + .prepare = ct_xfitimer_prepare, + .start = ct_xfitimer_start, + .stop = ct_xfitimer_stop, + .interrupt = ct_xfitimer_callback, + .free_global = ct_xfitimer_free_global, +}; + +/* + * timer instance + */ + +struct ct_timer_instance * +ct_timer_instance_new(struct ct_timer *atimer, struct ct_atc_pcm *apcm) +{ + struct ct_timer_instance *ti; + + ti = kzalloc(sizeof(*ti), GFP_KERNEL); + if (!ti) + return NULL; + spin_lock_init(&ti->lock); + INIT_LIST_HEAD(&ti->instance_list); + INIT_LIST_HEAD(&ti->running_list); + ti->timer_base = atimer; + ti->apcm = apcm; + ti->substream = apcm->substream; + if (atimer->ops->init) + atimer->ops->init(ti); + + spin_lock_irq(&atimer->list_lock); + list_add(&ti->instance_list, &atimer->instance_head); + spin_unlock_irq(&atimer->list_lock); + + return ti; +} + +void ct_timer_prepare(struct ct_timer_instance *ti) +{ + if (ti->timer_base->ops->prepare) + ti->timer_base->ops->prepare(ti); + ti->position = 0; + ti->running = 0; +} + +void ct_timer_start(struct ct_timer_instance *ti) +{ + struct ct_timer *atimer = ti->timer_base; + atimer->ops->start(ti); +} + +void ct_timer_stop(struct ct_timer_instance *ti) +{ + struct ct_timer *atimer = ti->timer_base; + atimer->ops->stop(ti); +} + +void ct_timer_instance_free(struct ct_timer_instance *ti) +{ + struct ct_timer *atimer = ti->timer_base; + + atimer->ops->stop(ti); /* to be sure */ + if (atimer->ops->free_instance) + atimer->ops->free_instance(ti); + + spin_lock_irq(&atimer->list_lock); + list_del(&ti->instance_list); + spin_unlock_irq(&atimer->list_lock); + + kfree(ti); +} + +/* + * timer manager + */ + +#define USE_SYSTEM_TIMER 0 + +static void ct_timer_interrupt(void *data, unsigned int status) +{ + struct ct_timer *timer = data; + + /* Interval timer interrupt */ + if ((status & IT_INT) && timer->ops->interrupt) + timer->ops->interrupt(timer); +} + +struct ct_timer *ct_timer_new(struct ct_atc *atc) +{ + struct ct_timer *atimer; + struct hw *hw; + + atimer = kzalloc(sizeof(*atimer), GFP_KERNEL); + if (!atimer) + return NULL; + spin_lock_init(&atimer->lock); + spin_lock_init(&atimer->list_lock); + INIT_LIST_HEAD(&atimer->instance_head); + INIT_LIST_HEAD(&atimer->running_head); + atimer->atc = atc; + hw = atc->hw; + if (!USE_SYSTEM_TIMER && hw->set_timer_irq) { + printk(KERN_INFO "ctxfi: Use xfi-native timer\n"); + atimer->ops = &ct_xfitimer_ops; + hw->irq_callback_data = atimer; + hw->irq_callback = ct_timer_interrupt; + } else { + printk(KERN_INFO "ctxfi: Use system timer\n"); + atimer->ops = &ct_systimer_ops; + } + return atimer; +} + +void ct_timer_free(struct ct_timer *atimer) +{ + struct hw *hw = atimer->atc->hw; + hw->irq_callback = NULL; + if (atimer->ops->free_global) + atimer->ops->free_global(atimer); + kfree(atimer); +} + 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 @@ +/* + * Timer handling + */ + +#ifndef __CTTIMER_H +#define __CTTIMER_H + +#include +#include +#include + +struct snd_pcm_substream; +struct ct_atc; +struct ct_atc_pcm; + +struct ct_timer; +struct ct_timer_instance; + +struct ct_timer *ct_timer_new(struct ct_atc *atc); +void ct_timer_free(struct ct_timer *atimer); + +struct ct_timer_instance * +ct_timer_instance_new(struct ct_timer *atimer, struct ct_atc_pcm *apcm); +void ct_timer_instance_free(struct ct_timer_instance *ti); +void ct_timer_start(struct ct_timer_instance *ti); +void ct_timer_stop(struct ct_timer_instance *ti); +void ct_timer_prepare(struct ct_timer_instance *ti); + +#endif /* __CTTIMER_H */